From f9679fa4df238fff9060178ccca9adcb6a795a7d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 8 Jul 2026 19:07:34 +0000 Subject: [PATCH 01/31] Split IncrCompSession out of Session This will allow introducing a separate incr comp session dir for the post LTO artifacts in the future. In addition it statically encodes the lifetime of the incr comp session rather than requiring an enum behind a mutex stored in the Session. --- compiler/rustc_codegen_cranelift/src/lib.rs | 5 +- compiler/rustc_codegen_gcc/src/lib.rs | 5 +- compiler/rustc_codegen_llvm/src/lib.rs | 5 +- compiler/rustc_codegen_ssa/src/back/write.rs | 25 ++++- .../rustc_codegen_ssa/src/traits/backend.rs | 3 +- compiler/rustc_driver_impl/src/lib.rs | 4 +- compiler/rustc_incremental/src/persist/fs.rs | 48 ++++---- .../rustc_incremental/src/persist/load.rs | 105 ++++++++++-------- .../rustc_incremental/src/persist/save.rs | 24 ++-- .../src/persist/work_product.rs | 13 ++- compiler/rustc_interface/src/passes.rs | 16 ++- compiler/rustc_interface/src/queries.rs | 20 +++- compiler/rustc_interface/src/util.rs | 3 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 5 +- compiler/rustc_session/src/session.rs | 65 ++--------- src/librustdoc/doctest.rs | 55 ++++----- src/librustdoc/lib.rs | 33 +++--- .../codegen-backend/auxiliary/the_backend.rs | 3 +- tests/ui-fulldeps/run-compiler-twice.rs | 11 +- 20 files changed, 235 insertions(+), 215 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index ba586f83ba30d..8ee0e71d82dec 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -41,8 +41,8 @@ use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo, TargetConfig, back}; use rustc_log::tracing::info; use rustc_middle::dep_graph::WorkProductMap; -use rustc_session::Session; use rustc_session::config::{NATIVE_CPU, OutputFilenames}; +use rustc_session::{IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, CfgAbi, Env, Os}; @@ -233,13 +233,14 @@ impl CodegenBackend for CraneliftCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, + incr_comp_session: Option<&IncrCompSession>, _outputs: &OutputFilenames, crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap) { ongoing_codegen .downcast::>() .unwrap() - .join(sess, crate_info) + .join(sess, incr_comp_session, crate_info) } fn fallback_intrinsics(&self) -> Vec { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 4cc4a2d258d14..c570f4e2165b2 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -94,8 +94,8 @@ use rustc_errors::{DiagCtxt, DiagCtxtHandle}; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; -use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames}; +use rustc_session::{IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{Arch, RelocModel}; use tempfile::TempDir; @@ -297,13 +297,14 @@ impl CodegenBackend for GccCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, + incr_comp_session: Option<&IncrCompSession>, _outputs: &OutputFilenames, crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap) { ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") - .join(sess, crate_info) + .join(sess, incr_comp_session, crate_info) } fn target_config(&self, sess: &Session) -> TargetConfig { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1dd460c409737..3c095d9e07ad0 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -38,8 +38,8 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; -use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; +use rustc_session::{IncrCompSession, Session}; use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; @@ -379,13 +379,14 @@ impl CodegenBackend for LlvmCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, + incr_comp_session: Option<&IncrCompSession>, outputs: &OutputFilenames, crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap) { let (compiled_modules, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") - .join(sess, crate_info); + .join(sess, incr_comp_session, crate_info); if sess.opts.unstable_opts.llvm_time_trace { sess.time("llvm_dump_timing_file", || { diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1db2321f7b249..750a4e8c3bde6 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -21,11 +21,11 @@ use rustc_metadata::fs::copy_to_stdout; use rustc_middle::bug; use rustc_middle::dep_graph::{WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; use rustc_session::config::{ self, CrateType, Lto, OptLevel, OutFileName, OutputFilenames, OutputType, Passes, SwitchWithOptPath, }; +use rustc_session::{IncrCompSession, Session}; use rustc_span::source_map::SourceMap; use rustc_span::{FileName, InnerSpan, Span, SpanData}; use rustc_target::spec::{MergeFunctions, SanitizerSet}; @@ -461,6 +461,7 @@ pub(crate) fn start_async_codegen( fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( sess: &Session, + incr_comp_session: Option<&IncrCompSession>, compiled_modules: &CompiledModules, ) -> WorkProductMap { let mut work_products = WorkProductMap::default(); @@ -494,6 +495,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( } let (id, product) = copy_cgu_workproduct_to_incr_comp_cache_dir( sess, + incr_comp_session.unwrap(), &module.name, files.as_slice(), &module.links_from_incr_cache, @@ -1286,7 +1288,10 @@ fn start_executing_work( time_trace: sess.opts.unstable_opts.llvm_time_trace, remark: sess.opts.cg.remark.clone(), remark_dir, - incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + incr_comp_session_dir: tcx + .incr_comp_session + .as_ref() + .map(|incr_comp_session| incr_comp_session.session_directory.clone()), output_filenames: Arc::clone(tcx.output_filenames(())), module_config: regular_config, opt_level, @@ -2118,7 +2123,12 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session, crate_info: &CrateInfo) -> (CompiledModules, WorkProductMap) { + pub fn join( + self, + sess: &Session, + incr_comp_session: Option<&IncrCompSession>, + crate_info: &CrateInfo, + ) -> (CompiledModules, WorkProductMap) { self.shared_emitter_main.check(sess, true); let maybe_lto_modules = sess.time("join_worker_thread", || match self.coordinator.join() { @@ -2196,8 +2206,11 @@ impl OngoingCodegen { // out deterministic results. compiled_modules.modules.sort_by(|a, b| a.name.cmp(&b.name)); - let work_products = - copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, &compiled_modules); + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess, + incr_comp_session, + &compiled_modules, + ); produce_final_output_artifacts(sess, &compiled_modules, &self.output_filenames); (compiled_modules, work_products) @@ -2249,7 +2262,7 @@ pub(crate) fn submit_pre_lto_module_to_llvm( module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); - let bitcode_path = in_incr_comp_dir_sess(tcx.sess, &filename); + let bitcode_path = in_incr_comp_dir_sess(tcx.incr_comp_session.unwrap(), &filename); // Schedule the module to be loaded drop( coordinator diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 6014f1af4bfc3..85882af9e7cd0 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -8,8 +8,8 @@ use rustc_metadata::creader::MetadataLoaderDyn; use rustc_middle::dep_graph::WorkProductMap; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; -use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, PrintRequest}; +use rustc_session::{IncrCompSession, Session}; use rustc_span::Symbol; use super::CodegenObject; @@ -127,6 +127,7 @@ pub trait CodegenBackend { &self, ongoing_codegen: Box, sess: &Session, + incr_comp_session: Option<&IncrCompSession>, outputs: &OutputFilenames, crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 4411ecb4f128b..6274397fe2b6a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -336,8 +336,8 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Linking is done outside the `compiler.enter()` so that the // `GlobalCtxt` within `Queries` can be freed as early as possible. - if let Some(linker) = linker { - linker.link(sess, codegen_backend); + if let (Some(linker), incr_comp_session) = linker { + linker.link(sess, incr_comp_session, codegen_backend); } }) } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index c40aa49c29d11..de543ef0c53bc 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -116,7 +116,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; use rustc_middle::bug; -use rustc_session::{Session, StableCrateId}; +use rustc_session::{IncrCompSession, Session, StableCrateId}; use rustc_span::Symbol; use tracing::debug; @@ -138,25 +138,25 @@ const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; const INT_ENCODE_BASE: usize = base_n::CASE_INSENSITIVE; /// Returns the path to a session's dependency graph. -pub(crate) fn dep_graph_path(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, DEP_GRAPH_FILENAME) +pub(crate) fn dep_graph_path(incr_comp_session: &IncrCompSession) -> PathBuf { + in_incr_comp_dir_sess(incr_comp_session, DEP_GRAPH_FILENAME) } /// Returns the path to a session's staging dependency graph. /// /// On the difference between dep-graph and staging dep-graph, /// see `build_dep_graph`. -pub(crate) fn staging_dep_graph_path(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, STAGING_DEP_GRAPH_FILENAME) +pub(crate) fn staging_dep_graph_path(incr_comp_session: &IncrCompSession) -> PathBuf { + in_incr_comp_dir_sess(incr_comp_session, STAGING_DEP_GRAPH_FILENAME) } -pub(crate) fn work_products_path(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, WORK_PRODUCTS_FILENAME) +pub(crate) fn work_products_path(incr_comp_session: &IncrCompSession) -> PathBuf { + in_incr_comp_dir_sess(incr_comp_session, WORK_PRODUCTS_FILENAME) } /// Returns the path to a session's query cache. -pub(crate) fn query_cache_path(sess: &Session) -> PathBuf { - in_incr_comp_dir_sess(sess, QUERY_CACHE_FILENAME) +pub(crate) fn query_cache_path(incr_comp_session: &IncrCompSession) -> PathBuf { + in_incr_comp_dir_sess(incr_comp_session, QUERY_CACHE_FILENAME) } /// Locks a given session directory. @@ -183,8 +183,8 @@ fn lock_file_path(session_dir: &Path) -> PathBuf { /// Returns the path for a given filename within the incremental compilation directory /// in the current session. -pub fn in_incr_comp_dir_sess(sess: &Session, file_name: &str) -> PathBuf { - sess.incr_comp_session_dir().join(file_name) +pub fn in_incr_comp_dir_sess(incr_comp_session: &IncrCompSession, file_name: &str) -> PathBuf { + incr_comp_session.session_directory.join(file_name) } /// Allocates the private session directory. @@ -206,7 +206,7 @@ pub(crate) fn prepare_session_directory( sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId, -) { +) -> IncrCompSession { assert!(sess.opts.incremental.is_some()); let _timer = sess.timer("incr_comp_prepare_session_directory"); @@ -257,8 +257,7 @@ pub(crate) fn prepare_session_directory( directory." ); - sess.init_incr_comp_session(session_dir, directory_lock); - return; + return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; }; debug!("attempting to copy data from source: {}", source_directory.display()); @@ -271,8 +270,7 @@ pub(crate) fn prepare_session_directory( sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir }); } - sess.init_incr_comp_session(session_dir, directory_lock); - return; + return IncrCompSession { session_directory: session_dir, _lock_file: directory_lock }; } else { debug!("copying failed - trying next directory"); @@ -295,18 +293,23 @@ pub(crate) fn prepare_session_directory( /// This function finalizes and thus 'publishes' the session directory by /// renaming it to `s-{timestamp}-{svh}` and releasing the file lock. /// This must not be called if there have been any compilation errors. -pub fn finalize_session_directory(sess: &Session, svh: Option) { +pub fn finalize_session_directory( + sess: &Session, + incr_comp_session: Option, + svh: Option, +) { assert!(sess.dcx().has_errors_or_delayed_bugs().is_none()); if sess.opts.incremental.is_none() { return; } + let incr_comp_session = incr_comp_session.unwrap(); // The svh is always produced when incr. comp. is enabled. let svh = svh.unwrap(); let _timer = sess.timer("incr_comp_finalize_session_directory"); - let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone(); + let incr_comp_session_dir = incr_comp_session.session_directory.clone(); debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display()); @@ -342,14 +345,15 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { } } - // This unlocks the directory - sess.finalize_incr_comp_session(); + drop(incr_comp_session); // Unlock incr comp session dir let _ = garbage_collect_session_directories(sess, &new_path); } -pub(crate) fn delete_all_session_dir_contents(sess: &Session) -> io::Result<()> { - let sess_dir_iterator = sess.incr_comp_session_dir().read_dir()?; +pub(crate) fn delete_all_session_dir_contents( + incr_comp_session: &IncrCompSession, +) -> io::Result<()> { + let sess_dir_iterator = incr_comp_session.session_directory.read_dir()?; for entry in sess_dir_iterator { let entry = entry?; safe_remove_file(&entry.path())? diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 352ee59aaa0d4..3cf08961ed7b3 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -11,7 +11,7 @@ use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Encodable}; use rustc_session::config::IncrementalStateAssertion; -use rustc_session::{Session, StableCrateId}; +use rustc_session::{IncrCompSession, Session, StableCrateId}; use rustc_span::Symbol; use tracing::{debug, warn}; @@ -32,56 +32,55 @@ enum LoadResult { IoError { path: PathBuf, err: io::Error }, } -fn delete_dirty_work_product(sess: &Session, swp: SerializedWorkProduct) { +fn delete_dirty_work_product( + sess: &Session, + incr_comp_session: &IncrCompSession, + swp: SerializedWorkProduct, +) { debug!("delete_dirty_work_product({:?})", swp); - work_product::delete_workproduct_files(sess, &swp.work_product); + work_product::delete_workproduct_files(sess, incr_comp_session, &swp.work_product); } -fn load_dep_graph(sess: &Session) -> LoadResult { +fn load_dep_graph(sess: &Session, incr_comp_session: &IncrCompSession) -> LoadResult { assert!(sess.opts.incremental.is_some()); let _timer = sess.prof.generic_activity("incr_comp_prepare_load_dep_graph"); // Calling `sess.incr_comp_session_dir()` will panic if `sess.opts.incremental.is_none()`. // Fortunately, we just checked that this isn't the case. - let path = dep_graph_path(sess); + let path = dep_graph_path(incr_comp_session); let expected_hash = sess.opts.dep_tracking_hash(false); let mut prev_work_products = UnordMap::default(); - // If we are only building with -Zquery-dep-graph but without an actual - // incr. comp. session directory, we skip this. Otherwise we'd fail - // when trying to load work products. - if sess.incr_comp_session_dir_opt().is_some() { - let work_products_path = work_products_path(sess); - - if let Ok(OpenFile { mmap, start_pos }) = - file_format::open_incremental_file(sess, &work_products_path) - { - // Decode the list of work_products - let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else { - sess.dcx().emit_warn(diagnostics::CorruptFile { path: &work_products_path }); - return LoadResult::DataOutOfDate; - }; - let work_products: Vec = - Decodable::decode(&mut work_product_decoder); - - for swp in work_products { - let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { - let exists = in_incr_comp_dir_sess(sess, path).exists(); - if !exists && sess.opts.unstable_opts.incremental_info { - eprintln!("incremental: could not find file for work product: {path}",); - } - exists - }); - - if all_files_exist { - debug!("reconcile_work_products: all files for {:?} exist", swp); - prev_work_products.insert(swp.id, swp.work_product); - } else { - debug!("reconcile_work_products: some file for {:?} does not exist", swp); - delete_dirty_work_product(sess, swp); + let work_products_path = work_products_path(incr_comp_session); + + if let Ok(OpenFile { mmap, start_pos }) = + file_format::open_incremental_file(sess, &work_products_path) + { + // Decode the list of work_products + let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else { + sess.dcx().emit_warn(diagnostics::CorruptFile { path: &work_products_path }); + return LoadResult::DataOutOfDate; + }; + let work_products: Vec = + Decodable::decode(&mut work_product_decoder); + + for swp in work_products { + let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { + let exists = in_incr_comp_dir_sess(incr_comp_session, path).exists(); + if !exists && sess.opts.unstable_opts.incremental_info { + eprintln!("incremental: could not find file for work product: {path}",); } + exists + }); + + if all_files_exist { + debug!("reconcile_work_products: all files for {:?} exist", swp); + prev_work_products.insert(swp.id, swp.work_product); + } else { + debug!("reconcile_work_products: some file for {:?} does not exist", swp); + delete_dirty_work_product(sess, incr_comp_session, swp); } } } @@ -124,14 +123,18 @@ fn load_dep_graph(sess: &Session) -> LoadResult { /// If we are not in incremental compilation mode, returns `None`. /// Otherwise, tries to load the query result cache from disk, /// creating an empty cache if it could not be loaded. -pub fn load_query_result_cache(sess: &Session) -> Option { +pub fn load_query_result_cache( + sess: &Session, + incr_comp_session: Option<&IncrCompSession>, +) -> Option { if sess.opts.incremental.is_none() { return None; } + let incr_comp_session = incr_comp_session.unwrap(); let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); - let path = query_cache_path(sess); + let path = query_cache_path(incr_comp_session); match file_format::open_incremental_file(sess, &path) { Ok(OpenFile { mmap, start_pos }) => { let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| { @@ -181,18 +184,20 @@ pub fn setup_dep_graph( sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId, -) -> DepGraph { +) -> (DepGraph, Option) { if sess.opts.incremental.is_none() { - return DepGraph::new_disabled(); + return (DepGraph::new_disabled(), None); } // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess, crate_name, stable_crate_id); + let incr_comp_session = prepare_session_directory(sess, crate_name, stable_crate_id); // Try to load the previous session's dep graph and work products. - let load_result = load_dep_graph(sess); + let load_result = load_dep_graph(sess, &incr_comp_session); sess.time("incr_comp_garbage_collect_session_directories", || { - if let Err(e) = garbage_collect_session_directories(sess, &sess.incr_comp_session_dir()) { + if let Err(e) = + garbage_collect_session_directories(sess, &incr_comp_session.session_directory) + { warn!( "Error while trying to garbage collect incremental compilation \ cache directory: {e}", @@ -209,9 +214,11 @@ pub fn setup_dep_graph( Default::default() } LoadResult::DataOutOfDate => { - if let Err(err) = delete_all_session_dir_contents(sess) { - sess.dcx() - .emit_err(diagnostics::DeleteIncompatible { path: dep_graph_path(sess), err }); + if let Err(err) = delete_all_session_dir_contents(&incr_comp_session) { + sess.dcx().emit_err(diagnostics::DeleteIncompatible { + path: dep_graph_path(&incr_comp_session), + err, + }); } Default::default() } @@ -219,7 +226,7 @@ pub fn setup_dep_graph( }; // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors. - let path_buf = staging_dep_graph_path(sess); + let path_buf = staging_dep_graph_path(&incr_comp_session); let mut encoder = FileEncoder::new(&path_buf).unwrap_or_else(|err| { // We're in incremental mode but couldn't set up streaming output of the dep graph. @@ -232,5 +239,5 @@ pub fn setup_dep_graph( // First encode the commandline arguments hash sess.opts.dep_tracking_hash(false).encode(&mut encoder); - DepGraph::new(sess, prev_graph, prev_work_products, encoder) + (DepGraph::new(sess, prev_graph, prev_work_products, encoder), Some(incr_comp_session)) } diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 544ab66766f39..12f674fe2a859 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -6,7 +6,7 @@ use rustc_middle::query::on_disk_cache; use rustc_middle::ty::TyCtxt; use rustc_serialize::Encodable as RustcEncodable; use rustc_serialize::opaque::FileEncoder; -use rustc_session::Session; +use rustc_session::{IncrCompSession, Session}; use tracing::debug; use super::data::*; @@ -34,9 +34,10 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { return; } - let query_cache_path = query_cache_path(sess); - let dep_graph_path = dep_graph_path(sess); - let staging_dep_graph_path = staging_dep_graph_path(sess); + let incr_comp_session = tcx.incr_comp_session.unwrap(); + let query_cache_path = query_cache_path(incr_comp_session); + let dep_graph_path = dep_graph_path(incr_comp_session); + let staging_dep_graph_path = staging_dep_graph_path(incr_comp_session); sess.time("assert_dep_graph", || assert_dep_graph(tcx)); sess.time("check_clean", || clean::check_clean_annotations(tcx)); @@ -91,6 +92,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { /// Saves the work product index. pub fn save_work_product_index( sess: &Session, + incr_comp_session: Option<&IncrCompSession>, dep_graph: &DepGraph, new_work_products: WorkProductMap, ) { @@ -104,7 +106,7 @@ pub fn save_work_product_index( debug!("save_work_product_index()"); dep_graph.assert_ignored(); - let path = work_products_path(sess); + let path = work_products_path(incr_comp_session.unwrap()); file_format::save_in(sess, path, "work product index", |mut e| { encode_work_product_index(&new_work_products, &mut e); e.finish() @@ -116,9 +118,13 @@ pub fn save_work_product_index( let previous_work_products = dep_graph.previous_work_products(); for (id, wp) in previous_work_products.to_sorted_stable_ord() { if !new_work_products.contains_key(id) { - work_product::delete_workproduct_files(sess, wp); + work_product::delete_workproduct_files(sess, incr_comp_session.unwrap(), wp); debug_assert!( - !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess( + incr_comp_session.unwrap(), + path + ) + .exists()) ); } } @@ -126,7 +132,9 @@ pub fn save_work_product_index( // Check that we did not delete one of the current work-products: debug_assert!({ new_work_products.items().all(|(_, wp)| { - wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + wp.saved_files + .items() + .all(|(_, path)| in_incr_comp_dir_sess(incr_comp_session.unwrap(), path).exists()) }) }); } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 910860bfafd6e..7bb66fee4d1a3 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -8,7 +8,7 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; -use rustc_session::Session; +use rustc_session::{IncrCompSession, Session}; use tracing::debug; use crate::diagnostics; @@ -20,6 +20,7 @@ use crate::persist::fs::*; /// Panics when incr comp is disabled. pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, + incr_comp_session: &IncrCompSession, cgu_name: &str, files: &[(&'static str, &Path)], known_links: &[PathBuf], @@ -30,7 +31,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( let mut saved_files = UnordMap::default(); for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); - let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); + let path_in_incr_dir = in_incr_comp_dir_sess(incr_comp_session, &file_name); if known_links.contains(&path_in_incr_dir) { let _ = saved_files.insert(ext.to_string(), file_name); continue; @@ -56,9 +57,13 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( } /// Removes files for a given work product. -pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { +pub(crate) fn delete_workproduct_files( + sess: &Session, + incr_comp_session: &IncrCompSession, + work_product: &WorkProduct, +) { for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { - let path = in_incr_comp_dir_sess(sess, path); + let path = in_incr_comp_dir_sess(incr_comp_session, path); if let Err(err) = std_fs::remove_file(&path) { sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2f32a6b208b6c..498b59cf5be9f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -35,12 +35,12 @@ use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; -use rustc_session::Session; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::diagnostics::feature_err; use rustc_session::output::{filename_for_input, invalid_output_for_target}; use rustc_session::search_paths::PathKind; +use rustc_session::{IncrCompSession, Session}; use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; @@ -929,7 +929,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( compiler: &Compiler, krate: rustc_ast::Crate, f: F, -) -> T { +) -> (T, Option) { let sess = &compiler.sess; let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); @@ -951,7 +951,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id); + let (dep_graph, incr_comp_session) = setup_dep_graph(sess, crate_name, stable_crate_id); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -966,7 +966,8 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( // incr. comp. yet. dep_graph.assert_ignored(); - let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); + let query_result_on_disk_cache = + rustc_incremental::load_query_result_cache(sess, incr_comp_session.as_ref()); let codegen_backend = &compiler.codegen_backend; let mut providers = *DEFAULT_QUERY_PROVIDERS; @@ -993,7 +994,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( let arena = WorkerLocal::new(|_| Arena::default()); let hir_arena = WorkerLocal::new(|_| rustc_hir::Arena::default()); - TyCtxt::create_global_ctxt( + let res = TyCtxt::create_global_ctxt( &gcx_cell, &compiler.sess, crate_types, @@ -1001,6 +1002,7 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( &arena, &hir_arena, untracked, + incr_comp_session.as_ref(), dep_graph, rustc_query_impl::make_dep_kind_vtables(&arena), rustc_query_impl::query_system( @@ -1046,7 +1048,9 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( tcx.finish(); res }, - ) + ); + + (res, incr_comp_session) } struct DiagCallback<'tcx> { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 24e033bdee088..490888f87b38e 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -9,8 +9,8 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{DepGraph, WorkProductMap}; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; use rustc_session::config::{self, OutputFilenames, OutputType}; +use rustc_session::{IncrCompSession, Session}; use crate::diagnostics::FailedWritingFile; use crate::passes; @@ -46,7 +46,12 @@ impl Linker { } } - pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) { + pub fn link( + self, + sess: &Session, + incr_comp_session: Option, + codegen_backend: &dyn CodegenBackend, + ) { let (compiled_modules, mut work_products) = sess.time("finish_ongoing_codegen", || { match self.ongoing_codegen.downcast::() { // This was a check only build @@ -55,6 +60,7 @@ impl Linker { Err(ongoing_codegen) => codegen_backend.join_codegen( ongoing_codegen, sess, + incr_comp_session.as_ref(), &self.output_filenames, &self.crate_info, ), @@ -92,6 +98,7 @@ impl Linker { { let (id, product) = rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, + incr_comp_session.as_ref().unwrap(), "metadata", &[("rmeta", path)], &[], @@ -106,7 +113,12 @@ impl Linker { let _timer = sess.timer("link"); sess.time("serialize_work_products", || { - rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products) + rustc_incremental::save_work_product_index( + sess, + incr_comp_session.as_ref(), + &self.dep_graph, + work_products, + ) }); let prof = sess.prof.clone(); @@ -114,7 +126,7 @@ impl Linker { // Now that we won't touch anything in the incremental compilation directory // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(sess, self.crate_hash); + rustc_incremental::finalize_session_directory(sess, incr_comp_session, self.crate_hash); if !sess .opts diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 39c5ee8193256..7b6166c8ac9c6 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -23,7 +23,7 @@ use rustc_query_impl::{CollectActiveJobsKind, collect_active_query_jobs}; use rustc_session::config::{ Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple, }; -use rustc_session::{EarlyDiagCtxt, Session, filesearch}; +use rustc_session::{EarlyDiagCtxt, IncrCompSession, Session, filesearch}; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; use rustc_span::{SessionGlobals, Symbol, sym}; @@ -413,6 +413,7 @@ impl CodegenBackend for DummyCodegenBackend { &self, ongoing_codegen: Box, _sess: &Session, + _incr_comp_session: Option<&IncrCompSession>, _outputs: &OutputFilenames, _crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8fa0c1b2dcdd8..c1dd65370d278 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2471,7 +2471,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { let saved_path = &work_product.saved_files["rmeta"]; - let incr_comp_session_dir = tcx.sess.incr_comp_session_dir(); + let incr_comp_session_dir = &tcx.incr_comp_session.unwrap().session_directory; let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path); debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}"); match rustc_fs_util::link_or_copy(&source_file_in_incr_dir, path) { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 4ae165cb015bd..92596787097a3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -36,10 +36,10 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; use rustc_macros::Diagnostic; -use rustc_session::Session; use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; +use rustc_session::{IncrCompSession, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; @@ -712,6 +712,7 @@ pub struct GlobalCtxt<'tcx> { /// `rustc_symbol_mangling` crate for more information. stable_crate_id: StableCrateId, + pub incr_comp_session: Option<&'tcx IncrCompSession>, pub dep_graph: DepGraph, pub prof: SelfProfilerRef, @@ -935,6 +936,7 @@ impl<'tcx> TyCtxt<'tcx> { arena: &'tcx WorkerLocal>, hir_arena: &'tcx WorkerLocal>, untracked: Untracked, + incr_comp_session: Option<&'tcx IncrCompSession>, dep_graph: DepGraph, dep_kind_vtables: &'tcx [DepKindVTable<'tcx>], query_system: QuerySystem<'tcx>, @@ -957,6 +959,7 @@ impl<'tcx> TyCtxt<'tcx> { arena, hir_arena, interners, + incr_comp_session, dep_graph, hooks, prof: sess.prof.clone(), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index eebead6fc1f47..975b09f84cb0d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -7,9 +7,7 @@ use std::{env, io}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; -use rustc_data_structures::sync::{ - AppendOnlyVec, DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock, -}; +use rustc_data_structures::sync::{AppendOnlyVec, DynSend, DynSync, Lock}; use rustc_data_structures::{Limit, flock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::codes::*; @@ -341,8 +339,6 @@ pub struct Session { /// Input, input file path and output file path to this compilation process. pub io: CompilerIO, - incr_comp_session: RwLock, - /// Used by `-Z self-profile`. pub prof: SelfProfilerRef, @@ -688,45 +684,6 @@ impl Session { } } - pub fn init_incr_comp_session(&self, session_dir: PathBuf, lock_file: flock::Lock) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::NotInitialized = *incr_comp_session { - } else { - panic!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session) - } - - *incr_comp_session = - IncrCompSession::Active { session_directory: session_dir, _lock_file: lock_file }; - } - - pub fn finalize_incr_comp_session(&self) { - let mut incr_comp_session = self.incr_comp_session.borrow_mut(); - - if let IncrCompSession::Active { .. } = *incr_comp_session { - } else { - panic!("trying to finalize `IncrCompSession` `{:?}`", *incr_comp_session); - } - - // Note: this will also drop the lock file, thus unlocking the directory. - *incr_comp_session = IncrCompSession::FinalizedOrRemoved; - } - - pub fn incr_comp_session_dir(&self) -> MappedReadGuard<'_, PathBuf> { - let incr_comp_session = self.incr_comp_session.borrow(); - ReadGuard::map(incr_comp_session, |incr_comp_session| match incr_comp_session { - IncrCompSession::NotInitialized | IncrCompSession::FinalizedOrRemoved => panic!( - "trying to get session directory from `IncrCompSession`: {:?}", - incr_comp_session, - ), - IncrCompSession::Active { session_directory, .. } => session_directory, - }) - } - - pub fn incr_comp_session_dir_opt(&self) -> Option> { - self.opts.incremental.as_ref().map(|_| self.incr_comp_session_dir()) - } - /// Is this edition 2015? pub fn is_rust_2015(&self) -> bool { self.edition().is_rust_2015() @@ -1355,7 +1312,6 @@ pub fn build_session( check_config: CheckCfg::default(), proc_macro_quoted_spans: Default::default(), io, - incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, timings, code_stats: Default::default(), @@ -1689,20 +1645,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } /// Holds data on the current incremental compilation session, if there is one. -#[derive(Debug)] -enum IncrCompSession { - /// This is the state the session will be in until the incr. comp. dir is - /// needed. - NotInitialized, - /// This is the state during which the session directory is private and can - /// be modified. `_lock_file` is never directly used, but its presence +pub struct IncrCompSession { + /// The directory containing all cached data. Cached data from a previous + /// session can be read out of it and new data for the current session will + /// be written into it. + pub session_directory: PathBuf, + /// `_lock_file` is never directly used, but its presence /// alone has an effect, because the file will unlock when the session is /// dropped. - Active { session_directory: PathBuf, _lock_file: flock::Lock }, - /// This is the state after the session directory has been finalized or - /// removed after errors. In this state, the contents of the directory must - /// not be modified any more. - FinalizedOrRemoved, + pub _lock_file: flock::Lock, } /// A wrapper around an [`DiagCtxt`] that is used for early error emissions. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 2b7f9c4dbb7fa..7ba409626ea89 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -217,34 +217,37 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions let result = interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); - let collector = rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { - let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); - let opts = scrape_test_config(tcx, crate_name, args_path); - - let hir_collector = HirCollector::new( - ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), - tcx, - ); - let tests = hir_collector.collect_crate(); - if extract_doctests { - let mut collector = extracted::ExtractedDocTests::new(); - tests.into_iter().for_each(|t| collector.add_test(t, &opts, &options)); - - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - if let Err(error) = serde_json::ser::to_writer(&mut stdout, &collector) { - eprintln!(); - Err(format!("Failed to generate JSON output for doctests: {error:?}")) + let (collector, _incr_comp_session) = + rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { + let crate_name = tcx.crate_name(LOCAL_CRATE).to_string(); + let opts = scrape_test_config(tcx, crate_name, args_path); + + let hir_collector = HirCollector::new( + ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()), + tcx, + ); + let tests = hir_collector.collect_crate(); + if extract_doctests { + let mut collector = extracted::ExtractedDocTests::new(); + tests.into_iter().for_each(|t| collector.add_test(t, &opts, &options)); + + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + if let Err(error) = serde_json::ser::to_writer(&mut stdout, &collector) { + eprintln!(); + Err(format!("Failed to generate JSON output for doctests: {error:?}")) + } else { + Ok(None) + } } else { - Ok(None) - } - } else { - let mut collector = CreateRunnableDocTests::new(options, opts); - tests.into_iter().for_each(|t| collector.add_test(t, Some(compiler.sess.dcx()))); + let mut collector = CreateRunnableDocTests::new(options, opts); + tests + .into_iter() + .for_each(|t| collector.add_test(t, Some(compiler.sess.dcx()))); - Ok(Some(collector)) - } - }); + Ok(Some(collector)) + } + }); compiler.sess.dcx().abort_if_errors(); collector diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index be830cad6c735..5fddb432edcd1 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -882,21 +882,24 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { id: ast::DUMMY_NODE_ID, is_placeholder: false, }; - rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { - let has_dep_info = render_options.dep_info().is_some(); - if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) { - markdown::render_and_write(file, render_options, edition)?; - } - if has_dep_info { - // Register the loaded external files in the source map so they show up in depinfo. - // We can't load them via the source map because it gets created after we process the options. - for external_path in &loaded_paths { - let _ = compiler.sess.source_map().load_binary_file(external_path); + let (res, _incr_comp_session) = + rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { + let has_dep_info = render_options.dep_info().is_some(); + if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) { + markdown::render_and_write(file, render_options, edition)?; } - rustc_interface::passes::write_dep_info(tcx); - } - Ok(()) - }) + if has_dep_info { + // Register the loaded external files in the source map so they show up in depinfo. + // We can't load them via the source map because it gets created after we process the options. + for external_path in &loaded_paths { + let _ = + compiler.sess.source_map().load_binary_file(external_path); + } + rustc_interface::passes::write_dep_info(tcx); + } + Ok(()) + }); + res }), ); } @@ -1005,7 +1008,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { unreachable!() } } - }) + }); }) } diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 610a4990a5a4b..5ddaed75aa323 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -16,8 +16,8 @@ use rustc_codegen_ssa::{CompiledModules, CrateInfo}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProductMap; use rustc_middle::ty::TyCtxt; -use rustc_session::Session; use rustc_session::config::OutputFilenames; +use rustc_session::{IncrCompSession, Session}; struct TheBackend; @@ -38,6 +38,7 @@ impl CodegenBackend for TheBackend { &self, ongoing_codegen: Box, _sess: &Session, + _incr_comp_session: Option<&IncrCompSession>, _outputs: &OutputFilenames, _crate_info: &CrateInfo, ) -> (CompiledModules, WorkProductMap) { diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index d99d9c42d547f..ae0f41a205bf4 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -76,10 +76,11 @@ fn compile(code: String, output: PathBuf, sysroot: Sysroot, linker: Option<&Path interface::run_compiler(config, |compiler| { let krate = rustc_interface::passes::parse(&compiler.sess); - let linker = rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { - let _ = tcx.analysis(()); - Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend) - }); - linker.link(&compiler.sess, &*compiler.codegen_backend); + let (linker, incr_comp_session) = + rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| { + let _ = tcx.analysis(()); + Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend) + }); + linker.link(&compiler.sess, incr_comp_session, &*compiler.codegen_backend); }); } From 0439f4ef09af50b440af245a0f2173442fbcd567 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Thu, 23 Jul 2026 18:32:21 +0100 Subject: [PATCH 02/31] Hint that memchr returns an in-bounds index --- library/core/src/slice/memchr.rs | 7 ++++++- library/coretests/tests/slice.rs | 11 +++++++++++ .../codegen-llvm/lib-optimizations/memchr-result.rs | 13 +++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/lib-optimizations/memchr-result.rs diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 1e1053583a617..6762015181d85 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -28,7 +28,12 @@ pub const fn memchr(x: u8, text: &[u8]) -> Option { return memchr_naive(x, text); } - memchr_aligned(x, text) + let result = memchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result } #[inline] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index a4db7304fff90..b05f54d4df0a2 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -1781,6 +1781,17 @@ pub mod memchr { assert_eq!(None, memchr(b'a', b"xyz")); } + #[test] + fn each_alignment() { + let mut data = [1u8; 64]; + let needle = 2; + let pos = 40; + data[pos] = needle; + for start in 0..16 { + assert_eq!(Some(pos - start), memchr(needle, &data[start..])); + } + } + #[test] fn matches_one_reversed() { assert_eq!(Some(0), memrchr(b'a', b"a")); diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs new file mode 100644 index 0000000000000..fbdbdcc3fe9f3 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -0,0 +1,13 @@ +// Ensure `memchr` communicates that a returned index is in bounds. +//@ compile-flags: -Copt-level=3 -Zinline-mir=false +//@ only-64bit + +#![crate_type = "lib"] + +// CHECK-LABEL: @find_char +#[no_mangle] +pub fn find_char(haystack: &str, needle: char) -> Option { + // CHECK-NOT: phi { i64, i64 } + // CHECK: ret { i64, i64 } + haystack.find(needle) +} From c1f36d5f4bde0f955e9d0cbdec406d22b5044360 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:29:23 +0100 Subject: [PATCH 03/31] Hint that memrchr returns an in-bounds index --- library/core/src/slice/memchr.rs | 10 ++++++++++ .../codegen-llvm/lib-optimizations/memchr-result.rs | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 6762015181d85..c83e8b218da08 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -112,8 +112,18 @@ const fn memchr_aligned(x: u8, text: &[u8]) -> Option { } /// Returns the last index matching the byte `x` in `text`. +#[inline] #[must_use] pub fn memrchr(x: u8, text: &[u8]) -> Option { + let result = memrchr_aligned(x, text); + if let Some(index) = result { + // SAFETY: `memrchr_aligned` only returns the index of a matching byte in `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + result +} + +fn memrchr_aligned(x: u8, text: &[u8]) -> Option { // Scan for a single byte value by reading two `usize` words at a time. // // Split `text` in three parts: diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs index fbdbdcc3fe9f3..f18335075451c 100644 --- a/tests/codegen-llvm/lib-optimizations/memchr-result.rs +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -3,6 +3,11 @@ //@ only-64bit #![crate_type = "lib"] +#![feature(slice_internals)] + +extern crate core; + +use core::slice::memchr::memrchr; // CHECK-LABEL: @find_char #[no_mangle] @@ -11,3 +16,11 @@ pub fn find_char(haystack: &str, needle: char) -> Option { // CHECK: ret { i64, i64 } haystack.find(needle) } + +// CHECK-LABEL: @rfind_byte +#[no_mangle] +pub fn rfind_byte(haystack: &[u8], needle: u8) -> Option { + // CHECK-NOT: panic_bounds_check + // CHECK: ret { i1, i8 } + memrchr(needle, haystack).map(|index| haystack[index]) +} From 3313cd6f7fae3ad4f4673ae88fb38c0faad2a8b9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 19 Jun 2026 18:53:44 +0100 Subject: [PATCH 04/31] std: fix stack buffer overflow in Windows junction_point The guard checked `data_len > u16::MAX`, allowing paths far larger than `PathBuffer` (a fixed 16384-element array), which the subsequent single `copy_from` then overflows. Bound against `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` plus header instead, matching the kernel's limit. --- library/std/src/fs/tests.rs | 19 ++++++++++++++++ library/std/src/sys/fs/windows.rs | 37 +++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 4f2fa7fbc591e..c3fdb2bf741e3 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1723,6 +1723,25 @@ fn create_dir_all_with_junctions() { assert!(d.exists()); } +#[test] +#[cfg(windows)] +fn junction_point_overlong_path() { + // Regression test: an `original` path long enough to exceed the inline + // reparse buffer used to be copied past the end of the stack array. It must + // now be rejected with a clean error instead of overflowing. + let tmpdir = tmpdir(); + let link = tmpdir.join("junction"); + + // The `\\?\` prefix bypasses MAX_PATH normalization so the path is copied + // through verbatim. 20_000 code units lands in the old overflow window: it + // passed the previous `> u16::MAX` byte check yet exceeded the buffer. + let mut original = String::from(r"\\?\C:\"); + original.push_str(&"a".repeat(20_000)); + + let err = junction_point(Path::new(&original), &link).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InvalidInput); +} + #[test] fn metadata_access_times() { let start_time = SystemTime::now(); diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index e3e7b081b47d5..67cd8e8612344 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1676,33 +1676,46 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { SubstituteNameLength: u16, PrintNameOffset: u16, PrintNameLength: u16, - PathBuffer: [MaybeUninit; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + // `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` is a size in bytes, but this is a + // buffer of `u16`s, so it holds half as many elements. + PathBuffer: [MaybeUninit; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize / 2], } - let data_len = 12 + (abs_path.len() * 2); - if data_len > u16::MAX as usize { - return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); - } - let data_len = data_len as u16; let mut header = MountPointBuffer { ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT, - ReparseDataLength: data_len, + ReparseDataLength: 0, // filled in below Reserved: 0, SubstituteNameOffset: 0, SubstituteNameLength: (abs_path.len() * 2) as u16, + // The print name follows the substitute name and its null terminator. PrintNameOffset: ((abs_path.len() + 1) * 2) as u16, PrintNameLength: 0, - PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize], + PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize / 2], }; + // A mount point reparse point requires both the substitute name and the + // (empty) print name to be null terminated, even though their lengths are + // explicit. Bounds-check and copy in a single step so an over-long path + // fails cleanly instead of overflowing the buffer. + let Some(path_buffer) = header.PathBuffer.get_mut(..abs_path.len() + 2) else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long")); + }; + let (substitute_name, terminators) = path_buffer.split_at_mut(abs_path.len()); + substitute_name.write_copy_of_slice(&abs_path); + terminators.write_copy_of_slice(&[0, 0]); + // Total size of the structure: the fixed header fields, the path, and the + // two null terminators. + let total_len = offset_of!(MountPointBuffer, PathBuffer) + (abs_path.len() + 2) * 2; + // `ReparseDataLength` counts only the bytes after the 8-byte common header + // (`ReparseTag`, `ReparseDataLength`, `Reserved`), i.e. + // `SubstituteNameLength + PrintNameLength + 12`. + header.ReparseDataLength = + (total_len - offset_of!(MountPointBuffer, SubstituteNameOffset)) as u16; unsafe { - let ptr = header.PathBuffer.as_mut_ptr(); - ptr.copy_from(abs_path.as_ptr().cast_uninit(), abs_path.len()); - let mut ret = 0; cvt(c::DeviceIoControl( d.as_raw_handle(), c::FSCTL_SET_REPARSE_POINT, (&raw const header).cast::(), - data_len as u32 + 8, + total_len as u32, ptr::null_mut(), 0, &mut ret, From 844c01e43be5782643646d73a6f65539db046a33 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:14:14 +0000 Subject: [PATCH 05/31] Cover memchr fast path with bounds assertion --- library/core/src/slice/memchr.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index c83e8b218da08..017661f0448c2 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,13 +24,13 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - if text.len() < 2 * USIZE_BYTES { - return memchr_naive(x, text); - } - - let result = memchr_aligned(x, text); + let result = if text.len() < 2 * USIZE_BYTES { + memchr_naive(x, text) + } else { + memchr_aligned(x, text) + }; if let Some(index) = result { - // SAFETY: `memchr_aligned` only returns the index of a matching byte in `text`. + // SAFETY: Both implementations only return an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; } result From 49c1f02279a37b85fcd9448dc7b87e20923f57dd Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:32:44 +0000 Subject: [PATCH 06/31] Fix memchr result CI checks --- library/core/src/slice/memchr.rs | 7 ++----- tests/codegen-llvm/lib-optimizations/memchr-result.rs | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index 017661f0448c2..fb99e86139d7e 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,11 +24,8 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - let result = if text.len() < 2 * USIZE_BYTES { - memchr_naive(x, text) - } else { - memchr_aligned(x, text) - }; + let result = + if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; if let Some(index) = result { // SAFETY: Both implementations only return an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; diff --git a/tests/codegen-llvm/lib-optimizations/memchr-result.rs b/tests/codegen-llvm/lib-optimizations/memchr-result.rs index f18335075451c..77abc33adde83 100644 --- a/tests/codegen-llvm/lib-optimizations/memchr-result.rs +++ b/tests/codegen-llvm/lib-optimizations/memchr-result.rs @@ -1,6 +1,6 @@ // Ensure `memchr` communicates that a returned index is in bounds. //@ compile-flags: -Copt-level=3 -Zinline-mir=false -//@ only-64bit +//@ only-x86_64 #![crate_type = "lib"] #![feature(slice_internals)] From 807750a1fcdb31f4bf527089ff44cf95ac199046 Mon Sep 17 00:00:00 2001 From: SomeFlyingThing <306498559+SomeFlyingThing@users.noreply.github.com> Date: Tue, 28 Jul 2026 20:57:45 +0000 Subject: [PATCH 07/31] Preserve memchr codegen on LLVM 21 --- library/core/src/slice/memchr.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/library/core/src/slice/memchr.rs b/library/core/src/slice/memchr.rs index fb99e86139d7e..68826ecac31f3 100644 --- a/library/core/src/slice/memchr.rs +++ b/library/core/src/slice/memchr.rs @@ -24,10 +24,18 @@ const fn contains_zero_byte(x: usize) -> bool { #[must_use] pub const fn memchr(x: u8, text: &[u8]) -> Option { // Fast path for small slices. - let result = - if text.len() < 2 * USIZE_BYTES { memchr_naive(x, text) } else { memchr_aligned(x, text) }; + if text.len() < 2 * USIZE_BYTES { + let result = memchr_naive(x, text); + if let Some(index) = result { + // SAFETY: `memchr_naive` only returns an index from within `text`. + unsafe { crate::hint::assert_unchecked(index < text.len()) }; + } + return result; + } + + let result = memchr_aligned(x, text); if let Some(index) = result { - // SAFETY: Both implementations only return an index from within `text`. + // SAFETY: `memchr_aligned` only returns an index from within `text`. unsafe { crate::hint::assert_unchecked(index < text.len()) }; } result From 7232830d10b6af772e0e4670a2ff61dd23830ed8 Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Fri, 3 Jul 2026 11:45:09 +0300 Subject: [PATCH 08/31] std: move futex implementations into sys::sync::futex Pure file moves; the module path repointing and platform gating follow in the next commit. Recorded in .git-blame-ignore-revs so blame skips the rename. --- library/std/src/sys/{pal/hermit/futex.rs => sync/futex/hermit.rs} | 0 library/std/src/sys/{pal/unix/futex.rs => sync/futex/unix.rs} | 0 .../sys/{pal/wasi/wasilibc_futex.rs => sync/futex/wasilibc.rs} | 0 .../std/src/sys/{pal/wasm/atomics/futex.rs => sync/futex/wasm.rs} | 0 .../std/src/sys/{pal/windows/futex.rs => sync/futex/windows.rs} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename library/std/src/sys/{pal/hermit/futex.rs => sync/futex/hermit.rs} (100%) rename library/std/src/sys/{pal/unix/futex.rs => sync/futex/unix.rs} (100%) rename library/std/src/sys/{pal/wasi/wasilibc_futex.rs => sync/futex/wasilibc.rs} (100%) rename library/std/src/sys/{pal/wasm/atomics/futex.rs => sync/futex/wasm.rs} (100%) rename library/std/src/sys/{pal/windows/futex.rs => sync/futex/windows.rs} (100%) diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/sync/futex/hermit.rs similarity index 100% rename from library/std/src/sys/pal/hermit/futex.rs rename to library/std/src/sys/sync/futex/hermit.rs diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/sync/futex/unix.rs similarity index 100% rename from library/std/src/sys/pal/unix/futex.rs rename to library/std/src/sys/sync/futex/unix.rs diff --git a/library/std/src/sys/pal/wasi/wasilibc_futex.rs b/library/std/src/sys/sync/futex/wasilibc.rs similarity index 100% rename from library/std/src/sys/pal/wasi/wasilibc_futex.rs rename to library/std/src/sys/sync/futex/wasilibc.rs diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/sync/futex/wasm.rs similarity index 100% rename from library/std/src/sys/pal/wasm/atomics/futex.rs rename to library/std/src/sys/sync/futex/wasm.rs diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/sync/futex/windows.rs similarity index 100% rename from library/std/src/sys/pal/windows/futex.rs rename to library/std/src/sys/sync/futex/windows.rs From 5b40f3d400ed3ebb3794b1c9911d3640267635fc Mon Sep 17 00:00:00 2001 From: Valentyn Kit Date: Fri, 3 Jul 2026 11:59:56 +0300 Subject: [PATCH 09/31] std: connect sys::sync::futex and drop the pal declarations Select the platform implementation with a cfg_select! in sys::sync::futex, repoint each one at the pal primitives it uses (time, fuchsia, the windows api module, hermit_abi), and remove the now-unused futex declarations from the pal modules. The sync primitives import crate::sys::sync::futex rather than the crate::sys::futex glob re-export. --- .git-blame-ignore-revs | 3 ++ library/std/src/sys/pal/hermit/mod.rs | 1 - library/std/src/sys/pal/motor/mod.rs | 2 - library/std/src/sys/pal/unix/mod.rs | 1 - library/std/src/sys/pal/wasi/mod.rs | 18 --------- library/std/src/sys/pal/wasm/mod.rs | 4 -- library/std/src/sys/pal/windows/mod.rs | 2 - library/std/src/sys/sync/condvar/futex.rs | 2 +- library/std/src/sys/sync/futex/hermit.rs | 2 +- library/std/src/sys/sync/futex/mod.rs | 39 +++++++++++++++++++ library/std/src/sys/sync/futex/unix.rs | 20 +++------- library/std/src/sys/sync/futex/windows.rs | 2 +- library/std/src/sys/sync/mod.rs | 1 + library/std/src/sys/sync/mutex/futex.rs | 2 +- library/std/src/sys/sync/once/futex.rs | 2 +- library/std/src/sys/sync/rwlock/futex.rs | 2 +- .../std/src/sys/sync/thread_parking/futex.rs | 2 +- 17 files changed, 55 insertions(+), 50 deletions(-) create mode 100644 library/std/src/sys/sync/futex/mod.rs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index af071c706856e..4e2bef94982cc 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -33,3 +33,6 @@ c682aa162b0d41e21cc6748f4fecfe01efb69d1f 1fcae03369abb4c2cc180cd5a49e1f4440a81300 # Breaking up of compiletest runtest.rs 60600a6fa403216bfd66e04f948b1822f6450af7 + +# std: move futex implementations into sys::sync::futex +7232830d10b6af772e0e4670a2ff61dd23830ed8 diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 53f6ddd7065d7..e8c9bf70b99df 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -21,7 +21,6 @@ use crate::os::hermit::hermit_abi; use crate::os::raw::c_char; use crate::sys::env; -pub mod futex; #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index ac10d81ecfb89..5bf217db9013a 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -1,7 +1,5 @@ #![allow(unsafe_op_in_unsafe_fn)] -pub use moto_rt::futex; - use crate::io; pub(crate) fn map_motor_error(err: moto_rt::Error) -> io::Error { diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 2bd28ba498370..8fca169d93119 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -5,7 +5,6 @@ use crate::io; pub mod conf; #[cfg(target_os = "fuchsia")] pub mod fuchsia; -pub mod futex; pub mod stack_overflow; pub mod sync; pub mod thread_parking; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 9069d0f0064a7..056f632ae0be2 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -11,24 +11,6 @@ pub mod stack_overflow; #[path = "../unix/time.rs"] pub mod time; -// The wasi-libc based futex is new enough that it's not present in older -// wasi-libc builds. For now that means it's only required on wasip3 (which -// requires a newer wasi-libc anyway). In the future this'll probably switch to -// unconditionally using `wasilibc_futex` as the implementation for all WASI -// targets (and switching all synchronization primitives to the futex version). -cfg_select! { - target_env = "p3" => { - pub mod wasilibc_futex; - pub use wasilibc_futex as futex; - } - target_feature = "atomics" => { - #[allow(unused)] - #[path = "../wasm/atomics/futex.rs"] - pub mod futex; - } - _ => {} -} - #[cfg(not(target_env = "p1"))] mod cabi_realloc; diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 24a2ab8eca30f..72e5982fc0732 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -16,10 +16,6 @@ #![deny(unsafe_op_in_unsafe_fn)] -#[cfg(target_feature = "atomics")] -#[path = "atomics/futex.rs"] -pub mod futex; - #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] mod common; diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index b67ba37749789..4fa8c1b9a1323 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -15,8 +15,6 @@ pub mod compat; pub mod api; pub mod c; -#[cfg(not(target_vendor = "win7"))] -pub mod futex; pub mod handle; pub mod time; cfg_select! { diff --git a/library/std/src/sys/sync/condvar/futex.rs b/library/std/src/sys/sync/condvar/futex.rs index 0d0c5f0dbe701..b5b82e4c38257 100644 --- a/library/std/src/sys/sync/condvar/futex.rs +++ b/library/std/src/sys/sync/condvar/futex.rs @@ -1,6 +1,6 @@ use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::futex::{Futex, futex_wait, futex_wake, futex_wake_all}; use crate::sys::sync::Mutex; +use crate::sys::sync::futex::{Futex, futex_wait, futex_wake, futex_wake_all}; use crate::time::Duration; pub struct Condvar { diff --git a/library/std/src/sys/sync/futex/hermit.rs b/library/std/src/sys/sync/futex/hermit.rs index 78c86071fdd53..783052526c525 100644 --- a/library/std/src/sys/sync/futex/hermit.rs +++ b/library/std/src/sys/sync/futex/hermit.rs @@ -1,4 +1,4 @@ -use super::hermit_abi; +use crate::os::hermit::hermit_abi; use crate::ptr::null; use crate::sync::atomic::Atomic; use crate::time::Duration; diff --git a/library/std/src/sys/sync/futex/mod.rs b/library/std/src/sys/sync/futex/mod.rs new file mode 100644 index 0000000000000..0edb46cc10f86 --- /dev/null +++ b/library/std/src/sys/sync/futex/mod.rs @@ -0,0 +1,39 @@ +cfg_select! { + any( + target_os = "linux", + target_os = "android", + all(target_os = "emscripten", target_feature = "atomics"), + target_os = "freebsd", + target_os = "openbsd", + target_os = "dragonfly", + target_os = "fuchsia", + ) => { + mod unix; + pub use unix::*; + } + all(target_os = "windows", not(target_vendor = "win7")) => { + mod windows; + pub use windows::*; + } + target_os = "hermit" => { + mod hermit; + pub use hermit::*; + } + // The wasi-libc based futex is new enough that it's not present in older + // wasi-libc builds. For now that means it's only required on wasip3 (which + // requires a newer wasi-libc anyway). In the future this'll probably switch to + // unconditionally using `wasilibc` as the implementation for all WASI + // targets (and switching all synchronization primitives to the futex version). + all(target_os = "wasi", target_env = "p3") => { + mod wasilibc; + pub use wasilibc::*; + } + all(target_family = "wasm", target_feature = "atomics") => { + mod wasm; + pub use wasm::*; + } + target_os = "motor" => { + pub use moto_rt::futex::*; + } + _ => {} +} diff --git a/library/std/src/sys/sync/futex/unix.rs b/library/std/src/sys/sync/futex/unix.rs index 2948d3d594eaa..16fda3ecbc7c3 100644 --- a/library/std/src/sys/sync/futex/unix.rs +++ b/library/std/src/sys/sync/futex/unix.rs @@ -1,13 +1,3 @@ -#![cfg(any( - target_os = "linux", - target_os = "android", - all(target_os = "emscripten", target_feature = "atomics"), - target_os = "freebsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "fuchsia", -))] - use crate::sync::atomic::Atomic; use crate::time::Duration; @@ -28,9 +18,9 @@ pub type SmallPrimitive = u32; /// Returns false on timeout, and true in all other cases. #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { - use super::time::Timespec; use crate::ptr::null; use crate::sync::atomic::Ordering::Relaxed; + use crate::sys::pal::time::Timespec; // Calculate the timeout as an absolute timespec. // @@ -149,8 +139,8 @@ pub fn futex_wake_all(futex: &Atomic) { #[cfg(target_os = "openbsd")] pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { - use super::time::Timespec; use crate::ptr::{null, null_mut}; + use crate::sys::pal::time::Timespec; // Overflows are rounded up to an infinite timeout (None). let timespec = timeout @@ -258,7 +248,7 @@ pub fn futex_wake_all(futex: &Atomic) { #[cfg(target_os = "fuchsia")] pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) -> bool { - use super::fuchsia::*; + use crate::sys::pal::fuchsia::*; // Sleep forever if the timeout is longer than fits in a i64. let deadline = timeout @@ -274,11 +264,11 @@ pub fn futex_wait(futex: &Atomic, expected: u32, timeout: Option) // Fuchsia doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "fuchsia")] pub fn futex_wake(futex: &Atomic) -> bool { - unsafe { super::fuchsia::zx_futex_wake(futex, 1) }; + unsafe { crate::sys::pal::fuchsia::zx_futex_wake(futex, 1) }; false } #[cfg(target_os = "fuchsia")] pub fn futex_wake_all(futex: &Atomic) { - unsafe { super::fuchsia::zx_futex_wake(futex, u32::MAX) }; + unsafe { crate::sys::pal::fuchsia::zx_futex_wake(futex, u32::MAX) }; } diff --git a/library/std/src/sys/sync/futex/windows.rs b/library/std/src/sys/sync/futex/windows.rs index cfa0a6b3815bd..eed0bb2548c1d 100644 --- a/library/std/src/sys/sync/futex/windows.rs +++ b/library/std/src/sys/sync/futex/windows.rs @@ -6,7 +6,7 @@ use core::sync::atomic::{ }; use core::time::Duration; -use super::api::{self, WinError}; +use crate::sys::pal::api::{self, WinError}; use crate::sys::{c, dur2timeout}; /// An atomic for use as a futex that is at least 32-bits but may be larger diff --git a/library/std/src/sys/sync/mod.rs b/library/std/src/sys/sync/mod.rs index 0691e96785198..8ee0b2649ed3d 100644 --- a/library/std/src/sys/sync/mod.rs +++ b/library/std/src/sys/sync/mod.rs @@ -1,4 +1,5 @@ mod condvar; +mod futex; mod mutex; mod once; mod once_box; diff --git a/library/std/src/sys/sync/mutex/futex.rs b/library/std/src/sys/sync/mutex/futex.rs index 70e2ea9f60586..015b5aacbc53b 100644 --- a/library/std/src/sys/sync/mutex/futex.rs +++ b/library/std/src/sys/sync/mutex/futex.rs @@ -1,5 +1,5 @@ use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::futex::{self, futex_wait, futex_wake}; +use crate::sys::sync::futex::{self, futex_wait, futex_wake}; type Futex = futex::SmallFutex; type State = futex::SmallPrimitive; diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 236bc9ca4b7c7..8f17f065669a8 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -2,7 +2,7 @@ use crate::cell::Cell; use crate::sync as public; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::sync::once::OnceExclusiveState; -use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake_all}; +use crate::sys::sync::futex::{Futex, Primitive, futex_wait, futex_wake_all}; // On some platforms, the OS is very nice and handles the waiter queue for us. // This means we only need one atomic value with 4 states: diff --git a/library/std/src/sys/sync/rwlock/futex.rs b/library/std/src/sys/sync/rwlock/futex.rs index 0e8e954de0758..c9389fe144b4d 100644 --- a/library/std/src/sys/sync/rwlock/futex.rs +++ b/library/std/src/sys/sync/rwlock/futex.rs @@ -1,5 +1,5 @@ use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sys::futex::{Futex, Primitive, futex_wait, futex_wake, futex_wake_all}; +use crate::sys::sync::futex::{Futex, Primitive, futex_wait, futex_wake, futex_wake_all}; pub struct RwLock { // The state consists of a 30-bit reader counter, a 'readers waiting' flag, and a 'writers waiting' flag. diff --git a/library/std/src/sys/sync/thread_parking/futex.rs b/library/std/src/sys/sync/thread_parking/futex.rs index c8f7f26386a01..691d839c41e6d 100644 --- a/library/std/src/sys/sync/thread_parking/futex.rs +++ b/library/std/src/sys/sync/thread_parking/futex.rs @@ -1,7 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] use crate::pin::Pin; use crate::sync::atomic::Ordering::{Acquire, Release}; -use crate::sys::futex::{self, futex_wait, futex_wake}; +use crate::sys::sync::futex::{self, futex_wait, futex_wake}; use crate::time::Duration; type Futex = futex::SmallFutex; From 012c35624ed80e409f2bbb301e2824d547f01ef1 Mon Sep 17 00:00:00 2001 From: Makro Date: Wed, 29 Jul 2026 09:00:38 +0000 Subject: [PATCH 10/31] Select cache values to verify by key fingerprint, not value fingerprint --- compiler/rustc_middle/src/dep_graph/graph.rs | 9 +++++++ compiler/rustc_query_impl/src/execution.rs | 26 +++++++++++++------- compiler/rustc_query_impl/src/plumbing.rs | 3 +-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 7892404badef3..b59fc263eec9a 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -705,6 +705,15 @@ impl DepGraphData { self.previous.value_fingerprint_for_index(prev_index) } + /// The number of incremental sessions in this graph's lineage, from + /// [`SerializedDepGraph::session_count`]. Advances by one per successful + /// session; a failed session does not commit a graph, so a re-run sees + /// the same count. + #[inline] + pub fn session_count(&self) -> u64 { + self.previous.session_count() + } + #[inline] pub(crate) fn prev_node_of(&self, prev_index: SerializedDepNodeIndex) -> &DepNode { self.previous.index_to_node(prev_index) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index a9192d0417712..a1d68fc0dc7ab 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -1,7 +1,7 @@ use std::hash::Hash; use std::mem::ManuallyDrop; -use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::{DynSend, DynSync}; @@ -490,12 +490,21 @@ fn execute_job_incr<'tcx, C: QueryCache>( /// specified, re-hash results from the cache and make sure that they have the /// expected fingerprint. /// -/// If not, we still seek to verify a subset of fingerprints loaded from disk. -/// Re-hashing results is fairly expensive, so we can't currently afford to -/// verify every hash. This subset should still give us some coverage of -/// potential bugs. -pub(crate) fn should_verify_loaded_value(tcx: TyCtxt<'_>, prev_fingerprint: Fingerprint) -> bool { - prev_fingerprint.split().1.as_u64().is_multiple_of(32) +/// If not, we still verify a subset: re-hashing is too expensive to do for +/// every value. The subset rotates with the session count, covering the whole +/// cache every 32 sessions, and is deterministic so that a verification +/// failure reproduces on retry. +/// +/// `to_smaller_hash` mixes both fingerprint halves because neither half is +/// evenly distributed on its own (`DefPathHash` keys share the +/// `StableCrateId`, `HirId` keys contain a sequential id). +pub(crate) fn should_verify_loaded_value( + tcx: TyCtxt<'_>, + dep_graph_data: &DepGraphData, + key_fingerprint: PackedFingerprint, +) -> bool { + let hash = Fingerprint::from(key_fingerprint).to_smaller_hash().as_u64(); + hash % 32 == dep_graph_data.session_count() % 32 || tcx.sess.opts.unstable_opts.incremental_verify_ich } @@ -532,8 +541,7 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( dep_graph_data.mark_debug_loaded_from_disk(*dep_node) } - let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); - let verify = should_verify_loaded_value(tcx, prev_fingerprint); + let verify = should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint); (value, verify) } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index c53293447040b..83badcb269af6 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -179,8 +179,7 @@ pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( // Verify the fingerprints of the same subset of loaded values as // `load_from_disk_or_invoke_provider_green` does. - let prev_fingerprint = dep_graph_data.prev_value_fingerprint_of(prev_index); - if should_verify_loaded_value(tcx, prev_fingerprint) { + if should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint) { incremental_verify_ich( tcx, dep_graph_data, From 833ec34ae8f7b582ea4f9202fd19b01943f89cbe Mon Sep 17 00:00:00 2001 From: jyn Date: Thu, 18 Jun 2026 10:36:08 +0200 Subject: [PATCH 11/31] [blocked] Link to proposed LLM policy in CONTRIBUTING and pull request template --- .github/pull_request_template.md | 11 +++++++++++ CONTRIBUTING.md | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 93388ddd24075..872c8a0ade1ab 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,16 @@ + +- [ ] I did not use an LLM to create a change in this PR. +- [ ] I used an LLM to create a change in this PR, and I have explained below how it was used. + $DIR/macro-determinacy-non-module-issue-160195.rs:12:22 + | +LL | include!(concat!(env!())); + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr new file mode 100644 index 0000000000000..cf8c7221c2367 --- /dev/null +++ b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.env_second.stderr @@ -0,0 +1,8 @@ +error: `env!()` takes 1 or 2 arguments + --> $DIR/macro-determinacy-non-module-issue-160195.rs:12:22 + | +LL | include!(concat!(env!())); + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs new file mode 100644 index 0000000000000..ca08a665e907b --- /dev/null +++ b/tests/ui/resolve/macro-determinacy-non-module-issue-160195.rs @@ -0,0 +1,21 @@ +//@ revisions: env_first env_second + +#[cfg(env_first)] +pub mod env { + #[derive(Default)] + pub struct BusinessData; +} + +pub mod interface { + use crate::env::{self}; + + include!(concat!(env!())); //~ ERROR `env!()` takes 1 or 2 arguments +} + +#[cfg(env_second)] +pub mod env { + #[derive(Default)] + pub struct BusinessData; +} + +fn main() {} From e0830fa2bec1da2e1ec8ba8c3d9eb4be332e8136 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 4 Aug 2026 16:01:41 +0200 Subject: [PATCH 16/31] implement unsafe speculative flag to be used by `CmRefCell::borrow`, which does tracked and untracked borrowing --- compiler/rustc_resolve/src/check_unused.rs | 2 +- .../rustc_resolve/src/diagnostics/impls.rs | 4 +- .../src/effective_visibilities.rs | 4 +- compiler/rustc_resolve/src/ident.rs | 25 +++--- compiler/rustc_resolve/src/imports.rs | 18 ++-- .../rustc_resolve/src/late/diagnostics.rs | 10 ++- compiler/rustc_resolve/src/lib.rs | 86 +++++++++++++++---- compiler/rustc_resolve/src/macros.rs | 2 +- 8 files changed, 107 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 41573749abbe7..dcbda2f96323e 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -559,7 +559,7 @@ impl Resolver<'_, '_> { let mut check_redundant_imports = FxIndexSet::default(); for module in &self.local_modules { for (_key, resolution) in self.resolutions(module.to_module()).iter() { - if let Some(decl) = resolution.borrow().best_decl() + if let Some(decl) = resolution.borrow(self).best_decl() && let DeclKind::Import { import, .. } = decl.kind && let ImportKind::Single { id, .. } = import.kind { diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index cc2c72ad59906..4e451665398e9 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1873,7 +1873,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.resolutions(parent_scope.module).iter().any(|(key, name_resolution)| { if key.ns == TypeNS && key.ident == *ident - && let Some(decl) = name_resolution.borrow().best_decl() + && let Some(decl) = name_resolution.borrow(self).best_decl() { match decl.res() { // No disambiguation needed if the identically named item we @@ -3603,7 +3603,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut res = false; let m = r.expect_module(parent_module); if m.is_local() { - for importer in m.glob_importers.borrow().iter() { + for importer in m.glob_importers.borrow(r).iter() { if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() { if next_parent_module == module diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index ff976b080d40d..840a8a8682538 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -126,7 +126,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) { let module = self.r.expect_module(module_id.to_def_id()); for (_, name_resolution) in self.r.resolutions(module).iter() { - let Some(decl) = name_resolution.borrow().best_decl() else { + let Some(decl) = name_resolution.borrow(self.r).best_decl() else { continue; }; self.update_decl_chain(decl, ParentId::Def(module_id)); @@ -310,7 +310,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { if self.macro_reachable.insert((module_def_id, defining_mod)) { let module = self.r.expect_module(module_def_id.to_def_id()); for (_, name_resolution) in self.r.resolutions(module).iter() { - let Some(decl) = name_resolution.borrow().best_decl() else { + let Some(decl) = name_resolution.borrow(self.r).best_decl() else { continue; }; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 3f34af1d01d83..42fc5964f5aa1 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -714,7 +714,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() { Some(decl) => Ok(decl), - None => Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())), + None => { + Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations(&self))) + } }, Scope::BuiltinAttrs => match self.builtin_attr_decls.get(&ident.name) { Some(decl) => Ok(*decl), @@ -727,9 +729,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize.is_some(), ) { Some(decl) => Ok(decl), - None => { - Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())) - } + None => Err(Determinacy::determined( + !self.graph_root.has_unexpanded_invocations(&self), + )), } } Scope::ExternPreludeFlags => { @@ -1158,7 +1160,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { // finalize implies that the module is fully expanded - assert!(!module.has_unexpanded_invocations()); + assert!(!module.has_unexpanded_invocations(&self)); return self.get_mut().finalize_module_binding( ident, orig_ident_span, @@ -1195,7 +1197,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Check if one of unexpanded macros can still define the name. - if module.has_unexpanded_invocations() { + if module.has_unexpanded_invocations(&self) { return Err(ControlFlow::Continue(Undetermined)); } @@ -1224,7 +1226,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { // finalize implies that the module is fully expanded - assert!(!module.has_unexpanded_invocations()); + assert!(!module.has_unexpanded_invocations(&self)); return self.get_mut().finalize_module_binding( ident, orig_ident_span, @@ -1268,7 +1270,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted // shadowing is enabled, see `macro_expanded_macro_export_errors`). if let Some(binding) = binding { - return if binding.determined() || ns == MacroNS || shadowing == Shadowing::Restricted { + return if binding.determined(&self) + || ns == MacroNS + || shadowing == Shadowing::Restricted + { let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) } } else { @@ -1283,13 +1288,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // scopes we return `Undetermined` with `ControlFlow::Continue`. // Check if one of unexpanded macros can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - if module.has_unexpanded_invocations() { + if module.has_unexpanded_invocations(&self) { return Err(ControlFlow::Continue(Undetermined)); } // Check if one of glob imports can still define the name, // if it can then our "no resolution" result is not determined and can be invalidated. - for glob_import in module.globs.borrow().iter() { + for glob_import in module.globs.borrow(&self).iter() { if ignore_import == Some(*glob_import) { continue; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6e2ea9abf2de8..499f9ea297362 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -781,14 +781,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut imports_to_resolve = mem::take(&mut self.indeterminate_imports); - self.assert_speculative = true; + // SAFETY: This is a "top-level" function used by the macro expansion code, unless some + // weird thing is done, all `tracked` borrows done in the previous call of + // `resolve_imports` are dropped when that call ended. + unsafe { self.speculative_flag.set(true) }; rustc_data_structures::sync::par_for_each_slice( &mut imports_to_resolve, |(import, resolution, indeterminate_count)| { (*resolution, *indeterminate_count) = self.resolve_import(*import); }, ); - self.assert_speculative = false; + // SAFETY: All `untracked` borrows are dropped after the `par_for_each_slice` call, + // as they cannot escape since they are tied to the `CmRefCell` they borrowed from. + // + // Note: Some `CmRefCell`s are arena allocated and thus have the `'ra` lifetime, + // allowing these borrows to escape, but that does not and should not happen. + unsafe { self.speculative_flag.set(false) }; self.write_import_resolutions(&imports_to_resolve); @@ -1003,7 +1011,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn lint_reexports(&mut self, exported_ambiguities: FxHashSet>) { for module in &self.local_modules { for (key, resolution) in self.resolutions(module.to_module()).iter() { - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self); let Some(binding) = resolution.best_decl() else { continue }; // Report "cannot reexport" errors for exotic cases involving macros 2.0 @@ -1490,7 +1498,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return None; } // `use _` is never valid - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self); if let Some(name_binding) = resolution.best_decl() { match name_binding.kind { DeclKind::Import { source_decl, .. } => { @@ -1800,7 +1808,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .resolutions(module) .iter() .filter_map(|(key, resolution)| { - let res = resolution.borrow(); + let res = resolution.borrow(self); let decl = res.determined_decl()?; let mut key = *key; let scope = match key.ident.ctxt.update_unchecked(|ctxt| { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6350f79ed007f..b126272583692 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -194,7 +194,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if key.ident.name != assoc_name { return None; } - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self.r); let binding = resolution.best_decl()?; match binding.res() { Res::Def(DefKind::AssocTy, def_id) => Some(def_id), @@ -1165,7 +1165,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| { for resolution in r.resolutions(m).values() { let Some(did) = - resolution.borrow().best_decl().and_then(|binding| binding.res().opt_def_id()) + resolution.borrow(r).best_decl().and_then(|binding| binding.res().opt_def_id()) else { continue; }; @@ -1905,7 +1905,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .resolutions(module) .iter() .filter_map(|(key, resolution)| { - let resolution = resolution.borrow(); + let resolution = resolution.borrow(self.r); resolution.best_decl().map(|binding| binding.res()).and_then(|res| { if filter_fn(res) { Some((key.ident.name, resolution.orig_ident_span, res)) @@ -2766,7 +2766,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .resolutions(*module) .iter() - .filter_map(|(key, res)| res.borrow().best_decl().map(|binding| (key, binding.res()))) + .filter_map(|(key, res)| { + res.borrow(self.r).best_decl().map(|binding| (key, binding.res())) + }) .filter(|(_, res)| match (kind, res) { (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst { .. }, _)) => true, (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index a3c804e56ee22..b7e57ad8ec37e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -21,7 +21,7 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -use std::cell::{Ref, RefMut}; +use std::cell::RefMut; use std::collections::BTreeSet; use std::ops::ControlFlow; use std::sync::{Arc, OnceLock}; @@ -81,6 +81,7 @@ use crate::diagnostics::impls::{ ImportSuggestion, LabelSuggestion, OnUnknownData, StructCtor, Suggestion, }; use crate::imports::{ImportResolution, NameResolutionRef}; +use crate::ref_mut::speculative::SpeculativeFlag; use crate::ref_mut::{CmCell, CmRef, CmRefCell}; mod build_reduced_graph; @@ -767,8 +768,8 @@ impl<'ra> ModuleData<'ra> { self.kind.is_local() } - fn has_unexpanded_invocations(&self) -> bool { - !self.unexpanded_invocations.borrow().is_empty() + fn has_unexpanded_invocations<'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> bool { + !self.unexpanded_invocations.borrow(r).is_empty() } fn res(&self) -> Option { @@ -793,7 +794,7 @@ impl<'ra> Module<'ra> { mut f: impl FnMut(&R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_ref().resolutions(self).iter() { - let name_resolution = name_resolution.borrow(); + let name_resolution = name_resolution.borrow(resolver.as_ref()); if let Some(decl) = name_resolution.best_decl() { f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } @@ -806,7 +807,7 @@ impl<'ra> Module<'ra> { mut f: impl FnMut(&mut R, IdentKey, Span, Namespace, Decl<'ra>), ) { for (key, name_resolution) in resolver.as_mut().resolutions(self).iter() { - let name_resolution = name_resolution.borrow(); + let name_resolution = name_resolution.borrow(resolver.as_mut()); if let Some(decl) = name_resolution.best_decl() { f(resolver, key.ident, name_resolution.orig_ident_span, key.ns, decl); } @@ -1252,10 +1253,11 @@ impl<'ra> DeclData<'ra> { /// the declaration may not be as "determined" as we think. /// FIXME: relationship between this function and similar `NameResolution::determined_decl` /// is unclear. - fn determined(&self) -> bool { + fn determined<'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> bool { match &self.kind { DeclKind::Import { source_decl, import, .. } if import.is_glob() => { - !import.parent_scope.module.has_unexpanded_invocations() && source_decl.determined() + !import.parent_scope.module.has_unexpanded_invocations(r) + && source_decl.determined(r) } _ => true, } @@ -1336,7 +1338,7 @@ pub struct Resolver<'ra, 'tcx> { graph_root: LocalModule<'ra>, /// Assert that we are in speculative resolution mode (unsafe field). - assert_speculative: bool, + speculative_flag: SpeculativeFlag, prelude: Option> = None, extern_prelude: FxIndexMap>, @@ -1810,7 +1812,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // The outermost module has def ID 0; this is not reflected in the // AST. graph_root, - assert_speculative: false, // Only set/cleared in Resolver::resolve_imports for now + // Only set/cleared in Resolver::resolve_imports for now + speculative_flag: SpeculativeFlag::default(), extern_prelude, empty_module, @@ -2011,7 +2014,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Returns a conditionally mutable resolver that can be mutated. /// Will panic if the `assert_speculative` field is true. fn cm_mut(&mut self) -> CmResolver<'_, 'ra, 'tcx> { - assert!(!self.assert_speculative, "can't mutably borrow speculative resolver"); + assert!( + !self.speculative_flag.is_speculative(), + "can't mutably borrow speculative resolver" + ); CmResolver::Mut(self) } @@ -2127,7 +2133,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { found_traits: &mut Vec>, ) { module.ensure_traits(self); - let traits = module.traits.borrow(); + let traits = module.traits.borrow(self); for &(trait_name, trait_binding, trait_module, lint_ambiguous) in traits.as_ref().unwrap().iter() { @@ -2178,7 +2184,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolutions(&self, module: Module<'ra>) -> CmRef<'ra, ResolutionTable<'ra>> { match &module.0.0.lazy_resolutions { - Resolutions::Local(local_res) => CmRef::Tracked(local_res.borrow()), + Resolutions::Local(local_res) => local_res.borrow(self), Resolutions::Extern(extern_res) => { // It is fine to return a `CmRef::Untracked`, we never give out a `&mut` // to an external table. @@ -2206,8 +2212,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &self, module: Module<'ra>, key: BindingKey, - ) -> Option>> { - self.resolutions(module).get(&key).map(|resolution| resolution.0.borrow()) + ) -> Option>> { + self.resolutions(module).get(&key).map(|resolution| resolution.0.borrow(self)) } #[track_caller] @@ -2917,7 +2923,7 @@ mod ref_mut { } pub(crate) fn set<'ra, 'tcx>(&self, val: T, r: &Resolver<'ra, 'tcx>) { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutate a `CmCell` during speculative resolution") } self.0.set(val); @@ -2946,6 +2952,27 @@ mod ref_mut { } } + pub(crate) mod speculative { + #[derive(Debug, Clone, Copy, Default)] + pub(crate) struct SpeculativeFlag(bool); + + impl SpeculativeFlag { + /// # SAFETY + /// + /// All borrows created by `CmRefCell::borrow` must be dropped before changing + /// the speculative flag: + /// - `tracked` borrows before setting it to `true`. + /// - `untracked` borrows before setting it to `false`. + pub(crate) unsafe fn set(&mut self, value: bool) { + self.0 = value; + } + + pub(crate) fn is_speculative(&self) -> bool { + self.0 + } + } + } + /// A wrapper around a [`RefCell`] that only allows writes (mutable borrows) based on a condition in the resolver. #[derive(Default)] pub(crate) struct CmRefCell(RefCell); @@ -2965,21 +2992,42 @@ mod ref_mut { &self, r: &Resolver<'ra, 'tcx>, ) -> Result, BorrowMutError> { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutably borrow a `CmRefCell` during speculative resolution"); } self.0.try_borrow_mut() } #[track_caller] - pub(crate) fn borrow(&self) -> Ref<'_, T> { - self.0.borrow() + pub(crate) fn borrow<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> CmRef<'_, T> { + if r.speculative_flag.is_speculative() { + // `try_borrow_unguarded` is unsafe because it returns a `&T` instead + // of `Ref<'_, T>`. It does provides an extra check to make sure no live + // `RefMut`s are still alive, but the other way can not be checked, so: + // + // SAFETY: This is only safe because we know that every `Untracked` borrow + // is only created during the import resolutions phase: + // + // ```rust + // // tracked borrows + // unsafe { resolver.speculative_flag.set_true() }; + // import_resolution(); // untracked borrows + // unsafe { resolver.speculative_flag.set_true() }; + // // tracked borrows + // ``` + // + // `speculative::Flag` requires all of the borrows that happened during a + // particular phase are dropped before being set to true/false. + CmRef::Untracked(unsafe { self.0.try_borrow_unguarded().unwrap() }) + } else { + CmRef::Tracked(self.0.borrow()) + } } } impl CmRefCell { pub(crate) fn take<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> T { - if r.assert_speculative { + if r.speculative_flag.is_speculative() { panic!("not allowed to mutate a CmRefCell during speculative resolution"); } self.0.take() diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 1e9d60ca21551..6921d0ed595fe 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -562,7 +562,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); - if target_trait.has_unexpanded_invocations() { + if target_trait.has_unexpanded_invocations(self) { return Err(Indeterminate); } // FIXME: Instead of waiting try generating all trait methods, and pruning From 73f94b6b9f2f71532971f9fd1f0910d8a8953a16 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 3 Aug 2026 22:53:41 +0200 Subject: [PATCH 17/31] Make the `rustc_unsafe_specialization_marker` attribute actually `unsafe` Also renames it to `rustc_specialization_ignore_lifetime_constraints` --- .../rustc_attr_parsing/src/attributes/traits.rs | 15 +++++++++++---- compiler/rustc_attr_parsing/src/context.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- compiler/rustc_hir/src/attrs/data_structures.rs | 6 +++--- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 2 +- .../src/impl_wf_check/min_specialization.rs | 2 +- compiler/rustc_middle/src/ty/trait_def.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 3 ++- compiler/rustc_span/src/symbol.rs | 2 +- library/alloc/src/rc.rs | 2 +- library/alloc/src/vec/into_iter.rs | 2 +- library/core/src/array/iter.rs | 2 +- library/core/src/clone.rs | 2 +- library/core/src/iter/traits/marker.rs | 4 ++-- library/core/src/slice/sort/shared/mod.rs | 2 +- library/core/src/slice/sort/shared/smallsort.rs | 2 +- .../min_specialization/spec-marker-supertraits.rs | 2 +- .../min_specialization/specialization_marker.rs | 6 +++--- .../min_specialization/specialize_on_marker.rs | 4 ++-- .../unconstrained-var-specialization.rs | 2 +- 21 files changed, 38 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index 69bdccb85c5cd..1d0d26ea62cb3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_feature::AttributeStability; use super::prelude::*; +use crate::AttributeSafety; use crate::attributes::{NoArgsAttributeParser, SingleAttributeParser}; use crate::context::AcceptContext; use crate::parser::ArgParser; @@ -98,12 +99,18 @@ impl NoArgsAttributeParser for RustcSpecializationTraitParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcSpecializationTrait; } -pub(crate) struct RustcUnsafeSpecializationMarkerParser; -impl NoArgsAttributeParser for RustcUnsafeSpecializationMarkerParser { - const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker]; +pub(crate) struct RustcAllowLifetimeDependentSpecializationParser; +impl NoArgsAttributeParser for RustcAllowLifetimeDependentSpecializationParser { + const PATH: &[Symbol] = &[sym::rustc_allow_lifetime_dependent_specialization]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcUnsafeSpecializationMarker; + const CREATE: fn(Span) -> AttributeKind = + |_| AttributeKind::RustcAllowLifetimeDependentSpecialization; + const SAFETY: AttributeSafety = AttributeSafety::Unsafe { + note: "this attribute requires `unsafe` because lifetime constraints from \ + the implementations of the trait are not considered when specializing", + unsafe_since: None, + }; } // Coherence diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6254dd73f3263..55732edfbd166 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -295,6 +295,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -355,7 +356,6 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 1f6f97f1310ae..72b51ad204b9d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -365,7 +365,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_reservation_impl, sym::rustc_test_entrypoint_marker, sym::rustc_test_marker, - sym::rustc_unsafe_specialization_marker, + sym::rustc_allow_lifetime_dependent_specialization, sym::rustc_specialization_trait, sym::rustc_main, sym::rustc_skip_during_method_dispatch, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 78a15eeb923a5..530483e87329c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1350,6 +1350,9 @@ pub enum AttributeKind { /// Represents `#[rustc_allow_incoherent_impl]`. RustcAllowIncoherentImpl(Span), + /// Represents `#[rustc_allow_lifetime_dependent_specialization]`. + RustcAllowLifetimeDependentSpecialization, + /// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint). RustcAsPtr, @@ -1654,9 +1657,6 @@ pub enum AttributeKind { /// Represents `#[rustc_trivial_field_reads]` RustcTrivialFieldReads, - /// Represents `#[rustc_unsafe_specialization_marker]`. - RustcUnsafeSpecializationMarker, - /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index a5a1fc2482b4e..455af47142446 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -106,6 +106,7 @@ impl AttributeKind { RustcAllocatorZeroedVariant { .. } => Yes, RustcAllowConstFnUnstable(..) => No, RustcAllowIncoherentImpl(..) => No, + RustcAllowLifetimeDependentSpecialization => No, RustcAsPtr => Yes, RustcAutodiff(..) => Yes, RustcBodyStability { .. } => No, @@ -196,7 +197,6 @@ impl AttributeKind { RustcTestMarker(..) => No, RustcThenThisWouldNeed(..) => No, RustcTrivialFieldReads => Yes, - RustcUnsafeSpecializationMarker => No, Sanitize { .. } => No, ShouldPanic { .. } => No, Splat(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a1cc500a2f18f..19c06b720e825 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -963,7 +963,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { ) .unwrap_or([false; 2]); - let specialization_kind = if find_attr!(attrs, RustcUnsafeSpecializationMarker) { + let specialization_kind = if find_attr!(attrs, RustcAllowLifetimeDependentSpecialization) { ty::trait_def::TraitSpecializationKind::Marker } else if find_attr!(attrs, RustcSpecializationTrait) { ty::trait_def::TraitSpecializationKind::AlwaysApplicable diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index b8cb4c7f0e7c7..c146c3e62a301 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -55,7 +55,7 @@ //! `specialization` or `min_specialization` is enabled to implement these //! traits. //! -//! ### rustc_unsafe_specialization_marker +//! ### rustc_allow_lifetime_dependent_specialization //! //! There are also some specialization on traits with no methods, including the //! stable `FusedIterator` trait. We allow marking marker traits with an diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 3b0d78d34af76..da514036b20b9 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -91,7 +91,7 @@ pub enum TraitSpecializationKind { None, /// Specializing on this trait is allowed because it doesn't have any /// methods. For example `Sized` or `FusedIterator`. - /// Applies to traits with the `rustc_unsafe_specialization_marker` + /// Applies to traits with the `rustc_allow_lifetime_dependent_specialization` /// attribute. Marker, /// Specializing on this trait is allowed because all of the impls of this diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e95b9b2ffdf01..54d17e4cd7ff7 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -304,6 +304,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcAllocatorZeroed => (), AttributeKind::RustcAllocatorZeroedVariant { .. } => (), AttributeKind::RustcAllowIncoherentImpl(..) => (), + AttributeKind::RustcAllowLifetimeDependentSpecialization => (), AttributeKind::RustcAsPtr => (), AttributeKind::RustcAutodiff(..) => (), AttributeKind::RustcBodyStability { .. } => (), @@ -384,6 +385,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcShouldNotBeCalledOnConstItems => (), AttributeKind::RustcSimdMonomorphizeLaneLimit(..) => (), AttributeKind::RustcSkipDuringMethodDispatch { .. } => (), + AttributeKind::RustcSpecializationTrait => (), AttributeKind::RustcStdInternalSymbol => (), AttributeKind::RustcStrictCoherence(..) => (), @@ -391,7 +393,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcTestMarker(..) => (), AttributeKind::RustcThenThisWouldNeed(..) => (), AttributeKind::RustcTrivialFieldReads => (), - AttributeKind::RustcUnsafeSpecializationMarker => (), AttributeKind::Sanitize { .. } => {} AttributeKind::ShouldPanic { .. } => (), AttributeKind::Splat(..) => (), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fd555e6d97fd8..1e7527b68b80d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1762,6 +1762,7 @@ symbols! { rustc_allocator_zeroed_variant, rustc_allow_const_fn_unstable, rustc_allow_incoherent_impl, + rustc_allow_lifetime_dependent_specialization, rustc_allowed_through_unstable_modules, rustc_as_ptr, rustc_attrs, @@ -1869,7 +1870,6 @@ symbols! { rustc_test_marker, rustc_then_this_would_need, rustc_trivial_field_reads, - rustc_unsafe_specialization_marker, rustdoc, rustdoc_internals, rustdoc_missing_doc_code_examples, diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index ffd02e2f4f6e5..f37c73f790755 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2619,7 +2619,7 @@ impl RcEqIdent for Rc { } // Hack to allow specializing on `Eq` even though `Eq` has a method. -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] pub(crate) trait MarkerEq: PartialEq {} impl MarkerEq for T {} diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index ff3c9433ab58b..4b25634326e16 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -551,7 +551,7 @@ where #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait NonDrop {} // T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 0877b7bad9512..f10ed3edc0e6b 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -367,7 +367,7 @@ unsafe impl TrustedLen for IntoIter {} #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait NonDrop {} // T: Copy as approximation for !Drop since get_unchecked does not advance self.alive diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index a67dc9d87499d..2996c753faea4 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -275,7 +275,7 @@ pub const trait Clone: Sized { // lifetime-dependent. Therefore, if `TrivialClone` is implemented for any lifetime, // its invariant holds whenever `Clone` is implemented, even if the actual // `TrivialClone` bound would not be satisfied because of lifetime bounds. -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] // If `#[derive(Clone, Clone, Copy)]` is written, there will be multiple // implementations of `TrivialClone`. To keep it from appearing in error // messages, make it a `#[marker]` trait. diff --git a/library/core/src/iter/traits/marker.rs b/library/core/src/iter/traits/marker.rs index 542d283fe95ab..1e6704fe524a9 100644 --- a/library/core/src/iter/traits/marker.rs +++ b/library/core/src/iter/traits/marker.rs @@ -25,7 +25,7 @@ pub unsafe trait TrustedFused {} /// /// [`Fuse`]: crate::iter::Fuse #[stable(feature = "fused", since = "1.26.0")] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] // FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused // but that ICEs iter::Fuse specializations. #[lang = "fused_iterator"] @@ -62,7 +62,7 @@ impl FusedIterator for &mut I {} /// This trait must only be implemented when the contract is upheld. Consumers /// of this trait must inspect [`Iterator::size_hint()`]’s upper bound. #[unstable(feature = "trusted_len", issue = "37572")] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] #[rustc_const_unstable(feature = "const_iter", issue = "92476")] pub const unsafe trait TrustedLen: [const] Iterator {} diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs index e2cdcb3dd511d..e1977d79f3207 100644 --- a/library/core/src/slice/sort/shared/mod.rs +++ b/library/core/src/slice/sort/shared/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod smallsort; /// SAFETY: this is safety relevant, how does this interact with the soundness holes in /// specialization? -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] pub(crate) trait FreezeMarker {} impl FreezeMarker for T {} diff --git a/library/core/src/slice/sort/shared/smallsort.rs b/library/core/src/slice/sort/shared/smallsort.rs index 0017feb75b641..40939f922bcb6 100644 --- a/library/core/src/slice/sort/shared/smallsort.rs +++ b/library/core/src/slice/sort/shared/smallsort.rs @@ -134,7 +134,7 @@ impl UnstableSmallSortFreezeTypeImpl for T { } /// SAFETY: Only used for run-time optimization heuristic. -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait CopyMarker {} impl CopyMarker for T {} diff --git a/tests/ui/specialization/min_specialization/spec-marker-supertraits.rs b/tests/ui/specialization/min_specialization/spec-marker-supertraits.rs index 3bb2480e9e2be..57319e0e7bb21 100644 --- a/tests/ui/specialization/min_specialization/spec-marker-supertraits.rs +++ b/tests/ui/specialization/min_specialization/spec-marker-supertraits.rs @@ -8,7 +8,7 @@ trait HasMethod { fn method(&self); } -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait Marker: HasMethod {} trait Spec { diff --git a/tests/ui/specialization/min_specialization/specialization_marker.rs b/tests/ui/specialization/min_specialization/specialization_marker.rs index 93462d02ea578..55de99d7557f4 100644 --- a/tests/ui/specialization/min_specialization/specialization_marker.rs +++ b/tests/ui/specialization/min_specialization/specialization_marker.rs @@ -1,14 +1,14 @@ -// Test that `rustc_unsafe_specialization_marker` is only allowed on marker traits. +// Test that `rustc_allow_lifetime_dependent_specialization` is only allowed on marker traits. #![feature(rustc_attrs)] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait SpecMarker { fn f(); //~^ ERROR marker traits } -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait SpecMarker2 { type X; //~^ ERROR marker traits diff --git a/tests/ui/specialization/min_specialization/specialize_on_marker.rs b/tests/ui/specialization/min_specialization/specialize_on_marker.rs index f7bc057d3ba8a..8e1acf319f340 100644 --- a/tests/ui/specialization/min_specialization/specialize_on_marker.rs +++ b/tests/ui/specialization/min_specialization/specialize_on_marker.rs @@ -1,4 +1,4 @@ -// Test that specializing on a `rustc_unsafe_specialization_marker` trait is +// Test that specializing on a `rustc_allow_lifetime_dependent_specialization` trait is // allowed. //@ check-pass @@ -6,7 +6,7 @@ #![feature(min_specialization)] #![feature(rustc_attrs)] -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] trait SpecMarker {} trait X { diff --git a/tests/ui/traits/const-traits/unconstrained-var-specialization.rs b/tests/ui/traits/const-traits/unconstrained-var-specialization.rs index 4330e0aead1ac..d48880deefaf9 100644 --- a/tests/ui/traits/const-traits/unconstrained-var-specialization.rs +++ b/tests/ui/traits/const-traits/unconstrained-var-specialization.rs @@ -13,7 +13,7 @@ pub trait Iterator { type Item; } -#[rustc_unsafe_specialization_marker] +#[unsafe(rustc_allow_lifetime_dependent_specialization)] pub trait MoreSpecificThanIterator: Iterator {} pub trait Tr { From 180c6379b8ed8b8ff5d9545c716a17d2225c915c Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 3 Aug 2026 23:21:15 +0200 Subject: [PATCH 18/31] Remove rustc_middle dependency on rustc_hir_pretty There is a `impl PpAnn for TyCtxt` that is unneeded. None of the big crates (middle, trait_selection) actually do any hir pretty printing so it can be removed and can either be implemented for local structs elsewhere or done by casting to `&dyn PpAnn` instead. --- Cargo.lock | 2 +- compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_driver_impl/src/pretty.rs | 10 +- compiler/rustc_hir_typeck/src/_match.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 5 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 10 + .../src/fn_ctxt/suggestions.rs | 4 +- compiler/rustc_hir_typeck/src/lib.rs | 244 +++++++++--------- compiler/rustc_hir_typeck/src/pat.rs | 17 +- compiler/rustc_middle/Cargo.toml | 1 - compiler/rustc_middle/src/hir/map.rs | 7 - .../rustc_public_bridge/src/context/impls.rs | 14 +- src/librustdoc/json/conversions.rs | 8 +- .../src/matches/match_wild_err_arm.rs | 3 +- .../src/unnecessary_mut_passed.rs | 5 +- 16 files changed, 180 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76b17e02c2359..2190fa22b77ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3954,6 +3954,7 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", + "rustc_hir", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_index", @@ -4414,7 +4415,6 @@ dependencies = [ "rustc_graphviz", "rustc_hashes", "rustc_hir", - "rustc_hir_pretty", "rustc_index", "rustc_lint_defs", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c7d3e4fae3fc5..4871c7eb9e8b0 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -16,6 +16,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } +rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 3a0a6687dd812..4bf1a3d875866 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -7,7 +7,9 @@ use std::io; use rustc_ast as ast; use rustc_ast_pretty::pprust as pprust_ast; +use rustc_hir::intravisit; use rustc_hir_pretty as pprust_hir; +use rustc_hir_pretty::PpAnn; use rustc_middle::bug; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; @@ -71,7 +73,8 @@ struct HirIdentifiedAnn<'tcx> { impl<'tcx> pprust_hir::PpAnn for HirIdentifiedAnn<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - self.tcx.nested(state, nested) + let this = &self.tcx as &dyn intravisit::HirTyCtxt<'_>; + this.nested(state, nested) } fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) { @@ -149,11 +152,12 @@ struct HirTypedAnn<'tcx> { impl<'tcx> pprust_hir::PpAnn for HirTypedAnn<'tcx> { fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { + let this = &self.tcx as &dyn intravisit::HirTyCtxt<'_>; let old_maybe_typeck_results = self.maybe_typeck_results.get(); if let pprust_hir::Nested::Body(id) = nested { self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id))); } - self.tcx.nested(state, nested); + this.nested(state, nested); self.maybe_typeck_results.set(old_maybe_typeck_results); } @@ -281,7 +285,7 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { ) }; match s { - PpHirMode::Normal => f(&tcx), + PpHirMode::Normal => f(&(&tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn), PpHirMode::Identified => { let annotation = HirIdentifiedAnn { tcx }; f(&annotation) diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index ebf9907e64e64..a1ff036574fdc 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return self.get_fn_decl(hir_id).map(|(_, fn_decl)| { let (ty, span) = match fn_decl.output { hir::FnRetTy::DefaultReturn(span) => ("()".to_string(), span), - hir::FnRetTy::Return(ty) => (ty_to_string(&self.tcx, ty), ty.span), + hir::FnRetTy::Return(ty) => (ty_to_string(self, ty), ty.span), }; (span, format!("expected `{ty}` because of this return type")) }); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 288a1903bf675..3074a5900773d 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -903,7 +903,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); unit_variant = - Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(&self.tcx, qpath))); + Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(self, qpath))); } let callee_ty = self.resolve_vars_if_possible(callee_ty); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f89d67eced3fb..12e7f82cadd43 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -50,7 +50,7 @@ use crate::diagnostics::{ use crate::op::contains_let_in_chain; use crate::{ BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, GatherLocalsVisitor, Needs, - TupleArgumentsFlag, cast, fatally_break_rust, report_unexpected_variant_res, type_error_struct, + TupleArgumentsFlag, cast, fatally_break_rust, type_error_struct, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -589,8 +589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, Some(expr), &[], diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 287e3857087e7..7a5eeccb98260 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -220,6 +220,16 @@ impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { } } +impl<'tcx> rustc_hir_pretty::PpAnn for FnCtxt<'_, 'tcx> { + fn nested(&self, state: &mut rustc_hir_pretty::State<'_>, nested: rustc_hir_pretty::Nested) { + rustc_hir_pretty::PpAnn::nested( + &(&self.tcx as &dyn rustc_hir::intravisit::HirTyCtxt<'_>), + state, + nested, + ) + } +} + impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b28eb8ad940d9..fc99dd67289bb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -723,12 +723,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::FnDecl { inputs, output, .. } = fn_ptr_ty.decl; let inputs_str = - inputs.iter().map(|ty| rustc_hir_pretty::ty_to_string(&self.tcx, ty)).join(", "); + inputs.iter().map(|ty| rustc_hir_pretty::ty_to_string(self, ty)).join(", "); let output_str = match output { hir::FnRetTy::DefaultReturn(_) => String::new(), hir::FnRetTy::Return(ty) => { - format!(" -> {}", rustc_hir_pretty::ty_to_string(&self.tcx, ty)) + format!(" -> {}", rustc_hir_pretty::ty_to_string(self, ty)) } }; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c67b8f7cdaf5c..d20d8375fc228 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -478,134 +478,138 @@ impl<'tcx> EnclosingBreakables<'tcx> { } } } - -fn report_unexpected_variant_res( - tcx: TyCtxt<'_>, - res: Res, - expr: Option<&hir::Expr<'_>>, - sub_pats: &[hir::Pat<'_>], - qpath: &hir::QPath<'_>, - span: Span, - err_code: ErrCode, - expected: &str, -) -> ErrorGuaranteed { - let res_descr = match res { - Res::Def(DefKind::Variant, _) => "struct variant", - _ => res.descr(), - }; - let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); - let mut err = tcx - .dcx() - .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) - .with_code(err_code); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == E0164 => { - let patterns_url = "https://doc.rust-lang.org/book/ch19-00-patterns.html"; - err.with_span_label(span, "`fn` calls are not allowed in patterns") - .with_help(format!("for more information, visit {patterns_url}")) - } - Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { - err.span_label(span, format!("not a {expected}")); - let variant = tcx.expect_variant_res(res); - let sugg = if variant.fields.is_empty() { - " {}".to_string() - } else { - format!( - " {{ {} }}", - variant - .fields - .iter() - .map(|f| format!("{}: /* value */", f.name)) - .collect::>() - .join(", ") - ) - }; - let descr = "you might have meant to create a new value of the struct"; - let mut suggestion = vec![]; - match tcx.parent_hir_node(expr.hir_id) { - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(..), - span: call_span, - .. - }) => { - suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); - } - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { - suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); - if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id) - && let hir::ExprKind::If(condition, block, None) = parent.kind - && condition.hir_id == *hir_id - && let hir::ExprKind::Block(block, _) = block.kind - && block.stmts.is_empty() - && let Some(expr) = block.expr - && let hir::ExprKind::Path(..) = expr.kind - { - // Special case: you can incorrectly write an equality condition: - // if foo == Struct { field } { /* if body */ } - // which should have been written - // if foo == (Struct { field }) { /* if body */ } - suggestion.push((block.span.shrink_to_hi(), ")".to_string())); - } else { - suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + fn report_unexpected_variant_res( + &self, + res: Res, + expr: Option<&hir::Expr<'_>>, + sub_pats: &[hir::Pat<'_>], + qpath: &hir::QPath<'_>, + span: Span, + err_code: ErrCode, + expected: &str, + ) -> ErrorGuaranteed { + let tcx = self.tcx; + let res_descr = match res { + Res::Def(DefKind::Variant, _) => "struct variant", + _ => res.descr(), + }; + let path_str = rustc_hir_pretty::qpath_to_string(self, qpath); + let mut err = tcx + .dcx() + .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) + .with_code(err_code); + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == E0164 => { + let patterns_url = "https://doc.rust-lang.org/book/ch19-00-patterns.html"; + err.with_span_label(span, "`fn` calls are not allowed in patterns") + .with_help(format!("for more information, visit {patterns_url}")) + } + Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { + err.span_label(span, format!("not a {expected}")); + let variant = tcx.expect_variant_res(res); + let sugg = if variant.fields.is_empty() { + " {}".to_string() + } else { + format!( + " {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }; + let descr = "you might have meant to create a new value of the struct"; + let mut suggestion = vec![]; + match tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(..), + span: call_span, + .. + }) => { + suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); + } + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Binary(..), hir_id, .. + }) => { + suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); + if let hir::Node::Expr(parent) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::If(condition, block, None) = parent.kind + && condition.hir_id == *hir_id + && let hir::ExprKind::Block(block, _) = block.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + && let hir::ExprKind::Path(..) = expr.kind + { + // Special case: you can incorrectly write an equality condition: + // if foo == Struct { field } { /* if body */ } + // which should have been written + // if foo == (Struct { field }) { /* if body */ } + suggestion.push((block.span.shrink_to_hi(), ")".to_string())); + } else { + suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); + } + } + _ => { + suggestion.push((span.shrink_to_hi(), sugg)); } } - _ => { - suggestion.push((span.shrink_to_hi(), sugg)); - } + + err.multipart_suggestion(descr, suggestion, Applicability::HasPlaceholders); + err } + Res::Def(DefKind::Variant, _) if expr.is_none() => { + err.span_label(span, format!("not a {expected}")); - err.multipart_suggestion(descr, suggestion, Applicability::HasPlaceholders); - err - } - Res::Def(DefKind::Variant, _) if expr.is_none() => { - err.span_label(span, format!("not a {expected}")); - - let fields = &tcx.expect_variant_res(res).fields.raw; - let span = qpath.span().shrink_to_hi().to(span.shrink_to_hi()); - let (msg, sugg) = if fields.is_empty() { - ("use the struct variant pattern syntax", " {}".to_string()) - } else { - let msg = if fields.is_empty() { - "use struct variant pattern syntax" + let fields = &tcx.expect_variant_res(res).fields.raw; + let span = qpath.span().shrink_to_hi().to(span.shrink_to_hi()); + let (msg, sugg) = if fields.is_empty() { + ("use the struct variant pattern syntax", " {}".to_string()) } else { - "add the names to match a struct variant's fields" + let msg = if fields.is_empty() { + "use struct variant pattern syntax" + } else { + "add the names to match a struct variant's fields" + }; + let fields_sugg = fields + .iter() + .enumerate() + .map(|(i, field)| { + let field_name = field.ident(tcx).to_string(); + + let pat_snippet = sub_pats + .get(i) + .and_then(|sub_pat| { + tcx.sess.source_map().span_to_snippet(sub_pat.span).ok() + }) + .unwrap_or_else(|| "_".to_string()); + + if field_name == pat_snippet { + field_name + } else { + format!("{field_name}: {pat_snippet}") + } + }) + .collect::>() + .join(", "); + let sugg = format!(" {{ {} }}", fields_sugg); + (msg, sugg) }; - let fields_sugg = fields - .iter() - .enumerate() - .map(|(i, field)| { - let field_name = field.ident(tcx).to_string(); - - let pat_snippet = sub_pats - .get(i) - .and_then(|sub_pat| { - tcx.sess.source_map().span_to_snippet(sub_pat.span).ok() - }) - .unwrap_or_else(|| "_".to_string()); - - if field_name == pat_snippet { - field_name - } else { - format!("{field_name}: {pat_snippet}") - } - }) - .collect::>() - .join(", "); - let sugg = format!(" {{ {} }}", fields_sugg); - (msg, sugg) - }; - - err.span_suggestion_verbose( - qpath.span().shrink_to_hi().to(span.shrink_to_hi()), - msg, - sugg, - Applicability::HasPlaceholders, - ); - err + + err.span_suggestion_verbose( + qpath.span().shrink_to_hi().to(span.shrink_to_hi()), + msg, + sugg, + Applicability::HasPlaceholders, + ); + err + } + _ => err.with_span_label(span, format!("not a {expected}")), } - _ => err.with_span_label(span, format!("not a {expected}")), + .emit() } - .emit() } /// Controls whether all arguments are tupled. This is used for the call operator only. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 52602b8041d66..01c48c0ae790c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -32,7 +32,6 @@ use tracing::{debug, instrument, trace}; use ty::VariantDef; use ty::adjustment::{PatAdjust, PatAdjustment}; -use super::report_unexpected_variant_res; use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; use crate::{FnCtxt, diagnostics}; @@ -1585,8 +1584,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, None, &[], @@ -1604,8 +1602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // Ok, we allow unit struct ctors in patterns only. } else { - let e = report_unexpected_variant_res( - tcx, + let e = self.report_unexpected_variant_res( res, None, &[], @@ -1775,8 +1772,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::TupleStruct(_, sub_pats, _) => sub_pats, _ => &[], }; - let e = report_unexpected_variant_res( - tcx, res, None, sub_pats, qpath, pat.span, E0164, expected, + let e = self.report_unexpected_variant_res( + res, None, sub_pats, qpath, pat.span, E0164, expected, ); Err(e) }; @@ -2237,7 +2234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); if has_shorthand_field_name { - let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); + let path = rustc_hir_pretty::qpath_to_string(self, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -2422,7 +2419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we don't care to report errors for a struct if the struct itself is tainted variant.has_errors()?; - let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath); + let path = rustc_hir_pretty::qpath_to_string(self, qpath); let mut err = struct_span_code_err!( self.dcx(), pat.span, @@ -2472,7 +2469,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { f } } - Err(_) => rustc_hir_pretty::pat_to_string(&self.tcx, field.pat), + Err(_) => rustc_hir_pretty::pat_to_string(self, field.pat), } }) .collect::>() diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index f624fcee78f59..55608083d3751 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -22,7 +22,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } -rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index c01d9e98e9b9c..8ec27921a5787 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -15,7 +15,6 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::lints::DelayedLints; use rustc_hir::*; -use rustc_hir_pretty as pprust_hir; use rustc_span::def_id::{CRATE_MOD_ID, StableCrateId}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; @@ -1156,12 +1155,6 @@ impl<'tcx> intravisit::HirTyCtxt<'tcx> for TyCtxt<'tcx> { } } -impl<'tcx> pprust_hir::PpAnn for TyCtxt<'tcx> { - fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) { - pprust_hir::PpAnn::nested(&(self as &dyn intravisit::HirTyCtxt<'_>), state, nested) - } -} - pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { let krate = tcx.hir_crate_items(()); let upstream_crates = upstream_crates(tcx); diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 4a2fbb8f8b7af..f648a85249dfc 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -52,6 +52,16 @@ impl<'tcx, B: Bridge> AllocRangeHelpers<'tcx> for CompilerCtxt<'tcx, B> { } } +impl<'tcx, B: Bridge> rustc_hir_pretty::PpAnn for CompilerCtxt<'tcx, B> { + fn nested(&self, state: &mut rustc_hir_pretty::State<'_>, nested: rustc_hir_pretty::Nested) { + rustc_hir_pretty::PpAnn::nested( + &(&self.tcx as &dyn rustc_hir::intravisit::HirTyCtxt<'_>), + state, + nested, + ) + } +} + impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { pub fn lift>>(&self, value: T) -> T::Lifted { self.tcx.lift(value) @@ -295,7 +305,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { .get_attrs_by_path(def_id, &attr_name) .filter_map(|attribute| { if let Attribute::Unparsed(u) = attribute { - let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(self, attribute); Some((attr_str, u.span)) } else { None @@ -314,7 +324,7 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { attrs_iter .filter_map(|attribute| { if let Attribute::Unparsed(u) = attribute { - let attr_str = rustc_hir_pretty::attribute_to_string(&self.tcx, attribute); + let attr_str = rustc_hir_pretty::attribute_to_string(self, attribute); Some((attr_str, u.span)) } else { None diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7e46b2f593e49..eb382f368905f 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -12,7 +12,8 @@ use rustc_hir::attrs::{ }; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; -use rustc_hir::{HeaderSafety, Safety, find_attr}; +use rustc_hir::{HeaderSafety, Safety, find_attr, intravisit}; +use rustc_hir_pretty::PpAnn; use rustc_metadata::rendered_const; use rustc_middle::ty::TyCtxt; use rustc_middle::{bug, ty}; @@ -1243,7 +1244,10 @@ fn maybe_from_hir_attr(attr: &hir::Attribute, item_id: ItemId, tcx: TyCtxt<'_>) } fn other_attr(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Attribute { - let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr); + let mut s = rustc_hir_pretty::attribute_to_string( + &(&tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn, + attr, + ); assert_eq!(s.pop(), Some('\n')); Attribute::Other(s) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index e38ba801c0bf7..9fc9f9944465c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -6,6 +6,7 @@ use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; +use rustc_hir::intravisit; use super::MATCH_WILD_ERR_ARM; @@ -19,7 +20,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<' if ex_ty.is_diag_item(cx, sym::Result) { for arm in arms { if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { - let path_str = rustc_hir_pretty::qpath_to_string(&cx.tcx, path); + let path_str = rustc_hir_pretty::qpath_to_string(#[allow(trivial_casts)] &(&cx.tcx as &dyn intravisit::HirTyCtxt<'_>), path); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); let mut ident_bind_name = kw::Underscore; diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs index 60a6688927ab5..43721fa252837 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs @@ -6,6 +6,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use std::iter; +use rustc_hir_pretty::PpAnn; +use rustc_hir::intravisit; declare_clippy_lint! { /// ### What it does @@ -51,7 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { cx, &mut arguments.iter(), cx.typeck_results().expr_ty(fn_expr), - &rustc_hir_pretty::qpath_to_string(&cx.tcx, path), + #[allow(trivial_casts)] + &rustc_hir_pretty::qpath_to_string(&(&cx.tcx as &dyn intravisit::HirTyCtxt<'_>) as &dyn PpAnn, path), "function", ); } From 18e0dd9aa8993a19e332fd080904a72270d18a0d Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Tue, 4 Aug 2026 15:31:15 -0300 Subject: [PATCH 19/31] Document zero-sized autodiff slice handling Clarify that slice-tail layout checks apply to the sized prefix rather than the slice element, and cover zero-sized slice elements in the type-tree run-make test. --- compiler/rustc_middle/src/ty/typetree.rs | 6 ++++-- .../autodiff/type-trees/slice-dst-typetree/rmake.rs | 1 + .../type-trees/slice-dst-typetree/slice-dst.check | 5 +++++ .../autodiff/type-trees/slice-dst-typetree/test.rs | 13 +++++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/typetree.rs b/compiler/rustc_middle/src/ty/typetree.rs index 90fb0316fe39b..100c3170e12a9 100644 --- a/compiler/rustc_middle/src/ty/typetree.rs +++ b/compiler/rustc_middle/src/ty/typetree.rs @@ -66,14 +66,16 @@ fn handle_indirection<'a>( // LLVM arguments, while its child describes the memory reached through `data`. let typing_env = ty::TypingEnv::fully_monomorphized(); if let ty::Slice(element_ty) = tcx.struct_tail_for_codegen(inner_ty, typing_env).kind() { + // `layout.size` here is the sized prefix of `inner_ty`, not the slice element size. + // Direct slices, transparent wrappers (`OsStr`), and ZST-prefixed DSTs have no byte + // offset to preserve. Nonzero prefixes (e.g. `Header<[f32]>`) keep field offsets. + // ZST elements still take this path and yield an empty child TypeTree (size 0). let child = if tcx .layout_of(typing_env.as_query_input(inner_ty)) .is_ok_and(|layout| layout.size.bytes() == 0) { - // Direct slices and transparent wrappers such as `OsStr` contain elements everywhere. typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false) } else { - // Preserve field offsets for a sized prefix before the slice tail. typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true) }; return TypeTree(vec![Type { diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs index c19202fa41fe5..e0c8c87ca9e33 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/rmake.rs @@ -15,4 +15,5 @@ fn main() { let ir = rfs::read("test.ll"); llvm_filecheck().patterns("slice-dst.check").check_prefix("OSSTR").stdin_buf(&ir).run(); llvm_filecheck().patterns("slice-dst.check").check_prefix("HEADER").stdin_buf(&ir).run(); + llvm_filecheck().patterns("slice-dst.check").check_prefix("ZST").stdin_buf(&ir).run(); } diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check b/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check index 6031b728213ae..4b149c9ee090c 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/slice-dst.check @@ -7,3 +7,8 @@ OSSTR: call void @llvm.memcpy{{.*}}"enzyme_type"="{[0]:Pointer, [0,0]:Pointer, [ HEADER-LABEL: define{{.*}}@header_sum( HEADER-SAME: ptr{{.*}}"enzyme_type"="{[-1]:Pointer, [-1,0]:Float@float, [-1,4]:Float@float}" HEADER-SAME: i64 "enzyme_type"="{[0]:Integer}" + +; ZST elements produce no child metadata under the slice data pointer. +ZST-LABEL: define{{.*}}@zst_slice_len( +ZST-SAME: ptr{{.*}}"enzyme_type"="{[-1]:Pointer}" +ZST-SAME: i64 "enzyme_type"="{[0]:Integer}" diff --git a/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs b/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs index 8cd4d7b57f270..e34a8c71f4cc5 100644 --- a/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs +++ b/tests/run-make/autodiff/type-trees/slice-dst-typetree/test.rs @@ -36,3 +36,16 @@ pub fn header_sum(value: &Header<[f32]>) -> f32 { pub fn exercise_header_sum(value: &Header<[f32]>, derivative: &mut Header<[f32]>) -> f32 { d_header_sum(value, derivative, 1.0) } + +// ZST slice elements yield an empty child TypeTree; element size 0 is expected. +#[autodiff_reverse(d_zst_slice_len, Duplicated, Active)] +#[no_mangle] +#[inline(never)] +pub fn zst_slice_len(slice: &[()]) -> f32 { + slice.len() as f32 +} + +#[no_mangle] +pub fn exercise_zst_slice_len(slice: &[()], derivative: &mut [()]) -> f32 { + d_zst_slice_len(slice, derivative, 1.0) +} From 679481475548fc354e162809e4c64591423c66ef Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 3 Aug 2026 22:52:37 +0000 Subject: [PATCH 20/31] Add some tests for specialization. --- tests/crashes/{126268.rs => 102252-2.rs} | 5 +- tests/crashes/125014.rs | 17 --- ...associated-types-in-default-impl-bounds.rs | 18 +++ ...efault-assoc-type-recursion-issue-80700.rs | 35 +++++ ...ault-impl-coherence-overlap-issue-77026.rs | 27 ++++ ...-impl-coherence-overlap-issue-77026.stderr | 12 ++ ...efault-impl-not-a-candidate-issue-48515.rs | 64 +++++++++ ...lt-impl-not-a-candidate-issue-48515.stderr | 63 +++++++++ .../default-impl-not-an-impl.rs | 71 ++++++++++ .../default-impl-not-an-impl.stderr | 69 ++++++++++ .../default-impl-partial-and-inherits.rs | 111 +++++++++++++++ .../default-type-normalize-issue-50318.rs | 24 ++++ .../default-type-normalize-issue-50318.stderr | 18 +++ ...lf-projection-ice-issue-125014.next.stderr | 66 +++++++++ ...t-type-self-projection-ice-issue-125014.rs | 27 ++++ .../spec-influences-inference-issue-36262.rs | 128 ++++++++++++++++++ ...specialized-impl-projection-issue-32483.rs | 27 ++++ .../trait-alias-specialization-issue-74809.rs | 44 ++++++ 18 files changed, 808 insertions(+), 18 deletions(-) rename tests/crashes/{126268.rs => 102252-2.rs} (86%) delete mode 100644 tests/crashes/125014.rs create mode 100644 tests/ui/specialization/associated-types-in-default-impl-bounds.rs create mode 100644 tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs create mode 100644 tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs create mode 100644 tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr create mode 100644 tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs create mode 100644 tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr create mode 100644 tests/ui/specialization/default-impl-not-an-impl.rs create mode 100644 tests/ui/specialization/default-impl-not-an-impl.stderr create mode 100644 tests/ui/specialization/default-impl-partial-and-inherits.rs create mode 100644 tests/ui/specialization/default-type-normalize-issue-50318.rs create mode 100644 tests/ui/specialization/default-type-normalize-issue-50318.stderr create mode 100644 tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr create mode 100644 tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs create mode 100644 tests/ui/specialization/spec-influences-inference-issue-36262.rs create mode 100644 tests/ui/specialization/specialized-impl-projection-issue-32483.rs create mode 100644 tests/ui/specialization/trait-alias-specialization-issue-74809.rs diff --git a/tests/crashes/126268.rs b/tests/crashes/102252-2.rs similarity index 86% rename from tests/crashes/126268.rs rename to tests/crashes/102252-2.rs index 82e52fa115dc9..ccb15b82736e2 100644 --- a/tests/crashes/126268.rs +++ b/tests/crashes/102252-2.rs @@ -1,4 +1,5 @@ -//@ known-bug: #126268 +//@ known-bug: #102252 + #![feature(min_specialization)] trait Trait {} @@ -16,3 +17,5 @@ struct DatasetIter<'a, R: Data> { pub struct ArrayBase {} impl<'a> Trait for DatasetIter<'a, ArrayBase> {} + +fn main() {} diff --git a/tests/crashes/125014.rs b/tests/crashes/125014.rs deleted file mode 100644 index b29042ee5983a..0000000000000 --- a/tests/crashes/125014.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: rust-lang/rust#125014 -//@ compile-flags: -Znext-solver=coherence -#![feature(specialization)] - -trait Foo {} - -impl Foo for ::Output {} - -impl Foo for u32 {} - -trait Assoc { - type Output; -} -impl Output for u32 {} -impl Assoc for ::Output { - default type Output = bool; -} diff --git a/tests/ui/specialization/associated-types-in-default-impl-bounds.rs b/tests/ui/specialization/associated-types-in-default-impl-bounds.rs new file mode 100644 index 0000000000000..ea7188810db07 --- /dev/null +++ b/tests/ui/specialization/associated-types-in-default-impl-bounds.rs @@ -0,0 +1,18 @@ +//@ check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +// Tests that you can use a trait's associated types in the bounds of a default impl. +// Regression test for #52396. + +trait Foo { + type Baz; + fn bar(&self, _: Self::Baz); +} + +default impl> Foo for A { + fn bar(&self, _: isize) { } +} + +fn main() {} diff --git a/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs b/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs new file mode 100644 index 0000000000000..e013610121efe --- /dev/null +++ b/tests/ui/specialization/default-assoc-type-recursion-issue-80700.rs @@ -0,0 +1,35 @@ +//@ check-pass + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a blanket impl supplying a `default type` does not make a +// recursive trait requirement diverge. +// Regression test for #80700. + +use std::marker::PhantomData; + +struct Nil; +struct Cons(PhantomData<(Head, Tail)>); +struct Error; + +trait GetLast { + type Output; +} + +impl GetLast for T { + default type Output = Error; +} + +impl GetLast for Cons { + type Output = Nil; +} + +impl GetLast for Cons> +where + Cons: GetLast, +{ + type Output = as GetLast>::Output; +} + +fn main() {} diff --git a/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs new file mode 100644 index 0000000000000..6d610805608af --- /dev/null +++ b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.rs @@ -0,0 +1,27 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// `default impl` still participates in coherence. However, we shouldn't get an overflow here. +// Regresion test for #77026. + +pub enum Either { + Left(L), + Right(R), +} + +default impl From for Either { + fn from(l: L) -> Self { + Either::Left(l) + } +} + +impl From for Either { + //~^ ERROR conflicting implementations of trait `From<_>` for type `Either<_, _>` + fn from(r: R) -> Self { + Either::Right(r) + } +} + +fn main() {} diff --git a/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr new file mode 100644 index 0000000000000..c9b30bf2f6495 --- /dev/null +++ b/tests/ui/specialization/default-impl-coherence-overlap-issue-77026.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `From<_>` for type `Either<_, _>` + --> $DIR/default-impl-coherence-overlap-issue-77026.rs:20:1 + | +LL | default impl From for Either { + | ------------------------------------------- first implementation here +... +LL | impl From for Either { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Either<_, _>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs new file mode 100644 index 0000000000000..4e31dcf117daa --- /dev/null +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.rs @@ -0,0 +1,64 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that we don't overflow when using `default impl`. +// Regression test for #48515, #98478, and #117909. + +// #48515 + +trait TypeString { + fn type_string() -> &'static str; +} + +default impl TypeString for T { + fn type_string() -> &'static str { + "unknown type" + } +} + +impl TypeString for () { + fn type_string() -> &'static str { + "()" + } +} + +// #98478 + +trait Spam {} + +trait SpamMore: Spam {} + +default impl Spam for T where T: SpamMore {} + +struct A; + +impl SpamMore for A {} +//~^ ERROR the trait bound `A: Spam` is not satisfied + +fn needs_spam() {} + +// #117909 + +trait Set { + fn contains(&self, bit: T); +} + +default impl Set<&T> for S +where + S: Set, +{ + fn contains(&self, _: &T) {} +} + +fn main() { + let _ = ::type_string(); + //~^ ERROR the trait bound `usize: TypeString` is not satisfied + + needs_spam::(); + //~^ ERROR the trait bound `A: Spam` is not satisfied + + 0u32.contains(()); + //~^ ERROR no method named `contains` found for type `u32` in the current scope +} diff --git a/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr new file mode 100644 index 0000000000000..91df5005d7095 --- /dev/null +++ b/tests/ui/specialization/default-impl-not-a-candidate-issue-48515.stderr @@ -0,0 +1,63 @@ +error[E0277]: the trait bound `A: Spam` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:37:19 + | +LL | impl SpamMore for A {} + | ^ unsatisfied trait bound + | +help: the trait `Spam` is not implemented for `A` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:35:1 + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `SpamMore` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:31:17 + | +LL | trait SpamMore: Spam {} + | ^^^^ required by this bound in `SpamMore` + +error[E0277]: the trait bound `usize: TypeString` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:56:14 + | +LL | let _ = ::type_string(); + | ^^^^^ the trait `TypeString` is not implemented for `usize` + | +help: the trait `TypeString` is implemented for `()` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:21:1 + | +LL | impl TypeString for () { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `A: Spam` is not satisfied + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:59:18 + | +LL | needs_spam::(); + | ^ unsatisfied trait bound + | +help: the trait `Spam` is not implemented for `A` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:35:1 + | +LL | struct A; + | ^^^^^^^^ +note: required by a bound in `needs_spam` + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:40:18 + | +LL | fn needs_spam() {} + | ^^^^ required by this bound in `needs_spam` + +error[E0599]: no method named `contains` found for type `u32` in the current scope + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:62:10 + | +LL | 0u32.contains(()); + | ^^^^^^^^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Set` defines an item `contains`, perhaps you need to implement it + --> $DIR/default-impl-not-a-candidate-issue-48515.rs:44:1 + | +LL | trait Set { + | ^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/specialization/default-impl-not-an-impl.rs b/tests/ui/specialization/default-impl-not-an-impl.rs new file mode 100644 index 0000000000000..2b0902173158e --- /dev/null +++ b/tests/ui/specialization/default-impl-not-an-impl.rs @@ -0,0 +1,71 @@ +//@ check-fail + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a `default impl` does not count as an *actual* impl, so it cannot +// be used to satisfy trait bounds. + +// A `default impl` may omit trait items, but a real impl may not. + +trait Gapped { + fn a(&self) -> u32; + fn b(&self) -> u32; +} + +default impl Gapped for T { + fn a(&self) -> u32 { + 1 + } +} + +impl Gapped for u8 {} +//~^ ERROR not all trait items implemented, missing: `b` + +// A `default impl` that defines *every* trait item is still not an impl. + +trait Foo { + fn f(&self) -> u32; +} + +default impl Foo for T { + fn f(&self) -> u32 { + 1 + } +} + +fn need_foo(t: &T) -> u32 { + t.f() +} + +trait Bar { + fn b(&self) -> u32; +} + +impl Bar for T { + fn b(&self) -> u32 { + self.f() + } +} + +fn need_bar(t: &T) -> u32 { + t.b() +} + +fn main() { + // as a bound (UFCS `::f` is the same trait-selection path, omitted) + need_foo(&0u32); + //~^ ERROR the trait bound `u32: Foo` is not satisfied + + // as a method-probe candidate + 0u32.f(); + //~^ ERROR no method named `f` found for type `u32` in the current scope + + // when building a vtable + let _: &dyn Foo = &0u32; + //~^ ERROR the trait bound `u32: Foo` is not satisfied + + // transitively, as another impl's where-clause + need_bar(&0i64); + //~^ ERROR the trait bound `i64: Bar` is not satisfied +} diff --git a/tests/ui/specialization/default-impl-not-an-impl.stderr b/tests/ui/specialization/default-impl-not-an-impl.stderr new file mode 100644 index 0000000000000..cde757b3f98a7 --- /dev/null +++ b/tests/ui/specialization/default-impl-not-an-impl.stderr @@ -0,0 +1,69 @@ +error[E0046]: not all trait items implemented, missing: `b` + --> $DIR/default-impl-not-an-impl.rs:22:1 + | +LL | fn b(&self) -> u32; + | ------------------- `b` from trait +... +LL | impl Gapped for u8 {} + | ^^^^^^^^^^^^^^^^^^ missing `b` in implementation + +error[E0277]: the trait bound `u32: Foo` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:57:14 + | +LL | need_foo(&0u32); + | -------- ^^^^^ the trait `Foo` is not implemented for `u32` + | | + | required by a bound introduced by this call + | +note: required by a bound in `need_foo` + --> $DIR/default-impl-not-an-impl.rs:37:16 + | +LL | fn need_foo(t: &T) -> u32 { + | ^^^ required by this bound in `need_foo` + +error[E0599]: no method named `f` found for type `u32` in the current scope + --> $DIR/default-impl-not-an-impl.rs:61:10 + | +LL | 0u32.f(); + | ^ method not found in `u32` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `f`, perhaps you need to implement it + --> $DIR/default-impl-not-an-impl.rs:27:1 + | +LL | trait Foo { + | ^^^^^^^^^ + +error[E0277]: the trait bound `u32: Foo` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:65:23 + | +LL | let _: &dyn Foo = &0u32; + | ^^^^^ the trait `Foo` is not implemented for `u32` + | + = note: required for the cast from `&u32` to `&dyn Foo` + +error[E0277]: the trait bound `i64: Bar` is not satisfied + --> $DIR/default-impl-not-an-impl.rs:69:14 + | +LL | need_bar(&0i64); + | -------- ^^^^^ the trait `Foo` is not implemented for `i64` + | | + | required by a bound introduced by this call + | +note: required for `i64` to implement `Bar` + --> $DIR/default-impl-not-an-impl.rs:45:14 + | +LL | impl Bar for T { + | --- ^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `need_bar` + --> $DIR/default-impl-not-an-impl.rs:51:16 + | +LL | fn need_bar(t: &T) -> u32 { + | ^^^ required by this bound in `need_bar` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0277, E0599. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/specialization/default-impl-partial-and-inherits.rs b/tests/ui/specialization/default-impl-partial-and-inherits.rs new file mode 100644 index 0000000000000..a2c622daafb08 --- /dev/null +++ b/tests/ui/specialization/default-impl-partial-and-inherits.rs @@ -0,0 +1,111 @@ +//@ run-pass + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that a `default impl` does not need all items, but does contribute to +// the chain of specialization. + +// A partial `default impl` at each level of a 3-level chain. + +trait Foo { + type Assoc; + const N: u32; + fn from_root(&self) -> &'static str; + fn from_mid(&self) -> &'static str; + fn from_leaf(&self) -> &'static str; + fn from_trait(&self) -> &'static str { + "trait body" + } +} + +// root: assoc type, assoc const, one method +default impl Foo for T { + type Assoc = u8; + const N: u32 = 1; + fn from_root(&self) -> &'static str { + "root" + } +} + +// middle: one method +default impl Foo for T { + fn from_mid(&self) -> &'static str { + "mid" + } +} + +// leaf: one method. Everything else must come from the two ancestors, except +// `from_trait`, which no impl in the chain defines. +impl Foo for u32 { + fn from_leaf(&self) -> &'static str { + "leaf" + } +} + +// sibling leaf: overrides every inherited item, including assoc type and const +impl Foo for i8 { + type Assoc = bool; + const N: u32 = 2; + fn from_root(&self) -> &'static str { + "i8 root" + } + fn from_mid(&self) -> &'static str { + "i8 mid" + } + fn from_leaf(&self) -> &'static str { + "i8 leaf" + } + fn from_trait(&self) -> &'static str { + "i8 trait" + } +} + +fn generic(t: &T) -> [&'static str; 4] { + [t.from_root(), t.from_mid(), t.from_leaf(), t.from_trait()] +} + +// An empty `default impl`, and an empty real impl that inherits every item. + +trait Marker { + type A; + fn m(&self) -> &'static str; +} + +// Contributes nothing at all, and is still accepted. +default impl Marker for T {} + +// Covers every item of the trait. +default impl Marker for T { + type A = u8; + fn m(&self) -> &'static str { + "from default impl" + } +} + +// Declaration of intent and nothing else. This is what the `default impl` above +// is missing, and the only thing it is missing. +impl Marker for u32 {} + +fn main() { + // inherited across the chain, via a concrete receiver... + assert_eq!(0u32.from_root(), "root"); + assert_eq!(0u32.from_mid(), "mid"); + assert_eq!(0u32.from_leaf(), "leaf"); + assert_eq!(0u32.from_trait(), "trait body"); + assert_eq!(::N, 1); + // The omitting impl finalizes the ancestor's definition, so this normalizes. + let _: ::Assoc = 0u8; + + // ...and through a generic bound + assert_eq!(generic(&0u32), ["root", "mid", "leaf", "trait body"]); + assert_eq!(generic(&0i8), ["i8 root", "i8 mid", "i8 leaf", "i8 trait"]); + assert_eq!(::N, 2); + let _: ::Assoc = true; + + // empty impl really does implement: method, projection, and vtable + assert_eq!(0u32.m(), "from default impl"); + let _: ::A = 0u8; + let _: &dyn Marker = &0u32; + +} diff --git a/tests/ui/specialization/default-type-normalize-issue-50318.rs b/tests/ui/specialization/default-type-normalize-issue-50318.rs new file mode 100644 index 0000000000000..b69acfe47a929 --- /dev/null +++ b/tests/ui/specialization/default-type-normalize-issue-50318.rs @@ -0,0 +1,24 @@ +//@ check-fail +//@ known-bug: #50318 + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Tests that we can normalize a `default type`. + +trait Trait { + type AssocType; +} + +struct Struct {} + +impl Trait for Struct { + default type AssocType = i32; +} + +type AssocType = ::AssocType; + +fn main() { + assert_eq!(std::any::type_name::(), "i32"); + let x: AssocType = 0; +} diff --git a/tests/ui/specialization/default-type-normalize-issue-50318.stderr b/tests/ui/specialization/default-type-normalize-issue-50318.stderr new file mode 100644 index 0000000000000..b0d69287adac2 --- /dev/null +++ b/tests/ui/specialization/default-type-normalize-issue-50318.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/default-type-normalize-issue-50318.rs:23:24 + | +LL | let x: AssocType = 0; + | --------- ^ expected associated type, found integer + | | + | expected due to this + | + = note: expected associated type `::AssocType` + found type `{integer}` + = help: consider constraining the associated type `::AssocType` to `{integer}` or calling a method that returns `::AssocType` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + = note: the associated type `::AssocType` is defined as `{integer}` in the implementation, but the where-bound `Struct` shadows this definition + see issue #152409 for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr new file mode 100644 index 0000000000000..7280c8213e5dd --- /dev/null +++ b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.next.stderr @@ -0,0 +1,66 @@ +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:23:22 + | +LL | default type B = (); + | ^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `u16: A` is not satisfied + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:12 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^ the trait `A` is not implemented for `u16` + | +help: the trait `A` is implemented for `::B` + --> $DIR/default-type-self-projection-ice-issue-125014.rs:18:1 + | +LL | impl A for ::B { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs new file mode 100644 index 0000000000000..9b4e3eade03b6 --- /dev/null +++ b/tests/ui/specialization/default-type-self-projection-ice-issue-125014.rs @@ -0,0 +1,27 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[current] known-bug: #125014 +//@[current] failure-status: 101 +//@[current] dont-check-compiler-stderr + +// Tests that we don't ICE when a `default type` is potentially used as a self-type in an impl. +// Regression for #125014. + +#![feature(specialization)] +#![allow(incomplete_features)] + +trait A { + type B; +} + +impl A for ::B { + //[next]~^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^^ ERROR the trait bound `u16: A` is not satisfied + //[next]~^^^^ ERROR the trait bound `u16: A` is not satisfied + default type B = (); + //[next]~^ ERROR the trait bound `u16: A` is not satisfied +} + +fn main() {} diff --git a/tests/ui/specialization/spec-influences-inference-issue-36262.rs b/tests/ui/specialization/spec-influences-inference-issue-36262.rs new file mode 100644 index 0000000000000..1e96fd5065097 --- /dev/null +++ b/tests/ui/specialization/spec-influences-inference-issue-36262.rs @@ -0,0 +1,128 @@ +//@ edition: 2021 +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass +//@[current] known-bug: #36262 +//@[current] dont-check-compiler-stderr + +// Tests that specialization does not leak into type inference. +// Regression for #36262 and duplicate issues. + +#![feature(specialization)] +#![allow(incomplete_features)] + +// Site 1: the receiver's type parameter, observed through a return position (#36262). +mod receiver_return { + struct My(T); + + trait Conv { + fn conv(self) -> T; + } + + impl Conv for My { + default fn conv(self) -> T { + self.0 + } + } + + impl Conv for My { + fn conv(self) -> u32 { + self.0 + } + } + + fn use_it() { + // Should infer `i32`; the sole `My` impl steers it to `u32`. + let x = My(0); + let _ = x.conv() + 0i32; + } +} + +// Site 2: a method argument's trait type parameter (#91973, #38516, #67918). +mod method_arg { + struct Foo; + + trait Bar { + fn bar(&self, _: T); + } + + impl Bar for Foo { + default fn bar(&self, _: T) {} + } + + impl Bar for Foo { + fn bar(&self, _: bool) {} + } + + fn use_it() { + // Should infer `{integer}`; the sole `Bar` impl steers it to `bool`. + Foo.bar(42); + } +} + +// Site 3: an explicit `_` in a UFCS trait reference (#40718). +mod ufcs_infer { + use std::vec; + + struct Foo(T); + + impl Foo { + fn build>(it: I) -> Foo { + // The second argument should infer to `I::IntoIter`; the sole + // `vec::IntoIter` impl steers it there. + >::from_iter(it.into_iter()) + } + } + + trait SpecExtend { + fn from_iter(iter: I) -> Self; + } + + impl SpecExtend for Foo + where + I: Iterator, + { + default fn from_iter(_: I) -> Self { + panic!() + } + } + + impl SpecExtend> for Foo { + fn from_iter(_: vec::IntoIter) -> Self { + panic!() + } + } +} + +// Site 4: an operator, where the sole specialization is derive-generated (#55243). +mod derived_specializer { + use std::borrow::Borrow; + + #[derive(PartialEq)] + struct MyString(String); + + impl Borrow for MyString { + fn borrow(&self) -> &str { + &self.0 + } + } + + impl PartialEq for MyString + where + Rhs: ?Sized + Borrow, + { + default fn eq(&self, rhs: &Rhs) -> bool { + self.0 == rhs.borrow() + } + } + + fn use_it() { + // Should select `PartialEq`; the derived `PartialEq` is the + // sole specialization and inference commits `Rhs = MyString`. + let s = MyString(String::from("Hello, world!")); + let _ = s == "Hello, world!"; + } +} + +fn main() {} diff --git a/tests/ui/specialization/specialized-impl-projection-issue-32483.rs b/tests/ui/specialization/specialized-impl-projection-issue-32483.rs new file mode 100644 index 0000000000000..5b26679422a40 --- /dev/null +++ b/tests/ui/specialization/specialized-impl-projection-issue-32483.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![allow(incomplete_features)] +#![feature(specialization)] + +// Tests that we allow some projections in specialized impls. +// Regression test for issue #32483. + +pub trait Foo { + type TypeA; + type TypeB: Bar; +} + +pub trait Bar { +} + +pub struct ImplsBar; +impl Bar for ImplsBar { +} + +impl Foo for T { + type TypeA = u8; + // WF checking `TypeB` here requires us to project `Self::TypeA` + default type TypeB = ImplsBar; +} + +fn main() {} diff --git a/tests/ui/specialization/trait-alias-specialization-issue-74809.rs b/tests/ui/specialization/trait-alias-specialization-issue-74809.rs new file mode 100644 index 0000000000000..e62532e8ab033 --- /dev/null +++ b/tests/ui/specialization/trait-alias-specialization-issue-74809.rs @@ -0,0 +1,44 @@ +//@ check-pass + +#![feature(specialization)] +#![feature(trait_alias)] +#![allow(incomplete_features)] + +// Tests that we can specialize on a trait alias. +// Regression test for #74809. + +pub trait Marker1 {} +pub trait Marker2 {} + +pub trait CombinedMarker = Marker1 + Marker2; + +pub struct Container { + p: std::marker::PhantomData<(T, U)>, +} + +pub struct Struct; +impl Marker1 for Struct {} + +pub trait Trait { + fn do_thing(&self); +} + +impl> Trait for Container { + default fn do_thing(&self) { + println!("default behavior"); + } +} + +impl> Trait for Container { + default fn do_thing(&self) { + println!("partially specialized behavior"); + } +} + +impl Trait for Container { + fn do_thing(&self) { + println!("fully specialized behavior") + } +} + +fn main() {} From 54877b4f7c6643362df6b89e9876c2c594480faf Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 2 Aug 2026 15:58:14 +0300 Subject: [PATCH 21/31] rustc_codegen_ssa: Correctly apply the static `--jobs-backend` limit to backend parallelism --- compiler/rustc_codegen_ssa/src/back/write.rs | 34 ++++++++++++-------- src/doc/rustc/src/command-line-arguments.md | 4 --- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index e35300902aabd..d4929308fb13f 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::num::NonZero; use std::panic::AssertUnwindSafe; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -356,10 +357,10 @@ pub struct CodegenContext { /// The incremental compilation session directory, or None if we are not /// compiling incrementally pub incr_comp_session_dir: Option, - /// `true` if the codegen should be run in parallel. + /// `Some(limit)` if the codegen should be run in parallel. /// /// Depends on [`WriteBackendMethods::supports_parallel()`] and `--jobs-backend`. - pub parallel: bool, + pub parallel: Option>, } fn generate_thin_lto_work( @@ -1021,7 +1022,7 @@ fn do_thin_lto( // Note that using `jobserver::Proxy` is not necessary here, the code below always acquires // tokens before releasing them, so we can never accidentally release the last token // permanently held by rustc process. - let jobserver_helper = cgcx.parallel.then(|| { + let jobserver_helper = cgcx.parallel.map(|_| { let coordinator_send2 = coordinator_send.clone(); jobserver::client() .into_helper_thread(move |token| { @@ -1037,18 +1038,23 @@ fn do_thin_lto( // bunch of work items onto our queue to do LTO. This all // happens on the coordinator thread but it's very quick so // we don't worry about tokens. - for (work, cost) in generate_thin_lto_work::( + for (i, (work, cost)) in generate_thin_lto_work::( cgcx, prof, dcx, &exported_symbols_for_lto, &each_linked_rlib_for_lto, needs_thin_lto, - ) { + ) + .into_iter() + .enumerate() + { let insertion_index = work_items.binary_search_by_key(&cost, |&(_, cost)| cost).unwrap_or_else(|e| e); work_items.insert(insertion_index, (work, cost)); - if let Some(helper) = &jobserver_helper { + if let Some(helper) = &jobserver_helper + && i < cgcx.parallel.unwrap().get() + { helper.request_token(); } } @@ -1255,12 +1261,11 @@ fn start_executing_work( // Note that using `jobserver::Proxy` is not necessary here, the code below always acquires // tokens before releasing them, so we can never accidentally release the last token // permanently held by rustc process. - // FIXME: the backend parallelism is currently limited solely by the jobserver, - // so if `--jobs-backend` is smaller than `--jobs(-frontend)`, or than the number of tokens - // that the external jobserver can give, then it won't be respected. - // Below we'll need to add some additional work limiting for `--jobs-backend` to be respected. - let parallel = sess.opts.jobs.backend.is_some() && backend.supports_parallel(); - let jobserver_helper = parallel.then(|| { + let parallel = match sess.opts.jobs.backend { + Some(n) if backend.supports_parallel() => Some(n), + _ => None, + }; + let jobserver_helper = parallel.map(|_| { let coordinator_send2 = coordinator_send.clone(); jobserver::client() .into_helper_thread(move |token| { @@ -1655,7 +1660,10 @@ fn start_executing_work( }; work_items.insert(insertion_index, (llvm_work_item, cost)); - if let Some(helper) = &jobserver_helper { + if let Some(helper) = &jobserver_helper + && running_with_any_token(main_thread_state, running_with_own_token) + < cgcx.parallel.unwrap().get() + { helper.request_token(); } assert_eq!(main_thread_state, MainThreadState::Codegenning); diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index f9e97530214fe..52dc58f65bb8b 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -516,10 +516,6 @@ Parallelism used by compilation stages converting backend IR to object files. In any case the parallelism here may be additionally limited dynamically by jobserver passed from a higher level build system like cargo. -Note: the backend parallelism limit may currently work incorrectly if `jobs-frontend` or `jobs` -have larger value than `jobs-backend`, or if the inherited jobserver can give a larger number -of tokens. - ### Linker parallelism Parallelism used by linker when combining object files into a final binary. From 9956e2f9a2e604ccacdb4499d5fcdd5c72fbb965 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 4 Aug 2026 23:46:39 +0300 Subject: [PATCH 22/31] Do not forget to initialize jobserver if only linker is parallel --- compiler/rustc_interface/src/interface.rs | 5 +++-- compiler/rustc_session/src/config.rs | 9 +++++++++ tests/ui/compile-flags/jobs/jobs-pass-link.rs | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 18f869d24cbfb..2737d2ca854a5 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -375,7 +375,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se // Initialize jobserver as early as possible. let early_dcx = EarlyDiagCtxt::new(config.opts.error_format); - if let Some(limit) = config.opts.jobs.frontend.max(config.opts.jobs.backend) { + let jobs = config.opts.jobs; + if let Some(limit) = jobs.frontend.max(jobs.backend).max(jobs.linker.limit()) { jobserver::initialize(limit.get(), |err| { let note = "the build environment is likely misconfigured"; early_dcx.early_struct_warn(err).with_note(note).emit() @@ -398,7 +399,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se util::run_in_thread_pool_with_globals( &early_dcx, config.opts.edition, - config.opts.jobs, + jobs, &config.extra_symbols, SourceMapInputs { file_loader, path_mapping, hash_kind, checksum_hash_kind }, |current_gcx| { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e3579c87e69f7..75b87a5909ac9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1665,6 +1665,15 @@ pub enum LinkerJobs { Explicit(NonZero), } +impl LinkerJobs { + pub fn limit(self) -> Option> { + match self { + LinkerJobs::Default => None, + LinkerJobs::Explicit(n) => Some(n), + } + } +} + /// `None` for frontend and backend means everything is single-threaded /// and synchronization can be disabled. #[derive(Clone, Copy)] diff --git a/tests/ui/compile-flags/jobs/jobs-pass-link.rs b/tests/ui/compile-flags/jobs/jobs-pass-link.rs index 70058dfcf7bf6..847a0d10ff977 100644 --- a/tests/ui/compile-flags/jobs/jobs-pass-link.rs +++ b/tests/ui/compile-flags/jobs/jobs-pass-link.rs @@ -1,4 +1,4 @@ //@ build-pass -//@ compile-flags: -Z unstable-options --jobs-linker 2 +//@ compile-flags: -Z unstable-options --jobs-linker 2 --jobs-backend 1 fn main() {} From 228bbb36ad155d5fbd2d1f783742a4c7ecd2e0c5 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 4 Aug 2026 20:11:32 +0100 Subject: [PATCH 23/31] fix(bootstrap): Normalize the names of proc macro dependency crates --- src/bootstrap/src/utils/proc_macro_deps.rs | 56 +++++++++++----------- src/tools/tidy/src/deps.rs | 11 ++++- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 8e91f95e18f3f..02d8fe41f5869 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -6,46 +6,46 @@ pub static CRATES: &[&str] = &[ "anyhow", "askama_derive", "askama_parser", - "basic-toml", + "basic_toml", "bitflags", - "block-buffer", + "block_buffer", "bumpalo", - "cfg-if", + "cfg_if", "cpufeatures", - "crypto-common", + "crypto_common", "darling", "darling_core", "derive_builder_core", "digest", "equivalent", - "fluent-bundle", - "fluent-langneg", - "fluent-syntax", + "fluent_bundle", + "fluent_langneg", + "fluent_syntax", "fnv", "foldhash", - "generic-array", + "generic_array", "glob", "hashbrown", "heck", - "id-arena", + "id_arena", "ident_case", "indexmap", - "intl-memoizer", + "intl_memoizer", "intl_pluralrules", "itoa", "leb128fmt", "libc", "log", "memchr", - "minimal-lexical", + "minimal_lexical", "nom", "pest", "pest_generator", "pest_meta", "prettyplease", - "proc-macro2", + "proc_macro2", "quote", - "rustc-hash", + "rustc_hash", "ryu", "self_cell", "semver", @@ -61,25 +61,25 @@ pub static CRATES: &[&str] = &[ "synstructure", "thiserror", "tinystr", - "type-map", + "type_map", "typenum", - "ucd-trie", - "unic-langid", - "unic-langid-impl", - "unic-langid-macros", - "unicode-ident", - "unicode-xid", + "ucd_trie", + "unic_langid", + "unic_langid_impl", + "unic_langid_macros", + "unicode_ident", + "unicode_xid", "version_check", - "wasm-bindgen-macro-support", - "wasm-bindgen-shared", - "wasm-encoder", - "wasm-metadata", + "wasm_bindgen_macro_support", + "wasm_bindgen_shared", + "wasm_encoder", + "wasm_metadata", "wasmparser", "winnow", - "wit-bindgen-core", - "wit-bindgen-rust", - "wit-component", - "wit-parser", + "wit_bindgen_core", + "wit_bindgen_rust", + "wit_component", + "wit_parser", "yoke", "zerofrom", "zerovec", diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 479199414d7ec..734ca79518090 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -719,8 +719,15 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut // Remove the proc-macro crates themselves proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg])); // Sort and deduplicate the crate names. - let proc_macro_deps = - proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect::>(); + // Cargo package names may contain `-`, but will normalize these to `_` before passing to rustc. + // As bootstrap parses the `--crate-name` flag, use the name of the actual lib target which has + // been normalized. + let proc_macro_deps = proc_macro_deps + .into_iter() + .filter_map(|dep| { + metadata[dep].targets.iter().find_map(|target| target.is_lib().then_some(&target.name)) + }) + .collect::>(); let expected = { use std::fmt::Write; From a6dfd0cc18614a4232d0e533539bad9981a49efd Mon Sep 17 00:00:00 2001 From: derek-homel Date: Tue, 4 Aug 2026 19:02:23 -0400 Subject: [PATCH 24/31] docs: fix typo in AllowExprMetavar comment Fixes a small typo in the documentation comment for AllowExprMetavar. Changes decrarative to `declarative`. Change in compiler/rustc_attr_parsing/src/parser.rs: Line 492 --- compiler/rustc_attr_parsing/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index c4b2a5b509051..76587ba9f0ead 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -489,7 +489,7 @@ fn expr_to_lit<'sess>( } } -/// Whether expansions of `expr` metavariables from decrarative macros +/// Whether expansions of `expr` metavariables from declarative macros /// are permitted. Used when parsing meta items; currently, only `cfg` predicates /// enable this option #[derive(Clone, Copy, PartialEq, Eq)] From b5687751ea4f06cd14b48dd159d283e22af1a778 Mon Sep 17 00:00:00 2001 From: CacinieP Date: Wed, 5 Aug 2026 11:22:20 +0800 Subject: [PATCH 25/31] Update expect messages in tcp.rs doc examples to follow the style guide Reword the `.expect(...)` messages in the TcpStream/TcpListener doc examples in library/std/src/net/tcp.rs to follow the 'expect as precondition' style from the std library guidance (describe why the operation is expected to succeed, rather than restating the failure). Examples: "set_nodelay call failed" -> "set_nodelay should succeed" "could not set TTL" -> "set_ttl should succeed" "Cannot set non-blocking" -> "set_nonblocking should succeed" Doc-only change, no behavior change. --- library/std/src/net/tcp.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index b673abdff7ba1..d9090320bd5a6 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -239,7 +239,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); + /// stream.shutdown(Shutdown::Both).expect("shutdown should succeed"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -260,7 +260,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// let stream_clone = stream.try_clone().expect("clone failed..."); + /// let stream_clone = stream.try_clone().expect("clone should succeed"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_clone(&self) -> io::Result { @@ -290,7 +290,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// stream.set_read_timeout(None).expect("set_read_timeout should succeed"); /// ``` /// /// An [`Err`] is returned if the zero [`Duration`] is passed to this @@ -334,7 +334,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// stream.set_write_timeout(None).expect("set_write_timeout should succeed"); /// ``` /// /// An [`Err`] is returned if the zero [`Duration`] is passed to this @@ -372,7 +372,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// stream.set_read_timeout(None).expect("set_read_timeout should succeed"); /// assert_eq!(stream.read_timeout().unwrap(), None); /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] @@ -397,7 +397,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// stream.set_write_timeout(None).expect("set_write_timeout should succeed"); /// assert_eq!(stream.write_timeout().unwrap(), None); /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] @@ -420,7 +420,7 @@ impl TcpStream { /// let stream = TcpStream::connect("127.0.0.1:8000") /// .expect("Couldn't connect to the server..."); /// let mut buf = [0; 10]; - /// let len = stream.peek(&mut buf).expect("peek failed"); + /// let len = stream.peek(&mut buf).expect("peek should succeed"); /// ``` #[stable(feature = "peek", since = "1.18.0")] pub fn peek(&self, buf: &mut [u8]) -> io::Result { @@ -445,7 +445,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); + /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger should succeed"); /// ``` #[unstable(feature = "tcp_linger", issue = "88494")] pub fn set_linger(&self, linger: Option) -> io::Result<()> { @@ -466,7 +466,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger call failed"); + /// stream.set_linger(Some(Duration::from_secs(0))).expect("set_linger should succeed"); /// assert_eq!(stream.linger().unwrap(), Some(Duration::from_secs(0))); /// ``` #[unstable(feature = "tcp_linger", issue = "88494")] @@ -498,7 +498,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_keepalive(true).expect("set_keepalive call failed"); + /// stream.set_keepalive(true).expect("set_keepalive should succeed"); #[unstable(feature = "tcp_keepalive", issue = "155889")] pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> { self.0.set_keepalive(keepalive) @@ -517,7 +517,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_keepalive(true).expect("set_keepalive call failed"); + /// stream.set_keepalive(true).expect("set_keepalive should succeed"); /// assert_eq!(stream.keepalive().unwrap_or(false), true); /// ``` #[unstable(feature = "tcp_keepalive", issue = "155889")] @@ -540,7 +540,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// stream.set_nodelay(true).expect("set_nodelay should succeed"); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { @@ -558,7 +558,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// stream.set_nodelay(true).expect("set_nodelay should succeed"); /// assert_eq!(stream.nodelay().unwrap_or(false), true); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] @@ -578,7 +578,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); + /// stream.set_ttl(100).expect("set_ttl should succeed"); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { @@ -596,7 +596,7 @@ impl TcpStream { /// /// let stream = TcpStream::connect("127.0.0.1:8080") /// .expect("Couldn't connect to the server..."); - /// stream.set_ttl(100).expect("set_ttl call failed"); + /// stream.set_ttl(100).expect("set_ttl should succeed"); /// assert_eq!(stream.ttl().unwrap_or(0), 100); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] @@ -647,7 +647,7 @@ impl TcpStream { /// /// let mut stream = TcpStream::connect("127.0.0.1:7878") /// .expect("Couldn't connect to the server..."); - /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); + /// stream.set_nonblocking(true).expect("set_nonblocking should succeed"); /// /// # fn wait_for_fd() { unimplemented!() } /// let mut buf = vec![]; @@ -1006,7 +1006,7 @@ impl TcpListener { /// use std::net::TcpListener; /// /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); + /// listener.set_ttl(100).expect("set_ttl should succeed"); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { @@ -1023,7 +1023,7 @@ impl TcpListener { /// use std::net::TcpListener; /// /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); - /// listener.set_ttl(100).expect("could not set TTL"); + /// listener.set_ttl(100).expect("set_ttl should succeed"); /// assert_eq!(listener.ttl().unwrap_or(0), 100); /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] @@ -1086,7 +1086,7 @@ impl TcpListener { /// use std::net::TcpListener; /// /// let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); - /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); + /// listener.set_nonblocking(true).expect("set_nonblocking should succeed"); /// /// # fn wait_for_fd() { unimplemented!() } /// # fn handle_connection(stream: std::net::TcpStream) { unimplemented!() } From eace512093ce4d96afcb7352f595bb72f1ba6bb8 Mon Sep 17 00:00:00 2001 From: YingqiDuan <141370165+YingqiDuan@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:31:06 +0000 Subject: [PATCH 26/31] Suggest cast_signed for overflowing integer literals Co-authored-by: Roland Xu --- compiler/rustc_lint/src/lints.rs | 39 +++++++++++++------ compiler/rustc_lint/src/types/literal.rs | 26 +++++++++---- .../no-inline-literals-out-of-range.stderr | 9 +++-- tests/ui/lint/type-overflow.stderr | 14 ++++--- 4 files changed, 59 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index bff3b79df8655..07279a04b0c8c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2030,18 +2030,33 @@ pub(crate) enum OverflowingBinHexSub<'a> { } #[derive(Subdiagnostic)] -#[suggestion( - "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", - code = "{lit_no_suffix}{uint_ty} as {int_ty}", - applicability = "maybe-incorrect" -)] -pub(crate) struct OverflowingBinHexSignBitSub<'a> { - #[primary_span] - pub span: Span, - pub lit_no_suffix: &'a str, - pub negative_val: String, - pub uint_ty: &'a str, - pub int_ty: &'a str, +pub(crate) enum OverflowingBinHexSignBitSub<'a> { + #[suggestion( + "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", + code = "{lit_no_suffix}{uint_ty}.cast_signed()", + applicability = "maybe-incorrect" + )] + CastSigned { + #[primary_span] + span: Span, + lit_no_suffix: &'a str, + negative_val: String, + uint_ty: &'a str, + int_ty: &'a str, + }, + #[suggestion( + "to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`", + code = "{lit_no_suffix}{uint_ty} as {int_ty}", + applicability = "maybe-incorrect" + )] + AsCast { + #[primary_span] + span: Span, + lit_no_suffix: &'a str, + negative_val: String, + uint_ty: &'a str, + int_ty: &'a str, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index bed26ee6f3d25..4759f087ed08b 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -205,13 +205,25 @@ fn report_bin_hex_error( &repr_str }; - Some(OverflowingBinHexSignBitSub { - span, - lit_no_suffix, - negative_val: actually, - int_ty: int_ty.name_str(), - uint_ty: Integer::fit_unsigned(val).uint_ty_str(), - }) + let uint_ty = Integer::fit_unsigned(val); + // `cast_signed` only supports equal-width integer casts. + if uint_ty.size() == size { + Some(OverflowingBinHexSignBitSub::CastSigned { + span, + lit_no_suffix, + negative_val: actually, + uint_ty: uint_ty.uint_ty_str(), + int_ty: int_ty.name_str(), + }) + } else { + Some(OverflowingBinHexSignBitSub::AsCast { + span, + lit_no_suffix, + negative_val: actually, + uint_ty: uint_ty.uint_ty_str(), + int_ty: int_ty.name_str(), + }) + } }) .flatten(); diff --git a/tests/ui/fmt/no-inline-literals-out-of-range.stderr b/tests/ui/fmt/no-inline-literals-out-of-range.stderr index 0800fb2497619..744a4e5625fef 100644 --- a/tests/ui/fmt/no-inline-literals-out-of-range.stderr +++ b/tests/ui/fmt/no-inline-literals-out-of-range.stderr @@ -13,8 +13,9 @@ LL + format_args!("{}", 0x8f_u8); // issue #115423 | help: to use as a negative number (decimal `-113`), consider using the type `u8` for the literal and cast it to `i8` | -LL | format_args!("{}", 0x8f_u8 as i8); // issue #115423 - | +++++ +LL - format_args!("{}", 0x8f_i8); // issue #115423 +LL + format_args!("{}", 0x8f_u8.cast_signed()); // issue #115423 + | error: literal out of range for `u8` --> $DIR/no-inline-literals-out-of-range.rs:6:24 @@ -50,8 +51,8 @@ LL | format_args!("{}", 0xffff_ffff); // treat unsuffixed literals as i32 = help: consider using the type `u32` instead help: to use as a negative number (decimal `-1`), consider using the type `u32` for the literal and cast it to `i32` | -LL | format_args!("{}", 0xffff_ffffu32 as i32); // treat unsuffixed literals as i32 - | ++++++++++ +LL | format_args!("{}", 0xffff_ffffu32.cast_signed()); // treat unsuffixed literals as i32 + | +++++++++++++++++ error: aborting due to 5 previous errors diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index 065c530adcf57..66d856dac3bdf 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -26,8 +26,9 @@ LL + let fail = 0b1000_0001u8; | help: to use as a negative number (decimal `-127`), consider using the type `u8` for the literal and cast it to `i8` | -LL | let fail = 0b1000_0001u8 as i8; - | +++++ +LL - let fail = 0b1000_0001i8; +LL + let fail = 0b1000_0001u8.cast_signed(); + | warning: literal out of range for `i64` --> $DIR/type-overflow.rs:15:16 @@ -43,8 +44,9 @@ LL + let fail = 0x8000_0000_0000_0000u64; | help: to use as a negative number (decimal `-9223372036854775808`), consider using the type `u64` for the literal and cast it to `i64` | -LL | let fail = 0x8000_0000_0000_0000u64 as i64; - | ++++++ +LL - let fail = 0x8000_0000_0000_0000i64; +LL + let fail = 0x8000_0000_0000_0000u64.cast_signed(); + | warning: literal out of range for `u32` --> $DIR/type-overflow.rs:19:16 @@ -64,8 +66,8 @@ LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; = help: consider using the type `u128` instead help: to use as a negative number (decimal `-170141183460469231731687303715884105728`), consider using the type `u128` for the literal and cast it to `i128` | -LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128 as i128; - | ++++++++++++ +LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.cast_signed(); + | ++++++++++++++++++ warning: literal out of range for `i32` --> $DIR/type-overflow.rs:27:16 From a91590b3f11005b2146066bd9a52e1fc1b16f71a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Aug 2026 10:43:34 +0200 Subject: [PATCH 27/31] move mir-opt miri tests to CI logic also refactor check-miri a bit to make it easier to read --- src/bootstrap/src/core/build_steps/test.rs | 24 ------------------ .../host-x86_64/x86_64-gnu-miri/check-miri.sh | 25 ++++++++++--------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 3015d5a83db8d..f9846b7b41150 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -769,30 +769,6 @@ impl CommandLineStep for Miri { let _time = helpers::timeit(builder); cargo.run(builder); } - - // Run it again for mir-opt-level 4 to catch some miscompilations. - if builder.config.test_args().is_empty() { - cargo.env( - "MIRIFLAGS", - format!( - "{} -O -Zmir-opt-level=4 -Cdebug-assertions=yes", - env::var("MIRIFLAGS").unwrap_or_default() - ), - ); - // Optimizations can change backtraces - cargo.env("MIRI_SKIP_UI_CHECKS", "1"); - // `MIRI_SKIP_UI_CHECKS` and `RUSTC_BLESS` are incompatible - cargo.env_remove("RUSTC_BLESS"); - // Optimizations can change error locations and remove UB so don't run `fail` tests. - cargo.args(["tests/pass", "tests/panic"]); - - { - let _guard = - builder.msg_test("miri (mir-opt-level 4)", target, target_compiler.stage); - let _time = helpers::timeit(builder); - cargo.run(builder); - } - } } } diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh index 8d7206d7391e2..9d4ec50e5f534 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/check-miri.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # ignore-tidy-file-linelength set -eu @@ -15,6 +15,8 @@ if [ -z "${PR_CI_JOB:-}" ]; then else python3 "$X_PY" test --stage 2 miri cargo-miri fi +# Run the test suite again with mir optimizations, to catch some miscompilations. +MIRIFLAGS="-O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 python3 "$X_PY" test --stage 2 miri -- tests/{pass,panic} # We natively run this script on x86_64-unknown-linux-gnu and x86_64-pc-windows-msvc. # Also cover some other targets via cross-testing, in particular all tier 1 targets. case $HOST_TARGET in @@ -23,13 +25,12 @@ case $HOST_TARGET in # Fully test all main OSes, and all main architectures. python3 "$X_PY" test --stage 2 miri cargo-miri --target aarch64-apple-darwin python3 "$X_PY" test --stage 2 miri cargo-miri --target i686-pc-windows-msvc - # Only run "pass" tests for the remaining targets, which is quite a bit faster. - # We have to use `miri` instead of `src/tools/miri` here to avoid also running the cargo-miri - # tests. - python3 "$X_PY" test --stage 2 miri --target x86_64-pc-windows-gnu --test-args pass - python3 "$X_PY" test --stage 2 miri --target i686-unknown-linux-gnu --test-args pass - python3 "$X_PY" test --stage 2 miri --target aarch64-unknown-linux-gnu --test-args pass - python3 "$X_PY" test --stage 2 miri --target s390x-unknown-linux-gnu --test-args pass + # Only run "pass" tests for the remaining targets, which is a bit faster. We have to use `miri` + # instead of `src/tools/miri` here to avoid also running the cargo-miri tests. + python3 "$X_PY" test --stage 2 miri --target x86_64-pc-windows-gnu -- tests/pass + python3 "$X_PY" test --stage 2 miri --target i686-unknown-linux-gnu -- tests/pass + python3 "$X_PY" test --stage 2 miri --target aarch64-unknown-linux-gnu -- tests/pass + python3 "$X_PY" test --stage 2 miri --target s390x-unknown-linux-gnu -- tests/pass ;; x86_64-pc-windows-msvc) # Strangely, Linux targets do not work here. cargo always says @@ -38,7 +39,7 @@ case $HOST_TARGET in #FIXME: Re-enable this once CI issues are fixed # See # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. - #python3 "$X_PY" test --stage 2 miri --target x86_64-apple-darwin --test-args pass + #python3 "$X_PY" test --stage 2 miri --target x86_64-apple-darwin -- pass ;; *) echo "FATAL: unexpected host $HOST_TARGET" @@ -50,7 +51,7 @@ esac #FIXME: Re-enable this for msvc once CI issues are fixed if [ "$HOST_TARGET" != "x86_64-pc-windows-msvc" ]; then - python3 "$X_PY" miri --stage 2 library/core --test-args notest - python3 "$X_PY" miri --stage 2 library/alloc --test-args notest - python3 "$X_PY" miri --stage 2 library/std --test-args notest + python3 "$X_PY" miri --stage 2 library/core -- notest + python3 "$X_PY" miri --stage 2 library/alloc -- notest + python3 "$X_PY" miri --stage 2 library/std -- notest fi From 5b9efe22022bcac5214ee6744ef871078f2c313c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 30 Jul 2026 00:30:37 -0700 Subject: [PATCH 28/31] Rework `smallest_range_containing` to handle duplicates --- compiler/rustc_abi/src/tests.rs | 62 ++++++++++++++++++++++++ compiler/rustc_abi/src/wrapping_range.rs | 54 ++++++++++----------- 2 files changed, 88 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_abi/src/tests.rs b/compiler/rustc_abi/src/tests.rs index d49c2d44af84d..1ec6a35401068 100644 --- a/compiler/rustc_abi/src/tests.rs +++ b/compiler/rustc_abi/src/tests.rs @@ -6,6 +6,68 @@ fn align_constants() { assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap()); } +#[test] +#[should_panic(expected = "Value 299 is too big for Size(1 bytes)")] +fn wrapping_range_smallest_range_containing_size_mismatch() { + WrappingRange::smallest_range_containing(200..300, Size::from_bytes(1)); +} + +#[test] +fn wrapping_range_smallest_range_containing() { + #[track_caller] + fn check(x: impl IntoIterator, bytes: u64, start: u128, end: u128) { + assert_eq!( + WrappingRange::smallest_range_containing(x, Size::from_bytes(bytes)), + Some(WrappingRange { start, end }), + ); + } + + assert_eq!(WrappingRange::smallest_range_containing([], Size::from_bytes(1)), None); + + check([7], 1, 7, 7); + check([7, 7, 7], 1, 7, 7); + + check(0..=127, 1, 0, 127); + check((0..=127).chain([255]), 1, 255, 127); + + check((-100..=100_i128).map(i128::cast_unsigned), 16, (-100_i128).cast_unsigned(), 100); + + // A wraparound case that's not just "sort them as signed" + check([10, 100, 160, 220], 1, 100, 10); + + check([0, 0xFF], 1, 0xFF, 0); + check([0, 0xFF], 2, 0, 0xFF); + check([0, 0xFFFF], 2, 0xFFFF, 0); + check([0, 0xFFFF], 4, 0, 0xFFFF); + check([0, 0xFFFFFFFF], 4, 0xFFFFFFFF, 0); + check([0, 0xFFFFFFFF], 8, 0, 0xFFFFFFFF); + + check([100, 200], 1, 100, 200); + check([100, 200, 50], 1, 50, 200); + check([100, 200, 250], 1, 100, 250); + check([100, 200, 250, 50], 1, 200, 100); + + check([200, 50], 1, 200, 50); + check([200, 50, 190], 1, 190, 50); + check([200, 50, 60], 1, 200, 60); + check([200, 50, 125], 1, 50, 200); + + // The mem::Alignment case + check((0..64).map(|n| 1 << n), 8, 1, i64::MIN.cast_unsigned().into()); + + // Both `100..=228` and `..=228 | 100..` are the same size, but we pick the one without zero. + check([100, 228], 1, 100, 228); + + // The wraparound one here is slightly smaller, so we pick it despite including zero. + // (The distance 10→96 is 86, compared to 85 for 96→181 and 181→10.) + check([10, 96, 181], 1, 96, 10); + + // These 4 values are evenly spaced so all 4 candidate ranges have length 193: + // `(..=32) | (96..)`, `(..=96) | (160..)`, `(..=160) | (224..)`, and `32..=224`. + // We pick the last one as the only one that doesn't contain zero. + check([0xA0, 0xE0, 0x20, 0x60], 1, 0x20, 0xE0); +} + #[test] fn wrapping_range_contains_range() { let size16 = Size::from_bytes(16); diff --git a/compiler/rustc_abi/src/wrapping_range.rs b/compiler/rustc_abi/src/wrapping_range.rs index ecdc7dae88a67..06e1388931988 100644 --- a/compiler/rustc_abi/src/wrapping_range.rs +++ b/compiler/rustc_abi/src/wrapping_range.rs @@ -1,5 +1,5 @@ -use std::fmt; use std::ops::RangeFull; +use std::{fmt, iter}; use crate::Size; #[cfg(feature = "nightly")] @@ -149,51 +149,49 @@ impl WrappingRange { /// /// # Examples /// - /// /// ``` /// use rustc_abi::{Size, WrappingRange}; /// - /// let range = WrappingRange::smallest_range_containing([2, 6, 12, 4], Size::from_bytes(2)); - /// assert_eq!(range.unwrap(), WrappingRange { start: 2, end: 12 }); + /// let chain = std::iter::chain(10..20, 30..40); + /// let range = WrappingRange::smallest_range_containing(chain, Size::from_bytes(2)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 10, end: 39 }); /// - /// let range = WrappingRange::smallest_range_containing(0..=127, Size::from_bytes(1)); - /// assert_eq!(range.unwrap(), WrappingRange { start: 0, end: 127 }); - /// let range = WrappingRange::smallest_range_containing([129, 128, 127], Size::from_bytes(1)); - /// assert_eq!(range.unwrap(), WrappingRange { start: 127, end: 129 }); + /// // Values don't need to be sorted nor unique + /// let range = WrappingRange::smallest_range_containing([3, 5, 3, 1, 3], Size::from_bytes(2)); + /// assert_eq!(range.unwrap(), WrappingRange { start: 1, end: 5 }); /// /// // The size matters because it changes where the wrapping can happen: /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(1)); /// assert_eq!(range.unwrap(), WrappingRange { start: 254, end: 1 }); /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(4)); /// assert_eq!(range.unwrap(), WrappingRange { start: 1, end: 254 }); - /// - /// // Both `100..=228` and `..=228 | 100..` are the same size, but we pick the one without zero. - /// let range = WrappingRange::smallest_range_containing([100, 228], Size::from_bytes(1)); - /// assert_eq!(range.unwrap(), WrappingRange { start: 100, end: 228 }); - /// // These 4 values are evenly spaced so all 4 candidate ranges have length 193: - /// // `(..=32) | (96..)`, `(..=96) | (160..)`, `(..=160) | (224..)`, and `32..=224`. - /// // We pick the last one as the only one that doesn't contain zero. - /// let range = WrappingRange::smallest_range_containing([0xA0, 0xE0, 0x20, 0x60], Size::from_bytes(1)); - /// assert_eq!(range.unwrap(), WrappingRange { start: 0x20, end: 0xE0 }); /// ``` pub fn smallest_range_containing( values: impl IntoIterator, size: Size, ) -> Option { let mut values: Vec<_> = values.into_iter().collect(); - let umax = size.unsigned_int_max(); - for value in &values { - debug_assert!(*value <= umax, "Value {value:?} is too big for {size:?}"); - } values.sort_unstable(); - // Having sorted all the values, every element is a possible start point for the - // range of values, up to the previous element (wrapping around the end of the vec). - // Look at all those candidates and pick the one that's as narrow as possible. - let pairs = std::iter::zip(values.iter().copied(), values.iter().copied().cycle().skip(1)); - let ranges = pairs.map(|(end, start)| WrappingRange { start, end }); - let smallest_range = ranges.min_by_key(|r| (r.width(size), r.start)); - smallest_range + // The simple answer is the non-wraparound range `min..=max`. + let obvious_range = WrappingRange { start: *values.first()?, end: *values.last()? }; + + // Having sorted the inputs, one test is enough to double-check they all fit in `size`. + let max_input = obvious_range.end; + assert!( + max_input <= size.unsigned_int_max(), + "Value {max_input:?} is too big for {size:?}", + ); + + // But every `[.., end, start, ..]` is also a potential candidate for a wraparound + // range `(..=end) | (start..)`, so long as `start` and `end` aren't duplicates. + let wraparound_ranges = values + .array_windows::<2>() + .filter_map(|&[end, start]| (start != end).then_some(WrappingRange { start, end })); + + // Pick whichever range is smallest. By putting the non-wraparound range first, + // it'll be preferred over a wraparound range with the same width. + iter::chain(iter::once(obvious_range), wraparound_ranges).min_by_key(|r| r.width(size)) } } From 31ce8e5b41a773707d4fbf3e98071bf8960cdc90 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 3 Aug 2026 23:26:58 +1000 Subject: [PATCH 29/31] Register `coverage-map` and `coverage-run` aliases via a separate step Using a separate step lets us remove the `default_to_suites_only` hack. --- src/bootstrap/src/core/build_steps/test.rs | 112 ++++++++++-------- .../snapshots/x_test_coverage_map.snap | 2 +- .../snapshots/x_test_coverage_run.snap | 2 +- src/bootstrap/src/core/builder/mod.rs | 23 +--- 4 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 3015d5a83db8d..ecb41667ce811 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -44,7 +44,7 @@ use crate::utils::helpers::{ up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, CodegenBackendKind, GitRepo, Mode, PathSet, TestTarget, envify, exit}; +use crate::{CLang, CodegenBackendKind, GitRepo, Mode, TestTarget, envify, exit}; mod compiletest; pub mod failed_tests; @@ -1998,6 +1998,12 @@ impl Coverage { const SUITE: &'static str = "coverage"; const ALL_MODES: &[CompiletestMode] = &[CompiletestMode::CoverageMap, CompiletestMode::CoverageRun]; + + fn new(run: &RunConfig<'_>, mode: CompiletestMode) -> Self { + let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); + let target = run.target; + Coverage { compiler, target, mode } + } } impl CommandLineStep for Coverage { @@ -2005,23 +2011,14 @@ impl CommandLineStep for Coverage { /// Compiletest will automatically skip the "coverage-run" tests if necessary. const IS_HOST: bool = false; - fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> { - // Support various invocation styles, including: + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // Handle these invocation styles: + // - `./x test` (including coverage tests) // - `./x test coverage` + // - `./x test tests/coverage` // - `./x test tests/coverage/trivial.rs` - // - `./x test coverage-map` - // - `./x test coverage-run -- tests/coverage/trivial.rs` - run = run.suite_path(Self::PATH); - for mode in Self::ALL_MODES { - run = run.alias(mode.as_str()); - } - - // Allow `./x test --skip=tests` to properly skip the coverage tests, - // by not treating the `coverage-map` and `coverage-run` aliases as - // implied command-line arguments. - run = run.default_to_suites_only(); - - run + // - `./x test tests/coverage/trivial.rs --skip=coverage-run` + run.suite_path(Coverage::PATH) } fn is_default_step(_builder: &Builder<'_>) -> bool { @@ -2029,41 +2026,13 @@ impl CommandLineStep for Coverage { } fn make_run(run: RunConfig<'_>) { - let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple()); - let target = run.target; - - // List of (coverage) test modes that the coverage test suite will be - // run in. It's OK for this to contain duplicates, because the call to - // `Builder::ensure` below will take care of deduplication. - let mut modes = vec![]; - - // From the pathsets that were selected on the command-line (or by default), - // determine which modes to run in. - for path in &run.paths { - match path { - PathSet::Set(_) => { - for &mode in Self::ALL_MODES { - if path.assert_single_path().path == Path::new(mode.as_str()) { - modes.push(mode); - break; - } - } - } - PathSet::Suite(_) => { - modes.extend_from_slice(Self::ALL_MODES); - break; - } - } - } - - // Skip any modes that were explicitly skipped/excluded on the command-line. + // Run the tests in all coverage-test modes, but skip any modes that + // were explicitly skipped on the command-line (e.g. `--skip=coverage-run`). // FIXME(Zalathar): Integrate this into central skip handling somehow? - modes.retain(|mode| { - !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode.as_str())) - }); - - for mode in modes { - run.builder.ensure(Coverage { compiler, target, mode }); + for &mode in Coverage::ALL_MODES { + if !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode.as_str())) { + run.builder.ensure(Coverage::new(&run, mode)); + } } } @@ -2082,6 +2051,49 @@ impl CommandLineStep for Coverage { } } +/// Registers the `coverage-map` and `coverage-run` aliases, which are then +/// forwarded to the [`Coverage`] step. +/// +/// If the aliases were registered by [`Coverage`] directly, they would also +/// be treated as implied command-line arguments when run by default. +/// That would cause things like `./x test --skip=tests` to still run coverage +/// tests, which is undesirable. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum CoverageModeAlias {} + +impl CommandLineStep for CoverageModeAlias { + type Output = (); + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + // Register the aliases "coverage-map" and "coverage-run", to handle + // these invocation styles: + // - `./x test coverage-map` + // - `./x test coverage-run -- tests/coverage/trivial.rs` + Coverage::ALL_MODES.iter().fold(run, |run, mode| run.alias(mode.as_str())) + } + + fn is_default_step(_builder: &Builder<'_>) -> bool { + false + } + + fn make_run(run: RunConfig<'_>) { + for path in &run.paths { + let single_path = &path.assert_single_path().path; + for &mode in Coverage::ALL_MODES { + if single_path == Path::new(mode.as_str()) { + // Instead of creating an intermediate `CoverageModeAlias` + // step instance, delegate straight to `Coverage`. + run.builder.ensure(Coverage::new(&run, mode)); + } + } + } + } + + fn run(self, _builder: &Builder<'_>) { + unreachable!("never instantiated; `make_run` creates a Coverage step instead"); + } +} + test!(CoverageRunRustdoc { path: "tests/coverage-run-rustdoc", mode: CompiletestMode::CoverageRun, diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_map.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_map.snap index 9f0ef84851d5a..7ae3a95a05bd4 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_map.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_map.snap @@ -2,6 +2,6 @@ source: src/bootstrap/src/core/builder/cli_paths/tests.rs expression: test coverage-map --- -[Test] test::Coverage +[Test] test::CoverageModeAlias targets: [aarch64-unknown-linux-gnu] - Set({coverage-map}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_run.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_run.snap index 41700ce9e210c..8657e72033248 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_run.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_coverage_run.snap @@ -2,6 +2,6 @@ source: src/bootstrap/src/core/builder/cli_paths/tests.rs expression: test coverage-run --- -[Test] test::Coverage +[Test] test::CoverageModeAlias targets: [aarch64-unknown-linux-gnu] - Set({coverage-run}) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 051e01a0a6666..603ef65854cf6 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -524,13 +524,11 @@ pub struct ShouldRun<'a> { // use a BTreeSet to maintain sort order paths: BTreeSet, - - default_to_suites_only: bool, } impl<'a> ShouldRun<'a> { fn new(builder: &'a Builder<'_>, kind: Kind) -> ShouldRun<'a> { - ShouldRun { builder, kind, paths: BTreeSet::new(), default_to_suites_only: false } + ShouldRun { builder, kind, paths: BTreeSet::new() } } /// The corresponding step should run if the bootstrap command-line selects @@ -643,26 +641,10 @@ impl<'a> ShouldRun<'a> { sets } - /// When generating pathsets for a step that is being run "by default" - /// (i.e. when running bootstrap without an explicit command-line path), - /// discard any paths that were not registered as test suites. - /// - /// This is basically a hack to make path-based skipping work properly for - /// coverage tests, since otherwise the `coverage-map` and `coverage-run` - /// aliases would prevent `./x test --skip=tests` from skipping them. - pub(crate) fn default_to_suites_only(mut self) -> Self { - self.default_to_suites_only = true; - self - } - /// When the corresponding step is run "by default" (without explicit command-line paths), /// act as though the user had explicitly specified these paths. fn default_pathsets(&self) -> Vec { - let mut default_pathsets = self.paths.iter().cloned().collect::>(); - if self.default_to_suites_only { - default_pathsets.retain(|p| matches!(p, PathSet::Suite(_))); - } - default_pathsets + self.paths.iter().cloned().collect::>() } } @@ -898,6 +880,7 @@ impl<'a> Builder<'a> { test::Ui, test::Crashes, test::Coverage, + test::CoverageModeAlias, test::MirOpt, test::CodegenLlvm, test::CodegenUnits, From a8e7de357391c6622fb2c4de579ebec448628e4d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 1 Aug 2026 15:56:07 +0200 Subject: [PATCH 30/31] misc: change mentions of `compare_{method,type}_predicate_entailment` ..to `compare_{method,type}_clause_entailment` --- compiler/rustc_hir_analysis/src/collect/clauses_of.rs | 2 +- compiler/rustc_middle/src/ty/generics.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 2 +- compiler/rustc_trait_selection/src/traits/mod.rs | 6 +++--- src/doc/rustc-dev-guide/src/effects.md | 8 ++++---- .../src/return-position-impl-trait-in-trait.md | 2 +- src/doc/rustc-dev-guide/src/typing-parameter-envs.md | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs index 3c91caf3d7eed..604dcb57685fd 100644 --- a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs @@ -1076,7 +1076,7 @@ pub(super) fn const_conditions<'tcx>( }, // While associated types are not really const, we do allow them to have `[const]` // bounds and where clauses. `const_conditions` is responsible for gathering - // these up so we can check them in `compare_type_predicate_entailment`, and + // these up so we can check them in `compare_type_clause_entailment`, and // in `HostEffect` goal computation. Node::TraitItem(item) => match item.kind { hir::TraitItemKind::Fn(_, _) | hir::TraitItemKind::Type(_, _) => { diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 0599f51305575..029c20d47e524 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -533,7 +533,7 @@ impl<'tcx> GenericClauses<'tcx> { /// `[const]` bounds for a given item. This is represented using a struct much like /// `GenericClauses`, where you can either choose to only instantiate the "own" /// bounds or all of the bounds including those from the parent. This distinction -/// is necessary for code like `compare_method_predicate_entailment`. +/// is necessary for code like `compare_method_clause_entailment`. #[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, StableHash)] pub struct ConstConditions<'tcx> { pub parent: Option, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 75937ff5531b5..2a6e2a539c964 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4565,7 +4565,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }); } - // Suppress `compare_type_predicate_entailment` errors for RPITITs, since they + // Suppress `compare_type_clause_entailment` errors for RPITITs, since they // should be implied by the parent method. ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } if tcx.is_impl_trait_in_trait(trait_item_def_id) => {} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 2d7269cc2606a..e534b1f6e0cc6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -334,13 +334,13 @@ fn do_normalize_clauses<'tcx>( // // FIXME: It's very weird that we ignore region obligations but apparently // still need to use `resolve_regions` as we need the resolved regions in - // the normalized predicates. + // the normalized clauses. // // FIXME(-Zhigher-ranked-assumptions): We're ignoring region errors for now. // There're placeholder constraints `leaking` out. This is a hack to work around // the fact that we don't support placeholder assumptions right now and is necessary - // for `compare_method_predicate_entailment`. We should remove this once we - // have proper support for implied bounds on binders. + // for `compare_method_clause_entailment`. We should remove this once we have proper + // support for implied bounds on binders. // // This is required by trait-system-refactor-initiative#166. The new solver encounters // this more frequently as we entirely ignore outlives predicates with the old solver. diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 4096c85a59f7a..9c54705e000fd 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -97,16 +97,16 @@ impl Foo for Vec { } ``` -These checks are done in [`compare_method_predicate_entailment`]. +These checks are done in [`compare_method_clause_entailment`]. A similar function that does the same check for associated types is called -[`compare_type_predicate_entailment`]. +[`compare_type_clause_entailment`]. Both of these need to consider `const_conditions` when in const contexts. In MIR, as part of const checking, `const_conditions` of items that are called are revalidated again in [`Checker::revalidate_conditional_constness`]. -[`compare_method_predicate_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html -[`compare_type_predicate_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_type_predicate_entailment.html +[`compare_method_clause_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_clause_entailment.html +[`compare_type_clause_entailment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_type_clause_entailment.html [`FnCtxt::enforce_context_effects`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.enforce_context_effects [`wfcheck::check_impl`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/fn.check_impl.html [`Checker::revalidate_conditional_constness`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/check_consts/check/struct.Checker.html#method.revalidate_conditional_constness diff --git a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md index e82a0bdbf4be2..586f697a78fb1 100644 --- a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md +++ b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md @@ -306,7 +306,7 @@ come after the `=` in `type Assoc = ...` for each RPITIT. Since `collect_return_position_impl_trait_in_trait_tys` does fulfillment and region resolution, we must provide it `assumed_wf_types` so that we can prove region obligations with the same expected implied bounds as -`compare_method_predicate_entailment` does. +`compare_method_clause_entailment` does. Since the return type of a method is understood to be one of the assumed WF types, and we eagerly fold the return type with inference variables to do diff --git a/src/doc/rustc-dev-guide/src/typing-parameter-envs.md b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md index f5a19ea328696..8e13f9fb43327 100644 --- a/src/doc/rustc-dev-guide/src/typing-parameter-envs.md +++ b/src/doc/rustc-dev-guide/src/typing-parameter-envs.md @@ -25,7 +25,7 @@ such as `ConstArgHasType` or (some) implied bounds. In most cases `ParamEnv`s are initially created via the [`param_env` query][query] which returns a `ParamEnv` derived from the provided item's where clauses. A `ParamEnv` can also be created with arbitrary sets of clauses that are not derived from a specific item, -such as in [`compare_method_predicate_entailment`][method_pred_entailment] where we create a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. +such as in [`compare_method_clause_entailment`][method_clause_entailment] where we create a hybrid `ParamEnv` consisting of the impl's where clauses and the trait definition's function's where clauses. --- @@ -76,7 +76,7 @@ fn foo2(a: T) { ``` [clauses_of]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/collect/clauses_of/fn.clauses_of.html -[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html +[method_clause_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_clause_entailment.html [query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.param_env [normalization]: normalization.md @@ -111,7 +111,7 @@ Creating an empty environment with `ParamEnv::empty` is typically only done eith or as part of some analysis that do not expect to ever encounter generic parameters (e.g. various parts of coherence/orphan check). -Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (e.g. [`compare_method_predicate_entailment`][method_pred_entailment]). +Creating an env from an arbitrary set of where clauses is usually unnecessary and should only be done if the environment you need does not correspond to an actual item in the source code (e.g. [`compare_method_clause_entailment`][method_clause_entailment]). [param_env_new]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.new [normalize_env_or_error]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/fn.normalize_param_env_or_error.html @@ -124,7 +124,7 @@ Creating an env from an arbitrary set of where clauses is usually unnecessary an [mirtypeck_param_env]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/type_check/struct.TypeChecker.html#structfield.param_env [env_empty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html#method.empty [param_env_query]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#structfield.param_env -[method_pred_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html +[method_clause_entailment]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_clause_entailment.html [predicate_emitting_relation]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/relate/combine/trait.PredicateEmittingRelation.html [tenv_mono]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypingEnv.html#method.fully_monomorphized [compiler_help]: https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp From c99698344dda026bb4fc8f1d8b123a0f6e2bd0f6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Jul 2026 00:20:00 +0200 Subject: [PATCH 31/31] rename `OutlivesPredicate` to `OutlivesClause` --- compiler/rustc_borrowck/src/lib.rs | 2 +- .../src/region_infer/opaque_types/mod.rs | 2 +- .../src/type_check/canonical.rs | 2 +- .../src/type_check/constraint_conversion.rs | 15 +- .../src/type_check/free_region_relations.rs | 10 +- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 12 +- .../src/collect/clauses_of.rs | 6 +- .../src/hir_ty_lowering/bounds.rs | 2 +- .../src/outlives/explicit.rs | 40 ++--- .../src/outlives/implicit_infer.rs | 168 +++++++++--------- .../rustc_hir_analysis/src/outlives/mod.rs | 36 ++-- .../rustc_hir_analysis/src/outlives/utils.rs | 25 ++- .../rustc_hir_analysis/src/variance/mod.rs | 2 +- .../src/infer/canonical/query_response.rs | 14 +- .../src/infer/lexical_region_resolve/mod.rs | 2 +- compiler/rustc_infer/src/infer/mod.rs | 4 +- .../rustc_infer/src/infer/outlives/env.rs | 20 +-- .../rustc_infer/src/infer/outlives/mod.rs | 4 +- .../src/infer/outlives/obligations.rs | 20 +-- .../src/infer/outlives/test_type_match.rs | 8 +- .../rustc_infer/src/infer/outlives/verify.rs | 24 +-- compiler/rustc_lint/src/builtin.rs | 4 +- compiler/rustc_middle/src/infer/canonical.rs | 2 +- compiler/rustc_middle/src/queries.rs | 10 +- compiler/rustc_middle/src/ty/context.rs | 12 +- .../src/ty/context/impl_interner.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 18 +- compiler/rustc_middle/src/ty/predicate.rs | 32 ++-- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- .../rustc_middle/src/ty/structural_impls.rs | 2 +- .../src/canonical/mod.rs | 2 +- .../eval_ctxt/solver_region_constraints.rs | 4 +- .../rustc_next_trait_solver/src/solve/mod.rs | 8 +- .../src/solve/trait_goals.rs | 6 +- compiler/rustc_privacy/src/lib.rs | 2 +- compiler/rustc_public/src/ty.rs | 17 +- .../src/unstable/convert/stable/ty.rs | 12 +- .../src/error_reporting/infer/region.rs | 2 +- .../src/traits/auto_trait.rs | 2 +- .../src/traits/fulfill.rs | 2 +- .../rustc_trait_selection/src/traits/mod.rs | 2 +- .../src/traits/outlives_for_liveness.rs | 7 +- .../query/type_op/implied_outlives_bounds.rs | 9 +- .../src/traits/query/type_op/normalize.rs | 6 +- .../src/traits/select/confirmation.rs | 17 +- .../src/traits/select/mod.rs | 4 +- .../rustc_trait_selection/src/traits/wf.rs | 18 +- .../rustc_traits/src/coroutine_witnesses.rs | 2 +- compiler/rustc_ty_utils/src/implied_bounds.rs | 2 +- compiler/rustc_type_ir/src/elaborate.rs | 26 +-- compiler/rustc_type_ir/src/flags.rs | 7 +- compiler/rustc_type_ir/src/inherent.rs | 6 +- compiler/rustc_type_ir/src/interner.rs | 6 +- compiler/rustc_type_ir/src/ir_print.rs | 8 +- compiler/rustc_type_ir/src/outlives.rs | 8 +- compiler/rustc_type_ir/src/predicate.rs | 16 +- compiler/rustc_type_ir/src/predicate_kind.rs | 4 +- .../rustc_type_ir/src/region_constraint.rs | 10 +- .../return-position-impl-trait-in-trait.md | 2 +- .../src/traits/implied-bounds.md | 17 +- src/librustdoc/clean/mod.rs | 22 +-- tests/rustdoc-js/auxiliary/interner.rs | 4 +- .../normalization-generality-2.rs | 2 +- .../global-where-bound-normalization.rs | 2 +- 66 files changed, 377 insertions(+), 397 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d50e8199c2240..d990d72e3fb42 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -304,7 +304,7 @@ struct CollectRegionConstraintsResult<'tcx> { location_map: Rc, universal_region_relations: Frozen>, region_bound_pairs: Frozen>, - known_type_outlives_obligations: Frozen>>, + known_type_outlives_obligations: Frozen>>, constraints: MirTypeckRegionConstraints<'tcx>, deferred_closure_requirements: DeferredClosureRequirements<'tcx>, deferred_opaque_type_errors: Vec>, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index a215258376ad4..e347dc2d13dfc 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -531,7 +531,7 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>( body: &Body<'tcx>, universal_regions: &UniversalRegions<'tcx>, region_bound_pairs: &RegionBoundPairs<'tcx>, - known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &[ty::PolyTypeOutlivesClause<'tcx>], constraints: &mut MirTypeckRegionConstraints<'tcx>, hidden_types: &mut FxIndexMap>, opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)], diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 5931d6f7370b0..bc2c75f0c01a4 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -24,7 +24,7 @@ pub(crate) fn fully_perform_op_raw<'tcx, R: fmt::Debug, Op>( body: &Body<'tcx>, universal_regions: &UniversalRegions<'tcx>, region_bound_pairs: &RegionBoundPairs<'tcx>, - known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &[ty::PolyTypeOutlivesClause<'tcx>], constraints: &mut MirTypeckRegionConstraints<'tcx>, locations: Locations, category: ConstraintCategory<'tcx>, diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 6469689d002e5..f1db73fdd7654 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -34,7 +34,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// logic expecting to see (e.g.) `ReStatic`, and if we supplied /// our special inference variable there, we would mess that up. region_bound_pairs: &'a RegionBoundPairs<'tcx>, - known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesClause<'tcx>], locations: Locations, span: Span, category: ConstraintCategory<'tcx>, @@ -47,7 +47,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { infcx: &'a BorrowckInferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesClause<'tcx>], locations: Locations, span: Span, category: ConstraintCategory<'tcx>, @@ -115,7 +115,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { self.category = outlives_requirement.category; self.span = outlives_requirement.blame_span; self.convert( - ty::OutlivesPredicate(subject, outlived_region), + ty::OutlivesClause(subject, outlived_region), self.category, &Default::default(), ); @@ -125,9 +125,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { fn convert( &mut self, - predicate: ty::ArgOutlivesPredicate<'tcx>, + clause: ty::ArgOutlivesClause<'tcx>, constraint_category: ConstraintCategory<'tcx>, - higher_ranked_assumptions: &FxHashSet>, + higher_ranked_assumptions: &FxHashSet>, ) { let tcx = self.infcx.tcx; debug!("generate: constraints at: {:#?}", self.locations); @@ -141,15 +141,14 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { .. } = *self; - let pred = predicate; // Constraint is implied by a coroutine's well-formedness. if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions - && higher_ranked_assumptions.contains(&pred) + && higher_ranked_assumptions.contains(&clause) { return; } - let ty::OutlivesPredicate(k1, r2) = pred; + let ty::OutlivesClause(k1, r2) = clause; match k1.kind() { GenericArgKind::Lifetime(r1) => { let r1_vid = self.to_region_vid(r1); diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index a89d45c4b0b2a..907a5c1898876 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -44,7 +44,7 @@ type NormalizedInputsAndOutput<'tcx> = Vec>; pub(crate) struct CreateResult<'tcx> { pub(crate) universal_region_relations: Frozen>, pub(crate) region_bound_pairs: Frozen>, - pub(crate) known_type_outlives_obligations: Frozen>>, + pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } @@ -342,9 +342,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { fn normalize_and_push_type_outlives_obligation( &self, - mut outlives: ty::PolyTypeOutlivesPredicate<'tcx>, + mut outlives: ty::PolyTypeOutlivesClause<'tcx>, span: Span, - known_type_outlives_obligations: &mut Vec>, + known_type_outlives_obligations: &mut Vec>, constraints: &mut Vec<&QueryRegionConstraints<'tcx>>, ) { // In the new solver, normalize the type-outlives obligation assumptions. @@ -413,12 +413,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { OutlivesBound::RegionSubParam(r_a, param_b) => { self.region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); + .insert(ty::OutlivesClause(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubAlias(r_a, alias_b) => { self.region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a)); + .insert(ty::OutlivesClause(GenericKind::Alias(alias_b), r_a)); } } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f0a2326747090..6825c28270c30 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -239,7 +239,7 @@ struct TypeChecker<'a, 'tcx> { /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, - known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesClause<'tcx>], reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a PoloniusLocationTable, @@ -257,7 +257,7 @@ pub(crate) struct MirTypeckResults<'tcx> { pub(crate) constraints: MirTypeckRegionConstraints<'tcx>, pub(crate) universal_region_relations: Frozen>, pub(crate) region_bound_pairs: Frozen>, - pub(crate) known_type_outlives_obligations: Frozen>>, + pub(crate) known_type_outlives_obligations: Frozen>>, pub(crate) deferred_closure_requirements: DeferredClosureRequirements<'tcx>, pub(crate) polonius_context: Option, } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 6986ee1aa837f..281325bf65c29 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -89,7 +89,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, RegionExt, Ty, TyCtxt, TypingMode, + self, GenericArgs, GenericArgsRef, OutlivesClause, Region, RegionExt, Ty, TyCtxt, TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_session::diagnostics::feature_err; @@ -413,7 +413,7 @@ fn bounds_from_generic_clauses<'tcx>( ty::ClauseKind::Projection(projection_pred) => { projections.push(bound_clause.rebind(projection_pred)); } - ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => { + ty::ClauseKind::RegionOutlives(OutlivesClause(a, b)) => { regions.entry(a).or_default().push(b); } _ => {} diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index caf64fd6894f7..331bc01989f96 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -510,7 +510,7 @@ pub(crate) fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) let unsatisfied_bounds: Vec<_> = required_bounds .into_iter() .filter(|clause| match clause.kind().skip_binder() { - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => { + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(a, b)) => { !region_known_to_outlive( tcx, gat_def_id, @@ -520,7 +520,7 @@ pub(crate) fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) b, ) } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => { !ty_known_to_outlive(tcx, gat_def_id, param_env, &FxIndexSet::default(), a, b) } _ => bug!("Unexpected ClauseKind"), @@ -642,10 +642,10 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( tcx, ty::EarlyParamRegion { index: region_param.index, name: region_param.name }, ); - // The predicate we expect to see. (In our example, + // The clause we expect to see. (In our example, // `Self: 'me`.) bounds.insert( - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param)) + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty_param, region_param)) .upcast(tcx), ); } @@ -677,9 +677,9 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( tcx, ty::EarlyParamRegion { index: region_b_param.index, name: region_b_param.name }, ); - // The predicate we expect to see. + // The clause we expect to see. bounds.insert( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( + ty::ClauseKind::RegionOutlives(ty::OutlivesClause( region_a_param, region_b_param, )) diff --git a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs index 604dcb57685fd..49e9944428f53 100644 --- a/compiler/rustc_hir_analysis/src/collect/clauses_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/clauses_of.rs @@ -319,7 +319,7 @@ fn gather_explicit_clauses_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generi } }; let clause = - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)).upcast(tcx); + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(r1, r2)).upcast(tcx); (clause, span) })) } @@ -389,12 +389,12 @@ fn compute_bidirectional_outlives_clauses<'tcx>( ); let span = tcx.def_span(param.def_id); clauses.push(( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_lifetime, dup_lifetime)) + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(orig_lifetime, dup_lifetime)) .upcast(tcx), span, )); clauses.push(( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_lifetime, orig_lifetime)) + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(dup_lifetime, orig_lifetime)) .upcast(tcx), span, )); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d730683b132d4..add45e83f7fdd 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -353,7 +353,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let region = self.lower_lifetime(lifetime, RegionInferReason::OutlivesBound); let bound = ty::Binder::bind_with_vars( - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(param_ty, region)), + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(param_ty, region)), bound_vars, ); bounds.push((bound.upcast(self.tcx()), lifetime.ident.span)); diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index fddb9182710d3..ffbbb316e5c1f 100644 --- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs @@ -1,53 +1,41 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, OutlivesPredicate, TyCtxt}; +use rustc_middle::ty::{self, OutlivesClause, TyCtxt}; use super::utils::*; #[derive(Debug)] -pub(crate) struct ExplicitPredicatesMap<'tcx> { - map: FxIndexMap>>, +pub(crate) struct ExplicitClausesMap<'tcx> { + map: FxIndexMap>>, } -impl<'tcx> ExplicitPredicatesMap<'tcx> { - pub(crate) fn new() -> ExplicitPredicatesMap<'tcx> { - ExplicitPredicatesMap { map: FxIndexMap::default() } +impl<'tcx> ExplicitClausesMap<'tcx> { + pub(crate) fn new() -> ExplicitClausesMap<'tcx> { + ExplicitClausesMap { map: FxIndexMap::default() } } pub(crate) fn explicit_clauses_of( &mut self, tcx: TyCtxt<'tcx>, def_id: DefId, - ) -> &ty::EarlyBinder<'tcx, RequiredPredicates<'tcx>> { + ) -> &ty::EarlyBinder<'tcx, RequiredClauses<'tcx>> { self.map.entry(def_id).or_insert_with(|| { let gen_clauses = if def_id.is_local() { tcx.explicit_clauses_of(def_id) } else { tcx.clauses_of(def_id) }; - let mut required_predicates = RequiredPredicates::default(); + let mut required_clauses = RequiredClauses::default(); - // Process clauses and convert to `RequiredPredicates` entry, see below. + // Process clauses and convert to `RequiredClauses` entry, see below. for &(clause, span) in gen_clauses.clauses { match clause.kind().skip_binder() { - ty::ClauseKind::TypeOutlives(OutlivesPredicate(ty, reg)) => { - insert_outlives_predicate( - tcx, - ty.into(), - reg, - span, - &mut required_predicates, - ) + ty::ClauseKind::TypeOutlives(OutlivesClause(ty, reg)) => { + insert_outlives_clause(tcx, ty.into(), reg, span, &mut required_clauses) } - ty::ClauseKind::RegionOutlives(OutlivesPredicate(reg1, reg2)) => { - insert_outlives_predicate( - tcx, - reg1.into(), - reg2, - span, - &mut required_predicates, - ) + ty::ClauseKind::RegionOutlives(OutlivesClause(reg1, reg2)) => { + insert_outlives_clause(tcx, reg1.into(), reg2, span, &mut required_clauses) } ty::ClauseKind::Trait(_) | ty::ClauseKind::Projection(_) @@ -59,7 +47,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> { } } - ty::EarlyBinder::bind_iter(required_predicates) + ty::EarlyBinder::bind_iter(required_clauses) }) } } diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index eaf1c32374df6..131812a364ebd 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -5,38 +5,38 @@ use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt}; use rustc_span::Span; use tracing::debug; -use super::explicit::ExplicitPredicatesMap; +use super::explicit::ExplicitClausesMap; use super::utils::*; -/// Infer outlives-predicates for the items in the local crate. -pub(super) fn infer_predicates( +/// Infer outlives-clauses for the items in the local crate. +pub(super) fn infer_clauses( tcx: TyCtxt<'_>, -) -> FxIndexMap>> { - debug!("infer_predicates"); +) -> FxIndexMap>> { + debug!("infer_clauses"); - let mut explicit_map = ExplicitPredicatesMap::new(); + let mut explicit_map = ExplicitClausesMap::new(); let mut global_inferred_outlives = FxIndexMap::default(); - // If new predicates were added then we need to re-calculate - // all crates since there could be new implied predicates. + // If new clauses were added then we need to re-calculate + // all crates since there could be new implied clauses. for i in 0.. { - let mut predicates_added = vec![]; + let mut clauses_added = vec![]; - // Visit all the crates and infer predicates + // Visit all the crates and infer clauses for id in tcx.hir_free_items() { let item_did = id.owner_id; debug!("InferVisitor::visit_item(item={:?})", item_did); - let mut item_required_predicates = RequiredPredicates::default(); + let mut item_required_clauses = RequiredClauses::default(); match tcx.def_kind(item_did) { DefKind::Union | DefKind::Enum | DefKind::Struct => { let adt_def = tcx.adt_def(item_did.to_def_id()); // Iterate over all fields in item_did for field_def in adt_def.all_fields() { - // Calculating the predicate requirements necessary + // Calculating the clause requirements necessary // for item_did. // // For field of type &'a T (reference) or Adt @@ -45,24 +45,24 @@ pub(super) fn infer_predicates( let field_ty = tcx.type_of(field_def.did).instantiate_identity().skip_norm_wip(); let field_span = tcx.def_span(field_def.did); - insert_required_predicates_to_be_wf( + insert_required_clauses_to_be_wf( tcx, field_ty, field_span, &global_inferred_outlives, - &mut item_required_predicates, + &mut item_required_clauses, &mut explicit_map, ); } } DefKind::TyAlias if tcx.type_alias_is_checked(item_did) => { - insert_required_predicates_to_be_wf( + insert_required_clauses_to_be_wf( tcx, tcx.type_of(item_did).instantiate_identity().skip_norm_wip(), tcx.def_span(item_did), &global_inferred_outlives, - &mut item_required_predicates, + &mut item_required_clauses, &mut explicit_map, ); } @@ -70,36 +70,36 @@ pub(super) fn infer_predicates( _ => {} }; - // If new predicates were added (`local_predicate_map` has more - // predicates than the `global_inferred_outlives`), the new predicates - // might result in implied predicates for their parent types. - // Therefore mark `predicates_added` as true and which will ensure - // we walk the crates again and re-calculate predicates for all + // If new clauses were added (`local_clause_map` has more + // clauses than the `global_inferred_outlives`), the new clauses + // might result in implied clauses for their parent types. + // Therefore mark `clauses_added` as true and which will ensure + // we walk the crates again and re-calculate clauses for all // items. - let item_predicates_len: usize = global_inferred_outlives + let item_clauses_len: usize = global_inferred_outlives .get(&item_did.to_def_id()) - .map_or(0, |p| p.as_ref().skip_binder().len()); - if item_required_predicates.len() > item_predicates_len { - predicates_added.push(item_did); + .map_or(0, |c| c.as_ref().skip_binder().len()); + if item_required_clauses.len() > item_clauses_len { + clauses_added.push(item_did); global_inferred_outlives.insert( item_did.to_def_id(), - ty::EarlyBinder::bind_iter(item_required_predicates), + ty::EarlyBinder::bind_iter(item_required_clauses), ); } } - if predicates_added.is_empty() { + if clauses_added.is_empty() { // We've reached a fixed point. break; } else if !tcx.recursion_limit().value_within_limit(i) { - let msg = if let &[id] = &predicates_added[..] { + let msg = if let &[id] = &clauses_added[..] { format!("overflow computing implied lifetime bounds for `{}`", tcx.def_path_str(id),) } else { "overflow computing implied lifetime bounds".to_string() }; tcx.dcx() .struct_span_fatal( - predicates_added.iter().map(|id| tcx.def_span(*id)).collect::>(), + clauses_added.iter().map(|id| tcx.def_span(*id)).collect::>(), msg, ) .emit(); @@ -109,19 +109,19 @@ pub(super) fn infer_predicates( global_inferred_outlives } -fn insert_required_predicates_to_be_wf<'tcx>( +fn insert_required_clauses_to_be_wf<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, - global_inferred_outlives: &FxIndexMap>>, - required_predicates: &mut RequiredPredicates<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, + global_inferred_outlives: &FxIndexMap>>, + required_clauses: &mut RequiredClauses<'tcx>, + explicit_map: &mut ExplicitClausesMap<'tcx>, ) { for arg in ty.walk() { let leaf_ty = match arg.kind() { GenericArgKind::Type(ty) => ty, - // No predicates from lifetimes or constants, except potentially + // No clauses from lifetimes or constants, except potentially // constants' types, but `walk` will get to them as well. GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, }; @@ -129,65 +129,65 @@ fn insert_required_predicates_to_be_wf<'tcx>( match *leaf_ty.kind() { ty::Ref(region, rty, _) => { // The type is `&'a T` which means that we will have - // a predicate requirement of `T: 'a` (`T` outlives `'a`). + // a clause requirement of `T: 'a` (`T` outlives `'a`). // - // We also want to calculate potential predicates for the `T`. + // We also want to calculate potential clauses for the `T`. debug!("Ref"); - insert_outlives_predicate(tcx, rty.into(), region, span, required_predicates); + insert_outlives_clause(tcx, rty.into(), region, span, required_clauses); } ty::Adt(def, args) => { - // For ADTs (structs/enums/unions), we check inferred and explicit predicates. + // For ADTs (structs/enums/unions), we check inferred and explicit clauses. debug!("Adt"); - check_inferred_predicates( + check_inferred_clauses( tcx, def.did(), args, global_inferred_outlives, - required_predicates, + required_clauses, ); - check_explicit_predicates( + check_explicit_clauses( tcx, def.did(), args, - required_predicates, + required_clauses, explicit_map, - IgnorePredicatesReferencingSelf::No, + IgnoreClausesReferencingSelf::No, ); } ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => { // This corresponds to a type like `Type<'a, T>`. - // We check inferred and explicit predicates. + // We check inferred and explicit clauses. debug!("Free"); - check_inferred_predicates( + check_inferred_clauses( tcx, def_id, args, global_inferred_outlives, - required_predicates, + required_clauses, ); - check_explicit_predicates( + check_explicit_clauses( tcx, def_id, args, - required_predicates, + required_clauses, explicit_map, - IgnorePredicatesReferencingSelf::No, + IgnoreClausesReferencingSelf::No, ); } ty::Dynamic(obj, ..) => { // This corresponds to `dyn Trait<..>`. In this case, we should - // use the explicit predicates as well. + // use the explicit clauses as well. debug!("Dynamic"); if let Some(trait_ref) = obj.principal() { let args = trait_ref .with_self_ty(tcx, tcx.types.trait_object_dummy_self) .skip_binder() .args; - // We skip predicates that reference the `Self` type parameter since we don't - // want to leak the dummy Self to the predicates map. + // We skip clauses that reference the `Self` type parameter since we don't + // want to leak the dummy Self to the clauses map. // // While filtering out bounds like `Self: 'a` as in `trait Trait<'a, T>: 'a {}` // doesn't matter since they can't affect the lifetime / type parameters anyway, @@ -195,33 +195,33 @@ fn insert_required_predicates_to_be_wf<'tcx>( // (see also #54467) it might conceivably be better to extract the binding // `AssocTy = U` from the trait object type (which must exist) and thus infer // an outlives requirement that `U: 'b`. - check_explicit_predicates( + check_explicit_clauses( tcx, trait_ref.def_id(), args, - required_predicates, + required_clauses, explicit_map, - IgnorePredicatesReferencingSelf::Yes, + IgnoreClausesReferencingSelf::Yes, ); } } ty::Alias(_, ty::AliasTy { kind: ty::Projection { def_id }, args, .. }) => { // This corresponds to a type like `<() as Trait<'a, T>>::Type`. - // We only use the explicit predicates of the trait but + // We only use the explicit clauses of the trait but // not the ones of the associated type itself. debug!("Projection"); - check_explicit_predicates( + check_explicit_clauses( tcx, tcx.parent(def_id), args, - required_predicates, + required_clauses, explicit_map, - IgnorePredicatesReferencingSelf::No, + IgnoreClausesReferencingSelf::No, ); } - // FIXME(inherent_associated_types): Use the explicit predicates from the parent impl. + // FIXME(inherent_associated_types): Use the explicit clauses from the parent impl. ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }) => {} _ => {} @@ -229,7 +229,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( } } -/// Check the explicit predicates declared on the type. +/// Check the explicit clauses declared on the type. /// /// ### Example /// @@ -242,48 +242,46 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// // ... /// } /// ``` -/// Here, we should fetch the explicit predicates, which +/// Here, we should fetch the explicit clauses, which /// will give us `U: 'static` and `U: Outer`. The latter we /// can ignore, but we will want to process `U: 'static`, /// applying the instantiation as above. -// FIXME: change this function's signature and docs to mention clauses instead of predicates #[tracing::instrument(level = "debug", skip(tcx))] -fn check_explicit_predicates<'tcx>( +fn check_explicit_clauses<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, args: &[GenericArg<'tcx>], - required_predicates: &mut RequiredPredicates<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, - ignore_preds_refing_self: IgnorePredicatesReferencingSelf, + required_clauses: &mut RequiredClauses<'tcx>, + explicit_map: &mut ExplicitClausesMap<'tcx>, + ignore_clauses_refing_self: IgnoreClausesReferencingSelf, ) { let explicit_clauses = explicit_map.explicit_clauses_of(tcx, def_id); - for (&clause @ ty::OutlivesPredicate(arg, _), &span) in explicit_clauses.as_ref().skip_binder() - { + for (&clause @ ty::OutlivesClause(arg, _), &span) in explicit_clauses.as_ref().skip_binder() { debug!(?clause); - if let IgnorePredicatesReferencingSelf::Yes = ignore_preds_refing_self + if let IgnoreClausesReferencingSelf::Yes = ignore_clauses_refing_self && arg.walk().any(|arg| arg == tcx.types.self_param.into()) { debug!("ignoring clause since it references `Self`"); continue; } - let clause @ ty::OutlivesPredicate(arg, region) = + let clause @ ty::OutlivesClause(arg, region) = explicit_clauses.rebind(clause).instantiate(tcx, args).skip_norm_wip(); debug!(?clause); - insert_outlives_predicate(tcx, arg, region, span, required_predicates); + insert_outlives_clause(tcx, arg, region, span, required_clauses); } } #[derive(Debug)] -enum IgnorePredicatesReferencingSelf { +enum IgnoreClausesReferencingSelf { Yes, No, } -/// Check the inferred predicates of the type. +/// Check the inferred clauses of the type. /// /// ### Example /// @@ -298,29 +296,29 @@ enum IgnorePredicatesReferencingSelf { /// ``` /// /// Here, when processing the type of field `outer`, we would request the -/// set of implicit predicates computed for `Inner` thus far. This will +/// set of implicit clauses computed for `Inner` thus far. This will /// initially come back empty, but in next round we will get `U: 'b`. /// We then apply the instantiation `['b => 'a, U => T]` and thus get the /// requirement that `T: 'a` holds for `Outer`. -fn check_inferred_predicates<'tcx>( +fn check_inferred_clauses<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>, - global_inferred_outlives: &FxIndexMap>>, - required_predicates: &mut RequiredPredicates<'tcx>, + global_inferred_outlives: &FxIndexMap>>, + required_clauses: &mut RequiredClauses<'tcx>, ) { - // Load the current set of inferred and explicit predicates from `global_inferred_outlives` + // Load the current set of inferred and explicit clauses from `global_inferred_outlives` // and filter the ones that are `TypeOutlives`. - let Some(predicates) = global_inferred_outlives.get(&def_id) else { + let Some(clauses) = global_inferred_outlives.get(&def_id) else { return; }; - for (&predicate, &span) in predicates.as_ref().skip_binder() { - // `predicate` is `U: 'b` in the example above. + for (&clause, &span) in clauses.as_ref().skip_binder() { + // `clause` is `U: 'b` in the example above. // So apply the instantiation to get `T: 'a`. - let ty::OutlivesPredicate(arg, region) = - predicates.rebind(predicate).instantiate(tcx, args).skip_norm_wip(); - insert_outlives_predicate(tcx, arg, region, span, required_predicates); + let ty::OutlivesClause(arg, region) = + clauses.rebind(clause).instantiate(tcx, args).skip_norm_wip(); + insert_outlives_clause(tcx, arg, region, span, required_clauses); } } diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index 6cec89afde2fa..493f2adaf30ca 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -1,6 +1,6 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_middle::ty::{self, CratePredicatesMap, GenericArgKind, TyCtxt, Upcast}; +use rustc_middle::ty::{self, CrateClausesMap, GenericArgKind, TyCtxt, Upcast}; use rustc_span::Span; pub(crate) mod dump; @@ -15,17 +15,17 @@ pub(super) fn inferred_outlives_of( match tcx.def_kind(item_def_id) { DefKind::Struct | DefKind::Enum | DefKind::Union => { let crate_map = tcx.inferred_outlives_crate(()); - crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) + crate_map.clauses.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } DefKind::TyAlias if tcx.type_alias_is_checked(item_def_id) => { let crate_map = tcx.inferred_outlives_crate(()); - crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) + crate_map.clauses.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]) } DefKind::AnonConst if tcx.features().generic_const_exprs() => { let id = tcx.local_def_id_to_hir_id(item_def_id); if tcx.hir_opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that - // we lose out on the predicates of our actual parent if we dont return those predicates here. + // we lose out on the clauses of our actual parent if we dont return those clauses here. // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) // // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ()); @@ -34,7 +34,7 @@ pub(super) fn inferred_outlives_of( // parent item we dont have set as the // parent of generics returned by `generics_of` // - // In the above code we want the anon const to have predicates in its param env for `'b: 'a` + // In the above code we want the anon const to have clauses in its param env for `'b: 'a` let item_def_id = tcx.hir_get_parent_item(id); // In the above code example we would be calling `inferred_outlives_of(Foo)` here tcx.inferred_outlives_of(item_def_id) @@ -46,36 +46,36 @@ pub(super) fn inferred_outlives_of( } } -pub(super) fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { +pub(super) fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CrateClausesMap<'_> { // Compute a map from each ADT (struct/enum/union) and lazy type alias to - // the **explicit** outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote. + // the **explicit** outlives clauses (`T: 'a`, `'a: 'b`) that the user wrote. // Typically there won't be many of these, except in older code where // they were mandatory. Nonetheless, we have to ensure that every such - // predicate is satisfied, so they form a kind of base set of requirements + // clause is satisfied, so they form a kind of base set of requirements // for the type. - // Compute the inferred predicates - let global_inferred_outlives = implicit_infer::infer_predicates(tcx); + // Compute the inferred clauses + let global_inferred_outlives = implicit_infer::infer_clauses(tcx); - // Convert the inferred predicates into the "collected" form the + // Convert the inferred clauses into the "collected" form the // global data structure expects. // // FIXME -- consider correcting impedance mismatch in some way, // probably by updating the global data structure. - let predicates = global_inferred_outlives + let clauses = global_inferred_outlives .iter() .map(|(&def_id, set)| { - let predicates = + let clauses = &*tcx.arena.alloc_from_iter(set.as_ref().skip_binder().iter().filter_map( - |(ty::OutlivesPredicate(arg1, region2), &span)| { + |(ty::OutlivesClause(arg1, region2), &span)| { match arg1.kind() { GenericArgKind::Type(ty1) => Some(( - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)) + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty1, *region2)) .upcast(tcx), span, )), GenericArgKind::Lifetime(region1) => Some(( - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( + ty::ClauseKind::RegionOutlives(ty::OutlivesClause( region1, *region2, )) .upcast(tcx), @@ -88,9 +88,9 @@ pub(super) fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicate } }, )); - (def_id, predicates) + (def_id, clauses) }) .collect(); - ty::CratePredicatesMap { predicates } + ty::CrateClausesMap { clauses } } diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index b2a5575584b42..2ffb343a8fc89 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -5,19 +5,18 @@ use rustc_middle::{bug, span_bug}; use rustc_span::Span; use smallvec::smallvec; -/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred +/// Tracks the `T: 'a` or `'a: 'a` clauses that we have inferred /// must be added to the struct header. -pub(crate) type RequiredPredicates<'tcx> = FxIndexMap, Span>; +pub(crate) type RequiredClauses<'tcx> = FxIndexMap, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the -/// outlives_component and add it to `required_predicates` -// FIXME: change this function's signature and docs to mention clauses instead of predicates -pub(crate) fn insert_outlives_predicate<'tcx>( +/// outlives_component and add it to `required_clauses` +pub(crate) fn insert_outlives_clause<'tcx>( tcx: TyCtxt<'tcx>, arg: GenericArg<'tcx>, outlived_region: Region<'tcx>, span: Span, - required_predicates: &mut RequiredPredicates<'tcx>, + required_clauses: &mut RequiredClauses<'tcx>, ) { // If the `'a` region is bound within the field type itself, we // don't want to propagate this constraint to the header. @@ -51,12 +50,12 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // u32`. Decomposing `&'b u32` into // components would yield `'b`, and we add the // where clause that `'b: 'a`. - insert_outlives_predicate( + insert_outlives_clause( tcx, r.into(), outlived_region, span, - required_predicates, + required_clauses, ); } @@ -75,8 +74,8 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // components would yield `U`, and we add the // where clause that `U: 'a`. let ty: Ty<'tcx> = param_ty.to_ty(tcx); - required_predicates - .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + required_clauses + .entry(ty::OutlivesClause(ty.into(), outlived_region)) .or_insert(span); } @@ -104,8 +103,8 @@ pub(crate) fn insert_outlives_predicate<'tcx>( // Here we want to add an explicit `where ::Item: 'a` // or `Opaque: 'a` depending on the alias kind. let ty = alias_ty.to_ty(tcx, is_rigid); - required_predicates - .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + required_clauses + .entry(ty::OutlivesClause(ty.into(), outlived_region)) .or_insert(span); } @@ -135,7 +134,7 @@ pub(crate) fn insert_outlives_predicate<'tcx>( if !is_free_region(r) { return; } - required_predicates.entry(ty::OutlivesPredicate(arg, outlived_region)).or_insert(span); + required_clauses.entry(ty::OutlivesClause(arg, outlived_region)).or_insert(span); } GenericArgKind::Const(_) => { diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 5c42d79621524..6b496e775288c 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -215,7 +215,7 @@ fn variance_of_opaque( } term.visit_with(&mut collector); } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_, region)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(_, region)) => { region.visit_with(&mut collector); } _ => { diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 733cf757a17cb..db4914f4fa8d8 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -193,11 +193,11 @@ impl<'tcx> InferCtxt<'tcx> { { let constraint = instantiate_value(self.tcx, &result_args, *constraint); match constraint { - ty::RegionConstraint::Outlives(predicate) => { - self.register_outlives_constraint(predicate, *vis, cause); + ty::RegionConstraint::Outlives(clause) => { + self.register_outlives_constraint(clause, *vis, cause); } - ty::RegionConstraint::Eq(predicate) => { - self.register_region_eq_constraint(predicate, *vis, cause); + ty::RegionConstraint::Eq(clause) => { + self.register_region_eq_constraint(clause, *vis, cause); } } } @@ -611,7 +611,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn make_query_region_constraints<'tcx>( outlives_obligations: Vec>, region_constraints: &RegionConstraintData<'tcx>, - assumptions: Vec>, + assumptions: Vec>, ) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys } = region_constraints; @@ -627,7 +627,7 @@ pub fn make_query_region_constraints<'tcx>( | ConstraintKind::VarSubReg | ConstraintKind::RegSubReg => { // Swap regions because we are going from sub (<=) to outlives (>=). - let constraint = ty::OutlivesPredicate(c.sup.into(), c.sub).into(); + let constraint = ty::OutlivesClause(c.sup.into(), c.sub).into(); QueryRegionConstraint { constraint, category: origin.to_constraint_category(), @@ -647,7 +647,7 @@ pub fn make_query_region_constraints<'tcx>( .chain(outlives_obligations.into_iter().map( |TypeOutlivesConstraint { sub_region, sup_type, origin }| { QueryRegionConstraint { - constraint: ty::OutlivesPredicate(sup_type.into(), sub_region).into(), + constraint: ty::OutlivesClause(sup_type.into(), sub_region).into(), category: origin.to_constraint_category(), // We don't do leak checks for type outlives visible_for_leak_check: ty::VisibleForLeakCheck::Unreachable, diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index e6af7b30f4ad3..e59f80417e398 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -110,7 +110,7 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>, // the placeholder `'b` ), - CannotNormalize(ty::PolyTypeOutlivesPredicate<'tcx>, SubregionOrigin<'tcx>), + CannotNormalize(ty::PolyTypeOutlivesClause<'tcx>, SubregionOrigin<'tcx>), } impl<'tcx> RegionResolutionError<'tcx> { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 562534155c8b6..4e584b82bea46 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -148,7 +148,7 @@ pub struct InferCtxtInner<'tcx> { /// are deduced from the well-formedness of the witness's types, and are /// necessary because of the way we anonymize the regions in a coroutine, /// which may cause types to no longer be considered well-formed. - region_assumptions: Vec>, + region_assumptions: Vec>, /// `-Znext-solver`: Successfully proven goals during HIR typeck which /// reference inference variables and get reproven in case MIR type check @@ -187,7 +187,7 @@ impl<'tcx> InferCtxtInner<'tcx> { } #[inline] - pub fn region_assumptions(&self) -> &[ty::ArgOutlivesPredicate<'tcx>] { + pub fn region_assumptions(&self) -> &[ty::ArgOutlivesClause<'tcx>] { &self.region_assumptions } diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 47b738a407967..b9eb1ed5a08e2 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -38,24 +38,24 @@ pub struct OutlivesEnvironment<'tcx> { /// everywhere is just enough of a perf regression to matter. This can/should be /// optimized in the future, though. region_bound_pairs: RegionBoundPairs<'tcx>, - known_type_outlives: Vec>, + known_type_outlives: Vec>, /// Assumptions that come from the well-formedness of coroutines that we prove /// auto trait bounds for during the type checking of this body. - higher_ranked_assumptions: FxHashSet>, + higher_ranked_assumptions: FxHashSet>, } /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = FxIndexSet>>; +pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { /// Create a new `OutlivesEnvironment` from normalized outlives bounds. pub fn from_normalized_bounds( param_env: ty::ParamEnv<'tcx>, - known_type_outlives: Vec>, + known_type_outlives: Vec>, extra_bounds: impl IntoIterator>, - higher_ranked_assumptions: FxHashSet>, + higher_ranked_assumptions: FxHashSet>, ) -> Self { let mut region_relation = TransitiveRelationBuilder::default(); let mut region_bound_pairs = RegionBoundPairs::default(); @@ -66,12 +66,10 @@ impl<'tcx> OutlivesEnvironment<'tcx> { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { OutlivesBound::RegionSubParam(r_a, param_b) => { - region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); + region_bound_pairs.insert(ty::OutlivesClause(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubAlias(r_a, alias_b) => { - region_bound_pairs - .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a)); + region_bound_pairs.insert(ty::OutlivesClause(GenericKind::Alias(alias_b), r_a)); } OutlivesBound::RegionSubRegion(r_a, r_b) => match (r_a.kind(), r_b.kind()) { ( @@ -104,11 +102,11 @@ impl<'tcx> OutlivesEnvironment<'tcx> { &self.region_bound_pairs } - pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] { + pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesClause<'tcx>] { &self.known_type_outlives } - pub fn higher_ranked_assumptions(&self) -> &FxHashSet> { + pub fn higher_ranked_assumptions(&self) -> &FxHashSet> { &self.higher_ranked_assumptions } } diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index ce2650dff9f18..4a107b1325932 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -29,7 +29,7 @@ pub fn explicit_outlives_bounds<'tcx>( .into_iter() .filter_map(ty::Clause::as_region_outlives_clause) .filter_map(ty::Binder::no_bound_vars) - .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)) + .map(|ty::OutlivesClause(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)) } impl<'tcx> InferCtxt<'tcx> { @@ -75,7 +75,7 @@ impl<'tcx> InferCtxt<'tcx> { storage.data.constraints.retain(|(c, _)| match c.kind { ConstraintKind::RegSubReg => !outlives_env .higher_ranked_assumptions() - .contains(&ty::OutlivesPredicate(c.sup.into(), c.sub)), + .contains(&ty::OutlivesClause(c.sup.into(), c.sub)), ConstraintKind::VarSubVar | ConstraintKind::RegSubVar diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 058aaa017cad4..570223b2b4881 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -65,8 +65,8 @@ use rustc_middle::bug; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::outlives::{Component, push_outlives_components}; use rustc_middle::ty::{ - self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesPredicate, Region, RegionExt, RegionVid, - Ty, TyCtxt, TypeVisitableExt, eager_resolve_vars, + self, GenericArgKind, GenericArgsRef, PolyTypeOutlivesClause, Region, RegionExt, RegionVid, Ty, + TyCtxt, TypeVisitableExt, eager_resolve_vars, }; use rustc_span::Span; use smallvec::smallvec; @@ -84,13 +84,13 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; impl<'tcx> InferCtxt<'tcx> { pub fn register_outlives_constraint( &self, - ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>, + ty::OutlivesClause(arg, r2): ty::ArgOutlivesClause<'tcx>, vis: ty::VisibleForLeakCheck, cause: &ObligationCause<'tcx>, ) { match arg.kind() { ty::GenericArgKind::Lifetime(r1) => { - self.register_region_outlives_constraint(ty::OutlivesPredicate(r1, r2), vis, cause); + self.register_region_outlives_constraint(ty::OutlivesClause(r1, r2), vis, cause); } ty::GenericArgKind::Type(ty1) => { self.register_type_outlives_constraint(ty1, r2, cause); @@ -113,7 +113,7 @@ impl<'tcx> InferCtxt<'tcx> { pub fn register_region_outlives_constraint( &self, - ty::OutlivesPredicate(r_a, r_b): ty::RegionOutlivesPredicate<'tcx>, + ty::OutlivesClause(r_a, r_b): ty::RegionOutlivesClause<'tcx>, vis: ty::VisibleForLeakCheck, cause: &ObligationCause<'tcx>, ) { @@ -190,13 +190,13 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow().region_obligations.clone() } - pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) { + pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesClause<'tcx>) { let mut inner = self.inner.borrow_mut(); inner.undo_log.push(UndoLog::PushRegionAssumption); inner.region_assumptions.push(assumption); } - pub fn take_registered_region_assumptions(&self) -> Vec> { + pub fn take_registered_region_assumptions(&self) -> Vec> { assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot"); std::mem::take(&mut self.inner.borrow_mut().region_assumptions) } @@ -217,7 +217,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, // this is always ConstraintConversion but lol conversion: impl TypeOutlivesDelegate<'tcx>, - known_type_outlives: &[PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives: &[PolyTypeOutlivesClause<'tcx>], region_outlives: TransitiveRelation, span: Span, ) { @@ -323,7 +323,7 @@ impl<'tcx> InferCtxt<'tcx> { if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions && outlives_env .higher_ranked_assumptions() - .contains(&ty::OutlivesPredicate(sup_type.into(), sub_region)) + .contains(&ty::OutlivesClause(sup_type.into(), sub_region)) { continue; } @@ -388,7 +388,7 @@ where tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, - caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesClause<'tcx>], ) -> Self { Self { delegate, diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index a90f7d58847e3..de42736ce2b06 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -77,12 +77,12 @@ pub fn extract_verify_if_eq<'tcx>( #[instrument(level = "debug", skip(tcx))] pub(super) fn can_match_erased_ty<'tcx>( tcx: TyCtxt<'tcx>, - outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, + outlives_clause: ty::Binder<'tcx, ty::TypeOutlivesClause<'tcx>>, erased_ty: Ty<'tcx>, ) -> bool { - assert!(!outlives_predicate.has_escaping_bound_vars()); - let erased_outlives_predicate = tcx.erase_and_anonymize_regions(outlives_predicate); - let outlives_ty = erased_outlives_predicate.skip_binder().0; + assert!(!outlives_clause.has_escaping_bound_vars()); + let erased_outlives_clause = tcx.erase_and_anonymize_regions(outlives_clause); + let outlives_ty = erased_outlives_clause.skip_binder().0; if outlives_ty == erased_ty { // pointless micro-optimization true diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index f1e927912ef51..6b92a7c9a476c 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,7 +1,7 @@ use std::assert_matches; use rustc_middle::ty::outlives::{Component, compute_alias_components_recursive}; -use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, OutlivesClause, Ty, TyCtxt}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -24,7 +24,7 @@ pub(crate) struct VerifyBoundCx<'cx, 'tcx> { /// Outside of borrowck the only way to prove `T: '?0` is by /// setting `'?0` to `'empty`. implicit_region_bound: Option>, - caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesClause<'tcx>], } impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { @@ -32,7 +32,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { tcx: TyCtxt<'tcx>, region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, - caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesClause<'tcx>], ) -> Self { Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds } } @@ -95,7 +95,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub(crate) fn approx_declared_bounds_from_env( &self, alias_ty: ty::AliasTy<'tcx>, - ) -> Vec> { + ) -> Vec> { let erased_alias_ty = self.tcx.erase_and_anonymize_regions( alias_ty.to_ty(self.tcx, ty::IsRigid::yes_if_next_solver(self.tcx)), ); @@ -107,7 +107,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // Search the env for where clauses like `P: 'a`. let env_bounds = self.approx_declared_bounds_from_env(alias_ty).into_iter().map(|binder| { // FIXME(#155345): We probably want to assert the alias is rigid here. - if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() + if let Some(ty::OutlivesClause(ty, r)) = binder.no_bound_vars() && let ty::Alias(_, alias_ty_from_bound) = *ty.kind() && alias_ty_from_bound == alias_ty { @@ -117,7 +117,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { VerifyBound::OutlivedBy(r) } else { let verify_if_eq_b = - binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }); + binder.map_bound(|ty::OutlivesClause(ty, bound)| VerifyIfEq { ty, bound }); VerifyBound::IfEq(verify_if_eq_b) } }); @@ -187,7 +187,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env( &self, generic_ty: Ty<'tcx>, - ) -> Vec> { + ) -> Vec> { assert_matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_)); self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } @@ -207,15 +207,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, - ) -> Vec> { + ) -> Vec> { let tcx = self.tcx; let mut bounds = vec![]; // To start, collect bounds from user environment. Note that // parameter environments are already elaborated, so we don't // have to worry about that. - bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_predicate| { - super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty) + bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_clause| { + super::test_type_match::can_match_erased_ty(tcx, *outlives_clause, erased_ty) })); // Next, collect regions we scraped from the well-formedness @@ -229,7 +229,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must outlive `'a`, but we don't know that // this holds from first principles. - bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesClause(p, r)| { debug!( "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", (r, p) @@ -248,7 +248,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { let p_ty = p.to_ty(tcx); let erased_p_ty = self.tcx.erase_and_anonymize_regions(p_ty); - (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) + (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesClause(p_ty, r))) })); bounds diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2737ec849ba24..fb2d200cd638f 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1885,7 +1885,7 @@ impl ExplicitOutlivesRequirements { inferred_outlives .filter_map(|(clause, _)| match clause.kind().skip_binder() { - ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a.kind() { + ty::ClauseKind::RegionOutlives(ty::OutlivesClause(a, b)) => match a.kind() { ty::ReEarlyParam(ebr) if item_generics.region_param(ebr, tcx).def_id == lifetime.to_def_id() => { @@ -1904,7 +1904,7 @@ impl ExplicitOutlivesRequirements { ) -> Vec> { inferred_outlives .filter_map(|(clause, _)| match clause.kind().skip_binder() { - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) => { a.is_param(index).then_some(b) } _ => None, diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 8f182d096e759..46429f7adfb12 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -80,7 +80,7 @@ pub struct QueryResponse<'tcx, R> { #[derive(StableHash, TypeFoldable, TypeVisitable)] pub struct QueryRegionConstraints<'tcx> { pub constraints: Vec>, - pub assumptions: Vec>, + pub assumptions: Vec>, } impl QueryRegionConstraints<'_> { diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 6ad87ada17ff4..f90274460e379 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -805,14 +805,14 @@ rustc_queries! { feedable } - /// Returns the *inferred outlives-predicates* of the item given by `DefId`. + /// Returns the *inferred outlives-clauses* of the item given by `DefId`. /// /// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`. /// /// **Tip**: You can use `#[rustc_dump_inferred_outlives]` on an item to basically /// print the result of this query for use in UI tests or for debugging purposes. query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] { - desc { "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) } + desc { "computing inferred outlives-clauses of `{}`", tcx.def_path_str(key) } cache_on_disk separate_provide_extern feedable @@ -1026,16 +1026,16 @@ rustc_queries! { separate_provide_extern } - /// Gets a map with the inferred outlives-predicates of every item in the local crate. + /// Gets a map with the inferred outlives-clauses of every item in the local crate. /// ///
/// /// **Do not call this query** directly, use [`Self::inferred_outlives_of`] instead. /// ///
- query inferred_outlives_crate(_: ()) -> &'tcx ty::CratePredicatesMap<'tcx> { + query inferred_outlives_crate(_: ()) -> &'tcx ty::CrateClausesMap<'tcx> { arena_cache - desc { "computing the inferred outlives-predicates for items in this crate" } + desc { "computing the inferred outlives-clauses for items in this crate" } } /// Maps from an impl/trait or struct/variant `DefId` diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 3ec11720ba06e..819ac2ad60daf 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -160,7 +160,7 @@ pub struct CtxtInterners<'tcx> { captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, valtree: InternedSet<'tcx, ty::ValTreeKind>>, patterns: InternedSet<'tcx, List>>, - outlives: InternedSet<'tcx, List>>, + outlives: InternedSet<'tcx, List>>, } impl<'tcx> CtxtInterners<'tcx> { @@ -1727,9 +1727,7 @@ nop_list_lift! { } nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind<'a> => ty::BoundVariableKind<'tcx> } nop_list_lift! { patterns; Pattern<'a> => Pattern<'tcx> } -nop_list_lift! { - outlives; ty::ArgOutlivesPredicate<'a> => ty::ArgOutlivesPredicate<'tcx> -} +nop_list_lift! { outlives; ty::ArgOutlivesClause<'a> => ty::ArgOutlivesClause<'tcx> } // This is the impl for `&'a GenericArgs<'a>`. nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> } @@ -2019,7 +2017,7 @@ slice_interners!( local_def_ids: intern_local_def_ids(LocalDefId), captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>), patterns: pub mk_patterns(Pattern<'tcx>), - outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>), + outlives: pub mk_outlives(ty::ArgOutlivesClause<'tcx>), predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)), ); @@ -2487,8 +2485,8 @@ impl<'tcx> TyCtxt<'tcx> { where I: Iterator, T: CollectAndApply< - ty::ArgOutlivesPredicate<'tcx>, - &'tcx ty::List>, + ty::ArgOutlivesClause<'tcx>, + &'tcx ty::List>, >, { T::collect_and_apply(iter, |xs| self.mk_outlives(xs)) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 983b4afefdb5f..9e7e4f2fe0c4f 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -123,7 +123,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type EarlyParamRegion = ty::EarlyParamRegion; type LateParamRegion = ty::LateParamRegion; - type RegionAssumptions = &'tcx ty::List>; + type RegionAssumptions = &'tcx ty::List>; type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6be105601d92c..149fa69f2abbb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -84,14 +84,14 @@ pub use self::list::{List, ListWithCachedTypeInfo}; pub use self::opaque_types::OpaqueTypeKey; pub use self::pattern::{Pattern, PatternKind}; pub use self::predicate::{ - AliasTerm, AliasTermKind, ArgOutlivesPredicate, Clause, ClauseKind, CoercePredicate, + AliasTerm, AliasTermKind, ArgOutlivesClause, Clause, ClauseKind, CoercePredicate, ExistentialPredicate, ExistentialPredicateStableCmpExt, ExistentialProjection, - ExistentialTraitRef, HostEffectPredicate, NormalizesTo, OutlivesPredicate, PolyCoercePredicate, + ExistentialTraitRef, HostEffectPredicate, NormalizesTo, OutlivesClause, PolyCoercePredicate, PolyExistentialPredicate, PolyExistentialProjection, PolyExistentialTraitRef, - PolyProjectionPredicate, PolyRegionOutlivesPredicate, PolySubtypePredicate, PolyTraitPredicate, - PolyTraitRef, PolyTypeOutlivesPredicate, Predicate, PredicateKind, ProjectionPredicate, - RegionConstraint, RegionEqPredicate, RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, - TraitRef, TypeOutlivesPredicate, + PolyProjectionPredicate, PolyRegionOutlivesClause, PolySubtypePredicate, PolyTraitPredicate, + PolyTraitRef, PolyTypeOutlivesClause, Predicate, PredicateKind, ProjectionPredicate, + RegionConstraint, RegionEqPredicate, RegionOutlivesClause, SubtypePredicate, TraitPredicate, + TraitRef, TypeOutlivesClause, }; pub use self::region::{ EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionExt, RegionKind, @@ -670,11 +670,11 @@ impl<'tcx> rustc_type_ir::Flags for Ty<'tcx> { /// `tcx.inferred_outlives_of()` to get the outlives for a *particular* /// item. #[derive(StableHash, Debug)] -pub struct CratePredicatesMap<'tcx> { +pub struct CrateClausesMap<'tcx> { /// For each struct with outlive bounds, maps to a vector of the - /// predicate of its outlive bounds. If an item has no outlives + /// clause of its outlive bounds. If an item has no outlives /// bounds, it will have no entry. - pub predicates: DefIdMap<&'tcx [(Clause<'tcx>, Span)]>, + pub clauses: DefIdMap<&'tcx [(Clause<'tcx>, Span)]>, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index edd1ea96fc7ae..99de04cd2dcac 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -21,15 +21,15 @@ pub type PredicateKind<'tcx> = ir::PredicateKind>; pub type NormalizesTo<'tcx> = ir::NormalizesTo>; pub type CoercePredicate<'tcx> = ir::CoercePredicate>; pub type SubtypePredicate<'tcx> = ir::SubtypePredicate>; -pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate, T>; -pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>; -pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>; -pub type ArgOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>; +pub type OutlivesClause<'tcx, T> = ir::OutlivesClause, T>; +pub type RegionOutlivesClause<'tcx> = OutlivesClause<'tcx, ty::Region<'tcx>>; +pub type TypeOutlivesClause<'tcx> = OutlivesClause<'tcx, Ty<'tcx>>; +pub type ArgOutlivesClause<'tcx> = OutlivesClause<'tcx, ty::GenericArg<'tcx>>; pub type RegionEqPredicate<'tcx> = ir::RegionEqPredicate>; pub type RegionConstraint<'tcx> = ir::RegionConstraint>; pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; +pub type PolyRegionOutlivesClause<'tcx> = ty::Binder<'tcx, RegionOutlivesClause<'tcx>>; +pub type PolyTypeOutlivesClause<'tcx> = ty::Binder<'tcx, TypeOutlivesClause<'tcx>>; pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; pub type PolyProjectionPredicate<'tcx> = ty::Binder<'tcx, ProjectionPredicate<'tcx>>; @@ -195,7 +195,7 @@ impl<'tcx> Clause<'tcx> { } } - pub fn as_type_outlives_clause(self) -> Option>> { + pub fn as_type_outlives_clause(self) -> Option>> { let clause = self.kind(); if let ty::ClauseKind::TypeOutlives(o) = clause.skip_binder() { Some(clause.rebind(o)) @@ -204,9 +204,7 @@ impl<'tcx> Clause<'tcx> { } } - pub fn as_region_outlives_clause( - self, - ) -> Option>> { + pub fn as_region_outlives_clause(self) -> Option>> { let clause = self.kind(); if let ty::ClauseKind::RegionOutlives(o) = clause.skip_binder() { Some(clause.rebind(o)) @@ -540,20 +538,20 @@ impl<'tcx> UpcastFrom, PolyTraitPredicate<'tcx>> for Clause<'tcx> { } } -impl<'tcx> UpcastFrom, RegionOutlivesPredicate<'tcx>> for Predicate<'tcx> { - fn upcast_from(from: RegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, RegionOutlivesClause<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: RegionOutlivesClause<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(from))).upcast(tcx) } } -impl<'tcx> UpcastFrom, PolyRegionOutlivesPredicate<'tcx>> for Predicate<'tcx> { - fn upcast_from(from: PolyRegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx) +impl<'tcx> UpcastFrom, PolyRegionOutlivesClause<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: PolyRegionOutlivesClause<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + from.map_bound(|c| PredicateKind::Clause(ClauseKind::RegionOutlives(c))).upcast(tcx) } } -impl<'tcx> UpcastFrom, TypeOutlivesPredicate<'tcx>> for Predicate<'tcx> { - fn upcast_from(from: TypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, TypeOutlivesClause<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: TypeOutlivesClause<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(from))).upcast(tcx) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 928b798eb5d1e..0d1d09d70572c 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2984,7 +2984,7 @@ where } } -impl<'tcx, T, P: PrettyPrinter<'tcx>> Print

for ty::OutlivesPredicate<'tcx, T> +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print

for ty::OutlivesClause<'tcx, T> where T: Print

, { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 68de7805a52e6..079f0db7e8a68 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -788,6 +788,6 @@ list_fold! { &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>: mk_predefined_opaques_in_body, &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, - &'tcx ty::List> : mk_outlives, + &'tcx ty::List> : mk_outlives, &'tcx ty::List> : mk_const_list, } diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 560bb15a4db30..58614bf15c7f3 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -481,7 +481,7 @@ fn register_region_constraints( { for (constraint, vis) in constraints { match constraint { - ty::RegionConstraint::Outlives(ty::OutlivesPredicate(lhs, rhs)) => match lhs.kind() { + ty::RegionConstraint::Outlives(ty::OutlivesClause(lhs, rhs)) => match lhs.kind() { ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, vis, span), ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs index f31f292240a56..ef6dd8293e788 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs @@ -11,7 +11,7 @@ use rustc_type_ir::region_constraint::{ Assumptions, RegionConstraint, eagerly_handle_placeholders_in_universe, }; use rustc_type_ir::{ - AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, OutlivesPredicate, Region, TypeVisitable, + AliasTy, Binder, ClauseKind, InferCtxtLike, Interner, OutlivesClause, Region, TypeVisitable, TypeVisitableExt, TypeVisitor, UniverseIndex, max_universe, }; use tracing::{debug, instrument}; @@ -108,7 +108,7 @@ where clauses.filter(move |clause| max_universe(&**self.delegate, *clause) == u).for_each( |clause| match clause.kind().skip_binder() { - RegionOutlives(OutlivesPredicate(r1, r2)) => { + RegionOutlives(OutlivesClause(r1, r2)) => { assert!(clause.kind().no_bound_vars().is_some()); region_outlives_builder.add(r1, r2); } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 8a869df067301..3504882834268 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -87,9 +87,9 @@ where #[instrument(level = "trace", skip(self))] fn compute_type_outlives_goal( &mut self, - goal: Goal>, + goal: Goal>, ) -> QueryResultOrRerunNonErased { - let ty::OutlivesPredicate(ty, lt) = goal.predicate; + let ty::OutlivesClause(ty, lt) = goal.predicate; let ty = self.normalize(GoalSource::Misc, goal.param_env, ty::Unnormalized::new_wip(ty))?; if self.cx().assumptions_on_binders() { @@ -114,9 +114,9 @@ where #[instrument(level = "trace", skip(self))] fn compute_region_outlives_goal( &mut self, - goal: Goal>>, + goal: Goal>>, ) -> QueryResultOrRerunNonErased { - let ty::OutlivesPredicate(a, b) = goal.predicate; + let ty::OutlivesClause(a, b) = goal.predicate; if self.cx().assumptions_on_binders() { let constraint = diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index ba5a809df5b0f..082fe1b9bce63 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -899,7 +899,7 @@ where } ecx.add_goal( GoalSource::Misc, - goal.with(cx, ty::OutlivesPredicate(ty_lifetime, lifetime)), + goal.with(cx, ty::OutlivesClause(ty_lifetime, lifetime)), )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } @@ -1084,7 +1084,7 @@ where )?; // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)))?; + ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesClause(a_ty, b_region)))?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } @@ -1200,7 +1200,7 @@ where // Also require that a_ty's lifetime outlives b_ty's lifetime. ecx.add_goal( GoalSource::ImplWhereBound, - Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)), + Goal::new(ecx.cx(), param_env, ty::OutlivesClause(a_region, b_region)), )?; ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 61baa4d838e36..bbc0c0499e992 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -144,7 +144,7 @@ where try_visit!(term.visit_with(self)); self.visit_projection_term(projection_ty) } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => ty.visit_with(self), + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, _region)) => ty.visit_with(self), ty::ClauseKind::RegionOutlives(..) => V::Result::output(), ty::ClauseKind::ConstArgHasType(ct, ty) => { try_visit!(ct.visit_with(self)); diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 68fd152921cf7..504b6f03fcc7d 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1568,8 +1568,8 @@ pub enum PredicateKind { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ClauseKind { Trait(TraitPredicate), - RegionOutlives(RegionOutlivesPredicate), - TypeOutlives(TypeOutlivesPredicate), + RegionOutlives(RegionOutlivesClause), + TypeOutlives(TypeOutlivesClause), Projection(ProjectionPredicate), ConstArgHasType(TyConst, Ty), WellFormed(TermKind), @@ -1602,10 +1602,17 @@ pub struct TraitPredicate { } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct OutlivesPredicate(pub A, pub B); +pub struct OutlivesClause(pub A, pub B); -pub type RegionOutlivesPredicate = OutlivesPredicate; -pub type TypeOutlivesPredicate = OutlivesPredicate; +pub type RegionOutlivesClause = OutlivesClause; +pub type TypeOutlivesClause = OutlivesClause; + +#[deprecated = "renamed to [`OutlivesClause`]"] +pub type OutlivesPredicate = OutlivesClause; +#[deprecated = "renamed to [`RegionOutlivesClause`]"] +pub type RegionOutlivesPredicate = RegionOutlivesClause; +#[deprecated = "renamed to [`TypeOutlivesClause`]"] +pub type TypeOutlivesPredicate = TypeOutlivesClause; #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct ProjectionPredicate { diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 17edd29dcbb42..54e11aca7da77 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -772,8 +772,8 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { crate::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables, cx)) } ClauseKind::TypeOutlives(type_outlives) => { - let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives; - crate::ty::ClauseKind::TypeOutlives(crate::ty::OutlivesPredicate( + let ty::OutlivesClause::<_, _>(a, b) = type_outlives; + crate::ty::ClauseKind::TypeOutlives(crate::ty::OutlivesClause( a.stable(tables, cx), b.stable(tables, cx), )) @@ -856,19 +856,19 @@ impl<'tcx> Stable<'tcx> for ty::TraitPredicate<'tcx> { } } -impl<'tcx, T> Stable<'tcx> for ty::OutlivesPredicate<'tcx, T> +impl<'tcx, T> Stable<'tcx> for ty::OutlivesClause<'tcx, T> where T: Stable<'tcx>, { - type T = crate::ty::OutlivesPredicate; + type T = crate::ty::OutlivesClause; fn stable<'cx>( &self, tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let ty::OutlivesPredicate(a, b) = self; - crate::ty::OutlivesPredicate(a.stable(tables, cx), b.stable(tables, cx)) + let ty::OutlivesClause(a, b) = self; + crate::ty::OutlivesClause(a.stable(tables, cx), b.stable(tables, cx)) } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index b1386a5c54071..f92c0e14c8f3a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -452,7 +452,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { for (clause, span) in self.tcx.clauses_of(def_id).instantiate_identity(self.tcx).into_iter() { - if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) = + if let ty::ClauseKind::TypeOutlives(ty::OutlivesClause(a, b)) = clause.kind().skip_binder() && let ty::Param(param) = a.kind() && param.name == kw::SelfUpper diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 13c4a9169d339..7700b442e4bc8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -844,7 +844,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { &dummy_cause, ); } - (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { + (Some(ty::OutlivesClause(t_a, r_b)), _) => { selcx.infcx.register_type_outlives_constraint(t_a, r_b, &dummy_cause); } _ => {} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 8179b0f6f01a1..4a30164cfea2a 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -499,7 +499,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(Default::default()) } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesClause( t_a, r_b, ))) => { diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index e534b1f6e0cc6..34872b9767ba1 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -343,7 +343,7 @@ fn do_normalize_clauses<'tcx>( // support for implied bounds on binders. // // This is required by trait-system-refactor-initiative#166. The new solver encounters - // this more frequently as we entirely ignore outlives predicates with the old solver. + // this more frequently as we entirely ignore outlives clauses with the old solver. let _errors = infcx.resolve_regions(cause.body_def_id, elaborated_env, []); match infcx.fully_resolve(clauses) { Ok(clauses) => Ok(clauses), diff --git a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs index 5f8802310978e..5cba32d742f62 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_for_liveness.rs @@ -55,8 +55,7 @@ pub(crate) fn live_args_for_alias_from_outlives_bounds<'tcx>( } else { test_type_match::extract_verify_if_eq( tcx, - &outlives - .map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }), + &outlives.map_bound(|ty::OutlivesClause(ty, bound)| VerifyIfEq { ty, bound }), // FIXME(#155345): Region handling should generally only // deal with rigid aliases, making sure we do so correctly // everywhere is effort, so we're just using `No` everywhere @@ -345,7 +344,7 @@ fn live_args_for_outlives_clause<'tcx>( tcx: TyCtxt<'tcx>, alias_def_id: DefId, ty: Ty<'tcx>, - outlives: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, + outlives: ty::Binder<'tcx, ty::TypeOutlivesClause<'tcx>>, ) -> Option>>> { // N.B. it's okay to skip the binder here (and in the rest of the function), // because all variables under binders do not escape @@ -370,7 +369,7 @@ fn live_args_for_outlives_clause<'tcx>( // we want *all* the identity regions in `ty` that match the outlives bound. test_type_match::extract_verify_if_eq( tcx, - &outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }), + &outlives.map_bound(|ty::OutlivesClause(ty, bound)| VerifyIfEq { ty, bound }), ty, )?; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index a126cc1f09b2e..81cf4ac607074 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -135,11 +135,12 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( } // We need to register region relationships - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives( - ty::OutlivesPredicate(r_a, r_b), - )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)), + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesClause( + r_a, + r_b, + ))) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)), - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesClause( ty_a, r_b, ))) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 1eef77b436ac6..7b330c942807c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -89,13 +89,13 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { } } -/// This impl is not needed, since we never normalize type outlives predicates +/// This impl is not needed, since we never normalize type outlives clauses /// in the old solver, but is required by trait bounds to be happy. -impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesPredicate<'tcx> { +impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesClause<'tcx> { fn type_op_method( _tcx: TyCtxt<'tcx>, _canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<'tcx, Self>>>, ) -> Result, NoSolution> { - unreachable!("we never normalize PolyTypeOutlivesPredicate") + unreachable!("we never normalize PolyTypeOutlivesClause") } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4b083d9d59452..83387e4b7331e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -318,7 +318,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { )] } Condition::Outlives { long, short } => { - let outlives = ty::OutlivesPredicate(long, short); + let outlives = ty::OutlivesClause(long, short); thin_vec![Obligation::with_depth( tcx, obligation.cause.clone(), @@ -1096,7 +1096,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|_| SelectionError::Unimplemented)?; // Register one obligation for 'a: 'b. - let outlives = ty::OutlivesPredicate(r_a, r_b); + let outlives = ty::OutlivesClause(r_a, r_b); obligations.push(Obligation::with_depth( tcx, obligation.cause.clone(), @@ -1146,7 +1146,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If the type is `Foo + 'a`, ensure that the type // being cast to `Foo + 'a` outlives `'a`: - let outlives = ty::OutlivesPredicate(source, r); + let outlives = ty::OutlivesClause(source, r); nested.push(predicate_to_obligation( ty::ClauseKind::TypeOutlives(outlives).upcast(tcx), )); @@ -1331,13 +1331,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.skip_binder().kind() { ty::Dynamic(_bounds, lifetime) => { - obligations.push( - obligation.with( - tcx, - ty_lifetime - .map_bound(|ty_lifetime| ty::OutlivesPredicate(ty_lifetime, lifetime)), - ), - ); + obligations.push(obligation.with( + tcx, + ty_lifetime.map_bound(|ty_lifetime| ty::OutlivesClause(ty_lifetime, lifetime)), + )); } ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f3382df7c11da..ea71d3f7da234 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2764,7 +2764,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + ty::Binder::dummy(ty::OutlivesClause(a_region, b_region)), )); Ok(Some(nested)) @@ -3267,5 +3267,5 @@ pub(crate) enum ProjectionMatchesProjection { #[derive(Clone, Debug, TypeFoldable, TypeVisitable)] pub(crate) struct AutoImplConstituents<'tcx> { pub types: Vec>, - pub assumptions: Vec>, + pub assumptions: Vec>, } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 6231f781db2c5..1b2e19fd05316 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -178,7 +178,7 @@ pub fn clause_obligations<'tcx>( // the corresponding trait predicate it should've been generated beside. } ty::ClauseKind::RegionOutlives(..) => {} - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, _reg)) => { wf.add_wf_preds_for_term(ty.into()); } ty::ClauseKind::Projection(t) => { @@ -653,7 +653,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { for implicit_bound in implicit_bounds { let cause = self.cause(ObligationCauseCode::ObjectTypeBound(ty, explicit_bound)); let outlives = - ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); + ty::Binder::dummy(ty::OutlivesClause(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::with_depth( self.tcx(), cause, @@ -847,7 +847,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.recursion_depth, self.param_env, ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives( - ty::OutlivesPredicate(rty, r), + ty::OutlivesClause(rty, r), ))), )); } @@ -1235,14 +1235,14 @@ pub fn object_region_bounds<'tcx>( ) -> Vec> { let erased_self_ty = tcx.types.trait_object_dummy_self; - let predicates = + let clauses = existential_predicates.iter().map(|predicate| predicate.with_self_ty(tcx, erased_self_ty)); - traits::elaborate(tcx, predicates) - .filter_map(|pred| { - debug!(?pred); - match pred.kind().skip_binder() { - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { + traits::elaborate(tcx, clauses) + .filter_map(|clause| { + debug!(?clause); + match clause.kind().skip_binder() { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ref t, ref r)) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> // erased_self_ty : 'a` (we interpret a diff --git a/compiler/rustc_traits/src/coroutine_witnesses.rs b/compiler/rustc_traits/src/coroutine_witnesses.rs index c874feb4e0bbb..762471eefe4dd 100644 --- a/compiler/rustc_traits/src/coroutine_witnesses.rs +++ b/compiler/rustc_traits/src/coroutine_witnesses.rs @@ -54,7 +54,7 @@ fn compute_assumptions<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, bound_tys: &'tcx ty::List>, -) -> &'tcx ty::List> { +) -> &'tcx ty::List> { if tcx.next_trait_solver_globally() || !tcx.sess.opts.unstable_opts.higher_ranked_assumptions { return &ty::List::empty(); } diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index c397982461a30..06c45fb539f63 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -66,7 +66,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // // Side-note: We don't really need to do this remapping for early-bound // lifetimes because they're already "linked" by the bidirectional outlives - // predicates we insert in the `explicit_clauses_of` query for RPITITs. + // clauses we insert in the `explicit_clauses_of` query for RPITITs. let mut mapping = FxHashMap::default(); let generics = tcx.generics_of(def_id); diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 1460a481b79a5..828bd107f5b34 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -197,7 +197,7 @@ impl> Elaborator { }, ), ), - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty_max, r_min)) => { // We know that `T: 'a` for some type `T`. We can // often elaborate this. For example, if we know that // `[U]: 'a`, that implies that `U: 'a`. Similarly, if @@ -259,18 +259,18 @@ fn elaborate_component_to_clause( if r.is_bound() { None } else { - Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r, outlives_region))) + Some(ty::ClauseKind::RegionOutlives(ty::OutlivesClause(r, outlives_region))) } } Component::Param(p) => { let ty = Ty::new_param(cx, p); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, outlives_region))) } Component::Placeholder(p) => { let ty = Ty::new_placeholder(cx, p); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, outlives_region))) } Component::UnresolvedInferenceVariable(_) => None, @@ -278,7 +278,7 @@ fn elaborate_component_to_clause( Component::Alias(is_rigid, alias_ty) => { // We might end up here if we have `Foo<::Assoc>: 'a`. // With this, we can deduce that `::Assoc: 'a`. - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesClause( alias_ty.to_ty(cx, is_rigid), outlives_region, ))) @@ -386,12 +386,12 @@ impl> Iterator for FilterToTraits( cx: I, - assumptions: impl IntoIterator>, -) -> HashSet> { + assumptions: impl IntoIterator>, +) -> HashSet> { let mut collected = HashSet::default(); - for ty::OutlivesPredicate(arg1, r2) in assumptions { - collected.insert(ty::OutlivesPredicate(arg1, r2)); + for ty::OutlivesClause(arg1, r2) in assumptions { + collected.insert(ty::OutlivesClause(arg1, r2)); match arg1.kind() { // Elaborate the components of an type, since we may have substituted a // generic coroutine with a more specific type. @@ -402,22 +402,22 @@ pub fn elaborate_outlives_assumptions( match c { Component::Region(r1) => { if !r1.is_bound() { - collected.insert(ty::OutlivesPredicate(r1.into(), r2)); + collected.insert(ty::OutlivesClause(r1.into(), r2)); } } Component::Param(p) => { let ty = Ty::new_param(cx, p); - collected.insert(ty::OutlivesPredicate(ty.into(), r2)); + collected.insert(ty::OutlivesClause(ty.into(), r2)); } Component::Placeholder(p) => { let ty = Ty::new_placeholder(cx, p); - collected.insert(ty::OutlivesPredicate(ty.into(), r2)); + collected.insert(ty::OutlivesClause(ty.into(), r2)); } Component::Alias(is_rigid, alias_ty) => { - collected.insert(ty::OutlivesPredicate( + collected.insert(ty::OutlivesClause( alias_ty.to_ty(cx, is_rigid).into(), r2, )); diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index adcda752632e7..6b89f842bd9b0 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -395,14 +395,11 @@ impl FlagComputation { })) => { self.add_args(trait_ref.args.as_slice()); } - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - a, - b, - ))) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesClause(a, b))) => { self.add_region(a); self.add_region(b); } - ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesClause( ty, region, ))) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 4776771f5e7f8..d4ced992829c4 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -463,8 +463,8 @@ pub trait Predicate>: + UpcastFrom>> + UpcastFrom> + UpcastFrom> - + UpcastFrom> - + UpcastFrom>> + + UpcastFrom> + + UpcastFrom>> + IntoKind>> + Elaboratable { @@ -511,7 +511,7 @@ pub trait Clause>: { fn as_predicate(self) -> I::Predicate; - fn as_type_outlives_clause(self) -> Option>> { + fn as_type_outlives_clause(self) -> Option>> { self.kind() .map_bound(|clause| { if let ty::ClauseKind::TypeOutlives(outlives) = clause { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 1fa698a4faeaf..9ca1ec19a1339 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -193,7 +193,7 @@ pub trait Interner: + Debug + Hash + Eq - + SliceLike> + + SliceLike> + TypeFoldable; // Predicates @@ -340,8 +340,8 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; - /// This is equivalent to computing the super-predicates of the trait for this impl - /// and filtering them to the outlives predicates. This is purely for performance. + /// This is equivalent to computing the super-clauses of the trait for this impl + /// and filtering them to the outlives clauses. This is purely for performance. fn impl_super_outlives( self, impl_def_id: Self::ImplId, diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 5d14f7fba5b07..ef70c50b3ae04 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -4,7 +4,7 @@ use std::fmt; use crate::{AliasConst, ClosureKind}; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind, Placeholder, + HostEffectPredicate, Interner, NormalizesTo, OutlivesClause, PatternKind, Placeholder, ProjectionPredicate, Region, SubtypePredicate, TraitPredicate, TraitRef, }; @@ -64,12 +64,12 @@ where } } -impl fmt::Display for OutlivesPredicate +impl fmt::Display for OutlivesClause where - I: IrPrint>, + I: IrPrint>, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - >>::print(self, fmt) + >>::print(self, fmt) } } diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index 1494da29527c4..15741c926092e 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -8,7 +8,7 @@ use smallvec::{SmallVec, smallvec}; use crate::data_structures::SsoHashSet; use crate::inherent::*; use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor}; -use crate::{self as ty, AliasTy, Interner, OutlivesPredicate, Region, Unnormalized}; +use crate::{self as ty, AliasTy, Interner, OutlivesClause, Region, Unnormalized}; #[derive_where(Debug; I: Interner)] pub enum Component { @@ -279,7 +279,7 @@ pub fn declared_bounds_from_definition( bounds .iter_instantiated(cx, alias_ty.args) .map(Unnormalized::skip_norm_wip) - .filter_map(|p| p.as_type_outlives_clause()) - .filter_map(|p| p.no_bound_vars()) - .map(|OutlivesPredicate(_, r)| r) + .filter_map(|c| c.as_type_outlives_clause()) + .filter_map(|c| c.no_bound_vars()) + .map(|OutlivesClause(_, r)| r) } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 910fd9d7f721b..2d04d28a41d58 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -21,9 +21,9 @@ use crate::{self as ty, Alias, Interner, Region}; feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] -pub struct OutlivesPredicate(pub A, pub Region); +pub struct OutlivesClause(pub A, pub Region); -impl Eq for OutlivesPredicate {} +impl Eq for OutlivesClause {} /// `'a == 'b`. /// For the rationale behind having this instead of a pair of bidirectional @@ -39,8 +39,8 @@ pub struct RegionEqPredicate(pub Region, pub Region); impl RegionEqPredicate { /// Decompose `'a == 'b` into `['a: 'b, 'b: 'a]` - pub fn into_bidirectional_outlives(self) -> [OutlivesPredicate; 2] { - [OutlivesPredicate(self.0.into(), self.1), OutlivesPredicate(self.1.into(), self.0)] + pub fn into_bidirectional_outlives(self) -> [OutlivesClause; 2] { + [OutlivesClause(self.0.into(), self.1), OutlivesClause(self.1.into(), self.0)] } } @@ -51,12 +51,12 @@ impl RegionEqPredicate { derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext) )] pub enum RegionConstraint { - Outlives(OutlivesPredicate), + Outlives(OutlivesClause), Eq(RegionEqPredicate), } -impl From> for RegionConstraint { - fn from(value: OutlivesPredicate) -> Self { +impl From> for RegionConstraint { + fn from(value: OutlivesClause) -> Self { RegionConstraint::Outlives(value) } } @@ -80,7 +80,7 @@ impl RegionConstraint { /// If `self` is an eq constraint, iterate through its decomposed bidirectional outlives /// bounds and if not, just iterate once for the outlives bound itself. - pub fn iter_outlives(self) -> impl Iterator> { + pub fn iter_outlives(self) -> impl Iterator> { match self { RegionConstraint::Outlives(outlives) => iter::once(outlives).chain(None), RegionConstraint::Eq(eq) => { diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 9973b64d7e9b9..2b245addc93f4 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -22,10 +22,10 @@ pub enum ClauseKind { Trait(ty::TraitPredicate), /// `where 'a: 'r` - RegionOutlives(ty::OutlivesPredicate>), + RegionOutlives(ty::OutlivesClause>), /// `where T: 'r` - TypeOutlives(ty::OutlivesPredicate), + TypeOutlives(ty::OutlivesClause), /// `where ::Name == X`, approximately. /// See the `ProjectionPredicate` struct for details. diff --git a/compiler/rustc_type_ir/src/region_constraint.rs b/compiler/rustc_type_ir/src/region_constraint.rs index 8f738e4d6f6f9..54020662b1470 100644 --- a/compiler/rustc_type_ir/src/region_constraint.rs +++ b/compiler/rustc_type_ir/src/region_constraint.rs @@ -51,7 +51,7 @@ use crate::inherent::*; use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo}; use crate::{ AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, FallibleTypeFolder, - GenericTypeVisitable, InferCtxtLike, Interner, IsRigid, OutlivesPredicate, Region, RegionKind, + GenericTypeVisitable, InferCtxtLike, Interner, IsRigid, OutlivesClause, Region, RegionKind, TyKind, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex, Variance, VisitorResult, max_universe, set_aliases_to_non_rigid, try_visit, walk_visitable_list, @@ -59,7 +59,7 @@ use crate::{ #[derive_where(Clone, Debug; I: Interner)] pub struct Assumptions { - pub type_outlives: Vec>>, + pub type_outlives: Vec>>, pub region_outlives: TransitiveRelation>, pub inverse_region_outlives: TransitiveRelation>, } @@ -74,7 +74,7 @@ impl Assumptions { } pub fn new( - type_outlives: Vec>>, + type_outlives: Vec>>, region_outlives: TransitiveRelation>, ) -> Self { Self { @@ -969,7 +969,7 @@ pub fn regions_outlived_by_placeholder( } assumptions.type_outlives.iter().flat_map(move |binder| match binder.no_bound_vars() { - Some(OutlivesPredicate(ty, r)) => (ty == t).then_some(r), + Some(OutlivesClause(ty, r)) => (ty == t).then_some(r), None => Some(Region::new_static(cx)), }) } @@ -1026,7 +1026,7 @@ fn alias_outlives_candidates_from_assumptions infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| { for bound_type_outlives in assumptions.type_outlives.iter() { - let OutlivesPredicate(alias2, r2) = + let OutlivesClause(alias2, r2) = infcx.instantiate_binder_with_infer(*bound_type_outlives); let mut relation = HigherRankedAliasMatcher { diff --git a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md index 586f697a78fb1..d87b97a8612cb 100644 --- a/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md +++ b/src/doc/rustc-dev-guide/src/return-position-impl-trait-in-trait.md @@ -198,7 +198,7 @@ RPITITs begin by copying the predicates of the method that defined it, both on the trait and impl side. Additionally, we install "bidirectional outlives" predicates. -Specifically, we add region-outlives predicates in both directions for +Specifically, we add region-outlives clauses in both directions for each captured early-bound lifetime that constrains it to be equal to the duplicated early-bound lifetime that results from lowering. This is best illustrated in an example: diff --git a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md index 3a419dd1193cd..732f6e81465b2 100644 --- a/src/doc/rustc-dev-guide/src/traits/implied-bounds.md +++ b/src/doc/rustc-dev-guide/src/traits/implied-bounds.md @@ -13,23 +13,24 @@ The explicit implied bounds are computed in [`fn inferred_outlives_of`]. Only AD lazy type aliases have explicit implied bounds which are computed via a fixpoint algorithm in the [`fn inferred_outlives_crate`] query. -We use [`fn insert_required_predicates_to_be_wf`] on all fields of all ADTs in the crate. +We use [`fn insert_required_clauses_to_be_wf`] on all fields of all ADTs in the crate. This function computes the outlives bounds for each component of the field using a separate implementation. -For ADTs, trait objects, and associated types the initially required predicates are -computed in [`fn check_explicit_predicates`]. This simply uses `fn explicit_clauses_of` +For ADTs, trait objects, and associated types the initially required clauses are +computed in [`fn check_explicit_clauses`]. This simply uses `fn explicit_clauses_of` without elaborating them. -Region predicates are added via [`fn insert_outlives_predicate`]. This function takes -an outlives predicate, decomposes it and adds the components as explicit predicates only +Region clauses are added via [`fn insert_outlives_clause`]. This function takes +an outlives clause, decomposes it and adds the components as explicit clauses only if the outlived region is a region parameter. [It does not add `'static` requirements][nostatic]. + [`fn inferred_outlives_of`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/mod.rs#L20 [`fn inferred_outlives_crate`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/mod.rs#L83 - [`fn insert_required_predicates_to_be_wf`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L89 - [`fn check_explicit_predicates`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L238 - [`fn insert_outlives_predicate`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L15 + [`fn insert_required_clauses_to_be_wf`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L89 + [`fn check_explicit_clauses`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs#L238 + [`fn insert_outlives_clause`]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L15 [nostatic]: https://github.com/rust-lang/rust/blob/5b8bc568d28b2e922290c9a966b3231d0ce9398b/compiler/rustc_hir_analysis/src/outlives/utils.rs#L159-L165 ## implicit implied bounds diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1e0c06cb031a7..9186b76f5d2b2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -432,9 +432,9 @@ pub(crate) fn clean_clause<'tcx>( let bound_clause = clause.kind(); match bound_clause.skip_binder() { ty::ClauseKind::Trait(pred) => clean_poly_trait_predicate(bound_clause.rebind(pred), cx), - ty::ClauseKind::RegionOutlives(pred) => Some(clean_region_outlives_predicate(pred, cx.tcx)), + ty::ClauseKind::RegionOutlives(pred) => Some(clean_region_outlives_clause(pred, cx.tcx)), ty::ClauseKind::TypeOutlives(pred) => { - Some(clean_type_outlives_predicate(bound_clause.rebind(pred), cx)) + Some(clean_type_outlives_clause(bound_clause.rebind(pred), cx)) } ty::ClauseKind::Projection(pred) => { Some(clean_projection_predicate(bound_clause.rebind(pred), cx)) @@ -467,11 +467,11 @@ fn clean_poly_trait_predicate<'tcx>( }) } -fn clean_region_outlives_predicate<'tcx>( - pred: ty::RegionOutlivesPredicate<'tcx>, +fn clean_region_outlives_clause<'tcx>( + clause: ty::RegionOutlivesClause<'tcx>, tcx: TyCtxt<'tcx>, ) -> WherePredicate { - let ty::OutlivesPredicate(a, b) = pred; + let ty::OutlivesClause(a, b) = clause; WherePredicate::RegionPredicate { lifetime: clean_middle_region(a, tcx).expect("failed to clean lifetime"), @@ -481,14 +481,14 @@ fn clean_region_outlives_predicate<'tcx>( } } -fn clean_type_outlives_predicate<'tcx>( - pred: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, +fn clean_type_outlives_clause<'tcx>( + clause: ty::Binder<'tcx, ty::TypeOutlivesClause<'tcx>>, cx: &mut DocContext<'tcx>, ) -> WherePredicate { - let ty::OutlivesPredicate(ty, lt) = pred.skip_binder(); + let ty::OutlivesClause(ty, lt) = clause.skip_binder(); WherePredicate::BoundPredicate { - ty: clean_middle_ty(pred.rebind(ty), cx, None, None), + ty: clean_middle_ty(clause.rebind(ty), cx, None, None), bounds: vec![GenericBound::Outlives( clean_middle_region(lt, cx.tcx).expect("failed to clean lifetimes"), )], @@ -903,7 +903,7 @@ fn clean_ty_generics_inner<'tcx>( ty::ClauseKind::Trait(pred) if let ty::Param(param) = pred.self_ty().kind() => { Some(param.index) } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(ty, _reg)) if let ty::Param(param) = ty.kind() => { Some(param.index) @@ -2401,7 +2401,7 @@ fn clean_middle_opaque_bounds<'tcx>( let bound_predicate = bound.kind(); let trait_ref = match bound_predicate.skip_binder() { ty::ClauseKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { + ty::ClauseKind::TypeOutlives(ty::OutlivesClause(_ty, reg)) => { return clean_middle_region(reg, cx.tcx).map(GenericBound::Outlives); } _ => return None, diff --git a/tests/rustdoc-js/auxiliary/interner.rs b/tests/rustdoc-js/auxiliary/interner.rs index 8af3b732ef7a0..c61d5a4dfec72 100644 --- a/tests/rustdoc-js/auxiliary/interner.rs +++ b/tests/rustdoc-js/auxiliary/interner.rs @@ -68,8 +68,8 @@ pub trait Interner: Sized { type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike; type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; type TraitPredicate: Copy + Debug + Hash + Eq; - type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; - type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; + type RegionOutlivesClause: Copy + Debug + Hash + Eq; + type TypeOutlivesClause: Copy + Debug + Hash + Eq; type ProjectionPredicate: Copy + Debug + Hash + Eq; type NormalizesTo: Copy + Debug + Hash + Eq; type SubtypePredicate: Copy + Debug + Hash + Eq; diff --git a/tests/ui/associated-types/normalization-generality-2.rs b/tests/ui/associated-types/normalization-generality-2.rs index 2a50f7e449add..2e7f317bd6f68 100644 --- a/tests/ui/associated-types/normalization-generality-2.rs +++ b/tests/ui/associated-types/normalization-generality-2.rs @@ -5,7 +5,7 @@ // Ensures that we don't regress on "implementation is not general enough" when // normalizating under binders. Unlike `normalization-generality.rs`, this also produces -// type outlives predicates that we must ignore. +// type outlives clauses that we must ignore. pub unsafe trait Yokeable<'a> { type Output: 'a; diff --git a/tests/ui/traits/next-solver/global-where-bound-normalization.rs b/tests/ui/traits/next-solver/global-where-bound-normalization.rs index e57fbf378a0d2..914a54f1aabb0 100644 --- a/tests/ui/traits/next-solver/global-where-bound-normalization.rs +++ b/tests/ui/traits/next-solver/global-where-bound-normalization.rs @@ -20,7 +20,7 @@ impl Proj for MyField { type Assoc = u8; } -// While wf-checking the global bounds of `fn foo`, elaborating this outlives predicate triggered a +// While wf-checking the global bounds of `fn foo`, elaborating this outlives clause triggered a // cycle in the search graph along a particular probe path, which was not an actual solution. // That cycle then resulted in a forced false-positive ambiguity due to a performance hack in the // search graph and then ended up floundering the root goal evaluation.