From 1b6736eb808c44b9a370f4d5500c98316dff97e6 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Wed, 15 Apr 2026 15:04:18 +0300 Subject: [PATCH 01/10] Implement prototype --- compiler/rustc_data_structures/src/sharded.rs | 10 + .../rustc_data_structures/src/vec_cache.rs | 51 ++ compiler/rustc_hir/src/definitions.rs | 53 +- compiler/rustc_hir/src/intravisit.rs | 16 +- compiler/rustc_interface/src/passes.rs | 2 + compiler/rustc_middle/src/dep_graph/graph.rs | 43 +- compiler/rustc_middle/src/hir/map.rs | 25 +- compiler/rustc_middle/src/hir/mod.rs | 45 +- compiler/rustc_middle/src/hooks/mod.rs | 3 + compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/query/caches.rs | 45 +- compiler/rustc_middle/src/query/inner.rs | 8 +- compiler/rustc_middle/src/query/plumbing.rs | 24 +- compiler/rustc_middle/src/ty/context.rs | 4 + compiler/rustc_middle/src/ty/context/tls.rs | 14 +- compiler/rustc_query_impl/src/execution.rs | 7 +- compiler/rustc_query_impl/src/lib.rs | 27 +- .../delegation/body-identity-glob.rs | 33 + .../delegation/body-identity-list.rs | 33 + .../explicit-paths-in-traits-pass.rs | 28 + .../delegation/explicit-paths-pass.rs | 50 ++ .../explicit-paths-signature-pass.rs | 40 + tests/incremental/delegation/glob-glob.rs | 37 + tests/incremental/delegation/glob.rs | 36 + .../incremental/delegation/impl-reuse-pass.rs | 293 +++++++ tests/incremental/delegation/impl-trait.rs | 28 + tests/incremental/delegation/list.rs | 47 ++ .../delegation/macro-inside-glob.rs | 27 + .../delegation/macro-inside-list.rs | 27 + .../delegation/mapping/free-to-free-pass.rs | 148 ++++ .../delegation/mapping/free-to-trait-pass.rs | 248 ++++++ .../mapping/impl-trait-to-free-pass.rs | 264 ++++++ .../mapping/impl-trait-to-trait-pass.rs | 250 ++++++ .../mapping/inherent-impl-to-free-pass.rs | 126 +++ .../mapping/inherent-impl-to-trait-pass.rs | 233 +++++ .../delegation/mapping/trait-to-free-pass.rs | 137 +++ .../delegation/mapping/trait-to-trait-pass.rs | 799 ++++++++++++++++++ .../delegation/method-call-priority.rs | 35 + tests/incremental/delegation/parse.rs | 43 + tests/incremental/delegation/rename.rs | 28 + tests/incremental/delegation/self-coercion.rs | 27 + .../generics/generic-params-defaults.rs | 2 + .../generics/generic-params-defaults.stderr | 15 +- tests/ui/delegation/query-cycle-oom.rs | 65 ++ tests/ui/delegation/query-cycle-oom.stderr | 126 +++ 45 files changed, 3525 insertions(+), 78 deletions(-) create mode 100644 tests/incremental/delegation/body-identity-glob.rs create mode 100644 tests/incremental/delegation/body-identity-list.rs create mode 100644 tests/incremental/delegation/explicit-paths-in-traits-pass.rs create mode 100644 tests/incremental/delegation/explicit-paths-pass.rs create mode 100644 tests/incremental/delegation/explicit-paths-signature-pass.rs create mode 100644 tests/incremental/delegation/glob-glob.rs create mode 100644 tests/incremental/delegation/glob.rs create mode 100644 tests/incremental/delegation/impl-reuse-pass.rs create mode 100644 tests/incremental/delegation/impl-trait.rs create mode 100644 tests/incremental/delegation/list.rs create mode 100644 tests/incremental/delegation/macro-inside-glob.rs create mode 100644 tests/incremental/delegation/macro-inside-list.rs create mode 100644 tests/incremental/delegation/mapping/free-to-free-pass.rs create mode 100644 tests/incremental/delegation/mapping/free-to-trait-pass.rs create mode 100644 tests/incremental/delegation/mapping/impl-trait-to-free-pass.rs create mode 100644 tests/incremental/delegation/mapping/impl-trait-to-trait-pass.rs create mode 100644 tests/incremental/delegation/mapping/inherent-impl-to-free-pass.rs create mode 100644 tests/incremental/delegation/mapping/inherent-impl-to-trait-pass.rs create mode 100644 tests/incremental/delegation/mapping/trait-to-free-pass.rs create mode 100644 tests/incremental/delegation/mapping/trait-to-trait-pass.rs create mode 100644 tests/incremental/delegation/method-call-priority.rs create mode 100644 tests/incremental/delegation/parse.rs create mode 100644 tests/incremental/delegation/rename.rs create mode 100644 tests/incremental/delegation/self-coercion.rs create mode 100644 tests/ui/delegation/query-cycle-oom.rs create mode 100644 tests/ui/delegation/query-cycle-oom.stderr diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index e10ccccad5bb4..3c90fcba3b540 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -199,6 +199,16 @@ impl ShardedHashMap { } } } + + #[inline] + pub fn remove(&self, key: &K) { + let hash = make_hash(key); + let mut shard = self.lock_shard_by_hash(hash); + + if let Entry::Occupied(e) = table_entry(&mut shard, hash, key) { + e.remove(); + } + } } impl ShardedHashMap { diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 6d026bb2c7f74..78eb27f139856 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -206,6 +206,24 @@ impl SlotIndex { index_and_lock.store(extra.checked_add(2).unwrap(), Ordering::Release); } + + #[inline] + unsafe fn remove(&self, buckets: &[AtomicPtr>; 21]) { + let bucket = &buckets[self.bucket_idx]; + let ptr = self.bucket_ptr(bucket); + + debug_assert!(self.index_in_bucket < self.bucket_idx.capacity()); + + // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this + // must be inbounds. + let slot = unsafe { ptr.add(self.index_in_bucket) }; + + // SAFETY: initialized bucket has zeroed all memory within the bucket, so we are valid for + // AtomicU32 access. + let index_and_lock = unsafe { &(*slot).index_and_lock }; + + index_and_lock.store(0, Ordering::Release); + } } /// In-memory cache for queries whose keys are densely-numbered IDs @@ -339,6 +357,39 @@ where pub fn len(&self) -> usize { self.len.load(Ordering::Acquire) } + + pub fn remove(&self, key: &K) { + let key = u32::try_from(key.index()).unwrap(); + let slot_idx = SlotIndex::from_index(key); + + unsafe { slot_idx.remove(&self.buckets) }; + } + + pub fn invalidate(&self, selector: impl Fn(K) -> bool) { + let mut to_remove = vec![]; + let mut remaining = vec![]; + + self.for_each(&mut |key, _, _| { + if selector(*key) { + to_remove.push(*key); + } else { + remaining.push(*key); + } + }); + + for key in to_remove { + self.remove(&key); + } + + for (index, key) in remaining.iter().enumerate() { + let slot = SlotIndex::from_index(u32::try_from(index).unwrap()); + let key = u32::try_from(key.index()).unwrap(); + + unsafe { slot.put_unique(&self.present, (), key) }; + } + + self.len.store(remaining.len(), Ordering::Release); + } } /// Index into an array of buckets. diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index 5e361891f6d04..d65f40be42b5f 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -7,6 +7,7 @@ use std::fmt::{self, Write}; use std::hash::Hash; +use rustc_data_structures::indexmap::IndexSet; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash64; @@ -30,6 +31,7 @@ pub struct DefPathTable { // We do only store the local hash, as all the definitions are from the current crate. def_path_hashes: IndexVec, def_path_hash_to_index: DefPathHashMap, + allow_overwrite: IndexSet, } impl DefPathTable { @@ -39,6 +41,7 @@ impl DefPathTable { index_to_key: Default::default(), def_path_hashes: Default::default(), def_path_hash_to_index: Default::default(), + allow_overwrite: Default::default(), } } @@ -46,31 +49,37 @@ impl DefPathTable { // Assert that all DefPathHashes correctly contain the local crate's StableCrateId. debug_assert_eq!(self.stable_crate_id, def_path_hash.stable_crate_id()); let local_hash = def_path_hash.local_hash(); - - let index = self.index_to_key.push(key); - debug!("DefPathTable::insert() - {key:?} <-> {index:?}"); - - self.def_path_hashes.push(local_hash); - debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); + let index = self.index_to_key.next_index(); // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = self.def_path_hash_to_index.insert(&local_hash, &index) { - let def_path1 = DefPath::make(LOCAL_CRATE, existing, |idx| self.def_key(idx)); - let def_path2 = DefPath::make(LOCAL_CRATE, index, |idx| self.def_key(idx)); - - // Continuing with colliding DefPathHashes can lead to correctness - // issues. We must abort compilation. - // - // The likelihood of such a collision is very small, so actually - // running into one could be indicative of a poor hash function - // being used. - // - // See the documentation for DefPathHash for more information. - panic!( - "found DefPathHash collision between {def_path1:#?} and {def_path2:#?}. \ + if !self.allow_overwrite.is_empty() && self.allow_overwrite.swap_remove(&existing) { + self.def_path_hash_to_index.insert(&local_hash, &existing); + return existing; + } else { + let def_path1 = DefPath::make(LOCAL_CRATE, existing, |idx| self.def_key(idx)); + let def_path2 = DefPath::make(LOCAL_CRATE, index, |idx| self.def_key(idx)); + + // Continuing with colliding DefPathHashes can lead to correctness + // issues. We must abort compilation. + // + // The likelihood of such a collision is very small, so actually + // running into one could be indicative of a poor hash function + // being used. + // + // See the documentation for DefPathHash for more information. + panic!( + "found DefPathHash collision between {def_path1:#?} and {def_path2:#?}. \ Compilation cannot continue." - ); + ); + } + } else { + self.index_to_key.push(key); + debug!("DefPathTable::insert() - {key:?} <-> {index:?}"); + + self.def_path_hashes.push(local_hash); + debug_assert!(self.def_path_hashes.len() == self.index_to_key.len()); } index @@ -422,6 +431,10 @@ impl Definitions { LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) } } + pub fn add_sandbox_def_id(&mut self, id: LocalDefId) { + assert_eq!(self.table.allow_overwrite.insert(id.local_def_index), true); + } + #[inline(always)] /// Returns `None` if the `DefPathHash` does not correspond to a `LocalDefId` /// in the current compilation session. This can legitimately happen if the diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 99511189e9283..9ffcd3461f2de 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -226,6 +226,11 @@ pub trait Visitor<'v>: Sized { /// or `ControlFlow`. type Result: VisitorResult = (); + #[inline] + fn visit_if_delayed(&self, _: LocalDefId) -> bool { + true + } + /// If `type NestedFilter` is set to visit nested items, this method /// must also be overridden to provide a map to retrieve nested items. fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { @@ -244,18 +249,23 @@ pub trait Visitor<'v>: Sized { /// this method is if you want a nested pattern but cannot supply a /// `TyCtxt`; see `maybe_tcx` for advice. fn visit_nested_item(&mut self, id: ItemId) -> Self::Result { - if Self::NestedFilter::INTER { + if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) { let item = self.maybe_tcx().hir_item(id); try_visit!(self.visit_item(item)); } Self::Result::output() } + // Now delayed owners are only delegations, which are either item, trait item or impl item. + fn should_visit_maybe_delayed_inter(&mut self, id: LocalDefId) -> bool { + Self::NestedFilter::INTER && self.visit_if_delayed(id) + } + /// Like `visit_nested_item()`, but for trait items. See /// `visit_nested_item()` for advice on when to override this /// method. fn visit_nested_trait_item(&mut self, id: TraitItemId) -> Self::Result { - if Self::NestedFilter::INTER { + if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) { let item = self.maybe_tcx().hir_trait_item(id); try_visit!(self.visit_trait_item(item)); } @@ -266,7 +276,7 @@ pub trait Visitor<'v>: Sized { /// `visit_nested_item()` for advice on when to override this /// method. fn visit_nested_impl_item(&mut self, id: ImplItemId) -> Self::Result { - if Self::NestedFilter::INTER { + if self.should_visit_maybe_delayed_inter(id.owner_id.def_id) { let item = self.maybe_tcx().hir_impl_item(id); try_visit!(self.visit_impl_item(item)); } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 23a055e1e26f4..24aafb80aa0fc 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1053,6 +1053,8 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. /// This function never fails. fn run_required_analyses(tcx: TyCtxt<'_>) { + tcx.force_delayed_owners_lowering(); + if tcx.sess.opts.unstable_opts.input_stats { rustc_passes::input_stats::print_hir_stats(tcx); } diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index be29e053a17c0..0e4ebb9a6f797 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -28,6 +28,7 @@ use super::{DepKind, DepNode, WorkProductId, read_deps, with_deps}; use crate::dep_graph::edges::EdgesVec; use crate::ich::StableHashingContext; use crate::ty::TyCtxt; +use crate::ty::tls::is_sandbox; use crate::verify_ich::incremental_verify_ich; /// Tracks 'side effects' for a particular query. @@ -187,23 +188,23 @@ impl DepGraph { #[inline] pub fn data(&self) -> Option<&DepGraphData> { - self.data.as_deref() + self.data.as_deref().filter(|_| !is_sandbox()) } /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. #[inline] pub fn is_fully_enabled(&self) -> bool { - self.data.is_some() + self.data().is_some() } pub fn with_retained_dep_graph(&self, f: impl Fn(&RetainedDepGraph)) { - if let Some(data) = &self.data { + if let Some(data) = self.data() { data.current.encoder.with_retained_dep_graph(f) } } pub fn assert_ignored(&self) { - if let Some(..) = self.data { + if let Some(..) = self.data() { read_deps(|task_deps| { assert_matches!( task_deps, @@ -450,7 +451,7 @@ impl DepGraphData { impl DepGraph { #[inline] pub fn read_index(&self, dep_node_index: DepNodeIndex) { - if let Some(ref data) = self.data { + if let Some(ref data) = self.data() { read_deps(|task_deps| { let mut task_deps = match task_deps { TaskDepsRef::Allow(deps) => deps.lock(), @@ -507,7 +508,7 @@ impl DepGraph { /// it with the node, for use in the next session. #[inline] pub fn record_diagnostic<'tcx>(&self, tcx: TyCtxt<'tcx>, diagnostic: &DiagInner) { - if let Some(ref data) = self.data { + if let Some(ref data) = self.data() { read_deps(|task_deps| match task_deps { TaskDepsRef::EvalAlways | TaskDepsRef::Ignore => return, TaskDepsRef::Forbid | TaskDepsRef::Allow(..) => { @@ -522,7 +523,7 @@ impl DepGraph { /// refer to a node created used `encode_side_effect` in the previous session. #[inline] pub fn force_side_effect<'tcx>(&self, tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) { - if let Some(ref data) = self.data { + if let Some(ref data) = self.data() { data.force_side_effect(tcx, prev_index); } } @@ -533,7 +534,7 @@ impl DepGraph { tcx: TyCtxt<'tcx>, side_effect: QuerySideEffect, ) -> DepNodeIndex { - if let Some(ref data) = self.data { + if let Some(ref data) = self.data() { data.encode_side_effect(tcx, side_effect) } else { self.next_virtual_depnode_index() @@ -563,7 +564,7 @@ impl DepGraph { hash_result: Option, &R) -> Fingerprint>, format_value_fn: fn(&R) -> String, ) -> DepNodeIndex { - if let Some(data) = self.data.as_ref() { + if let Some(data) = self.data().as_ref() { // The caller query has more dependencies than the node we are creating. We may // encounter a case where this created node is marked as green, but the caller query is // subsequently marked as red or recomputed. In this case, we will end up feeding a @@ -808,23 +809,23 @@ impl DepGraph { /// Checks whether a previous work product exists for `v` and, if /// so, return the path that leads to it. Used to skip doing work. pub fn previous_work_product(&self, v: &WorkProductId) -> Option { - self.data.as_ref().and_then(|data| data.previous_work_products.get(v).cloned()) + self.data().as_ref().and_then(|data| data.previous_work_products.get(v).cloned()) } /// Access the map of work-products created during the cached run. Only /// used during saving of the dep-graph. pub fn previous_work_products(&self) -> &WorkProductMap { - &self.data.as_ref().unwrap().previous_work_products + &self.data().as_ref().unwrap().previous_work_products } pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool { - self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) + self.data().as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) } pub fn debug_dep_kind_was_loaded_from_disk(&self, dep_kind: DepKind) -> bool { // We only check if we have a dep node corresponding to the given dep kind. #[allow(rustc::potential_query_instability)] - self.data + self.data() .as_ref() .unwrap() .debug_loaded_from_disk @@ -834,7 +835,7 @@ impl DepGraph { } fn node_color(&self, dep_node: &DepNode) -> DepNodeColor { - if let Some(ref data) = self.data { + if let Some(ref data) = self.data() { return data.node_color(dep_node); } @@ -980,7 +981,7 @@ impl DepGraph { dep_node: &DepNode, msg: impl FnOnce() -> S, ) { - if let Some(data) = &self.data { + if let Some(data) = &self.data() { data.assert_dep_node_not_yet_allocated_in_current_session(sess, dep_node, msg) } } @@ -996,7 +997,7 @@ impl DepGraph { pub fn exec_cache_promotions<'tcx>(&self, tcx: TyCtxt<'tcx>) { let _prof_timer = tcx.prof.generic_activity("incr_comp_query_cache_promotion"); - let data = self.data.as_ref().unwrap(); + let data = self.data().unwrap(); for prev_index in data.colors.values.indices() { match data.colors.get(prev_index) { DepNodeColor::Green(_) => { @@ -1017,11 +1018,15 @@ impl DepGraph { } pub(crate) fn finish_encoding(&self) -> FileEncodeResult { - if let Some(data) = &self.data { data.current.encoder.finish(&data.current) } else { Ok(0) } + if let Some(data) = &self.data() { + data.current.encoder.finish(&data.current) + } else { + Ok(0) + } } pub fn next_virtual_depnode_index(&self) -> DepNodeIndex { - debug_assert!(self.data.is_none()); + debug_assert!(self.data().is_none() || is_sandbox()); let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed); DepNodeIndex::from_u32(index) } @@ -1396,7 +1401,7 @@ pub(super) enum TrySetColorResult { #[inline(never)] #[cold] pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: &MarkFrame<'_>) { - let data = graph.data.as_ref().unwrap(); + let data = graph.data().unwrap(); eprintln!("there was a panic while trying to force a dep node"); eprintln!("try_mark_green dep node stack:"); diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 20aa0a809006f..78e511dac3c82 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -7,7 +7,7 @@ use rustc_ast::visit::{VisitorResult, walk_list}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, spawn, try_par_for_each_in}; +use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, try_par_for_each_in}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -21,6 +21,7 @@ use crate::hir::{ModuleItems, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; +use crate::ty::tls::is_sandbox; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -1245,25 +1246,7 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod } } -fn force_delayed_owners_lowering(tcx: TyCtxt<'_>) { - let krate = tcx.hir_crate(()); - for &id in &krate.delayed_ids { - tcx.ensure_done().lower_delayed_owner(id); - } - - let (_, krate) = krate.delayed_resolver.steal(); - let prof = tcx.sess.prof.clone(); - - // Drop AST to free memory. It can be expensive so try to drop it on a separate thread. - spawn(move || { - let _timer = prof.verbose_generic_activity("drop_ast"); - drop(krate); - }); -} - pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { - force_delayed_owners_lowering(tcx); - let mut collector = ItemCollector::new(tcx, true); // A "crate collector" and "module collector" start at a @@ -1352,6 +1335,10 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { self.tcx } + fn visit_if_delayed(&self, _: LocalDefId) -> bool { + !is_sandbox() + } + fn visit_item(&mut self, item: &'hir Item<'hir>) { if Node::Item(item).associated_body().is_some() { self.body_owners.push(item.owner_id.def_id); diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 814b333cfb0f8..ef111985cfdd5 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in}; +use rustc_data_structures::sync::{DynSend, DynSync, spawn, try_par_for_each_in}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lints::DelayedLint; @@ -24,6 +24,8 @@ use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, HashStableContext, Span}; use crate::query::Providers; +use crate::ty::print::with_no_trimmed_paths; +use crate::ty::tls::{ImplicitCtxt, enter_context, with_context}; use crate::ty::{ResolverAstLowering, TyCtxt}; /// The top-level data structure that stores the entire contents of @@ -207,6 +209,47 @@ impl ModuleItems { } impl<'tcx> TyCtxt<'tcx> { + fn sandbox(self, action: impl FnOnce() -> ()) { + with_context(|icx| { + let icx = ImplicitCtxt { is_sandbox: true, ..*icx }; + enter_context(&icx, || { + self.enter_query_sandbox(); + + self.dep_graph.with_query_deserialization(|| { + with_no_trimmed_paths!({ + action(); + }); + }); + + self.leave_query_sandbox(); + }) + }); + } + + pub fn force_delayed_owners_lowering(self) { + let krate = self.hir_crate(()); + + if !krate.delayed_ids.is_empty() { + self.sandbox(|| { + self.ensure_done().hir_crate_items(()); + self.ensure_done().crate_inherent_impls(()); + + for &id in &krate.delayed_ids { + self.ensure_done().lower_delayed_owner(id); + } + }); + } + + let (_, krate) = krate.delayed_resolver.steal(); + let prof = self.sess.prof.clone(); + + // Drop AST to free memory. It can be expensive so try to drop it on a separate thread. + spawn(move || { + let _timer = prof.verbose_generic_activity("drop_ast"); + drop(krate); + }); + } + pub fn parent_module(self, id: HirId) -> LocalModDefId { if !id.is_owner() && self.def_kind(id.owner) == DefKind::Mod { LocalModDefId::new_unchecked(id.owner.def_id) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index c70ceef1d47e9..9d3f75cd5a143 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -91,6 +91,9 @@ declare_hooks! { hook alloc_self_profile_query_strings() -> (); + hook enter_query_sandbox() -> (); + hook leave_query_sandbox() -> (); + /// Saves and writes the DepGraph to the file system. /// /// This function saves both the dep-graph and the query result cache, diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 1d6132ba2a3ea..b479361d99bb2 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -42,6 +42,7 @@ #![feature(extern_types)] #![feature(file_buffered)] #![feature(gen_blocks)] +#![feature(lock_value_accessors)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_middle/src/query/caches.rs b/compiler/rustc_middle/src/query/caches.rs index 0c71a98b7fb29..c31d2bae93c6e 100644 --- a/compiler/rustc_middle/src/query/caches.rs +++ b/compiler/rustc_middle/src/query/caches.rs @@ -1,4 +1,4 @@ -use std::sync::OnceLock; +use std::sync::Mutex; use rustc_data_structures::sharded::ShardedHashMap; pub use rustc_data_structures::vec_cache::VecCache; @@ -14,7 +14,7 @@ use crate::query::keys::QueryKey; /// /// Types implementing this trait are associated with actual key/value types /// by the `Cache` associated type of the `rustc_middle::query::Key` trait. -pub trait QueryCache: Sized { +pub trait QueryCache: Sized + Default { type Key: QueryKey; type Value: Copy; @@ -36,6 +36,8 @@ pub trait QueryCache: Sized { /// Useful for reserving capacity in data structures that will hold the /// output of a call to [`Self::for_each`]. fn len(&self) -> usize; + + fn invalidate(&self, selector: impl Fn(Self::Key) -> bool); } /// In-memory cache for queries whose keys aren't suitable for any of the @@ -81,17 +83,30 @@ where fn len(&self) -> usize { self.cache.len() } + + fn invalidate(&self, selector: impl Fn(Self::Key) -> bool) { + let mut to_remove = vec![]; + self.for_each(&mut |&key, _, _| { + if selector(key) { + to_remove.push(key); + } + }); + + for key in to_remove { + self.cache.remove(&key); + } + } } /// In-memory cache for queries whose key type only has one value (e.g. `()`). /// The cache therefore only needs to store one query return value. pub struct SingleCache { - cache: OnceLock<(V, DepNodeIndex)>, + cache: Mutex>, } impl Default for SingleCache { fn default() -> Self { - SingleCache { cache: OnceLock::new() } + SingleCache { cache: Mutex::new(None) } } } @@ -104,22 +119,28 @@ where #[inline(always)] fn lookup(&self, _key: &()) -> Option<(V, DepNodeIndex)> { - self.cache.get().copied() + self.cache.get_cloned().unwrap() } #[inline] fn complete(&self, _key: (), value: V, index: DepNodeIndex) { - self.cache.set((value, index)).ok(); + self.cache.set(Some((value, index))).ok(); } fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { - if let Some(value) = self.cache.get() { + if let Some(value) = self.cache.get_cloned().unwrap() { f(&(), &value.0, value.1) } } fn len(&self) -> usize { - self.cache.get().is_some().into() + self.cache.get_cloned().unwrap().is_some().into() + } + + fn invalidate(&self, selector: impl Fn(Self::Key) -> bool) { + if selector(()) { + self.cache.set(None).ok(); + } } } @@ -175,6 +196,10 @@ where fn len(&self) -> usize { self.local.len() + self.foreign.len() } + + fn invalidate(&self, selector: impl Fn(Self::Key) -> bool) { + self.local.invalidate(|idx| selector(DefId::local(idx))); + } } impl QueryCache for VecCache @@ -202,4 +227,8 @@ where fn len(&self) -> usize { self.len() } + + fn invalidate(&self, selector: impl Fn(Self::Key) -> bool) { + self.invalidate(selector); + } } diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 402c448a1fa3a..a04fb5ba83fc2 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -8,6 +8,7 @@ use crate::dep_graph::DepNodeKey; use crate::query::erase::{self, Erasable, Erased}; use crate::query::{EnsureMode, QueryCache, QueryMode, QueryVTable}; use crate::ty::TyCtxt; +use crate::ty::tls::is_sandbox; /// Checks whether there is already a value for this key in the in-memory /// query cache, returning that value if present. @@ -20,8 +21,12 @@ where { match cache.lookup(&key) { Some((value, index)) => { - tcx.prof.query_cache_hit(index.into()); + if !is_sandbox() { + tcx.prof.query_cache_hit(index.into()); + } + tcx.dep_graph.read_index(index); + Some(value) } None => None, @@ -165,6 +170,7 @@ pub(crate) fn query_feed<'tcx, C>( query.hash_value_fn, query.format_value, ); + query.cache.complete(key, value, dep_node_index); } } diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index fe3054dc18e65..7bb56715f6907 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -1,8 +1,9 @@ use std::fmt; use std::ops::Deref; +use std::sync::Mutex; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::hash_table::HashTable; use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal}; @@ -24,11 +25,12 @@ use crate::ty::{self, TyCtxt}; /// query's in-memory cache.) pub struct QueryState<'tcx, K> { pub active: Sharded)>>, + pub saved_keys: Mutex>>, } impl<'tcx, K> Default for QueryState<'tcx, K> { fn default() -> QueryState<'tcx, K> { - QueryState { active: Default::default() } + QueryState { active: Default::default(), saved_keys: Default::default() } } } @@ -133,6 +135,24 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, } +impl QueryVTable<'_, C> { + pub fn save_keys(&self) { + let mut keys = FxHashSet::default(); + self.cache.for_each(&mut |key, _, _| { + keys.insert(*key); + }); + + self.state.saved_keys.set(Some(keys)).ok(); + } + + pub fn invalidate_saved_keys(&self) { + let keys = + self.state.saved_keys.replace(None).unwrap().expect("must save keys to invalidate"); + + self.cache.invalidate(|key| !keys.contains(&key)); + } +} + impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // When debug-printing a query vtable (e.g. for ICE or tracing), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b908a6c6e8439..8c96eefc8c70b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -66,6 +66,7 @@ use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData, PredefinedOpaques}; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; +use crate::ty::tls::is_sandbox; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, Pattern, @@ -1386,6 +1387,9 @@ impl<'tcx> TyCtxt<'tcx> { // As a consequence, this LocalDefId is always re-created before it is needed by the incr. // comp. engine itself. let def_id = self.untracked.definitions.write().create_def(parent, data, disambiguator); + if is_sandbox() { + self.untracked.definitions.write().add_sandbox_def_id(def_id); + } // This function modifies `self.definitions` using a side-effect. // We need to ensure that these side effects are re-run by the incr. comp. engine. diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index d1561c37172c3..8d20a1512a5e1 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -22,12 +22,20 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// The current dep graph task. This is used to add dependencies to queries /// when executing them. pub task_deps: TaskDepsRef<'a>, + + pub is_sandbox: bool, } impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { let tcx = TyCtxt { gcx }; - ImplicitCtxt { tcx, query: None, query_depth: 0, task_deps: TaskDepsRef::Ignore } + ImplicitCtxt { + tcx, + query: None, + query_depth: 0, + task_deps: TaskDepsRef::Ignore, + is_sandbox: false, + } } } @@ -57,6 +65,10 @@ where }) } +pub fn is_sandbox() -> bool { + with_context_opt(|ctx| ctx.map(|c| c.is_sandbox).unwrap_or_default()) +} + /// Allows access to the current `ImplicitCtxt` in a closure if one is available. #[inline] #[track_caller] diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index ed9ad8c7a0a68..22767a8c2b56d 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -12,6 +12,7 @@ use rustc_middle::query::{ QueryMode, QueryState, QueryVTable, }; use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::tls::is_sandbox; use rustc_middle::verify_ich::incremental_verify_ich; use rustc_span::{DUMMY_SP, Span}; use tracing::warn; @@ -320,7 +321,11 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // Delegate to another function to actually execute the query job. let (value, dep_node_index) = if INCR { - execute_job_incr(query, tcx, key, dep_node.unwrap(), id) + if is_sandbox() { + execute_job_non_incr(query, tcx, key, id) + } else { + execute_job_incr(query, tcx, key, dep_node.unwrap(), id) + } } else { execute_job_non_incr(query, tcx, key, id) }; diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d5133aa04dccc..d5c0fef0b4639 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::TyCtxt; pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs}; pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack}; - +use crate::query_impl::for_each_query_vtable; mod dep_kind_vtables; mod error; mod execution; @@ -60,9 +60,34 @@ pub fn query_system<'tcx>( } } +fn enter_sandbox<'tcx, C: QueryCache>(query: &'tcx QueryVTable<'tcx, C>) { + query.save_keys(); +} + +fn leave_sandbox<'tcx, C: QueryCache>(query: &'tcx QueryVTable<'tcx, C>) { + if query.name == "lower_delayed_owner" + || query.name == "delayed_owner" + || query.name == "def_kind" + { + return; + } + + query.invalidate_saved_keys(); +} + pub fn provide(providers: &mut rustc_middle::util::Providers) { providers.hooks.alloc_self_profile_query_strings = profiling_support::alloc_self_profile_query_strings; providers.hooks.verify_query_key_hashes = plumbing::verify_query_key_hashes; providers.hooks.encode_query_values = plumbing::encode_query_values; + providers.hooks.enter_query_sandbox = |tcx| { + for_each_query_vtable!(ALL, tcx, |query| { + enter_sandbox(query); + }); + }; + providers.hooks.leave_query_sandbox = |tcx| { + for_each_query_vtable!(ALL, tcx, |query| { + leave_sandbox(query); + }); + }; } diff --git a/tests/incremental/delegation/body-identity-glob.rs b/tests/incremental/delegation/body-identity-glob.rs new file mode 100644 index 0000000000000..07274e5aa55ec --- /dev/null +++ b/tests/incremental/delegation/body-identity-glob.rs @@ -0,0 +1,33 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait::* { + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); + s.bar(); +} diff --git a/tests/incremental/delegation/body-identity-list.rs b/tests/incremental/delegation/body-identity-list.rs new file mode 100644 index 0000000000000..2517881ca6c0d --- /dev/null +++ b/tests/incremental/delegation/body-identity-list.rs @@ -0,0 +1,33 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) {} + fn bar(&self) {} +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait::{foo, bar} { + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); + s.bar(); +} diff --git a/tests/incremental/delegation/explicit-paths-in-traits-pass.rs b/tests/incremental/delegation/explicit-paths-in-traits-pass.rs new file mode 100644 index 0000000000000..219261882d985 --- /dev/null +++ b/tests/incremental/delegation/explicit-paths-in-traits-pass.rs @@ -0,0 +1,28 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait ToReuse { + fn foo(&self, x: i32) -> i32 { x } + fn foo1(x: i32) -> i32 { x } +} + +fn foo2() -> i32 { 42 } + +trait Trait: ToReuse { + reuse ToReuse::foo; + reuse ::foo1; + reuse foo2; +} + +struct S; +impl ToReuse for S {} +impl Trait for S {} + +fn main() { + assert_eq!(::foo(&S, 1), 1); + assert_eq!(::foo1(1), 1); + assert_eq!(::foo2(), 42); +} diff --git a/tests/incremental/delegation/explicit-paths-pass.rs b/tests/incremental/delegation/explicit-paths-pass.rs new file mode 100644 index 0000000000000..c59145fe603f1 --- /dev/null +++ b/tests/incremental/delegation/explicit-paths-pass.rs @@ -0,0 +1,50 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn bar(&self, x: i32) -> i32 { x } + fn description(&self) -> &str { + "hello world!" + } + fn static_method(x: i32) -> i32 { x } + fn static_method2(x: i32, y: i32) -> i32 { x + y } +} + +struct F; +impl Trait for F {} + +mod to_reuse { + pub fn foo(x: i32) -> i32 { x + 1 } +} + +struct S(F); +impl Trait for S { + reuse Trait::bar { self.0 } + reuse Trait::description { self.0 } + reuse ::static_method; + reuse ::static_method2 { S::static_method(self) } +} + +impl S { + reuse ::static_method { to_reuse::foo(self) } +} + +impl std::fmt::Display for S { + reuse ::fmt { self.description() } +} + +fn main() { + let s = S(F); + assert_eq!(42, s.bar(42)); + assert_eq!("hello world!", format!("{s}")); + assert_eq!(43, S::static_method(42)); + assert_eq!(42, ::static_method(42)); + assert_eq!(21, S::static_method2(10, 10)); + + #[inline] + reuse to_reuse::foo; + assert_eq!(43, foo(42)); +} diff --git a/tests/incremental/delegation/explicit-paths-signature-pass.rs b/tests/incremental/delegation/explicit-paths-signature-pass.rs new file mode 100644 index 0000000000000..e6e0b3d1e3025 --- /dev/null +++ b/tests/incremental/delegation/explicit-paths-signature-pass.rs @@ -0,0 +1,40 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod to_reuse { + use crate::S; + + pub fn foo<'a>(#[cfg(false)] a: u8, _b: &'a S) -> u32 { + 1 + } +} + +reuse to_reuse::foo; + +trait Trait { + fn foo(&self) -> u32 { 0 } + fn bar(self: Box) -> u32 { 2 } + fn baz(a: (i32, i32)) -> i32 { a.0 + a.1 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse to_reuse::foo { self } + reuse Trait::bar { Box::new(self.0) } + reuse ::baz; +} + +fn main() { + let s = S(F); + assert_eq!(1, foo(&s)); + assert_eq!(1, s.foo()); + assert_eq!(2, Box::new(s).bar()); + assert_eq!(4, S::baz((2, 2))); +} diff --git a/tests/incremental/delegation/glob-glob.rs b/tests/incremental/delegation/glob-glob.rs new file mode 100644 index 0000000000000..b2746f43f25bb --- /dev/null +++ b/tests/incremental/delegation/glob-glob.rs @@ -0,0 +1,37 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod inner { + pub trait TraitFoo { + fn foo(&self) -> u8; + } + pub trait TraitBar { + fn bar(&self) -> u8; + } + + impl TraitFoo for u8 { + fn foo(&self) -> u8 { 0 } + } + impl TraitBar for u8 { + fn bar(&self) -> u8 { 1 } + } +} + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + reuse inner::TraitFoo::*; + reuse inner::TraitBar::*; +} + +fn main() { + let u = 0u8; + u.foo(); + u.bar(); +} diff --git a/tests/incremental/delegation/glob.rs b/tests/incremental/delegation/glob.rs new file mode 100644 index 0000000000000..15d43176b228f --- /dev/null +++ b/tests/incremental/delegation/glob.rs @@ -0,0 +1,36 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8; + fn bar(&self) -> u8; +} + +impl Trait for u8 { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::* { &self.0 } +} + +impl Trait for Z { + reuse ::* { &self.0 } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(3); + z.foo(); + z.bar(); +} diff --git a/tests/incremental/delegation/impl-reuse-pass.rs b/tests/incremental/delegation/impl-reuse-pass.rs new file mode 100644 index 0000000000000..2254225fc544f --- /dev/null +++ b/tests/incremental/delegation/impl-reuse-pass.rs @@ -0,0 +1,293 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![allow(incomplete_features)] +#![feature(fn_delegation)] +#![feature(const_trait_impl)] +#![allow(warnings)] + +mod default { + trait T { + fn foo(&self) {} + fn bar(&self) {} + fn goo(&self) {} + } + + struct S; + impl T for S {} + + struct F(S); + reuse impl T for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + f.goo(); + } +} + +mod dyn_traits { + trait T { + fn foo(&self) -> Box; + } + + trait SecondTrait { + fn bar(&self); + } + + reuse impl SecondTrait for dyn T { self.foo().as_ref() } +} + +mod complex_path { + pub mod first { + pub mod second { + pub trait T { + fn foo(&self, x: usize); + } + } + } + + struct S; + impl first::second::T for S { + fn foo(&self, x: usize) { } + } + + struct F(S); + reuse impl first::second::T for F { self.0 } + + fn f() { + use complex_path::first::second::T; + + let f = F(S{}); + + f.foo(1); + } +} + +mod no_body_reuse { + trait T { + fn foo(&self) {} + fn bar(&mut self) {} + } + + struct F; + + reuse impl T for F; + + fn foo() { + let mut f = F{}; + + f.foo(); + f.bar(); + } +} + +mod unsafe_trait { + unsafe trait UnsafeTrait { + fn foo(&self) {} + fn bar(&self) {} + fn goo(&self) {} + } + + struct S; + unsafe impl UnsafeTrait for S {} + + struct F(S); + reuse unsafe impl UnsafeTrait for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + f.goo(); + } +} + +mod const_trait { + const trait ConstTrait { + fn foo(&self) -> usize { 0 } + fn bar(&self) -> usize { 1 } + } + + struct S; + const impl ConstTrait for S {} + + struct F(S); + reuse const impl ConstTrait for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + } +} + +mod different_selves { + trait T: Sized { + fn foo(&self) {} + fn boo(self) {} + fn goo(&mut self) {} + } + + struct S; + impl T for S {} + + struct F(S); + reuse impl T for F { self.0 } + + struct D(S); + macro_rules! self_0 { ($self:ident) => { $self.0 } } + + reuse impl T for D { self_0!(self) } + + fn f() { + let mut f = F(S{}); + f.foo(); + f.goo(); + f.boo(); + + let mut d = D(S{}); + d.foo(); + d.goo(); + d.boo(); + } +} + +mod macros { + trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } + } + + impl Trait for u8 {} + struct S(u8); + + macro_rules! self_0_ref { ($self:ident) => { &$self.0 } } + + reuse impl Trait for S { self_0_ref!(self) } + + struct M(u8); + macro_rules! m { () => { M } } + reuse impl Trait for m!() { self_0_ref!(self) } + + struct S1(u8); + macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + one_line_reuse!(self); + + struct S2(u8); + macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + one_line_reuse_expr!(self.0); + + struct S3(u8); + macro_rules! s3 { () => { S3 } } + macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + one_line_reuse_expr2!(self.0); + + fn f() { + let s = S(1); + s.foo(); + s.bar(); + + let m = M(41); + m.foo(); + m.bar(); + + let s1 = S1(2); + s1.foo(); + s1.bar(); + + let s2 = S2(4); + s2.foo(); + s2.bar(); + + let s3 = S3(5); + s3.foo(); + s3.bar(); + } +} + +mod generics { + trait Trait<'a, 'b, A, B, C> { + fn foo(&self, a: &A) {} + fn bar(&self, b: &B) {} + fn goo(&self, c: &C) {} + } + + struct S; + impl<'a, 'b, A, B, C> Trait<'a, 'b, A, B, C> for S {} + + struct F(S); + reuse impl<'a, 'b, A, B, C> Trait<'a, 'b, A, B, C> for F { &self.0 } + + struct S1; + struct F1(S1); + impl<'c, B> Trait<'static, 'c, usize, B, String> for S1 {} + reuse impl<'d, B> Trait<'static, 'd, usize, B, String> for F1 { &self.0 } + + struct S2; + struct F2(S2); + impl Trait<'static, 'static, u8, u16, u32> for S2 {} + reuse impl Trait<'static, 'static, u8, u16, u32> for F2 { &self.0 } + + fn f<'a, 'b, 'c, A, B, C>(a: A, b: B, c: C) { + let f = F(S{}); + + >::foo(&f, &a); + >::bar(&f, &b); + >::goo(&f, &c); + + let f = F1(S1{}); + >::foo(&f, &123); + >::bar(&f, &b); + >::goo(&f, &"s".to_string()); + + let f = F2(S2{}); + >::foo(&f, &1); + >::bar(&f, &2); + >::goo(&f, &3); + } +} + +mod reuse_in_different_places { + trait T { + fn foo(&self, x: usize) {} + } + + struct S; + impl T for S {} + + struct F1(S); + reuse impl T for F1 { + struct F2(S, S, S); + reuse impl T for F2 { self.1 } + + let f2 = F2(S{}, S{}, S{}); + f2.foo(123); + + &self.0 + } + + fn foo() { + struct F(S); + reuse impl T for F { self.0 } + + let f = F(S{}); + f.foo(1); + } + + fn bar() { + || { + struct F(S); + reuse impl T for F { self.0 } + + let f = F(S{}); + f.foo(1); + }; + } +} + +fn main() {} diff --git a/tests/incremental/delegation/impl-trait.rs b/tests/incremental/delegation/impl-trait.rs new file mode 100644 index 0000000000000..9e889f5da73c5 --- /dev/null +++ b/tests/incremental/delegation/impl-trait.rs @@ -0,0 +1,28 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod to_reuse { + pub fn foo() -> impl Clone { 0 } +} + +reuse to_reuse::foo; + +trait Trait { + fn bar() -> impl Clone { 1 } +} + +struct S; +impl Trait for S {} + +impl S { + reuse to_reuse::foo; + reuse ::bar; +} + +fn main() { + foo().clone(); + ::bar().clone(); +} diff --git a/tests/incremental/delegation/list.rs b/tests/incremental/delegation/list.rs new file mode 100644 index 0000000000000..11d195394feed --- /dev/null +++ b/tests/incremental/delegation/list.rs @@ -0,0 +1,47 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +impl Trait for u8 {} + +struct S(u8); +struct Z(u8); + +impl Trait for S { + reuse Trait::{foo, bar} { &self.0 } +} + +impl Trait for Z { + reuse ::{foo, bar} { &self.0 } +} + +trait Trait2 where Self: Trait { + reuse Trait::{foo, bar}; +} + +mod to_reuse { + pub fn a() {} + pub fn b() {} +} + +reuse to_reuse::{a, b}; + +fn main() { + let s = S(2); + s.foo(); + s.bar(); + + let z = Z(3); + z.foo(); + z.bar(); + + a(); + b(); +} diff --git a/tests/incremental/delegation/macro-inside-glob.rs b/tests/incremental/delegation/macro-inside-glob.rs new file mode 100644 index 0000000000000..dbeafc0806e48 --- /dev/null +++ b/tests/incremental/delegation/macro-inside-glob.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +impl Trait for u8 {} + +struct S(u8); + +// Macro expansion works inside delegation items. +macro_rules! u8 { () => { u8 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } +impl Trait for S { + reuse ::* { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} diff --git a/tests/incremental/delegation/macro-inside-list.rs b/tests/incremental/delegation/macro-inside-list.rs new file mode 100644 index 0000000000000..9a2f6c462eb8f --- /dev/null +++ b/tests/incremental/delegation/macro-inside-list.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +impl Trait for u8 {} + +struct S(u8); + +// Macro expansion works inside delegation items. +macro_rules! u8 { () => { u8 } } +macro_rules! self_0 { ($self:ident) => { &$self.0 } } +impl Trait for S { + reuse ::{foo, bar} { self_0!(self) } +} + +fn main() { + let s = S(2); + s.foo(); + s.bar(); +} diff --git a/tests/incremental/delegation/mapping/free-to-free-pass.rs b/tests/incremental/delegation/mapping/free-to-free-pass.rs new file mode 100644 index 0000000000000..dc7db7a78c90f --- /dev/null +++ b/tests/incremental/delegation/mapping/free-to-free-pass.rs @@ -0,0 +1,148 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types + consts, reusing with(out) +// user args, checking predicates inheritance, testing with impl Traits +mod test_1 { + trait Bound1 {} + trait Bound2 {} + trait Bound3 {} + + struct X {} + + impl Bound1 for X {} + impl Bound2 for X {} + impl Bound3 for X {} + + fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>( + _x: impl Bound1 + Bound2 + Bound3, + _f: impl FnOnce(T) -> U, + ) { + } + + pub fn check() { + reuse foo as bar; + bar::(X {}, |x| x); + + reuse foo::<'static, 'static, usize, String, 132> as bar1; + + bar1(X {}, |x| x.to_string()); + } +} + +// Testing lifetimes + types + consts, reusing without user args, +// providing delegation parent args in invocation +mod test_2 { + fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} + + pub fn check() { + reuse foo as bar; + bar::(); + } +} + +// Testing lifetimes + types + consts, reusing without user args, +// providing random types with delegation parent generics specified +mod test_3 { + fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} + + pub fn check() { + reuse foo as bar; + bar::(); + } +} + +// Testing late-bound lifetimes + types + consts, reusing without user args, +// providing random types with delegation parent generics specified, +// checking signature inheritance +mod test_4 { + fn foo<'a, 'b, T: Clone, U: Clone, const N: usize>(_t: &'a T, _u: &'b U) {} + + pub fn check() { + reuse foo as bar; + bar::(&1, &2); + } +} + +// Testing late-bound lifetimes + types + consts, reusing without user args, +// providing random types with delegation parent generics specified, +// checking signature inheritance, testing mixed order of types and consts +mod test_5 { + fn foo<'a, 'b, T: Clone, const N: usize, U: Clone>(_t: &'a T, _u: &'b U) {} + + pub fn check() { + reuse foo as bar; + bar::(&1, &2); + } +} + +// Testing late-bound lifetimes + types + consts, reusing with user args, +// checking signature inheritance, testing mixed order of types and consts +mod test_6 { + fn foo<'a, 'b, T: Clone, const N: usize, U: Clone>(_t: &'a T, _u: &'b U) {} + + pub fn check() { + reuse foo:: as bar; + bar(&"".to_string(), &"".to_string()); + } +} + +mod test_7 { + fn foo(t: T, u: U, f: impl FnOnce(T, U) -> U) -> U { + f(t, u) + } + + pub fn check() { + reuse foo as bar; + assert_eq!(bar::(1, 2, |_, y| y), 2); + } +} + +// Testing reuse of local fn with delegation parent generic params specified, +// late-bound lifetimes + types + consts, reusing with user args, +// checking signature inheritance, mixed consts and types ordering +mod test_8 { + pub fn check() { + fn foo<'a, 'b, const N: usize, T: Clone, U: Clone>(_t: &'a T, _u: &'b U) {} + + reuse foo::<1, String, String> as bar; + bar(&"".to_string(), &"".to_string()); + } +} + +// Testing reuse of local fn inside closure, +// late-bound lifetimes + types + consts, reusing with user args, +// checking signature inheritance, mixed consts and types ordering +mod test_9 { + pub fn check() { + let closure = || { + fn foo<'a, 'b, T: Clone, const N: usize, U: Clone>(_t: &'a T, _u: &'b U) {} + + reuse foo:: as bar; + bar(&"".to_string(), &"".to_string()); + }; + + closure(); + } +} + +pub fn main() { + test_1::check(); + test_2::check::(); + test_3::check::(); + test_4::check::(); + test_5::check::(); + test_6::check::(); + test_7::check(); + test_8::check::(); + test_9::check::(); +} diff --git a/tests/incremental/delegation/mapping/free-to-trait-pass.rs b/tests/incremental/delegation/mapping/free-to-trait-pass.rs new file mode 100644 index 0000000000000..b1f35f588ae78 --- /dev/null +++ b/tests/incremental/delegation/mapping/free-to-trait-pass.rs @@ -0,0 +1,248 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types + consts in both parent and child, reusing in +// a function without generic params, with impl traits +mod test_1 { + trait Trait<'b, 'c, 'a, T, const N: usize>: Sized { + fn foo<'d: 'd, U, const M: bool>(self, _f: impl FnOnce() -> ()) {} + } + + impl Trait<'static, 'static, 'static, i32, 1> for u8 {} + + pub fn check() { + fn no_ctx() { + reuse Trait::foo as bar; + bar::<'static, 'static, 'static, 'static, u8, i32, 1, String, true>(123, || ()); + } + + fn with_ctx<'a, 'b, 'c, A, B, C, const N: usize, const M: bool>() { + reuse Trait::foo as bar; + bar::<'static, 'static, 'static, 'a, u8, i32, 1, A, M>(123, || ()); + } + + no_ctx(); + with_ctx::(); + } +} + +// Testing lifetimes + types + consts in both parent and child, add user-specified args to parent +mod test_2 { + trait Trait<'a, T, const N: usize>: Sized { + fn foo<'b: 'b, U, const M: bool>(self) {} + } + + impl Trait<'static, i32, 1> for u8 {} + + pub fn check() { + reuse Trait::<'static, i32, 1>::foo as bar; + + bar::<'static, u8, String, true>(123); + } +} + +// Testing lifetimes + types + consts in both parent and child, +// add user-specified args to child +mod test_3 { + trait Trait<'a, T, const N: usize>: Sized { + fn foo<'b: 'b, U, const M: bool>(self) {} + } + + impl Trait<'static, String, 1> for u8 {} + + pub fn check() { + reuse Trait::foo::<'static, i32, true> as bar; + + bar::<'static, u8, String, 1>(123); + } +} + +// Testing types/consts in parent, lifetimes + types/consts in child, +// add user-specified args to child +mod test_4 { + trait Trait: Sized { + fn foo<'b: 'b, U, const M: bool>(self) {} + } + + impl Trait for u8 {} + + pub fn check() { + reuse Trait::foo::<'static, i32, true> as bar; + + bar::(123); + } +} + +// Testing consts in parent, lifetimes + types/consts in child, add user-specified args to child +mod test_5 { + trait Trait: Sized { + fn foo<'b: 'b, U, const M: bool>(self) {} + } + + impl Trait<1> for u8 {} + + pub fn check() { + reuse Trait::foo::<'static, i32, true> as bar; + + bar::(123); + } +} + +// Testing no generics in parent, lifetimes + types/consts in child, +// add user-specified args to child +mod test_6 { + trait Trait: Sized { + fn foo<'b: 'b, U, const M: bool>(self) {} + } + + impl Trait for u8 {} + + pub fn check() { + reuse Trait::foo::<'static, i32, true> as bar; + + bar::(123); + } +} + +// Testing lifetimes + types/consts in parent, types/consts in child, +// add user-specified args to parent +mod test_7 { + trait Trait<'a, T, const N: usize>: Sized { + fn foo(self) {} + } + + impl Trait<'static, i32, 1> for u8 {} + + pub fn check() { + reuse Trait::<'static, i32, 1>::foo as bar; + + bar::(123); + } +} + +// Testing lifetimes + types/consts in parent, consts in child, add user-specified args to parent +mod test_8 { + trait Trait<'a, T, const N: usize>: Sized { + fn foo(self) {} + } + + impl Trait<'static, i32, 1> for u8 {} + + pub fn check() { + reuse Trait::<'static, i32, 1>::foo as bar; + + bar::(123); + } +} + +// Testing lifetimes + types/consts in parent, none in child, add user-specified args to parent +mod test_9 { + trait Trait<'a, T, const N: usize>: Sized { + fn foo(self) {} + } + + impl Trait<'static, i32, 1> for u8 {} + + pub fn check() { + reuse Trait::<'static, i32, 1>::foo as bar; + + bar::(123); + } +} + +// Testing lifetimes + types in parent, lifetimes + types/consts in child, +// adding self ty to reuse, testing using generic params from delegation parent +// context, adding user-specified args to none, parent, parent and child +mod test_10 { + trait Trait<'b, 'c, T> { + fn foo<'d: 'd, U, const M: bool>() {} + } + + impl<'b, 'c, T> Trait<'b, 'c, T> for u8 {} + + pub fn check() { + fn with_ctx<'a, 'b, 'c, A, B, C, const N: usize, const M: bool>() { + reuse ::foo as bar; + bar::<'a, 'b, 'c, u8, C, A, M>(); + bar::<'static, 'static, 'static, u8, i32, i32, false>(); + + reuse >::foo as bar1; + bar1::<'static, u8, i32, true>(); + + reuse >::foo::<'static, u32, true> as bar2; + bar2::(); + } + + with_ctx::(); + } +} + +// Testing lifetimes + types in parent, lifetimes + types/consts in child, +// adding self ty to reuse, testing using generic params from delegation parent +// context, testing predicates inheritance +mod test_11 { + trait Bound0 {} + trait Bound1 {} + trait Bound2 {} + + trait Trait<'a: 'static, T, P> + where + Self: Sized, + T: Bound0, + P: Bound2>>>, + { + fn foo<'d: 'd, U: Bound1, const M: bool>() {} + } + + impl Bound0 for u32 {} + impl Bound1 for String {} + impl<'a: 'static, T: Bound0, P: Bound2>>>> Trait<'a, T, P> for usize {} + + struct Struct; + impl Bound2>>> for Struct {} + + pub fn check<'b>() { + reuse ::foo; + foo::<'static, 'b, usize, u32, Struct, String, false>(); + } +} + +// Testing lifetimes + types/consts in parent with defaults, none in child, +// reuse without user-specified args +mod test_12 { + trait Trait<'a, T = usize, const N: usize = 123>: Sized { + fn foo(self) {} + } + + impl Trait<'static, i32, 1> for u8 {} + + pub fn check() { + reuse Trait::foo as bar; + + bar::(123); + } +} + +pub fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); + test_8::check(); + test_9::check(); + test_10::check(); + test_11::check(); + test_12::check(); +} diff --git a/tests/incremental/delegation/mapping/impl-trait-to-free-pass.rs b/tests/incremental/delegation/mapping/impl-trait-to-free-pass.rs new file mode 100644 index 0000000000000..5e17e9b1a22e0 --- /dev/null +++ b/tests/incremental/delegation/mapping/impl-trait-to-free-pass.rs @@ -0,0 +1,264 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(late_bound_lifetime_arguments)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types/consts in child reuses, with impl traits, +// with (un)specified user args with additional generic params in delegation parent +mod test_1 { + mod to_reuse { + pub fn foo<'a: 'a, 'b: 'b, A, B, const N: usize>() {} + pub fn bar<'a: 'a, 'b: 'b, A, B, const N: usize>(_x: &super::XX, _f: impl FnOnce(A) -> B) {} + } + + trait Trait<'a, 'b, 'c, A, B, const N: usize>: Sized { + fn foo<'x: 'x, 'y: 'y, AA, BB, const NN: usize>() {} + fn bar<'x: 'x, 'y: 'y, AA, BB, const NN: usize>(&self, _f: impl FnOnce(AA) -> BB) {} + fn oof() {} + fn rab(&self, _f: impl FnOnce(A) -> B) {} + } + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X<'x1, 'x2, 'x3, 'x4, X1, X2, const X3: usize>( + &'x1 X1, &'x2 X2, &'x3 X1, &'x4 [usize; X3]); + type XX = X::<'static, 'static, 'static, 'static, i32, i32, 3>; + + impl<'a, 'b, 'c, A, B, const N: usize> Trait<'a, 'b, 'c, A, B, N> for XX { + reuse to_reuse::foo; + reuse to_reuse::bar; + + reuse to_reuse::foo::<'a, 'c, A, String, 322> as oof; + reuse to_reuse::bar::<'a, 'c, A, B, 223> as rab; + } + + pub fn check() { + let x = X(&1, &2, &3, &[1, 2, 3]); + + > + ::foo::<'static, 'static, i8, i16, 123>(); + > + ::bar::<'static, 'static, String, i16, 123>(&x, |_| 123); + >::oof(); + >::rab(&x, |_| 123.to_string()); + } +} + +// Testing types/consts in child reuses, +// with (un)specified user args, with additional generic params in delegation parent +mod test_2 { + mod to_reuse { + pub fn foo() {} + pub fn bar(_x: &super::X) {} + } + + trait Trait<'a, 'b, 'c, A, B, const N: usize>: Sized { + fn foo() {} + fn bar(&self) {} + fn oof() {} + fn rab(&self) {} + } + + struct X; + impl<'a, A, B, const N: usize> Trait<'a, 'static, 'static, A, B, N> for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + + reuse to_reuse::foo:: as oof; + reuse to_reuse::bar:: as rab; + } + + pub fn check() { + >::foo::(); + >::bar::(&X); + >::oof(); + >::rab(&X); + } +} + +// Testing none in child reuses, with unspecified user args, +// with additional generic params in delegation parent +mod test_3 { + mod to_reuse { + pub fn foo() {} + pub fn bar(_x: &super::X) {} + } + + trait Trait<'a, 'b, 'c, A, B, const N: usize>: Sized { + fn foo() {} + fn bar(&self) {} + } + + struct X; + impl<'a, A, B, const N: usize> Trait<'a, 'static, 'static, A, B, N> for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + } + + pub fn check() { + >::foo(); + >::bar(&X); + } +} + +// Testing lifetimes + types/consts in child reuses, +// with (un)specified user args, with additional generic params in delegation parent +mod test_4 { + mod to_reuse { + pub fn foo<'a: 'a, 'b: 'b, A, B, const N: usize>() {} + pub fn bar<'a: 'a, 'b: 'b, A, B, const N: usize>(_x: &super::X) {} + } + + trait Trait: Sized { + fn foo<'x: 'x, 'y: 'y, AA, BB, const NN: usize>() {} + fn bar<'x: 'x, 'y: 'y, AA, BB, const NN: usize>(&self) {} + fn oof() {} + fn rab(&self) {} + } + + struct X; + impl<'a, 'c, A, B, const N: usize> Trait for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + + reuse to_reuse::foo::<'a, 'c, A, String, 322> as oof; + reuse to_reuse::bar::<'a, 'c, i32, B, 223> as rab; + } + + pub fn check() { + >::foo::<'static, 'static, i8, i16, 123>(); + >::bar::<'static, 'static, X, i16, 123>(&X); + >::oof(); + >::rab(&X); + } +} + +// Testing lifetimes + types/consts in child reuses, +// with (un)specified user args, with additional generic params in delegation parent +mod test_5 { + mod to_reuse { + pub fn foo<'a: 'a, 'b: 'b, A, B, const N: usize>() {} + pub fn bar<'a: 'a, 'b: 'b, A, B, const N: usize>(_x: &super::X::) {} + } + + trait Trait: Sized { + fn foo<'x: 'x, 'y: 'y, AA, BB, const NN: usize>() {} + fn oof() {} + fn rab(&self) {} + } + + struct X(A, B); + impl<'a, 'c, A, B> Trait for X { + reuse to_reuse::foo; + + reuse to_reuse::foo::<'a, 'c, A, B, 322> as oof; + reuse to_reuse::bar::<'a, 'c, A, B, 223> as rab; + } + + pub fn check() { + as Trait>::foo::<'static, 'static, i8, i16, 123>(); + as Trait>::oof(); + as Trait>::rab(&X(1, 2)); + } +} + +// Testing types/consts in child reuses, +// with (un)specified user args, with additional generic params in delegation parent +mod test_6 { + mod to_reuse { + pub fn foo() {} + pub fn bar(_x: &super::X) {} + } + + trait Trait: Sized { + fn foo() {} + fn bar(&self) {} + fn oof() {} + fn rab(&self) {} + } + + struct X; + impl<'a, 'c, A, B, const N: usize> Trait for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + + reuse to_reuse::foo:: as oof; + reuse to_reuse::bar:: as rab; + } + + pub fn check() { + >::foo::(); + >::bar::(&X); + >::oof(); + >::rab(&X); + } +} + +// Testing none in child reuses, with unspecified user args, +// with additional generic params in delegation parent +mod test_7 { + mod to_reuse { + pub fn foo() {} + pub fn bar(_x: &super::X) {} + } + + trait Trait: Sized { + fn foo() {} + fn bar(&self) {} + } + + struct X; + impl<'a, 'c, A, B, const N: usize> Trait for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + } + + pub fn check() { + >::foo(); + >::bar(&X); + } +} + +// Testing none in child reuses, with unspecified user args +mod test_8 { + mod to_reuse { + pub fn foo() {} + pub fn bar(_x: &super::X) {} + } + + trait Trait: Sized { + fn foo() {} + fn bar(&self) {} + } + + struct X; + impl Trait for X { + reuse to_reuse::foo; + reuse to_reuse::bar; + } + + pub fn check() { + ::foo(); + ::bar(&X); + X::foo(); + X::bar(&X); + } +} + +fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); + test_8::check(); +} diff --git a/tests/incremental/delegation/mapping/impl-trait-to-trait-pass.rs b/tests/incremental/delegation/mapping/impl-trait-to-trait-pass.rs new file mode 100644 index 0000000000000..44c7906fa269f --- /dev/null +++ b/tests/incremental/delegation/mapping/impl-trait-to-trait-pass.rs @@ -0,0 +1,250 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(late_bound_lifetime_arguments)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing types in parent, types in child reuse, +// testing predicates inheritance, +// with additional generic params in delegation parent, with impl traits +mod test_1 { + trait Trait0 {} + + trait Trait1 { + fn foo(&self, _f: impl FnOnce(T, U) -> (U, T)) + where + T: Trait0, + U: Trait0, + { + } + } + + struct F; + impl Trait1 for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, T, A, B> Trait1 for S<'a, 'b, 'c, A, B> { + reuse Trait1::::foo { &self.0 } + } + + impl Trait0 for u16 {} + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait1>::foo::(&s, |x, y| (y, x)); + } +} + +// Testing none in parent, none in child reuse, +// with additional generic params in delegation parent +mod test_2 { + trait Trait { + fn foo(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait for S<'a, 'b, 'c, A, B, C> { + reuse Trait::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait>::foo(&s); + } +} + +// Testing lifetimes + types in parent, none in child reuse, +// with additional generic params in delegation parent +mod test_3 { + trait Trait<'a, 'b, 'c, X, Y, Z> { + fn foo(&self) {} + } + + struct F; + impl<'a, 'b, 'c, X, Y, Z> Trait<'a, 'b, 'c, X, Y, Z> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait<'a, 'b, 'static, A, String, bool> + for S<'a, 'b, 'c, A, B, C> { + reuse Trait::<'a, 'b, 'static, A, String, bool>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + + as Trait<'static, 'static, 'static, i32, String, bool>>::foo(&s); + } +} + +// Testing lifetimes + types in parent, lifetimes + types/consts in child reuse, +// with additional generic params in delegation parent +mod test_4 { + trait Trait<'a, 'b, 'c, X, Y, Z> { + fn foo<'x: 'x, 'y: 'y, 'z: 'z, A, B, C, const XX: usize>(&self) {} + } + + struct F; + impl<'a, 'b, 'c, X, Y, Z> Trait<'a, 'b, 'c, X, Y, Z> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait<'a, 'b, 'static, A, String, bool> + for S<'a, 'b, 'c, A, B, C> { + reuse Trait::<'a, 'b, 'static, A, String, bool>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + + as Trait<'static, 'static, 'static, i32, String, bool>> + ::foo::<'static, 'static, 'static, i32, i32, i32, 1>(&s); + } +} + +// Testing types in parent, lifetimes in child reuse +// with additional generic params in delegation parent +mod test_5 { + trait Trait { + fn foo<'a: 'a, 'b: 'b, 'c: 'c>(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait for S<'a, 'b, 'c, A, B, C> { + reuse Trait::::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait> + ::foo::<'static, 'static, 'static>(&s); + as Trait>::foo(&s); + } +} + +// Testing types in parent, types in child reuse +// with additional generic params in delegation parent +mod test_6 { + trait Trait { + fn foo(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait for S<'a, 'b, 'c, A, B, C> { + reuse Trait::::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait>::foo::(&s); + } +} + +// Testing types in parent, none in child reuse +// with additional generic params in delegation parent +mod test_7 { + trait Trait { + fn foo(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait for S<'a, 'b, 'c, A, B, C> { + reuse Trait::::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait>::foo(&s); + } +} + +// Testing lifetimes in parent, none in child reuse +// with additional generic params in delegation parent +mod test_8 { + trait Trait<'a, 'b, 'c> { + fn foo(&self) {} + } + + struct F; + impl<'a, 'b, 'c> Trait<'a, 'b, 'c> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait<'a, 'b, 'c> for S<'a, 'b, 'c, A, B, C> { + reuse Trait::<'a, 'static, 'b>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait<'static, 'static, 'static>>::foo(&s); + } +} + +// Testing lifetimes in parent, lifetimes in child reuse +// with additional generic params in delegation parent +mod test_9 { + trait Trait<'a, 'b, 'c> { + fn foo<'x: 'x, 'y: 'y>(&self) {} + } + + struct F; + impl<'a, 'b, 'c> Trait<'a, 'b, 'c> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B, const C: bool>(F, &'a A, &'b B, &'c B); + + impl<'a, 'b, 'c, A, B, const C: bool> Trait<'a, 'b, 'c> for S<'a, 'b, 'c, A, B, C> { + reuse Trait::<'a, 'static, 'b>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + as Trait<'static, 'static, 'static>>::foo(&s); + as Trait<'static, 'static, 'static>> + ::foo::<'static, 'static>(&s); + } +} + +fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); + test_8::check(); + test_9::check(); +} diff --git a/tests/incremental/delegation/mapping/inherent-impl-to-free-pass.rs b/tests/incremental/delegation/mapping/inherent-impl-to-free-pass.rs new file mode 100644 index 0000000000000..963c1e292d76b --- /dev/null +++ b/tests/incremental/delegation/mapping/inherent-impl-to-free-pass.rs @@ -0,0 +1,126 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types/consts OR types/consts OR none in delegation parent, +// lifetimes + types/consts in child reuse, +// with(out) user-specified args, with impl traits +mod test_1 { + mod to_reuse { + pub fn foo<'a: 'a, 'b: 'b, A, B, const N: usize>(_f: impl FnOnce(A, B) -> B) {} + } + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X1<'a, 'b, T, X, const N: usize>(&'a T, &'b X, &'a [i32; N]); + impl<'a, 'b, T, E, const N: usize> X1<'a, 'b, T, E, N> { + reuse to_reuse::foo; + reuse to_reuse::foo::<'static, 'static, i32, String, 1> as bar; + } + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X2(T, X, &'static [i32; N]); + impl X2 { + reuse to_reuse::foo; + reuse to_reuse::foo::<'static, 'static, i32, String, 1> as bar; + } + + struct X3; + impl X3 { + reuse to_reuse::foo; + reuse to_reuse::foo::<'static, 'static, i32, String, 1> as bar; + } + + pub fn check() { + X1::<'static, 'static, i32, i32, 1> + ::foo::<'static, 'static, String, String, 123>(|_, y| y); + X1::<'static, 'static, i32, i32, 1>::bar(|_, y| y); + + X2::::foo::<'static, 'static, String, String, 123>(|_, y| y); + X2::::bar(|_, y| y); + + X3::foo::<'static, 'static, String, String, 123>(|_, y| y); + X3::bar(|_, y| y); + } +} + +// Testing lifetimes + types/consts OR types/consts OR none in parent, +// types/consts in child reuse, with(out) user-specified args +mod test_2 { + fn foo() {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X1<'a, 'b, T, X, const N: usize>(&'a T, &'b X, &'a [i32; N]); + impl<'a, 'b, T, E, const N: usize> X1<'a, 'b, T, E, N> { + reuse foo; + reuse foo:: as bar; + } + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X2(T, X, &'static [i32; N]); + impl X2 { + reuse foo; + reuse foo:: as bar; + } + + struct X3; + impl X3 { + reuse foo; + reuse foo:: as bar; + } + + pub fn check() { + X1::<'static, 'static, i32, i32, 1>::foo::(); + X1::<'static, 'static, i32, i32, 1>::bar(); + + X2::::foo::(); + X2::::bar(); + + X3::foo::(); + X3::bar(); + } +} + +// Testing lifetimes + types/consts OR types/consts OR none in parent, +// none in child reuse +mod test_3 { + fn foo() {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X1<'a, 'b, T, X, const N: usize>(&'a T, &'b X, &'a [i32; N]); + impl<'a, 'b, T, E, const N: usize> X1<'a, 'b, T, E, N> { + reuse foo; + } + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct X2(T, X, &'static [i32; N]); + impl X2 { + reuse foo; + } + + struct X3; + impl X3 { + reuse foo; + } + + pub fn check() { + X1::<'static, 'static, i32, i32, 1>::foo(); + + X2::::foo(); + + X3::foo(); + } +} + +fn main() { + test_1::check(); + test_2::check(); + test_3::check(); +} diff --git a/tests/incremental/delegation/mapping/inherent-impl-to-trait-pass.rs b/tests/incremental/delegation/mapping/inherent-impl-to-trait-pass.rs new file mode 100644 index 0000000000000..db0d21480500a --- /dev/null +++ b/tests/incremental/delegation/mapping/inherent-impl-to-trait-pass.rs @@ -0,0 +1,233 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(late_bound_lifetime_arguments)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing types in parent, none in child, +// user-specified args in parent, checking predicates inheritance, +// with additional generic params in delegation parent, with impl traits +mod test_1 { + trait Trait { + fn foo(&self, _f: impl FnOnce(T) -> String) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s, |t| t.to_string()); + s.foo(|t| t.to_string()); + } +} + +// Testing lifetimes + types/consts in parent, none in child, +// with additional generic params in delegation parent +mod test_2 { + trait Trait<'x, 'y, T, const B: bool> { + fn foo(&self) {} + } + + struct F; + impl<'x, 'y, T, const B: bool> Trait<'x, 'y, T, B> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::<'a, 'b, String, true>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing lifetimes in parent, none in child, +// with additional generic params in delegation parent +mod test_3 { + trait Trait<'x, 'y> { + fn foo(&self) {} + } + + struct F; + impl<'x, 'y> Trait<'x, 'y> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::<'a, 'b>::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing none in parent, none in child, +// with additional generic params in delegation parent +mod test_4 { + trait Trait { + fn foo(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::foo { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing none in parent, lifetimes in child, +// with additional generic params in delegation parent +mod test_5 { + trait Trait { + fn foo<'a: 'a, 'b: 'b>(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::foo::<'a, 'b> { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing none in parent, lifetimes + types in child, +// with additional generic params in delegation parent +mod test_6 { + trait Trait { + fn foo<'a: 'a, 'b: 'b, A, B, C>(&self) {} + } + + struct F; + impl Trait for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::foo::<'a, 'b, A, B, String> { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing lifetimes in parent, lifetimes + types in child, +// with additional generic params in delegation parent +mod test_7 { + trait Trait<'x, 'y, 'z> { + fn foo<'a: 'a, 'b: 'b, A, B, C>(&self) {} + } + + struct F; + impl<'a, 'b, 'c> Trait<'a, 'b, 'c> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::<'a, 'b, 'c>::foo::<'a, 'b, A, B, String> { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing lifetimes + types in parent, lifetimes + types in child, +// with additional generic params in delegation parent +mod test_8 { + trait Trait<'x, 'y, 'z, X, Y, Z> { + fn foo<'a: 'a, 'b: 'b, A, B, C>(&self) {} + } + + struct F; + impl<'a, 'b, 'c, X, Y, Z> Trait<'a, 'b, 'c, X, Y, Z> for F {} + + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::<'a, 'b, 'c, B, A, i32>::foo::<'a, 'b, A, B, String> { &self.0 } + } + + pub fn check() { + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +// Testing lifetimes + types in parent, lifetimes + types in child, +// with additional generic params in delegation parent, +// inside a function with generic params +mod test_9 { + trait Trait<'x, 'y, 'z, X, Y, Z> { + fn foo<'a: 'a, 'b: 'b, A, B, C>(&self) {} + } + + struct F; + impl<'a, 'b, 'c, X, Y, Z> Trait<'a, 'b, 'c, X, Y, Z> for F {} + + pub fn check() { + #[allow(dead_code)] // Fields are used instead of phantom data for generics use + struct S<'a, 'b, 'c, A, B>(F, &'a A, &'b B, &'c B); + impl<'a, 'b, 'c, A, B> S<'a, 'b, 'c, A, B> { + reuse Trait::<'a, 'b, 'c, B, A, i32>::foo::<'a, 'b, A, B, String> { &self.0 } + } + + let s = S(F, &123, &123, &123); + S::<'static, 'static, 'static, i32, i32>::foo(&s); + s.foo(); + } +} + +pub fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); + test_8::check(); + test_9::check::(); +} diff --git a/tests/incremental/delegation/mapping/trait-to-free-pass.rs b/tests/incremental/delegation/mapping/trait-to-free-pass.rs new file mode 100644 index 0000000000000..fd6fe0277373b --- /dev/null +++ b/tests/incremental/delegation/mapping/trait-to-free-pass.rs @@ -0,0 +1,137 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types/consts in child, lifetimes + types/consts in delegation parent, +// with(out) user-specified args, with impl traits +mod test_1 { + fn foo<'a: 'a, 'b: 'b, T: Clone + ToString, U: Clone, const N: usize>( + _f: impl FnOnce(T) -> (T, U)) {} + + trait Trait<'a, A, B, C, const N: usize> { + reuse foo; + reuse foo::<'static, 'static, i32, String, 1> as bar; + } + + impl Trait<'static, i32, i32, i32, 1> for u32 {} + pub fn check() { + > + ::foo::<'static, 'static, i32, String, 1>(|t| (t, "".to_string())); + >::bar(|t| (t, t.to_string())); + u32::bar(|t| (t, t.to_string())); + } +} + +// Testing types/consts in child, lifetimes + types/consts in delegation parent, +// with(out) user-specified args +mod test_2 { + fn foo() {} + + trait Trait<'a, A, B, C, const N: usize> { + reuse foo; + reuse foo:: as bar; + } + + impl Trait<'static, i32, i32, i32, 1> for u32 {} + pub fn check() { + >::foo::(); + >::bar(); + } +} + +// Testing none in child, lifetimes + types/consts in delegation parent, +// with(out) user-specified args +mod test_3 { + fn foo() {} + + trait Trait<'a, A, B, C, const N: usize> { + reuse foo; + } + + impl Trait<'static, i32, i32, i32, 1> for u32 {} + pub fn check() { + >::foo(); + } +} + +// Testing lifetimes + types/consts in child, types/consts in delegation parent, +// with(out) user-specified args +mod test_4 { + fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} + + trait Trait { + reuse foo; + reuse foo::<'static, 'static, i32, String, 1> as bar; + } + + impl Trait for u32 {} + pub fn check() { + >::foo::<'static, 'static, i32, String, 1>(); + >::bar(); + } +} + +// Testing lifetimes + types/consts in child, none in delegation parent, +// with(out) user-specified args +mod test_5 { + fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} + + trait Trait { + reuse foo; + reuse foo::<'static, 'static, i32, String, 1> as bar; + } + + impl Trait for u32 {} + pub fn check() { + ::foo::<'static, 'static, i32, String, 1>(); + ::bar(); + } +} + +// Testing types/consts in child, none in delegation parent, with(out) user-specified args +mod test_6 { + fn foo() {} + + trait Trait { + reuse foo; + reuse foo:: as bar; + } + + impl Trait for u32 {} + pub fn check() { + ::foo::(); + ::bar(); + } +} + +// Testing none in child, none in delegation parent, with(out) user-specified args +mod test_7 { + fn foo() {} + + trait Trait { + reuse foo; + } + + impl Trait for u32 {} + pub fn check() { + ::foo(); + } +} + +pub fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); +} diff --git a/tests/incremental/delegation/mapping/trait-to-trait-pass.rs b/tests/incremental/delegation/mapping/trait-to-trait-pass.rs new file mode 100644 index 0000000000000..8b2cc711cde96 --- /dev/null +++ b/tests/incremental/delegation/mapping/trait-to-trait-pass.rs @@ -0,0 +1,799 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(late_bound_lifetime_arguments)] + +//! This is one of the mapping tests, which tests mapping of delegee parent and child +//! generic params, whose main goal is to create cases with +//! different number of lifetimes/types/consts in delegee child and parent; and in +//! delegation parent if applicable. At some tests predicates are +//! added. At some tests user-specified args are specified in reuse statement. + +// Testing lifetimes + types in parent, +// lifetimes + types/consts in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr, with impl traits +mod test_1 { + trait Trait<'b, 'c, 'a, T>: Sized { + fn foo<'d: 'd, U, const M: bool>(&self, _f: impl FnOnce(T) -> U) {} + } + + impl<'b, 'c, 'a, T> Trait<'b, 'c, 'a, T> for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> as bar2 { + Self::get() + } + + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar4 { self.get_self() } + + // FIXME(fn_delegation): Uncomment those tests when proper support for + // generics when method call is generated is added + + // reuse Trait::foo::<'static, String, false> as bar5 { Self::get() } + // reuse Trait::foo as bar6 { Self::get() } + // reuse Trait::foo::<'static, String, false> as bar7 { self.get_self() } + // reuse Trait::foo as bar8 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar2 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar4 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar2 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar4 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar2 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo::<'static, String, false> + as bar4 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, String, true>(&123, |x| x.to_string()); + ::bar1::<'static, String, true>(&123, |x| x.to_string()); + >::bar1::<'static, String, true>(&123, |x| x.to_string()); + >::bar1::<'static, String, true>(&123, |x| x.to_string()); + + >::bar2(&123, |x| x.to_string()); + ::bar2(&123, |x| x.to_string()); + >::bar2(&123, |x| x.to_string()); + >::bar2(&123, |x| x.to_string()); + + > + ::bar3::<'static, String, true>(&123, |x| x.to_string()); + ::bar3::<'static, String, true>(&123, |x| x.to_string()); + >::bar3::<'static, String, true>(&123, |x| x.to_string()); + >::bar3::<'static, String, true>(&123, |x| x.to_string()); + + >::bar4(&123, |x| x.to_string()); + ::bar4(&123, |x| x.to_string()); + >::bar4(&123, |x| x.to_string()); + >::bar4(&123, |x| x.to_string()); + } +} + +// Testing types in parent, +// lifetimes + types/consts in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_2 { + trait Trait: Sized { + fn foo<'d: 'd, U, const M: bool>(&self) {} + } + + impl Trait for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, String, false> as bar4 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, String, true>(&123); + ::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + > + ::bar3::<'static, String, true>(&123); + ::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +// Testing lifetimes in parent, +// lifetimes + types/consts in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_3 { + trait Trait<'b, 'c, 'a>: Sized { + fn foo<'d: 'd, U, const M: bool>(&self) {} + } + + impl<'b, 'c, 'a> Trait<'b, 'c, 'a> for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar4 { + self.get_self() + } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar4 { + self.get_self() + } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> as bar4 { + self.get_self() + } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> + as bar2 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, String, false> + as bar4 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, String, true>(&123); + ::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + > + ::bar3::<'static, String, true>(&123); + ::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +// Testing none in parent, +// lifetimes + types/consts in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_4 { + trait Trait: Sized { + fn foo<'d: 'd, U, const M: bool>(&self) {} + } + + impl Trait for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + reuse Trait::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + reuse Trait::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + reuse Trait::foo::<'static, String, false> as bar4 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo::<'static, String, false> as bar2 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + reuse Trait::foo::<'static, String, false> as bar4 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, String, true>(&123); + ::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + >::bar1::<'static, String, true>(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + > + ::bar3::<'static, String, true>(&123); + ::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + >::bar3::<'static, String, true>(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +// Testing lifetimes + types in parent, +// types/consts in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_5 { + trait Trait<'b, 'c, 'a, T>: Sized { + fn foo(&self) {} + } + + impl<'b, 'c, 'a, T> Trait<'b, 'c, 'a, T> for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar4 { + self.get_self() + } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar4 { + self.get_self() + } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar4 { + self.get_self() + } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static, i32>::foo:: as bar4 { + self.get_self() + } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + >::bar1::(&123); + ::bar1::(&123); + >::bar1::(&123); + >::bar1::(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + >::bar3::(&123); + ::bar3::(&123); + >::bar3::(&123); + >::bar3::(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +// Testing lifetimes + types in parent, +// none in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_6 { + trait Trait<'b, 'c, 'a, T>: Sized { + fn foo(&self) {} + } + + impl<'b, 'c, 'a, T> Trait<'b, 'c, 'a, T> for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static, i32>::foo as bar3 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + >::bar1(&123); + ::bar1(&123); + >::bar1(&123); + >::bar1(&123); + + >::bar3(&123); + ::bar3(&123); + >::bar3(&123); + >::bar3(&123); + } +} + +// Testing types in parent, +// none in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with user-specified args, with different target expr +mod test_7 { + trait Trait: Sized { + fn foo(&self) {} + } + + impl Trait for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + >::bar1(&123); + ::bar1(&123); + >::bar1(&123); + >::bar1(&123); + + >::bar3(&123); + ::bar3(&123); + >::bar3(&123); + >::bar3(&123); + } +} + +// Testing none in parent, +// none in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// without user-specified args, with different target expr +mod test_8 { + trait Trait: Sized { + fn foo(&self) {} + } + + impl Trait for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::foo as bar1 { Self::get() } + reuse Trait::foo as bar3 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + >::bar1(&123); + ::bar1(&123); + >::bar1(&123); + >::bar1(&123); + + >::bar3(&123); + ::bar3(&123); + >::bar3(&123); + >::bar3(&123); + } +} + +// Testing types in parent, +// lifetimes in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_9 { + trait Trait: Sized { + fn foo<'a: 'a, 'b: 'b>(&self) {} + } + + impl Trait for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, 'static> as bar4 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, 'static> as bar4 { self.get_self() } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, 'static> as bar4 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::::foo as bar1 { Self::get() } + reuse Trait::::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::::foo as bar3 { self.get_self() } + reuse Trait::::foo::<'static, 'static> as bar4 { self.get_self() } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, 'static>(&123); + ::bar1::<'static, 'static>(&123); + >::bar1::<'a, 'a>(&123); + >::bar1::<'a, 'a>(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + > + ::bar3::<'static, 'static>(&123); + ::bar3::<'static, 'static>(&123); + >::bar3::<'static, 'static>(&123); + >::bar3::<'static, 'static>(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +// Testing lifetimes in parent, +// lifetimes in child, +// in delegation parent with: +// lifetimes + types OR none OR lifetimes OR types, +// with(out) user-specified args, with different target expr +mod test_10 { + trait Trait<'x, 'y, 'z>: Sized { + fn foo<'a: 'a, 'b: 'b>(&self) {} + } + + impl<'x, 'y, 'z> Trait<'x, 'y, 'z> for u8 {} + + trait Trait2<'a, 'b, 'c, X, Y, Z> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'a, 'b, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'a, 'b, 'static>::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::<'a, 'b, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'a, 'b, 'static>::foo::<'static, 'static> as bar4 { self.get_self() } + } + + trait Trait3 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, 'static> as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, 'static> as bar4 { + self.get_self() + } + } + + trait Trait4<'a, 'b, 'c> { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'a, 'b, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'a, 'b, 'static>::foo::<'static, 'static> as bar2 { Self::get() } + reuse Trait::<'a, 'b, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'a, 'b, 'static>::foo::<'static, 'static> as bar4 { self.get_self() } + } + + trait Trait5 { + fn get() -> &'static u8 { &0 } + fn get_self(&self) -> &'static u8 { &0 } + reuse Trait::<'static, 'static, 'static>::foo as bar1 { Self::get() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, 'static> as bar2 { + Self::get() + } + reuse Trait::<'static, 'static, 'static>::foo as bar3 { self.get_self() } + reuse Trait::<'static, 'static, 'static>::foo::<'static, 'static> as bar4 { + self.get_self() + } + } + + impl<'a, 'b, 'c, X, Y, Z> Trait2<'a, 'b, 'c, X, Y, Z> for u32 {} + impl Trait3 for u32 {} + impl<'a, 'b, 'c> Trait4<'a, 'b, 'c> for u32 {} + impl Trait5 for u32 {} + + pub fn check<'a: 'a>() { + > + ::bar1::<'static, 'static>(&123); + ::bar1::<'static, 'static>(&123); + >::bar1::<'a, 'a>(&123); + >::bar1::<'a, 'a>(&123); + + >::bar2(&123); + ::bar2(&123); + >::bar2(&123); + >::bar2(&123); + + > + ::bar3::<'static, 'static>(&123); + ::bar3::<'static, 'static>(&123); + >::bar3::<'static, 'static>(&123); + >::bar3::<'static, 'static>(&123); + + >::bar4(&123); + ::bar4(&123); + >::bar4(&123); + >::bar4(&123); + } +} + +pub fn main() { + test_1::check(); + test_2::check(); + test_3::check(); + test_4::check(); + test_5::check(); + test_6::check(); + test_7::check(); + test_8::check(); + test_9::check(); + test_10::check(); +} diff --git a/tests/incremental/delegation/method-call-priority.rs b/tests/incremental/delegation/method-call-priority.rs new file mode 100644 index 0000000000000..702d9f2004a24 --- /dev/null +++ b/tests/incremental/delegation/method-call-priority.rs @@ -0,0 +1,35 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] +#![allow(dead_code)] + +trait Trait1 { + fn foo(&self) -> i32 { 1 } +} + +trait Trait2 { + fn foo(&self) -> i32 { 2 } +} + +struct F; +impl Trait1 for F {} +impl Trait2 for F {} + +impl F { + fn foo(&self) -> i32 { 3 } +} + +struct S(F); + +impl Trait1 for S { + // Make sure that the generated `self.0.foo()` does not turn into the inherent method `F::foo` + // that has a higher priority than methods from traits. + reuse Trait1::foo { self.0 } +} + +fn main() { + let s = S(F); + assert_eq!(s.foo(), 1); +} diff --git a/tests/incremental/delegation/parse.rs b/tests/incremental/delegation/parse.rs new file mode 100644 index 0000000000000..2786309ba8ffb --- /dev/null +++ b/tests/incremental/delegation/parse.rs @@ -0,0 +1,43 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(decl_macro)] +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +macro_rules! reuse { {} => {} } + +mod reuse { + pub fn to_unsafe(x: i32) -> i32 { x + 1 } + pub fn to_pub() {} + pub fn to_pub2() {} + + mod inner { + #[allow(non_camel_case_types)] + struct reuse { + a: i32, + b: i32, + c: i32, + } + + impl reuse { + reuse!(); + } + + fn baz() { + let (a, b, c) = (0, 0, 0); + reuse {a, b, c}; + } + } + + pub macro my_macro() {} +} + +reuse!(); +reuse::my_macro!(); + +#[inline] +pub reuse reuse::to_pub; +pub reuse crate::reuse::to_pub2; + +fn main() {} diff --git a/tests/incremental/delegation/rename.rs b/tests/incremental/delegation/rename.rs new file mode 100644 index 0000000000000..c6e74b3f623ca --- /dev/null +++ b/tests/incremental/delegation/rename.rs @@ -0,0 +1,28 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod to_reuse { + pub fn a() {} + pub fn b() {} +} + +reuse to_reuse::a as x; +reuse to_reuse::{a as y, b as z}; + +struct S; +impl S { + reuse to_reuse::a as x; + reuse to_reuse::{a as y, b as z}; +} + +fn main() { + x(); + y(); + z(); + S::x(); + S::y(); + S::z(); +} diff --git a/tests/incremental/delegation/self-coercion.rs b/tests/incremental/delegation/self-coercion.rs new file mode 100644 index 0000000000000..20348b9b511cd --- /dev/null +++ b/tests/incremental/delegation/self-coercion.rs @@ -0,0 +1,27 @@ +//@ run-pass +//@ revisions:rpass1 rpass2 + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +trait Trait : Sized { + fn by_value(self) -> i32 { 1 } + fn by_mut_ref(&mut self) -> i32 { 2 } + fn by_ref(&self) -> i32 { 3 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } +} + +fn main() { + let mut s = S(F); + assert_eq!(s.by_ref(), 3); + assert_eq!(s.by_mut_ref(), 2); + assert_eq!(s.by_value(), 1); +} diff --git a/tests/ui/delegation/generics/generic-params-defaults.rs b/tests/ui/delegation/generics/generic-params-defaults.rs index 66a9195551337..61f844dfae639 100644 --- a/tests/ui/delegation/generics/generic-params-defaults.rs +++ b/tests/ui/delegation/generics/generic-params-defaults.rs @@ -1,3 +1,5 @@ +//@ compile-flags: -Z deduplicate-diagnostics=yes + #![feature(fn_delegation)] #![allow(incomplete_features)] diff --git a/tests/ui/delegation/generics/generic-params-defaults.stderr b/tests/ui/delegation/generics/generic-params-defaults.stderr index 76fc3fde4753c..2aba6c0bf6f46 100644 --- a/tests/ui/delegation/generics/generic-params-defaults.stderr +++ b/tests/ui/delegation/generics/generic-params-defaults.stderr @@ -1,5 +1,5 @@ error: defaults for generic parameters are not allowed here - --> $DIR/generic-params-defaults.rs:5:12 + --> $DIR/generic-params-defaults.rs:7:12 | LL | fn foo(&self) { | ^^^^^^^^^ @@ -12,7 +12,18 @@ error: aborting due to 1 previous error Future incompatibility report: Future breakage diagnostic: error: defaults for generic parameters are not allowed here - --> $DIR/generic-params-defaults.rs:5:12 + --> $DIR/generic-params-defaults.rs:7:12 + | +LL | fn foo(&self) { + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #36887 + = note: `#[deny(invalid_type_param_default)]` (part of `#[deny(future_incompatible)]`) on by default + +Future breakage diagnostic: +error: defaults for generic parameters are not allowed here + --> $DIR/generic-params-defaults.rs:7:12 | LL | fn foo(&self) { | ^^^^^^^^^ diff --git a/tests/ui/delegation/query-cycle-oom.rs b/tests/ui/delegation/query-cycle-oom.rs new file mode 100644 index 0000000000000..ec25cb8765a13 --- /dev/null +++ b/tests/ui/delegation/query-cycle-oom.rs @@ -0,0 +1,65 @@ +//@ compile-flags: -Z deduplicate-diagnostics=yes + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod test_1 { + trait Trait { + fn foo(&self, x: T) -> S { x } + //~^ ERROR: missing generics for struct `test_1::S` + } + struct F; + + struct S(F, T); + + impl Trait for S { + reuse to_reuse::foo { &self.0 } + //~^ ERROR: cannot find module or crate `to_reuse` in this scope + } +} + +mod test_2 { + trait Trait { + fn foo() -> Self::Assoc; + //~^ ERROR: associated type `Assoc` not found for `Self` + //~| ERROR: this function takes 0 arguments but 1 argument was supplied + fn bar(&self) -> u8; + } + + impl Trait for u8 { + //~^ ERROR: not all trait items implemented, missing: `foo` + fn bar(&self) -> u8 { 1 } + } + + struct S(u8); + + impl Trait for S { + reuse Trait::* { &self.0 } + fn bar(&self) -> u8 { 2 } + } +} + +mod test_3 { + trait Trait { + fn foo(&self) -> Self::Assoc<3> { //~ ERROR: associated type `Assoc` not found for `Self` + //~^ ERROR: no method named `foo` found for reference `&()` in the current scope + [(); 3] + } + } + + impl () { //~ ERROR: cannot define inherent `impl` for primitive types + reuse Trait::*; + } +} + +mod test_4 { + trait Trait { + fn foo(&self, _: dyn Trait) {} + //~^ ERROR: missing generics for trait `test_4::Trait` + } + + reuse Trait::<_>::foo:: as x; + //~^ ERROR: the placeholder `_` is not allowed within types on item signatures for functions +} + +fn main() {} diff --git a/tests/ui/delegation/query-cycle-oom.stderr b/tests/ui/delegation/query-cycle-oom.stderr new file mode 100644 index 0000000000000..01eee02de0957 --- /dev/null +++ b/tests/ui/delegation/query-cycle-oom.stderr @@ -0,0 +1,126 @@ +error[E0107]: missing generics for struct `test_1::S` + --> $DIR/query-cycle-oom.rs:8:32 + | +LL | fn foo(&self, x: T) -> S { x } + | ^ expected 1 generic argument + | +note: struct defined here, with 1 generic parameter: `T` + --> $DIR/query-cycle-oom.rs:13:12 + | +LL | struct S(F, T); + | ^ - +help: add missing generic argument + | +LL | fn foo(&self, x: T) -> S { x } + | +++ + +error[E0107]: missing generics for trait `test_4::Trait` + --> $DIR/query-cycle-oom.rs:57:33 + | +LL | fn foo(&self, _: dyn Trait) {} + | ^^^^^ expected 1 generic argument + | +note: trait defined here, with 1 generic parameter: `T` + --> $DIR/query-cycle-oom.rs:56:11 + | +LL | trait Trait { + | ^^^^^ - +help: add missing generic argument + | +LL | fn foo(&self, _: dyn Trait) {} + | +++ + +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/query-cycle-oom.rs:23:27 + | +LL | fn foo() -> Self::Assoc; + | ^^^^^ associated type `Assoc` not found + +error[E0220]: associated type `Assoc` not found for `Self` + --> $DIR/query-cycle-oom.rs:44:32 + | +LL | fn foo(&self) -> Self::Assoc<3> { + | ^^^^^ associated type `Assoc` not found + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions + --> $DIR/query-cycle-oom.rs:61:19 + | +LL | reuse Trait::<_>::foo:: as x; + | ^ not allowed in type signatures + +error[E0046]: not all trait items implemented, missing: `foo` + --> $DIR/query-cycle-oom.rs:29:5 + | +LL | fn foo() -> Self::Assoc; + | ------------------------ `foo` from trait +... +LL | impl Trait for u8 { + | ^^^^^^^^^^^^^^^^^ missing `foo` in implementation + +error[E0390]: cannot define inherent `impl` for primitive types + --> $DIR/query-cycle-oom.rs:50:5 + | +LL | impl () { + | ^^^^^^^ + | + = help: consider using an extension trait instead + +error[E0433]: cannot find module or crate `to_reuse` in this scope + --> $DIR/query-cycle-oom.rs:16:15 + | +LL | reuse to_reuse::foo { &self.0 } + | ^^^^^^^^ use of unresolved module or unlinked crate `to_reuse` + | + = help: you might be missing a crate named `to_reuse` + +error[E0061]: this function takes 0 arguments but 1 argument was supplied + --> $DIR/query-cycle-oom.rs:23:12 + | +LL | fn foo() -> Self::Assoc; + | ^^^ +... +LL | reuse Trait::* { &self.0 } + | ------- unexpected argument + | +note: associated function defined here + --> $DIR/query-cycle-oom.rs:23:12 + | +LL | fn foo() -> Self::Assoc; + | ^^^ +help: remove the extra argument + | +LL - fn foo() -> Self::Assoc; +LL - +LL - +LL - fn bar(&self) -> u8; +LL - } +LL - +LL - impl Trait for u8 { +LL - +LL - fn bar(&self) -> u8 { 1 } +LL - } +LL - +LL - struct S(u8); +LL - +LL - impl Trait for S { +LL - reuse Trait::* { &self.0 } +LL + fn fo&self.0 } + | + +error[E0599]: no method named `foo` found for reference `&()` in the current scope + --> $DIR/query-cycle-oom.rs:44:12 + | +LL | fn foo(&self) -> Self::Assoc<3> { + | ^^^ method not found in `&()` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following traits define an item `foo`, perhaps you need to implement one of them: + candidate #1: `test_1::Trait` + candidate #2: `test_2::Trait` + candidate #3: `test_3::Trait` + candidate #4: `test_4::Trait` + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0046, E0061, E0107, E0121, E0220, E0390, E0433, E0599. +For more information about an error, try `rustc --explain E0046`. From 297f41a9b81ae3f295605c3b2df55970063ab348 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Wed, 15 Apr 2026 16:08:56 +0300 Subject: [PATCH 02/10] Fix tests --- .../incremental/delegation/impl-reuse-pass.rs | 25 -------------- tests/ui/delegation/query-cycle-oom.rs | 4 +-- tests/ui/delegation/query-cycle-oom.stderr | 33 +++++-------------- 3 files changed, 10 insertions(+), 52 deletions(-) diff --git a/tests/incremental/delegation/impl-reuse-pass.rs b/tests/incremental/delegation/impl-reuse-pass.rs index 2254225fc544f..483bcfd1d09b4 100644 --- a/tests/incremental/delegation/impl-reuse-pass.rs +++ b/tests/incremental/delegation/impl-reuse-pass.rs @@ -174,19 +174,6 @@ mod macros { macro_rules! m { () => { M } } reuse impl Trait for m!() { self_0_ref!(self) } - struct S1(u8); - macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } - one_line_reuse!(self); - - struct S2(u8); - macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } - one_line_reuse_expr!(self.0); - - struct S3(u8); - macro_rules! s3 { () => { S3 } } - macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } - one_line_reuse_expr2!(self.0); - fn f() { let s = S(1); s.foo(); @@ -195,18 +182,6 @@ mod macros { let m = M(41); m.foo(); m.bar(); - - let s1 = S1(2); - s1.foo(); - s1.bar(); - - let s2 = S2(4); - s2.foo(); - s2.bar(); - - let s3 = S3(5); - s3.foo(); - s3.bar(); } } diff --git a/tests/ui/delegation/query-cycle-oom.rs b/tests/ui/delegation/query-cycle-oom.rs index ec25cb8765a13..c6c8ec5be7984 100644 --- a/tests/ui/delegation/query-cycle-oom.rs +++ b/tests/ui/delegation/query-cycle-oom.rs @@ -22,7 +22,6 @@ mod test_2 { trait Trait { fn foo() -> Self::Assoc; //~^ ERROR: associated type `Assoc` not found for `Self` - //~| ERROR: this function takes 0 arguments but 1 argument was supplied fn bar(&self) -> u8; } @@ -35,6 +34,7 @@ mod test_2 { impl Trait for S { reuse Trait::* { &self.0 } + //~^ ERROR: this function takes 0 arguments but 1 argument was supplied fn bar(&self) -> u8 { 2 } } } @@ -42,13 +42,13 @@ mod test_2 { mod test_3 { trait Trait { fn foo(&self) -> Self::Assoc<3> { //~ ERROR: associated type `Assoc` not found for `Self` - //~^ ERROR: no method named `foo` found for reference `&()` in the current scope [(); 3] } } impl () { //~ ERROR: cannot define inherent `impl` for primitive types reuse Trait::*; + //~^ ERROR: no method named `foo` found for reference `&()` in the current scope } } diff --git a/tests/ui/delegation/query-cycle-oom.stderr b/tests/ui/delegation/query-cycle-oom.stderr index 01eee02de0957..1104a6e404b26 100644 --- a/tests/ui/delegation/query-cycle-oom.stderr +++ b/tests/ui/delegation/query-cycle-oom.stderr @@ -49,7 +49,7 @@ LL | reuse Trait::<_>::foo:: as x; | ^ not allowed in type signatures error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/query-cycle-oom.rs:29:5 + --> $DIR/query-cycle-oom.rs:28:5 | LL | fn foo() -> Self::Assoc; | ------------------------ `foo` from trait @@ -58,7 +58,7 @@ LL | impl Trait for u8 { | ^^^^^^^^^^^^^^^^^ missing `foo` in implementation error[E0390]: cannot define inherent `impl` for primitive types - --> $DIR/query-cycle-oom.rs:50:5 + --> $DIR/query-cycle-oom.rs:49:5 | LL | impl () { | ^^^^^^^ @@ -74,13 +74,10 @@ LL | reuse to_reuse::foo { &self.0 } = help: you might be missing a crate named `to_reuse` error[E0061]: this function takes 0 arguments but 1 argument was supplied - --> $DIR/query-cycle-oom.rs:23:12 + --> $DIR/query-cycle-oom.rs:36:22 | -LL | fn foo() -> Self::Assoc; - | ^^^ -... LL | reuse Trait::* { &self.0 } - | ------- unexpected argument + | ^ ------- unexpected argument | note: associated function defined here --> $DIR/query-cycle-oom.rs:23:12 @@ -89,29 +86,15 @@ LL | fn foo() -> Self::Assoc; | ^^^ help: remove the extra argument | -LL - fn foo() -> Self::Assoc; -LL - -LL - -LL - fn bar(&self) -> u8; -LL - } -LL - -LL - impl Trait for u8 { -LL - -LL - fn bar(&self) -> u8 { 1 } -LL - } -LL - -LL - struct S(u8); -LL - -LL - impl Trait for S { LL - reuse Trait::* { &self.0 } -LL + fn fo&self.0 } +LL + reuse Trait::&self.0 } | error[E0599]: no method named `foo` found for reference `&()` in the current scope - --> $DIR/query-cycle-oom.rs:44:12 + --> $DIR/query-cycle-oom.rs:50:22 | -LL | fn foo(&self) -> Self::Assoc<3> { - | ^^^ method not found in `&()` +LL | reuse Trait::*; + | ^ method not found in `&()` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `foo`, perhaps you need to implement one of them: From d9bd3a66555ca53d32570b74f01c6ae9d68e8900 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 12:50:02 +0300 Subject: [PATCH 03/10] Integrate force_delayed_owners_lowering call in front of hir_crate_items --- compiler/rustc_interface/src/passes.rs | 2 - compiler/rustc_macros/src/query.rs | 10 ++++- compiler/rustc_middle/src/queries.rs | 1 + compiler/rustc_middle/src/query/modifiers.rs | 3 ++ compiler/rustc_middle/src/query/plumbing.rs | 3 ++ compiler/rustc_query_impl/src/callback.rs | 5 +++ .../rustc_query_impl/src/dep_kind_vtables.rs | 1 + compiler/rustc_query_impl/src/execution.rs | 41 ++++++++++++++----- compiler/rustc_query_impl/src/lib.rs | 1 + compiler/rustc_query_impl/src/query_impl.rs | 9 ++++ 10 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 compiler/rustc_query_impl/src/callback.rs diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 24aafb80aa0fc..23a055e1e26f4 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1053,8 +1053,6 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { /// Runs all analyses that we guarantee to run, even if errors were reported in earlier analyses. /// This function never fails. fn run_required_analyses(tcx: TyCtxt<'_>) { - tcx.force_delayed_owners_lowering(); - if tcx.sess.opts.unstable_opts.input_stats { rustc_passes::input_stats::print_hir_stats(tcx); } diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 3880894572c93..9a4a203ed78a1 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -137,6 +137,7 @@ struct Desc { struct QueryModifiers { // tidy-alphabetical-start arena_cache: Option, + sandbox_callfront: Option, cache_on_disk: Option, depth_limit: Option, desc: Desc, @@ -152,6 +153,7 @@ struct QueryModifiers { fn parse_query_modifiers(input: ParseStream<'_>) -> Result { // tidy-alphabetical-start let mut arena_cache = None; + let mut sandbox_callfront = None; let mut cache_on_disk = None; let mut depth_limit = None; let mut desc = None; @@ -177,6 +179,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { if modifier == "arena_cache" { try_insert!(arena_cache = modifier); + } else if modifier == "sandbox_callfront" { + try_insert!(sandbox_callfront = modifier); } else if modifier == "cache_on_disk" { try_insert!(cache_on_disk = modifier); } else if modifier == "depth_limit" { @@ -210,6 +214,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { Ok(QueryModifiers { // tidy-alphabetical-start arena_cache, + sandbox_callfront, cache_on_disk, depth_limit, desc, @@ -246,6 +251,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let QueryModifiers { // tidy-alphabetical-start arena_cache, + sandbox_callfront, cache_on_disk, depth_limit, desc, @@ -260,6 +266,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // tidy-alphabetical-start let arena_cache = arena_cache.is_some(); + let sandbox_callfront = sandbox_callfront.is_some(); let cache_on_disk = cache_on_disk.is_some(); let depth_limit = depth_limit.is_some(); let desc = { @@ -284,7 +291,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // tidy-alphabetical-end // Giving an input span to the modifier names in the modifier list seems - // to give slightly more helpful errors when one of the callback macros + // to give slightly more helpful errors when one of the sandbox_callfront macros // fails to parse the modifier list. let query_name_span = query.name.span(); quote_spanned! { @@ -292,6 +299,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // Search for (QMODLIST) to find all occurrences of this query modifier list. // tidy-alphabetical-start arena_cache: #arena_cache, + sandbox_callfront: #sandbox_callfront, cache_on_disk: #cache_on_disk, depth_limit: #depth_limit, desc: #desc, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index dd36cbf1b8f6f..7cc73a8095c51 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -225,6 +225,7 @@ rustc_queries! { query hir_crate_items(_: ()) -> &'tcx rustc_middle::hir::ModuleItems { arena_cache eval_always + sandbox_callfront desc { "getting HIR crate items" } } diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 4fd91caa94cd7..c179589ed46e1 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -66,6 +66,9 @@ pub(crate) struct feedable; /// where `$name` is the query name. pub(crate) struct handle_cycle_error; +/// # `sandbox_callfront` query modifier +pub(crate) struct sandbox_callfront; + /// # `no_force` query modifier /// /// Dep nodes of queries with this modifier will never be "forced" when trying diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 7bb56715f6907..88fd93b835fb3 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -133,6 +133,8 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// /// [^1]: [`TyCtxt`], [`TyCtxtAt`], [`TyCtxtEnsureOk`], [`TyCtxtEnsureDone`] pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, + + pub sandbox_callfront_fn: Option) -> ()>, } impl QueryVTable<'_, C> { @@ -320,6 +322,7 @@ macro_rules! define_callbacks { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, + sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, diff --git a/compiler/rustc_query_impl/src/callback.rs b/compiler/rustc_query_impl/src/callback.rs new file mode 100644 index 0000000000000..7cec4f7194edc --- /dev/null +++ b/compiler/rustc_query_impl/src/callback.rs @@ -0,0 +1,5 @@ +use rustc_middle::ty::TyCtxt; + +pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>) { + tcx.force_delayed_owners_lowering(); +} diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index d12db3784f711..9eaf340e733be 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -133,6 +133,7 @@ macro_rules! define_dep_kind_vtables { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, + sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 22767a8c2b56d..802633213dc01 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -277,6 +277,20 @@ fn wait_for_query<'tcx, C: QueryCache>( } } +fn execute_query_and_callback<'tcx, R, C: QueryCache>( + tcx: TyCtxt<'tcx>, + query: &'tcx QueryVTable<'tcx, C>, + action: impl FnOnce() -> R, +) -> R { + if let Some(callback_fn) = query.sandbox_callfront_fn + && !is_sandbox() + { + (callback_fn)(tcx); + } + + action() +} + /// Shared main part of both [`execute_query_incr_inner`] and [`execute_query_non_incr_inner`]. #[inline(never)] fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( @@ -625,7 +639,9 @@ pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>( span: Span, key: C::Key, ) -> C::Value { - ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) + execute_query_and_callback(tcx, query, || { + ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) + }) } /// Called by a macro-generated impl of [`QueryVTable::execute_query_fn`], @@ -647,13 +663,16 @@ pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>( return None; } - let (result, dep_node_index) = ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, span, key, Some(dep_node)) - }); - if let Some(dep_node_index) = dep_node_index { - tcx.dep_graph.read_index(dep_node_index) - } - Some(result) + execute_query_and_callback(tcx, query, || { + let (result, dep_node_index) = ensure_sufficient_stack(|| { + try_execute_query::(query, tcx, span, key, Some(dep_node)) + }); + if let Some(dep_node_index) = dep_node_index { + tcx.dep_graph.read_index(dep_node_index) + } + + Some(result) + }) } /// Inner implementation of [`DepKindVTable::force_from_dep_node_fn`][force_fn] @@ -671,8 +690,10 @@ pub(crate) fn force_query_dep_node<'tcx, C: QueryCache>( return false; }; - ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) + execute_query_and_callback(tcx, query, || { + ensure_sufficient_stack(|| { + try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) + }); }); // We did manage to recover a key and force the node, though it's up to diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index d5c0fef0b4639..29e30f2748d82 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -19,6 +19,7 @@ pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs}; pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack}; use crate::query_impl::for_each_query_vtable; +mod callback; mod dep_kind_vtables; mod error; mod execution; diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 4425acc6b86b8..8e252112fb100 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -17,6 +17,7 @@ macro_rules! define_queries { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, + sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, @@ -200,6 +201,14 @@ macro_rules! define_queries { } else { crate::query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace }, + + #[cfg($sandbox_callfront)] + sandbox_callfront_fn: Some(|tcx| { + $crate::callback::$name(tcx); + }), + + #[cfg(not($sandbox_callfront))] + sandbox_callfront_fn: None, } } From 7fcb6e9b275efcab3d19b578b3aef4ffaa396977 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 13:29:00 +0300 Subject: [PATCH 04/10] Fix alphabetical --- compiler/rustc_macros/src/query.rs | 16 ++++++++-------- compiler/rustc_middle/src/query/modifiers.rs | 6 +++--- compiler/rustc_middle/src/query/plumbing.rs | 2 +- .../rustc_query_impl/src/dep_kind_vtables.rs | 2 +- compiler/rustc_query_impl/src/query_impl.rs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 9a4a203ed78a1..7951144fd78b2 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -137,7 +137,6 @@ struct Desc { struct QueryModifiers { // tidy-alphabetical-start arena_cache: Option, - sandbox_callfront: Option, cache_on_disk: Option, depth_limit: Option, desc: Desc, @@ -146,6 +145,7 @@ struct QueryModifiers { handle_cycle_error: Option, no_force: Option, no_hash: Option, + sandbox_callfront: Option, separate_provide_extern: Option, // tidy-alphabetical-end } @@ -153,7 +153,6 @@ struct QueryModifiers { fn parse_query_modifiers(input: ParseStream<'_>) -> Result { // tidy-alphabetical-start let mut arena_cache = None; - let mut sandbox_callfront = None; let mut cache_on_disk = None; let mut depth_limit = None; let mut desc = None; @@ -162,6 +161,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut handle_cycle_error = None; let mut no_force = None; let mut no_hash = None; + let mut sandbox_callfront = None; let mut separate_provide_extern = None; // tidy-alphabetical-end @@ -179,8 +179,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { if modifier == "arena_cache" { try_insert!(arena_cache = modifier); - } else if modifier == "sandbox_callfront" { - try_insert!(sandbox_callfront = modifier); } else if modifier == "cache_on_disk" { try_insert!(cache_on_disk = modifier); } else if modifier == "depth_limit" { @@ -202,6 +200,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(no_force = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); + } else if modifier == "sandbox_callfront" { + try_insert!(sandbox_callfront = modifier); } else if modifier == "separate_provide_extern" { try_insert!(separate_provide_extern = modifier); } else { @@ -214,7 +214,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { Ok(QueryModifiers { // tidy-alphabetical-start arena_cache, - sandbox_callfront, cache_on_disk, depth_limit, desc, @@ -223,6 +222,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { handle_cycle_error, no_force, no_hash, + sandbox_callfront, separate_provide_extern, // tidy-alphabetical-end }) @@ -251,7 +251,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let QueryModifiers { // tidy-alphabetical-start arena_cache, - sandbox_callfront, cache_on_disk, depth_limit, desc, @@ -260,13 +259,13 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { handle_cycle_error, no_force, no_hash, + sandbox_callfront, separate_provide_extern, // tidy-alphabetical-end } = &query.modifiers; // tidy-alphabetical-start let arena_cache = arena_cache.is_some(); - let sandbox_callfront = sandbox_callfront.is_some(); let cache_on_disk = cache_on_disk.is_some(); let depth_limit = depth_limit.is_some(); let desc = { @@ -287,6 +286,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let no_force = no_force.is_some(); let no_hash = no_hash.is_some(); let returns_error_guaranteed = returns_error_guaranteed(&query.return_ty); + let sandbox_callfront = sandbox_callfront.is_some(); let separate_provide_extern = separate_provide_extern.is_some(); // tidy-alphabetical-end @@ -299,7 +299,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // Search for (QMODLIST) to find all occurrences of this query modifier list. // tidy-alphabetical-start arena_cache: #arena_cache, - sandbox_callfront: #sandbox_callfront, cache_on_disk: #cache_on_disk, depth_limit: #depth_limit, desc: #desc, @@ -309,6 +308,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { no_force: #no_force, no_hash: #no_hash, returns_error_guaranteed: #returns_error_guaranteed, + sandbox_callfront: #sandbox_callfront, separate_provide_extern: #separate_provide_extern, // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index c179589ed46e1..d636afb2d6e9d 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -66,9 +66,6 @@ pub(crate) struct feedable; /// where `$name` is the query name. pub(crate) struct handle_cycle_error; -/// # `sandbox_callfront` query modifier -pub(crate) struct sandbox_callfront; - /// # `no_force` query modifier /// /// Dep nodes of queries with this modifier will never be "forced" when trying @@ -85,6 +82,9 @@ pub(crate) struct no_force; /// recomputed, always mark its node as red (dirty). pub(crate) struct no_hash; +/// # `sandbox_callfront` query modifier +pub(crate) struct sandbox_callfront; + /// # `separate_provide_extern` query modifier /// /// Use separate query provider functions for local and extern crates. diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 88fd93b835fb3..79f6e7cfb7f2d 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -322,7 +322,6 @@ macro_rules! define_callbacks { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, - sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, @@ -332,6 +331,7 @@ macro_rules! define_callbacks { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, + sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 9eaf340e733be..33488b1896959 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -133,7 +133,6 @@ macro_rules! define_dep_kind_vtables { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, - sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, @@ -143,6 +142,7 @@ macro_rules! define_dep_kind_vtables { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, + sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 8e252112fb100..1c2c05def9991 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -17,7 +17,6 @@ macro_rules! define_queries { { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, - sandbox_callfront: $sandbox_callfront:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, @@ -27,6 +26,7 @@ macro_rules! define_queries { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, + sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* From c27eac90210aebd7f19a7544968c9c4ebe3fca3b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 14:02:13 +0300 Subject: [PATCH 05/10] Remove is_sandbox from ImplicitCtxt, use atomics instead --- compiler/rustc_middle/src/dep_graph/graph.rs | 30 ++++++++++++++++---- compiler/rustc_middle/src/hir/map.rs | 3 +- compiler/rustc_middle/src/hir/mod.rs | 20 ++++++------- compiler/rustc_middle/src/query/inner.rs | 3 +- compiler/rustc_middle/src/ty/context.rs | 19 +++++++++++-- compiler/rustc_middle/src/ty/context/tls.rs | 14 +-------- compiler/rustc_query_impl/src/execution.rs | 5 ++-- 7 files changed, 55 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 0e4ebb9a6f797..05cd54e7d6be7 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -2,7 +2,7 @@ use std::assert_matches; use std::fmt::Debug; use std::hash::Hash; use std::sync::Arc; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -28,7 +28,6 @@ use super::{DepKind, DepNode, WorkProductId, read_deps, with_deps}; use crate::dep_graph::edges::EdgesVec; use crate::ich::StableHashingContext; use crate::ty::TyCtxt; -use crate::ty::tls::is_sandbox; use crate::verify_ich::incremental_verify_ich; /// Tracks 'side effects' for a particular query. @@ -56,6 +55,7 @@ pub enum QuerySideEffect { #[derive(Clone)] pub struct DepGraph { + is_in_sandbox: Arc, data: Option>, /// This field is used for assigning DepNodeIndices when running in @@ -179,16 +179,26 @@ impl DepGraph { debug_loaded_from_disk: Default::default(), })), virtual_dep_node_index: Arc::new(AtomicU32::new(0)), + is_in_sandbox: Default::default(), } } pub fn new_disabled() -> DepGraph { - DepGraph { data: None, virtual_dep_node_index: Arc::new(AtomicU32::new(0)) } + DepGraph { + data: None, + virtual_dep_node_index: Arc::new(AtomicU32::new(0)), + is_in_sandbox: Default::default(), + } } #[inline] pub fn data(&self) -> Option<&DepGraphData> { - self.data.as_deref().filter(|_| !is_sandbox()) + self.data.as_deref().filter(|_| !self.is_in_sandbox()) + } + + #[inline] + pub fn is_in_sandbox(&self) -> bool { + self.is_in_sandbox.load(Ordering::Relaxed) } /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. @@ -275,6 +285,16 @@ impl DepGraph { with_deps(TaskDepsRef::Forbid, op) } + pub fn with_sandbox(&self, op: impl FnOnce()) { + self.with_query_deserialization(|| { + self.is_in_sandbox.store(true, Ordering::Relaxed); + + op(); + + self.is_in_sandbox.store(false, Ordering::Relaxed); + }); + } + #[inline(always)] pub fn with_task<'tcx, OP, R>( &self, @@ -1026,7 +1046,7 @@ impl DepGraph { } pub fn next_virtual_depnode_index(&self) -> DepNodeIndex { - debug_assert!(self.data().is_none() || is_sandbox()); + debug_assert!(self.data().is_none()); let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed); DepNodeIndex::from_u32(index) } diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 78e511dac3c82..f12860235dd9e 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -21,7 +21,6 @@ use crate::hir::{ModuleItems, nested_filter}; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::query::LocalCrate; use crate::ty::TyCtxt; -use crate::ty::tls::is_sandbox; /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir_parent_iter(hir_id)`. @@ -1336,7 +1335,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { } fn visit_if_delayed(&self, _: LocalDefId) -> bool { - !is_sandbox() + !self.tcx.is_in_sandbox() } fn visit_item(&mut self, item: &'hir Item<'hir>) { diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ef111985cfdd5..6bb35f13f9713 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -25,7 +25,6 @@ use rustc_span::{ErrorGuaranteed, ExpnId, HashStableContext, Span}; use crate::query::Providers; use crate::ty::print::with_no_trimmed_paths; -use crate::ty::tls::{ImplicitCtxt, enter_context, with_context}; use crate::ty::{ResolverAstLowering, TyCtxt}; /// The top-level data structure that stores the entire contents of @@ -210,19 +209,16 @@ impl ModuleItems { impl<'tcx> TyCtxt<'tcx> { fn sandbox(self, action: impl FnOnce() -> ()) { - with_context(|icx| { - let icx = ImplicitCtxt { is_sandbox: true, ..*icx }; - enter_context(&icx, || { - self.enter_query_sandbox(); - - self.dep_graph.with_query_deserialization(|| { - with_no_trimmed_paths!({ - action(); - }); + self.with_sandbox(|| { + self.enter_query_sandbox(); + + self.dep_graph.with_sandbox(|| { + with_no_trimmed_paths!({ + action(); }); + }); - self.leave_query_sandbox(); - }) + self.leave_query_sandbox(); }); } diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index a04fb5ba83fc2..c569faad9bba1 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -8,7 +8,6 @@ use crate::dep_graph::DepNodeKey; use crate::query::erase::{self, Erasable, Erased}; use crate::query::{EnsureMode, QueryCache, QueryMode, QueryVTable}; use crate::ty::TyCtxt; -use crate::ty::tls::is_sandbox; /// Checks whether there is already a value for this key in the in-memory /// query cache, returning that value if present. @@ -21,7 +20,7 @@ where { match cache.lookup(&key) { Some((value, index)) => { - if !is_sandbox() { + if !tcx.is_in_sandbox() { tcx.prof.query_cache_hit(index.into()); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8c96eefc8c70b..d588536d5ff13 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -12,6 +12,7 @@ use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::marker::{PhantomData, PointeeSized}; use std::ops::{Bound, Deref}; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, OnceLock}; use std::{fmt, iter, mem}; @@ -66,7 +67,6 @@ use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData, PredefinedOpaques}; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; -use crate::ty::tls::is_sandbox; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, Pattern, @@ -798,6 +798,7 @@ pub struct GlobalCtxt<'tcx> { /// be called from rustc_middle. pub(crate) hooks: crate::hooks::Providers, + is_in_sandbox: AtomicBool, untracked: Untracked, pub query_system: QuerySystem<'tcx>, @@ -897,6 +898,19 @@ impl CurrentGcx { } impl<'tcx> TyCtxt<'tcx> { + #[inline] + pub fn is_in_sandbox(self) -> bool { + self.is_in_sandbox.load(std::sync::atomic::Ordering::Relaxed) + } + + pub fn with_sandbox(self, op: impl FnOnce()) { + self.is_in_sandbox.store(true, std::sync::atomic::Ordering::Relaxed); + + op(); + + self.is_in_sandbox.store(false, std::sync::atomic::Ordering::Relaxed); + } + pub fn has_typeck_results(self, def_id: LocalDefId) -> bool { // Closures' typeck results come from their outermost function, // as they are part of the same "inference environment". @@ -1048,6 +1062,7 @@ impl<'tcx> TyCtxt<'tcx> { types: common_types, lifetimes: common_lifetimes, consts: common_consts, + is_in_sandbox: Default::default(), untracked, query_system, dep_kind_vtables, @@ -1387,7 +1402,7 @@ impl<'tcx> TyCtxt<'tcx> { // As a consequence, this LocalDefId is always re-created before it is needed by the incr. // comp. engine itself. let def_id = self.untracked.definitions.write().create_def(parent, data, disambiguator); - if is_sandbox() { + if self.is_in_sandbox() { self.untracked.definitions.write().add_sandbox_def_id(def_id); } diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs index 8d20a1512a5e1..d1561c37172c3 100644 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ b/compiler/rustc_middle/src/ty/context/tls.rs @@ -22,20 +22,12 @@ pub struct ImplicitCtxt<'a, 'tcx> { /// The current dep graph task. This is used to add dependencies to queries /// when executing them. pub task_deps: TaskDepsRef<'a>, - - pub is_sandbox: bool, } impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { let tcx = TyCtxt { gcx }; - ImplicitCtxt { - tcx, - query: None, - query_depth: 0, - task_deps: TaskDepsRef::Ignore, - is_sandbox: false, - } + ImplicitCtxt { tcx, query: None, query_depth: 0, task_deps: TaskDepsRef::Ignore } } } @@ -65,10 +57,6 @@ where }) } -pub fn is_sandbox() -> bool { - with_context_opt(|ctx| ctx.map(|c| c.is_sandbox).unwrap_or_default()) -} - /// Allows access to the current `ImplicitCtxt` in a closure if one is available. #[inline] #[track_caller] diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 802633213dc01..3f6bafa03540f 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -12,7 +12,6 @@ use rustc_middle::query::{ QueryMode, QueryState, QueryVTable, }; use rustc_middle::ty::TyCtxt; -use rustc_middle::ty::tls::is_sandbox; use rustc_middle::verify_ich::incremental_verify_ich; use rustc_span::{DUMMY_SP, Span}; use tracing::warn; @@ -283,7 +282,7 @@ fn execute_query_and_callback<'tcx, R, C: QueryCache>( action: impl FnOnce() -> R, ) -> R { if let Some(callback_fn) = query.sandbox_callfront_fn - && !is_sandbox() + && !tcx.is_in_sandbox() { (callback_fn)(tcx); } @@ -335,7 +334,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // Delegate to another function to actually execute the query job. let (value, dep_node_index) = if INCR { - if is_sandbox() { + if tcx.is_in_sandbox() { execute_job_non_incr(query, tcx, key, id) } else { execute_job_incr(query, tcx, key, dep_node.unwrap(), id) From 9e7856cf6d70d81125e9fa6cdb0c804c28d9d208 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 14:30:59 +0300 Subject: [PATCH 06/10] sanbox_callfront -> callfront --- compiler/rustc_macros/src/query.rs | 18 +++++++++--------- compiler/rustc_middle/src/queries.rs | 2 +- compiler/rustc_middle/src/query/modifiers.rs | 6 +++--- compiler/rustc_middle/src/query/plumbing.rs | 4 ++-- .../rustc_query_impl/src/dep_kind_vtables.rs | 2 +- compiler/rustc_query_impl/src/execution.rs | 7 +------ compiler/rustc_query_impl/src/query_impl.rs | 10 +++++----- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 7951144fd78b2..7b191b170be40 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -138,6 +138,7 @@ struct QueryModifiers { // tidy-alphabetical-start arena_cache: Option, cache_on_disk: Option, + callfront: Option, depth_limit: Option, desc: Desc, eval_always: Option, @@ -145,7 +146,6 @@ struct QueryModifiers { handle_cycle_error: Option, no_force: Option, no_hash: Option, - sandbox_callfront: Option, separate_provide_extern: Option, // tidy-alphabetical-end } @@ -154,6 +154,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { // tidy-alphabetical-start let mut arena_cache = None; let mut cache_on_disk = None; + let mut callfront = None; let mut depth_limit = None; let mut desc = None; let mut eval_always = None; @@ -161,7 +162,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut handle_cycle_error = None; let mut no_force = None; let mut no_hash = None; - let mut sandbox_callfront = None; let mut separate_provide_extern = None; // tidy-alphabetical-end @@ -181,6 +181,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(arena_cache = modifier); } else if modifier == "cache_on_disk" { try_insert!(cache_on_disk = modifier); + } else if modifier == "callfront" { + try_insert!(callfront = modifier); } else if modifier == "depth_limit" { try_insert!(depth_limit = modifier); } else if modifier == "desc" { @@ -200,8 +202,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { try_insert!(no_force = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); - } else if modifier == "sandbox_callfront" { - try_insert!(sandbox_callfront = modifier); } else if modifier == "separate_provide_extern" { try_insert!(separate_provide_extern = modifier); } else { @@ -215,6 +215,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { // tidy-alphabetical-start arena_cache, cache_on_disk, + callfront, depth_limit, desc, eval_always, @@ -222,7 +223,6 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { handle_cycle_error, no_force, no_hash, - sandbox_callfront, separate_provide_extern, // tidy-alphabetical-end }) @@ -252,6 +252,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // tidy-alphabetical-start arena_cache, cache_on_disk, + callfront, depth_limit, desc, eval_always, @@ -259,7 +260,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { handle_cycle_error, no_force, no_hash, - sandbox_callfront, separate_provide_extern, // tidy-alphabetical-end } = &query.modifiers; @@ -267,6 +267,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // tidy-alphabetical-start let arena_cache = arena_cache.is_some(); let cache_on_disk = cache_on_disk.is_some(); + let callfront = callfront.is_some(); let depth_limit = depth_limit.is_some(); let desc = { // Put a description closure in the `desc` modifier. @@ -286,12 +287,11 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let no_force = no_force.is_some(); let no_hash = no_hash.is_some(); let returns_error_guaranteed = returns_error_guaranteed(&query.return_ty); - let sandbox_callfront = sandbox_callfront.is_some(); let separate_provide_extern = separate_provide_extern.is_some(); // tidy-alphabetical-end // Giving an input span to the modifier names in the modifier list seems - // to give slightly more helpful errors when one of the sandbox_callfront macros + // to give slightly more helpful errors when one of the callfront macros // fails to parse the modifier list. let query_name_span = query.name.span(); quote_spanned! { @@ -300,6 +300,7 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { // tidy-alphabetical-start arena_cache: #arena_cache, cache_on_disk: #cache_on_disk, + callfront: #callfront, depth_limit: #depth_limit, desc: #desc, eval_always: #eval_always, @@ -308,7 +309,6 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { no_force: #no_force, no_hash: #no_hash, returns_error_guaranteed: #returns_error_guaranteed, - sandbox_callfront: #sandbox_callfront, separate_provide_extern: #separate_provide_extern, // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 7cc73a8095c51..cc8ef1def52cc 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -225,7 +225,7 @@ rustc_queries! { query hir_crate_items(_: ()) -> &'tcx rustc_middle::hir::ModuleItems { arena_cache eval_always - sandbox_callfront + callfront desc { "getting HIR crate items" } } diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index d636afb2d6e9d..fb646db244bb6 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -28,6 +28,9 @@ pub(crate) struct arena_cache; /// be loadable from crate metadata instead. pub(crate) struct cache_on_disk; +/// # `callfront` query modifier +pub(crate) struct callfront; + /// # `depth_limit` query modifier /// /// Impose a recursion call depth limit on the query to prevent stack overflow. @@ -82,9 +85,6 @@ pub(crate) struct no_force; /// recomputed, always mark its node as red (dirty). pub(crate) struct no_hash; -/// # `sandbox_callfront` query modifier -pub(crate) struct sandbox_callfront; - /// # `separate_provide_extern` query modifier /// /// Use separate query provider functions for local and extern crates. diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 79f6e7cfb7f2d..b6d3f23995685 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -134,7 +134,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// [^1]: [`TyCtxt`], [`TyCtxtAt`], [`TyCtxtEnsureOk`], [`TyCtxtEnsureDone`] pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, - pub sandbox_callfront_fn: Option) -> ()>, + pub callfront_fn: Option) -> ()>, } impl QueryVTable<'_, C> { @@ -323,6 +323,7 @@ macro_rules! define_callbacks { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, + callfront: $callfront:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, eval_always: $eval_always:literal, @@ -331,7 +332,6 @@ macro_rules! define_callbacks { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, - sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 33488b1896959..4bb01bd422ecd 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -134,6 +134,7 @@ macro_rules! define_dep_kind_vtables { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, + callfront: $callfront:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, eval_always: $eval_always:literal, @@ -142,7 +143,6 @@ macro_rules! define_dep_kind_vtables { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, - sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 3f6bafa03540f..3b1c97ebc2d94 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -281,12 +281,7 @@ fn execute_query_and_callback<'tcx, R, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, action: impl FnOnce() -> R, ) -> R { - if let Some(callback_fn) = query.sandbox_callfront_fn - && !tcx.is_in_sandbox() - { - (callback_fn)(tcx); - } - + query.callfront_fn.filter(|_| !tcx.is_in_sandbox()).inspect(|f| (f)(tcx)); action() } diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 1c2c05def9991..25c8b93ea2cef 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -18,6 +18,7 @@ macro_rules! define_queries { // Search for (QMODLIST) to find all occurrences of this query modifier list. arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, + callfront: $callfront:literal, depth_limit: $depth_limit:literal, desc: $desc:expr, eval_always: $eval_always:literal, @@ -26,7 +27,6 @@ macro_rules! define_queries { no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, - sandbox_callfront: $sandbox_callfront:literal, separate_provide_extern: $separate_provide_extern:literal, } )* @@ -202,13 +202,13 @@ macro_rules! define_queries { crate::query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace }, - #[cfg($sandbox_callfront)] - sandbox_callfront_fn: Some(|tcx| { + #[cfg($callfront)] + callfront_fn: Some(|tcx| { $crate::callback::$name(tcx); }), - #[cfg(not($sandbox_callfront))] - sandbox_callfront_fn: None, + #[cfg(not($callfront))] + callfront_fn: None, } } From 43f8a524230a57539df6668cc13b9f6d22de1b40 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 14:56:14 +0300 Subject: [PATCH 07/10] Invalidate untracked caches --- compiler/rustc_middle/src/infer/canonical.rs | 4 ++++ compiler/rustc_middle/src/traits/cache.rs | 4 ++++ compiler/rustc_middle/src/ty/context.rs | 9 +++++++++ compiler/rustc_type_ir/src/search_graph/global_cache.rs | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 32c6b6e9c0ba1..02f86f62113f0 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -201,4 +201,8 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { } } } + + pub fn clear(&self) { + self.map.borrow_mut().clear(); + } } diff --git a/compiler/rustc_middle/src/traits/cache.rs b/compiler/rustc_middle/src/traits/cache.rs index 9391764bf1ce2..c4d00e73fe711 100644 --- a/compiler/rustc_middle/src/traits/cache.rs +++ b/compiler/rustc_middle/src/traits/cache.rs @@ -32,6 +32,10 @@ impl WithDepNodeCache { pub fn insert(&self, key: Key, dep_node: DepNodeIndex, value: Value) { self.hashmap.borrow_mut().insert(key, WithDepNode::new(dep_node, value)); } + + pub fn clear(&self) { + self.hashmap.borrow_mut().clear(); + } } #[derive(Debug, Clone, Eq, PartialEq)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d588536d5ff13..1d39fa98c3e50 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -909,6 +909,15 @@ impl<'tcx> TyCtxt<'tcx> { op(); self.is_in_sandbox.store(false, std::sync::atomic::Ordering::Relaxed); + + self.clauses_cache.borrow_mut().clear(); + self.highest_var_in_clauses_cache.borrow_mut().clear(); + self.canonical_param_env_cache.clear(); + self.new_solver_canonical_param_env_cache.borrow_mut().clear(); + self.new_solver_evaluation_cache.borrow_mut().clear(); + self.evaluation_cache.clear(); + self.selection_cache.clear(); + self.ty_rcache.borrow_mut().clear(); } pub fn has_typeck_results(self, def_id: LocalDefId) -> bool { diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs index 7e438fefffca0..f263ffe9d9a0b 100644 --- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs +++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs @@ -107,4 +107,8 @@ impl GlobalCache { None } + + pub fn clear(&mut self) { + self.map.clear(); + } } From c515347b97a4e32abc02df773986d594d531f38b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 15:33:33 +0300 Subject: [PATCH 08/10] Fix incorrect behavior of create_def in sandbox --- compiler/rustc_hir/src/definitions.rs | 19 +++++++++++-------- compiler/rustc_middle/src/ty/context.rs | 10 ++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index d65f40be42b5f..cbac1c3c8209b 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -45,7 +45,7 @@ impl DefPathTable { } } - fn allocate(&mut self, key: DefKey, def_path_hash: DefPathHash) -> DefIndex { + fn allocate(&mut self, key: DefKey, def_path_hash: DefPathHash, in_sandbox: bool) -> DefIndex { // Assert that all DefPathHashes correctly contain the local crate's StableCrateId. debug_assert_eq!(self.stable_crate_id, def_path_hash.stable_crate_id()); let local_hash = def_path_hash.local_hash(); @@ -54,7 +54,7 @@ impl DefPathTable { // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = self.def_path_hash_to_index.insert(&local_hash, &index) { - if !self.allow_overwrite.is_empty() && self.allow_overwrite.swap_remove(&existing) { + if !in_sandbox && self.allow_overwrite.swap_remove(&existing) { self.def_path_hash_to_index.insert(&local_hash, &existing); return existing; } else { @@ -383,7 +383,7 @@ impl Definitions { // Create the root definition. let mut table = DefPathTable::new(stable_crate_id); - let root = LocalDefId { local_def_index: table.allocate(key, def_path_hash) }; + let root = LocalDefId { local_def_index: table.allocate(key, def_path_hash, false) }; assert_eq!(root.local_def_index, CRATE_DEF_INDEX); Definitions { table } @@ -399,6 +399,7 @@ impl Definitions { parent: LocalDefId, data: DefPathData, disambiguator: &mut DisambiguatorState, + in_sandbox: bool, ) -> LocalDefId { // We can't use `Debug` implementation for `LocalDefId` here, since it tries to acquire a // reference to `Definitions` and we're already holding a mutable reference. @@ -427,12 +428,14 @@ impl Definitions { debug!("create_def: after disambiguation, key = {:?}", key); - // Create the definition. - LocalDefId { local_def_index: self.table.allocate(key, def_path_hash) } - } + let local_def_index = self.table.allocate(key, def_path_hash, in_sandbox); - pub fn add_sandbox_def_id(&mut self, id: LocalDefId) { - assert_eq!(self.table.allow_overwrite.insert(id.local_def_index), true); + if in_sandbox { + assert_eq!(self.table.allow_overwrite.insert(local_def_index), true); + } + + // Create the definition. + LocalDefId { local_def_index } } #[inline(always)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1d39fa98c3e50..0a69c9c7f9e3c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1410,10 +1410,12 @@ impl<'tcx> TyCtxt<'tcx> { // - has been created by this call to `create_def`. // As a consequence, this LocalDefId is always re-created before it is needed by the incr. // comp. engine itself. - let def_id = self.untracked.definitions.write().create_def(parent, data, disambiguator); - if self.is_in_sandbox() { - self.untracked.definitions.write().add_sandbox_def_id(def_id); - } + let def_id = self.untracked.definitions.write().create_def( + parent, + data, + disambiguator, + self.is_in_sandbox(), + ); // This function modifies `self.definitions` using a side-effect. // We need to ensure that these side effects are re-run by the incr. comp. engine. From a36a0dc185b08c3a43c9df39e9b53a29f7165248 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 16 Apr 2026 16:39:50 +0300 Subject: [PATCH 09/10] Generate callfront calls mainly in macros --- .../src/{callback.rs => callfront.rs} | 0 compiler/rustc_query_impl/src/execution.rs | 36 ++++++------------- compiler/rustc_query_impl/src/lib.rs | 2 +- compiler/rustc_query_impl/src/query_impl.rs | 14 +++++++- 4 files changed, 25 insertions(+), 27 deletions(-) rename compiler/rustc_query_impl/src/{callback.rs => callfront.rs} (100%) diff --git a/compiler/rustc_query_impl/src/callback.rs b/compiler/rustc_query_impl/src/callfront.rs similarity index 100% rename from compiler/rustc_query_impl/src/callback.rs rename to compiler/rustc_query_impl/src/callfront.rs diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 3b1c97ebc2d94..cf0a1117fe006 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -276,15 +276,6 @@ fn wait_for_query<'tcx, C: QueryCache>( } } -fn execute_query_and_callback<'tcx, R, C: QueryCache>( - tcx: TyCtxt<'tcx>, - query: &'tcx QueryVTable<'tcx, C>, - action: impl FnOnce() -> R, -) -> R { - query.callfront_fn.filter(|_| !tcx.is_in_sandbox()).inspect(|f| (f)(tcx)); - action() -} - /// Shared main part of both [`execute_query_incr_inner`] and [`execute_query_non_incr_inner`]. #[inline(never)] fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( @@ -633,9 +624,7 @@ pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>( span: Span, key: C::Key, ) -> C::Value { - execute_query_and_callback(tcx, query, || { - ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) - }) + ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) } /// Called by a macro-generated impl of [`QueryVTable::execute_query_fn`], @@ -657,16 +646,14 @@ pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>( return None; } - execute_query_and_callback(tcx, query, || { - let (result, dep_node_index) = ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, span, key, Some(dep_node)) - }); - if let Some(dep_node_index) = dep_node_index { - tcx.dep_graph.read_index(dep_node_index) - } + let (result, dep_node_index) = ensure_sufficient_stack(|| { + try_execute_query::(query, tcx, span, key, Some(dep_node)) + }); + if let Some(dep_node_index) = dep_node_index { + tcx.dep_graph.read_index(dep_node_index) + } - Some(result) - }) + Some(result) } /// Inner implementation of [`DepKindVTable::force_from_dep_node_fn`][force_fn] @@ -684,10 +671,9 @@ pub(crate) fn force_query_dep_node<'tcx, C: QueryCache>( return false; }; - execute_query_and_callback(tcx, query, || { - ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) - }); + query.callfront_fn.filter(|_| !tcx.is_in_sandbox()).inspect(|f| (f)(tcx)); + ensure_sufficient_stack(|| { + try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) }); // We did manage to recover a key and force the node, though it's up to diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 29e30f2748d82..4df7ee42fc4ac 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -19,7 +19,7 @@ pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs}; pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack}; use crate::query_impl::for_each_query_vtable; -mod callback; +mod callfront; mod dep_kind_vtables; mod error; mod execution; diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 25c8b93ea2cef..0a82a0d1c49fb 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -57,6 +57,12 @@ macro_rules! define_queries { key: Key<'tcx>, mode: QueryMode, ) -> Option>> { + #[cfg($callfront)] + { + let vtable = &tcx.query_system.query_vtables.$name; + vtable.callfront_fn.filter(|_| !tcx.is_in_sandbox()).inspect(|f| (f)(tcx)); + } + #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); crate::execution::execute_query_incr_inner( @@ -80,6 +86,12 @@ macro_rules! define_queries { key: Key<'tcx>, __mode: QueryMode, ) -> Option>> { + #[cfg($callfront)] + { + let vtable = &tcx.query_system.query_vtables.$name; + vtable.callfront_fn.filter(|_| !tcx.is_in_sandbox()).inspect(|f| (f)(tcx)); + } + Some(crate::execution::execute_query_non_incr_inner( &tcx.query_system.query_vtables.$name, tcx, @@ -204,7 +216,7 @@ macro_rules! define_queries { #[cfg($callfront)] callfront_fn: Some(|tcx| { - $crate::callback::$name(tcx); + $crate::callfront::$name(tcx); }), #[cfg(not($callfront))] From 55865e2860d1d0a41a352727a3580c2ea9c107a6 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 17 Apr 2026 12:26:33 +0300 Subject: [PATCH 10/10] Small improvements --- compiler/rustc_middle/src/dep_graph/graph.rs | 29 +++++++++----------- compiler/rustc_middle/src/hir/mod.rs | 17 +----------- compiler/rustc_middle/src/query/caches.rs | 17 ++++++------ compiler/rustc_middle/src/query/inner.rs | 5 +--- compiler/rustc_middle/src/ty/context.rs | 18 ++++++++---- 5 files changed, 35 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index 05cd54e7d6be7..f2d074cd904bf 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -56,7 +56,7 @@ pub enum QuerySideEffect { #[derive(Clone)] pub struct DepGraph { is_in_sandbox: Arc, - data: Option>, + data: [Option>; 2], /// This field is used for assigning DepNodeIndices when running in /// non-incremental mode. Even in non-incremental mode we make sure that @@ -171,13 +171,16 @@ impl DepGraph { } DepGraph { - data: Some(Arc::new(DepGraphData { - previous_work_products: prev_work_products, - current, - previous: prev_graph, - colors, - debug_loaded_from_disk: Default::default(), - })), + data: [ + Some(Arc::new(DepGraphData { + previous_work_products: prev_work_products, + current, + previous: prev_graph, + colors, + debug_loaded_from_disk: Default::default(), + })), + None, + ], virtual_dep_node_index: Arc::new(AtomicU32::new(0)), is_in_sandbox: Default::default(), } @@ -185,20 +188,14 @@ impl DepGraph { pub fn new_disabled() -> DepGraph { DepGraph { - data: None, + data: [None, None], virtual_dep_node_index: Arc::new(AtomicU32::new(0)), is_in_sandbox: Default::default(), } } - #[inline] pub fn data(&self) -> Option<&DepGraphData> { - self.data.as_deref().filter(|_| !self.is_in_sandbox()) - } - - #[inline] - pub fn is_in_sandbox(&self) -> bool { - self.is_in_sandbox.load(Ordering::Relaxed) + self.data[self.is_in_sandbox.load(Ordering::Relaxed) as usize].as_deref() } /// Returns `true` if we are actually building the full dep-graph, and `false` otherwise. diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 6bb35f13f9713..dc0c255dac9ab 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -24,7 +24,6 @@ use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, HashStableContext, Span}; use crate::query::Providers; -use crate::ty::print::with_no_trimmed_paths; use crate::ty::{ResolverAstLowering, TyCtxt}; /// The top-level data structure that stores the entire contents of @@ -208,25 +207,11 @@ impl ModuleItems { } impl<'tcx> TyCtxt<'tcx> { - fn sandbox(self, action: impl FnOnce() -> ()) { - self.with_sandbox(|| { - self.enter_query_sandbox(); - - self.dep_graph.with_sandbox(|| { - with_no_trimmed_paths!({ - action(); - }); - }); - - self.leave_query_sandbox(); - }); - } - pub fn force_delayed_owners_lowering(self) { let krate = self.hir_crate(()); if !krate.delayed_ids.is_empty() { - self.sandbox(|| { + self.with_sandbox(|| { self.ensure_done().hir_crate_items(()); self.ensure_done().crate_inherent_impls(()); diff --git a/compiler/rustc_middle/src/query/caches.rs b/compiler/rustc_middle/src/query/caches.rs index c31d2bae93c6e..63585973aa788 100644 --- a/compiler/rustc_middle/src/query/caches.rs +++ b/compiler/rustc_middle/src/query/caches.rs @@ -1,6 +1,5 @@ -use std::sync::Mutex; - use rustc_data_structures::sharded::ShardedHashMap; +use rustc_data_structures::sync::Lock; pub use rustc_data_structures::vec_cache::VecCache; use rustc_hir::def_id::LOCAL_CRATE; use rustc_index::Idx; @@ -101,12 +100,12 @@ where /// In-memory cache for queries whose key type only has one value (e.g. `()`). /// The cache therefore only needs to store one query return value. pub struct SingleCache { - cache: Mutex>, + cache: Lock>, } impl Default for SingleCache { fn default() -> Self { - SingleCache { cache: Mutex::new(None) } + SingleCache { cache: Default::default() } } } @@ -119,27 +118,27 @@ where #[inline(always)] fn lookup(&self, _key: &()) -> Option<(V, DepNodeIndex)> { - self.cache.get_cloned().unwrap() + self.cache.lock().clone() } #[inline] fn complete(&self, _key: (), value: V, index: DepNodeIndex) { - self.cache.set(Some((value, index))).ok(); + *self.cache.lock() = Some((value, index)) } fn for_each(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { - if let Some(value) = self.cache.get_cloned().unwrap() { + if let Some(value) = self.cache.lock().clone() { f(&(), &value.0, value.1) } } fn len(&self) -> usize { - self.cache.get_cloned().unwrap().is_some().into() + self.cache.lock().is_some().into() } fn invalidate(&self, selector: impl Fn(Self::Key) -> bool) { if selector(()) { - self.cache.set(None).ok(); + *self.cache.lock() = None; } } } diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index c569faad9bba1..c18b995e4a5e8 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -20,10 +20,7 @@ where { match cache.lookup(&key) { Some((value, index)) => { - if !tcx.is_in_sandbox() { - tcx.prof.query_cache_hit(index.into()); - } - + tcx.prof.query_cache_hit(index.into()); tcx.dep_graph.read_index(index); Some(value) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0a69c9c7f9e3c..ff90236118a8d 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -12,7 +12,7 @@ use std::ffi::OsStr; use std::hash::{Hash, Hasher}; use std::marker::{PhantomData, PointeeSized}; use std::ops::{Bound, Deref}; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; use std::sync::{Arc, OnceLock}; use std::{fmt, iter, mem}; @@ -67,6 +67,7 @@ use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData, PredefinedOpaques}; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; +use crate::ty::print::with_no_trimmed_paths; use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, GenericArg, GenericArgs, GenericArgsRef, GenericParamDefKind, List, ListWithCachedTypeInfo, ParamConst, Pattern, @@ -898,17 +899,22 @@ impl CurrentGcx { } impl<'tcx> TyCtxt<'tcx> { - #[inline] pub fn is_in_sandbox(self) -> bool { - self.is_in_sandbox.load(std::sync::atomic::Ordering::Relaxed) + self.is_in_sandbox.load(AtomicOrdering::Relaxed) } pub fn with_sandbox(self, op: impl FnOnce()) { - self.is_in_sandbox.store(true, std::sync::atomic::Ordering::Relaxed); + self.is_in_sandbox.store(true, AtomicOrdering::Relaxed); + self.enter_query_sandbox(); - op(); + self.dep_graph.with_sandbox(|| { + with_no_trimmed_paths!({ + op(); + }); + }); - self.is_in_sandbox.store(false, std::sync::atomic::Ordering::Relaxed); + self.leave_query_sandbox(); + self.is_in_sandbox.store(false, AtomicOrdering::Relaxed); self.clauses_cache.borrow_mut().clear(); self.highest_var_in_clauses_cache.borrow_mut().clear();