From 71d656edde5b2e588a129951beef1c5c7dbe35e7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Feb 2026 17:41:51 +0300 Subject: [PATCH 01/18] LinkedGraph: Use IndexVec instead of Vec in LinkedGraph nodes --- .../src/graph/linked_graph/mod.rs | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs index ecb0095626b4a..6d53b155e9a2a 100644 --- a/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs @@ -23,6 +23,7 @@ use std::fmt::Debug; use rustc_index::bit_set::DenseBitSet; +use rustc_index::{Idx, IndexSlice, IndexVec}; use tracing::debug; #[cfg(test)] @@ -45,7 +46,7 @@ mod tests; /// and does not implement those traits, so it has its own implementations of a /// few basic graph algorithms. pub struct LinkedGraph { - nodes: Vec>, + nodes: IndexVec>, edges: Vec>, } @@ -62,7 +63,7 @@ pub struct Edge { pub data: E, } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct NodeIndex(pub usize); #[derive(Copy, Clone, PartialEq, Debug)] @@ -87,19 +88,29 @@ impl NodeIndex { } } +impl Idx for NodeIndex { + fn new(idx: usize) -> NodeIndex { + NodeIndex(idx) + } + + fn index(self) -> usize { + self.0 + } +} + impl LinkedGraph { pub fn new() -> Self { - Self { nodes: Vec::new(), edges: Vec::new() } + Self { nodes: IndexVec::new(), edges: Vec::new() } } pub fn with_capacity(nodes: usize, edges: usize) -> Self { - Self { nodes: Vec::with_capacity(nodes), edges: Vec::with_capacity(edges) } + Self { nodes: IndexVec::with_capacity(nodes), edges: Vec::with_capacity(edges) } } // # Simple accessors #[inline] - pub fn all_nodes(&self) -> &[Node] { + pub fn all_nodes(&self) -> &IndexSlice> { &self.nodes } @@ -131,15 +142,15 @@ impl LinkedGraph { } pub fn mut_node_data(&mut self, idx: NodeIndex) -> &mut N { - &mut self.nodes[idx.0].data + &mut self.nodes[idx].data } pub fn node_data(&self, idx: NodeIndex) -> &N { - &self.nodes[idx.0].data + &self.nodes[idx].data } pub fn node(&self, idx: NodeIndex) -> &Node { - &self.nodes[idx.0] + &self.nodes[idx] } // # Edge construction and queries @@ -154,16 +165,16 @@ impl LinkedGraph { let idx = self.next_edge_index(); // read current first of the list of edges from each node - let source_first = self.nodes[source.0].first_edge[OUTGOING.repr]; - let target_first = self.nodes[target.0].first_edge[INCOMING.repr]; + let source_first = self.nodes[source].first_edge[OUTGOING.repr]; + let target_first = self.nodes[target].first_edge[INCOMING.repr]; // create the new edge, with the previous firsts from each node // as the next pointers self.edges.push(Edge { next_edge: [source_first, target_first], source, target, data }); // adjust the firsts for each node target be the next object. - self.nodes[source.0].first_edge[OUTGOING.repr] = idx; - self.nodes[target.0].first_edge[INCOMING.repr] = idx; + self.nodes[source].first_edge[OUTGOING.repr] = idx; + self.nodes[target].first_edge[INCOMING.repr] = idx; idx } From 96697d41ed0c2d70dc4bb64cb4e660da1b82150d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 14 Feb 2026 17:53:00 +0300 Subject: [PATCH 02/18] LinkedGraph: support adding nodes and edges in arbitrary order If an edge uses some not-yet-known node, we just leave the node's data empty, that data can be added later. Use this support to avoid skipping edges in DepGraphQuery --- .../src/graph/linked_graph/mod.rs | 24 ++++++++++++++----- .../src/graph/linked_graph/tests.rs | 2 +- .../rustc_middle/src/dep_graph/retained.rs | 17 ++++--------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs index 6d53b155e9a2a..2223e85a24957 100644 --- a/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/linked_graph/mod.rs @@ -52,7 +52,7 @@ pub struct LinkedGraph { pub struct Node { first_edge: [EdgeIndex; 2], // see module comment - pub data: N, + pub data: Option, } #[derive(Debug)] @@ -135,18 +135,30 @@ impl LinkedGraph { NodeIndex(self.nodes.len()) } + fn ensure_node(&mut self, idx: NodeIndex) -> &mut Node { + self.nodes.ensure_contains_elem(idx, || Node { + first_edge: [INVALID_EDGE_INDEX, INVALID_EDGE_INDEX], + data: None, + }) + } + + pub fn add_node_with_idx(&mut self, idx: NodeIndex, data: N) { + let old_data = self.ensure_node(idx).data.replace(data); + debug_assert!(old_data.is_none()); + } + pub fn add_node(&mut self, data: N) -> NodeIndex { let idx = self.next_node_index(); - self.nodes.push(Node { first_edge: [INVALID_EDGE_INDEX, INVALID_EDGE_INDEX], data }); + self.add_node_with_idx(idx, data); idx } pub fn mut_node_data(&mut self, idx: NodeIndex) -> &mut N { - &mut self.nodes[idx].data + self.nodes[idx].data.as_mut().unwrap() } pub fn node_data(&self, idx: NodeIndex) -> &N { - &self.nodes[idx].data + self.nodes[idx].data.as_ref().unwrap() } pub fn node(&self, idx: NodeIndex) -> &Node { @@ -165,8 +177,8 @@ impl LinkedGraph { let idx = self.next_edge_index(); // read current first of the list of edges from each node - let source_first = self.nodes[source].first_edge[OUTGOING.repr]; - let target_first = self.nodes[target].first_edge[INCOMING.repr]; + let source_first = self.ensure_node(source).first_edge[OUTGOING.repr]; + let target_first = self.ensure_node(target).first_edge[INCOMING.repr]; // create the new edge, with the previous firsts from each node // as the next pointers diff --git a/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs b/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs index 357aa81a57ca3..da416cd638d47 100644 --- a/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs +++ b/compiler/rustc_data_structures/src/graph/linked_graph/tests.rs @@ -40,7 +40,7 @@ fn each_node() { let expected = ["A", "B", "C", "D", "E", "F"]; graph.each_node(|idx, node| { assert_eq!(&expected[idx.0], graph.node_data(idx)); - assert_eq!(expected[idx.0], node.data); + assert_eq!(expected[idx.0], node.data.unwrap()); true }); } diff --git a/compiler/rustc_middle/src/dep_graph/retained.rs b/compiler/rustc_middle/src/dep_graph/retained.rs index 4427982e3708f..626b3b7821794 100644 --- a/compiler/rustc_middle/src/dep_graph/retained.rs +++ b/compiler/rustc_middle/src/dep_graph/retained.rs @@ -1,6 +1,5 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::graph::linked_graph::{Direction, INCOMING, LinkedGraph, NodeIndex}; -use rustc_index::IndexVec; use super::{DepNode, DepNodeIndex}; @@ -13,7 +12,6 @@ use super::{DepNode, DepNodeIndex}; pub struct RetainedDepGraph { pub inner: LinkedGraph, pub indices: FxHashMap, - pub dep_index_to_index: IndexVec>, } impl RetainedDepGraph { @@ -23,27 +21,22 @@ impl RetainedDepGraph { let inner = LinkedGraph::with_capacity(node_count, edge_count); let indices = FxHashMap::default(); - let dep_index_to_index = IndexVec::new(); - Self { inner, indices, dep_index_to_index } + Self { inner, indices } } pub fn push(&mut self, index: DepNodeIndex, node: DepNode, edges: &[DepNodeIndex]) { - let source = self.inner.add_node(node); - self.dep_index_to_index.insert(index, source); + let source = NodeIndex(index.as_usize()); + self.inner.add_node_with_idx(source, node); self.indices.insert(node, source); for &target in edges.iter() { - // We may miss the edges that are pushed while the `DepGraphQuery` is being accessed. - // Skip them to issues. - if let Some(&Some(target)) = self.dep_index_to_index.get(target) { - self.inner.add_edge(source, target, ()); - } + self.inner.add_edge(source, NodeIndex(target.as_usize()), ()); } } pub fn nodes(&self) -> Vec<&DepNode> { - self.inner.all_nodes().iter().map(|n| &n.data).collect() + self.inner.all_nodes().iter().map(|n| n.data.as_ref().unwrap()).collect() } pub fn edges(&self) -> Vec<(&DepNode, &DepNode)> { From accbfccd597b332a36d6d22335d1d7d8e9507619 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Mar 2026 07:22:26 +0000 Subject: [PATCH 03/18] Unconditionally error when trying to create a const coroutine --- compiler/rustc_ast_lowering/src/expr.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4a2992038003c..e86a2f33b87b1 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -231,6 +231,7 @@ impl<'hir> LoweringContext<'_, 'hir> { e.id, expr_hir_id, *coroutine_kind, + *constness, fn_decl, body, *fn_decl_span, @@ -1158,6 +1159,7 @@ impl<'hir> LoweringContext<'_, 'hir> { closure_id: NodeId, closure_hir_id: HirId, coroutine_kind: CoroutineKind, + constness: Const, decl: &FnDecl, body: &Expr, fn_decl_span: Span, @@ -1204,6 +1206,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let fn_decl = self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None); + if let Const::Yes(span) = constness { + self.dcx().span_err(span, "const coroutines are not supported"); + } + let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: binder_clause, @@ -1217,7 +1223,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // knows that a `FnDecl` output type like `-> &str` actually means // "coroutine that returns &str", rather than directly returning a `&str`. kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), - constness: hir::Constness::NotConst, + constness: self.lower_constness(constness), }); hir::ExprKind::Closure(c) } From ae4ddd2806b7abcabf33fb6ef61f01c5e675c8b9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Mar 2026 08:44:28 +0000 Subject: [PATCH 04/18] Reject const closures outside const contexts --- compiler/rustc_ast_lowering/src/expr.rs | 11 ++++++-- compiler/rustc_ast_lowering/src/item.rs | 24 +++++++++++++++--- compiler/rustc_ast_lowering/src/lib.rs | 2 ++ .../const-closure-issue-125866-error.rs | 4 +-- .../const-closure-issue-125866-pass.rs | 25 ------------------- ...closure-const_trait_impl-ice-113381.stderr | 7 +++--- tests/ui/traits/const-traits/gate.rs | 6 ++--- tests/ui/traits/const-traits/gate.stderr | 14 +++++------ ...-const-op-const-closure-non-const-outer.rs | 2 +- ...st-op-const-closure-non-const-outer.stderr | 7 +++--- 10 files changed, 50 insertions(+), 52 deletions(-) delete mode 100644 tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e86a2f33b87b1..46b33a481214b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1062,7 +1062,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, - constness: Const, + mut constness: Const, movability: Movability, decl: &FnDecl, body: &Expr, @@ -1072,11 +1072,18 @@ impl<'hir> LoweringContext<'_, 'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); + if let Const::Yes(span) = constness { + if !self.is_in_const_context { + self.dcx().span_err(span, "cannot use `const` closures outside of const contexts"); + constness = Const::No; + } + } + let (body_id, closure_kind) = self.with_new_scopes(fn_decl_span, move |this| { let mut coroutine_kind = find_attr!(attrs, Coroutine(_) => hir::CoroutineKind::Coroutine(Movability::Movable)); // FIXME(contracts): Support contracts on closures? - let body_id = this.lower_fn_body(decl, None, |this| { + let body_id = this.lower_fn_body(decl, None, constness, |this| { this.coroutine_kind = coroutine_kind; let e = this.lower_expr_mut(body); coroutine_kind = this.coroutine_kind; diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ed78b77a704f6..e2ac38caf2950 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,3 +1,5 @@ +use std::mem; + use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; @@ -345,6 +347,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body.as_deref(), attrs, contract.as_deref(), + header.constness, ); let itctx = ImplTraitContext::Universal; @@ -1024,6 +1027,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(body), attrs, contract.as_deref(), + sig.header.constness, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1217,6 +1221,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body.as_deref(), attrs, contract.as_deref(), + sig.header.constness, ); let (generics, sig) = self.lower_method_sig( generics, @@ -1346,11 +1351,13 @@ impl<'hir> LoweringContext<'_, 'hir> { f: impl FnOnce(&mut Self) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>), ) -> hir::BodyId { let prev_coroutine_kind = self.coroutine_kind.take(); + let prev_is_in_const_context = mem::take(&mut self.is_in_const_context); let task_context = self.task_context.take(); let (parameters, result) = f(self); let body_id = self.record_body(parameters, result); self.task_context = task_context; self.coroutine_kind = prev_coroutine_kind; + self.is_in_const_context = prev_is_in_const_context; body_id } @@ -1369,9 +1376,13 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, decl: &FnDecl, contract: Option<&FnContract>, + constness: Const, body: impl FnOnce(&mut Self) -> hir::Expr<'hir>, ) -> hir::BodyId { self.lower_body(|this| { + if let Const::Yes(_) = constness { + this.is_in_const_context = true; + } let params = this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))); @@ -1389,8 +1400,9 @@ impl<'hir> LoweringContext<'_, 'hir> { decl: &FnDecl, body: &Block, contract: Option<&FnContract>, + constness: Const, ) -> hir::BodyId { - self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body)) + self.lower_fn_body(decl, contract, constness, |this| this.lower_block_expr(body)) } pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId { @@ -1398,7 +1410,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ( &[], match expr { - Some(expr) => this.lower_expr_mut(expr), + Some(expr) => { + this.is_in_const_context = true; + this.lower_expr_mut(expr) + } None => this.expr_err(span, this.dcx().span_delayed_bug(span, "no block")), }, ) @@ -1417,12 +1432,13 @@ impl<'hir> LoweringContext<'_, 'hir> { body: Option<&Block>, attrs: &'hir [hir::Attribute], contract: Option<&FnContract>, + constness: Const, ) -> hir::BodyId { let Some(body) = body else { // Functions without a body are an error, except if this is an intrinsic. For those we // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. - return self.lower_fn_body(decl, contract, |this| { + return self.lower_fn_body(decl, contract, constness, |this| { if find_attr!(attrs, RustcIntrinsic) || this.tcx.is_sdylib_interface_build() { let span = this.lower_span(span); let empty_block = hir::Block { @@ -1447,7 +1463,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let Some(coroutine_kind) = coroutine_kind else { // Typical case: not a coroutine. - return self.lower_fn_body_block(decl, body, contract); + return self.lower_fn_body_block(decl, body, contract, constness); }; // FIXME(contracts): Support contracts on async fn. self.lower_body(|this| { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c3525d1246705..e8092b540ec87 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -129,6 +129,7 @@ struct LoweringContext<'a, 'hir> { loop_scope: Option, is_in_loop_condition: bool, is_in_dyn_type: bool, + is_in_const_context: bool, current_hir_id_owner: hir::OwnerId, item_local_id_counter: hir::ItemLocalId, @@ -190,6 +191,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { loop_scope: None, is_in_loop_condition: false, is_in_dyn_type: false, + is_in_const_context: false, coroutine_kind: None, task_context: None, current_item: None, diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs index 7a44920bb729d..e13ee39631a3a 100644 --- a/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs +++ b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs @@ -16,9 +16,9 @@ const fn create_array(mut f: impl FnMut(usize) -> u32 + Copy) -> } fn main() { - let x = create_array(const |i| 2 * i as u32); + let x = const { create_array(const |i| 2 * i as u32) }; assert_eq!(x, [0, 2, 4, 6, 8]); - let y = create_array(const |i| 2 * i as u32 + 1); + let y = const { create_array(const |i| 2 * i as u32 + 1) }; assert_eq!(y, [1, 3, 5, 7, 9]); } diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs b/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs deleted file mode 100644 index af7375172e674..0000000000000 --- a/tests/ui/traits/const-traits/const-closure-issue-125866-pass.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ check-pass - -#![allow(incomplete_features)] -#![feature(const_closures, const_trait_impl)] - -const fn create_array(mut f: impl [const] FnMut(usize) -> u32 + Copy) -> [u32; N] { - let mut array = [0; N]; - let mut i = 0; - loop { - array[i] = f(i); - i += 1; - if i == N { - break; - } - } - array -} - -fn main() { - let x = create_array(const |i| 2 * i as u32); - assert_eq!(x, [0, 2, 4, 6, 8]); - - let y = create_array(const |i| 2 * i as u32 + 1); - assert_eq!(y, [1, 3, 5, 7, 9]); -} diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr index dab3f14161fac..bbe736b94b5ba 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr @@ -1,9 +1,8 @@ -error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice-113381.rs:15:6: 15:14}: [const] Fn()` is not satisfied - --> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:5 +error: cannot use `const` closures outside of const contexts + --> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:6 | LL | (const || (()).foo())(); - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/gate.rs b/tests/ui/traits/const-traits/gate.rs index 3f348c8413930..86b42099d2896 100644 --- a/tests/ui/traits/const-traits/gate.rs +++ b/tests/ui/traits/const-traits/gate.rs @@ -1,13 +1,13 @@ // gate-test-const_closures fn main() { - (const || {})(); + const { (const || {})() }; //~^ ERROR: const closures are experimental - //~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:6: 4:14}: [const] Fn()` is not satisfied + //~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied } macro_rules! e { - ($e:expr) => {} + ($e:expr) => {}; } e!((const || {})); diff --git a/tests/ui/traits/const-traits/gate.stderr b/tests/ui/traits/const-traits/gate.stderr index 6bef2c511ce7d..b86217943d77b 100644 --- a/tests/ui/traits/const-traits/gate.stderr +++ b/tests/ui/traits/const-traits/gate.stderr @@ -1,8 +1,8 @@ error[E0658]: const closures are experimental - --> $DIR/gate.rs:4:6 + --> $DIR/gate.rs:4:14 | -LL | (const || {})(); - | ^^^^^ +LL | const { (const || {})() }; + | ^^^^^ | = note: see issue #106003 for more information = help: add `#![feature(const_closures)]` to the crate attributes to enable @@ -18,11 +18,11 @@ LL | e!((const || {})); = help: add `#![feature(const_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:6: 4:14}: [const] Fn()` is not satisfied - --> $DIR/gate.rs:4:5 +error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied + --> $DIR/gate.rs:4:13 | -LL | (const || {})(); - | ^^^^^^^^^^^^^^^ +LL | const { (const || {})() }; + | ^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs index de5bedf0ace76..a13fab3b21db2 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs @@ -13,5 +13,5 @@ fn main() { // #150052 deduplicate diagnostics for const trait supertraits // so we only get one error here (const || { (()).foo() })(); - //~^ ERROR: }: [const] Fn()` is not satisfied + //~^ ERROR: cannot use `const` closures outside of const contexts } diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index efbedca1c7e7f..43c98ec7c661c 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -1,9 +1,8 @@ -error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:15:6: 15:14}: [const] Fn()` is not satisfied - --> $DIR/non-const-op-const-closure-non-const-outer.rs:15:5 +error: cannot use `const` closures outside of const contexts + --> $DIR/non-const-op-const-closure-non-const-outer.rs:15:6 | LL | (const || { (()).foo() })(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. From e8a46117795f82f35e2f4087516a8743e28ba174 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Mar 2026 08:38:41 +0000 Subject: [PATCH 05/18] Const closure's const context is the same as their parent --- compiler/rustc_middle/src/hir/map.rs | 10 ++++++---- tests/ui/traits/const-traits/call-const-closure.rs | 3 +-- tests/ui/traits/const-traits/call-const-closure.stderr | 2 +- tests/ui/traits/const-traits/call.rs | 2 +- tests/ui/traits/const-traits/call.stderr | 2 +- tests/ui/traits/const-traits/gate.rs | 2 +- tests/ui/traits/const-traits/gate.stderr | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index cf1b863f754c8..fc2a7fbd4fa9c 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -310,16 +310,18 @@ impl<'tcx> TyCtxt<'tcx> { /// This should only be used for determining the context of a body, a return /// value of `Some` does not always suggest that the owner of the body is `const`, /// just that it has to be checked as if it were. - pub fn hir_body_const_context(self, def_id: LocalDefId) -> Option { - let def_id = def_id.into(); + pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option { + let def_id = local_def_id.into(); let ccx = match self.hir_body_owner_kind(def_id) { BodyOwnerKind::Const { inline } => ConstContext::Const { inline }, BodyOwnerKind::Static(mutability) => ConstContext::Static(mutability), BodyOwnerKind::Fn if self.is_constructor(def_id) => return None, - BodyOwnerKind::Fn | BodyOwnerKind::Closure if self.is_const_fn(def_id) => { - ConstContext::ConstFn + // Const closures use their parent's const context + BodyOwnerKind::Closure if self.is_const_fn(def_id) => { + return self.hir_body_const_context(self.local_parent(local_def_id)); } + BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn, BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None, }; diff --git a/tests/ui/traits/const-traits/call-const-closure.rs b/tests/ui/traits/const-traits/call-const-closure.rs index c4293579aea8e..a26f7be1148dd 100644 --- a/tests/ui/traits/const-traits/call-const-closure.rs +++ b/tests/ui/traits/const-traits/call-const-closure.rs @@ -14,8 +14,7 @@ impl Bar for () { const FOO: () = { (const || ().foo())(); - //~^ ERROR the trait bound `(): [const] Bar` is not satisfied - // FIXME(const_trait_impl): The constness environment for const closures is wrong. + //~^ ERROR the trait bound `(): const Bar` is not satisfied }; fn main() {} diff --git a/tests/ui/traits/const-traits/call-const-closure.stderr b/tests/ui/traits/const-traits/call-const-closure.stderr index 9a851a97f186a..16b936d58aaf6 100644 --- a/tests/ui/traits/const-traits/call-const-closure.stderr +++ b/tests/ui/traits/const-traits/call-const-closure.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `(): [const] Bar` is not satisfied +error[E0277]: the trait bound `(): const Bar` is not satisfied --> $DIR/call-const-closure.rs:16:18 | LL | (const || ().foo())(); diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index 360c08e1b7fe9..e93ea4ccbd708 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -5,7 +5,7 @@ const _: () = { assert!((const || true)()); - //~^ ERROR }: [const] Fn()` is not satisfied + //~^ ERROR }: const Fn()` is not satisfied }; fn main() {} diff --git a/tests/ui/traits/const-traits/call.stderr b/tests/ui/traits/const-traits/call.stderr index 8e32cab6dfcfb..3503938327a9f 100644 --- a/tests/ui/traits/const-traits/call.stderr +++ b/tests/ui/traits/const-traits/call.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] Fn()` is not satisfied +error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: const Fn()` is not satisfied --> $DIR/call.rs:7:13 | LL | assert!((const || true)()); diff --git a/tests/ui/traits/const-traits/gate.rs b/tests/ui/traits/const-traits/gate.rs index 86b42099d2896..0821a45edb5e5 100644 --- a/tests/ui/traits/const-traits/gate.rs +++ b/tests/ui/traits/const-traits/gate.rs @@ -3,7 +3,7 @@ fn main() { const { (const || {})() }; //~^ ERROR: const closures are experimental - //~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied + //~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: const Fn()` is not satisfied } macro_rules! e { diff --git a/tests/ui/traits/const-traits/gate.stderr b/tests/ui/traits/const-traits/gate.stderr index b86217943d77b..af5905524437d 100644 --- a/tests/ui/traits/const-traits/gate.stderr +++ b/tests/ui/traits/const-traits/gate.stderr @@ -18,7 +18,7 @@ LL | e!((const || {})); = help: add `#![feature(const_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: [const] Fn()` is not satisfied +error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: const Fn()` is not satisfied --> $DIR/gate.rs:4:13 | LL | const { (const || {})() }; From 8459d6bf3a6ba46368c6649f1cc7d9ba332c9e9c Mon Sep 17 00:00:00 2001 From: Sinan Nalkaya Date: Thu, 12 Mar 2026 13:14:40 +0100 Subject: [PATCH 06/18] Fix std doctest build for SGX target. --- library/std/src/os/fd/raw.rs | 2 +- library/std/src/sys/net/connection/sgx.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 39e374174646c..0d96958b6cca1 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -16,7 +16,7 @@ use crate::io; use crate::os::hermit::io::OwnedFd; #[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] use crate::os::raw; -#[cfg(all(doc, not(target_arch = "wasm32")))] +#[cfg(all(doc, not(any(target_arch = "wasm32", target_env = "sgx"))))] use crate::os::unix::io::AsFd; #[cfg(unix)] use crate::os::unix::io::OwnedFd; diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 8c9c17d3f1714..6a625664494b5 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -68,8 +68,8 @@ impl fmt::Debug for TcpStream { /// /// SGX doesn't support DNS resolution but rather accepts hostnames in /// the same place as socket addresses. So, to make e.g. -/// ```rust -/// TcpStream::connect("example.com:80")` +/// ```rust,ignore (incomplete example) +/// TcpStream::connect("example.com:80") /// ``` /// work, the DNS lookup returns a special error (`NonIpSockAddr`) instead, /// which contains the hostname being looked up. When `.to_socket_addrs()` From 86c5da6b8be5f77354d08748188942c436710a36 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 13 Mar 2026 10:28:04 +0000 Subject: [PATCH 07/18] delete some duplicated tests --- tests/ui/moves/move-1-unique.rs | 25 ------------------------- tests/ui/moves/move-2-unique.rs | 10 ---------- tests/ui/moves/move-4-unique.rs | 18 ------------------ tests/ui/moves/move-arg-2-unique.rs | 12 ------------ tests/ui/moves/move-scalar.rs | 10 ---------- 5 files changed, 75 deletions(-) delete mode 100644 tests/ui/moves/move-1-unique.rs delete mode 100644 tests/ui/moves/move-2-unique.rs delete mode 100644 tests/ui/moves/move-4-unique.rs delete mode 100644 tests/ui/moves/move-arg-2-unique.rs delete mode 100644 tests/ui/moves/move-scalar.rs diff --git a/tests/ui/moves/move-1-unique.rs b/tests/ui/moves/move-1-unique.rs deleted file mode 100644 index c97bfaaaf1a5a..0000000000000 --- a/tests/ui/moves/move-1-unique.rs +++ /dev/null @@ -1,25 +0,0 @@ -//@ run-pass -#![allow(unused_mut)] -#![allow(dead_code)] - -#[derive(Clone)] -struct Triple { - x: isize, - y: isize, - z: isize, -} - -fn test(x: bool, foo: Box) -> isize { - let bar = foo; - let mut y: Box; - if x { y = bar; } else { y = Box::new(Triple{x: 4, y: 5, z: 6}); } - return y.y; -} - -pub fn main() { - let x: Box<_> = Box::new(Triple{x: 1, y: 2, z: 3}); - assert_eq!(test(true, x.clone()), 2); - assert_eq!(test(true, x.clone()), 2); - assert_eq!(test(true, x.clone()), 2); - assert_eq!(test(false, x), 5); -} diff --git a/tests/ui/moves/move-2-unique.rs b/tests/ui/moves/move-2-unique.rs deleted file mode 100644 index 2204ea95741db..0000000000000 --- a/tests/ui/moves/move-2-unique.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -struct X { x: isize, y: isize, z: isize } - -pub fn main() { - let x: Box<_> = Box::new(X {x: 1, y: 2, z: 3}); - let y = x; - assert_eq!(y.y, 2); -} diff --git a/tests/ui/moves/move-4-unique.rs b/tests/ui/moves/move-4-unique.rs deleted file mode 100644 index 09e0f11a8b442..0000000000000 --- a/tests/ui/moves/move-4-unique.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -struct Triple {a: isize, b: isize, c: isize} - -fn test(foo: Box) -> Box { - let foo = foo; - let bar = foo; - let baz = bar; - let quux = baz; - return quux; -} - -pub fn main() { - let x = Box::new(Triple{a: 1, b: 2, c: 3}); - let y = test(x); - assert_eq!(y.c, 3); -} diff --git a/tests/ui/moves/move-arg-2-unique.rs b/tests/ui/moves/move-arg-2-unique.rs deleted file mode 100644 index d9a03be0ed2dc..0000000000000 --- a/tests/ui/moves/move-arg-2-unique.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass - -fn test(foo: Box> ) { assert_eq!((*foo)[0], 10); } - -pub fn main() { - let x = Box::new(vec![10]); - // Test forgetting a local by move-in - test(x); - - // Test forgetting a temporary by move-in. - test(Box::new(vec![10])); -} diff --git a/tests/ui/moves/move-scalar.rs b/tests/ui/moves/move-scalar.rs deleted file mode 100644 index e8cf5632b322a..0000000000000 --- a/tests/ui/moves/move-scalar.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -#![allow(unused_mut)] - -pub fn main() { - - let y: isize = 42; - let mut x: isize; - x = y; - assert_eq!(x, 42); -} From 1f44a11518096de272003492a8fe2c300dbf2989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 3 Mar 2026 11:34:00 +0100 Subject: [PATCH 08/18] Replace `visit_waiters` with `abstracted_waiters_of` --- compiler/rustc_middle/src/query/job.rs | 10 +- compiler/rustc_query_impl/src/job.rs | 165 ++++++++++++++----------- 2 files changed, 101 insertions(+), 74 deletions(-) diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index 2747935942b55..b2e5649106ce9 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -65,7 +65,7 @@ impl<'tcx> QueryJob<'tcx> { #[derive(Debug)] pub struct QueryWaiter<'tcx> { - pub query: Option, + pub parent: Option, pub condvar: Condvar, pub span: Span, pub cycle: Mutex>>, @@ -94,8 +94,12 @@ impl<'tcx> QueryLatch<'tcx> { return Ok(()); // already complete }; - let waiter = - Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); + let waiter = Arc::new(QueryWaiter { + parent: query, + span, + cycle: Mutex::new(None), + condvar: Condvar::new(), + }); // We push the waiter on to the `waiters` list. It can be accessed inside // the `wait` call below, by 1) the `set` method or 2) by deadlock detection. diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index ae32ad01b1578..900fca23c9524 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -111,38 +111,44 @@ pub(crate) fn find_dep_kind_root<'tcx>( last_layout } -/// A resumable waiter of a query. The usize is the index into waiters in the query's latch -type Waiter = (QueryJobId, usize); - -/// Visits all the non-resumable and resumable waiters of a query. -/// Only waiters in a query are visited. -/// `visit` is called for every waiter and is passed a query waiting on `query` -/// and a span indicating the reason the query waited on `query`. -/// If `visit` returns `Break`, this function also returns `Break`, -/// and if all `visit` calls returns `Continue` it also returns `Continue`. -/// For visits of non-resumable waiters it returns the return value of `visit`. -/// For visits of resumable waiters it returns information required to resume that waiter. -fn visit_waiters<'tcx>( - job_map: &QueryJobMap<'tcx>, - query: QueryJobId, - mut visit: impl FnMut(Span, QueryJobId) -> ControlFlow>, -) -> ControlFlow> { - // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = job_map.parent_of(query) { - visit(job_map.span_of(query), parent)?; - } +/// The locaton of a resumable waiter. The usize is the index into waiters in the query's latch. +/// We'll use this to remove the waiter using `QueryLatch::extract_waiter` if we're waking it up. +type ResumableWaiterLocation = (QueryJobId, usize); + +/// This abstracts over non-resumable waiters which are found in `QueryJob`'s `parent` field +/// and resumable waiters are in `latch` field. +struct AbstractedWaiter { + /// The span corresponding to the reason for why we're waiting on this query. + span: Span, + /// The query which we are waiting from, if none the waiter is from a compiler root. + parent: Option, + resumable: Option, +} + +/// Returns all the non-resumable and resumable waiters of a query. +/// This is used so we can uniformly loop over both non-resumable and resumable waiters. +fn abstracted_waiters_of(job_map: &QueryJobMap<'_>, query: QueryJobId) -> Vec { + let mut result = Vec::new(); - // Visit the explicit waiters which use condvars and are resumable + // Add the parent which is a non-resumable waiter since it's on the same stack + result.push(AbstractedWaiter { + span: job_map.span_of(query), + parent: job_map.parent_of(query), + resumable: None, + }); + + // Add the explicit waiters which use condvars and are resumable if let Some(latch) = job_map.latch_of(query) { for (i, waiter) in latch.waiters.lock().as_ref().unwrap().iter().enumerate() { - if let Some(waiter_query) = waiter.query { - // Return a value which indicates that this waiter can be resumed - visit(waiter.span, waiter_query).map_break(|_| Some((query, i)))?; - } + result.push(AbstractedWaiter { + span: waiter.span, + parent: waiter.parent, + resumable: Some((query, i)), + }); } } - ControlFlow::Continue(()) + result } /// Look for query cycles by doing a depth first search starting at `query`. @@ -155,13 +161,13 @@ fn cycle_check<'tcx>( span: Span, stack: &mut Vec<(Span, QueryJobId)>, visited: &mut FxHashSet, -) -> ControlFlow> { +) -> ControlFlow> { if !visited.insert(query) { - return if let Some(p) = stack.iter().position(|q| q.1 == query) { + return if let Some(pos) = stack.iter().position(|q| q.1 == query) { // We detected a query cycle, fix up the initial span and return Some // Remove previous stack entries - stack.drain(0..p); + stack.drain(0..pos); // Replace the span for the first query with the cycle cause stack[0].0 = span; ControlFlow::Break(None) @@ -174,16 +180,23 @@ fn cycle_check<'tcx>( stack.push((span, query)); // Visit all the waiters - let r = visit_waiters(job_map, query, |span, successor| { - cycle_check(job_map, successor, span, stack, visited) - }); - - // Remove the entry in our stack if we didn't find a cycle - if r.is_continue() { - stack.pop(); + for abstracted_waiter in abstracted_waiters_of(job_map, query) { + let Some(parent) = abstracted_waiter.parent else { + // Skip waiters which are not queries + continue; + }; + if let ControlFlow::Break(maybe_resumable) = + cycle_check(job_map, parent, abstracted_waiter.span, stack, visited) + { + // Return the resumable waiter in `waiter.resumable` if present + return ControlFlow::Break(abstracted_waiter.resumable.or(maybe_resumable)); + } } - r + // Remove the entry in our stack since we didn't find a cycle + stack.pop(); + + ControlFlow::Continue(()) } /// Finds out if there's a path to the compiler root (aka. code which isn't in a query) @@ -193,18 +206,26 @@ fn connected_to_root<'tcx>( job_map: &QueryJobMap<'tcx>, query: QueryJobId, visited: &mut FxHashSet, -) -> ControlFlow> { +) -> bool { // We already visited this or we're deliberately ignoring it if !visited.insert(query) { - return ControlFlow::Continue(()); + return false; } - // This query is connected to the root (it has no query parent), return true - if job_map.parent_of(query).is_none() { - return ControlFlow::Break(None); + // Visit all the waiters + for abstracted_waiter in abstracted_waiters_of(job_map, query) { + match abstracted_waiter.parent { + // This query is connected to the root + None => return true, + Some(parent) => { + if connected_to_root(job_map, parent, visited) { + return true; + } + } + } } - visit_waiters(job_map, query, |_, successor| connected_to_root(job_map, successor, visited)) + false } /// Looks for query cycles starting from the last query in `jobs`. @@ -220,7 +241,7 @@ fn remove_cycle<'tcx>( let mut visited = FxHashSet::default(); let mut stack = Vec::new(); // Look for a cycle starting with the last query in `jobs` - if let ControlFlow::Break(waiter) = + if let ControlFlow::Break(resumable) = cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) { // The stack is a vector of pairs of spans and queries; reverse it so that @@ -242,7 +263,7 @@ fn remove_cycle<'tcx>( struct EntryPoint { query_in_cycle: QueryJobId, - waiter: Option<(Span, QueryJobId)>, + query_waiting_on_cycle: Option<(Span, QueryJobId)>, } // Find the queries in the cycle which are @@ -250,36 +271,36 @@ fn remove_cycle<'tcx>( let entry_points = stack .iter() .filter_map(|&(_, query_in_cycle)| { - if job_map.parent_of(query_in_cycle).is_none() { - // This query is connected to the root (it has no query parent) - Some(EntryPoint { query_in_cycle, waiter: None }) - } else { - let mut waiter_on_cycle = None; - // Find a direct waiter who leads to the root - let _ = visit_waiters(job_map, query_in_cycle, |span, waiter| { - // Mark all the other queries in the cycle as already visited - let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - - if connected_to_root(job_map, waiter, &mut visited).is_break() { - waiter_on_cycle = Some((span, waiter)); - ControlFlow::Break(None) - } else { - ControlFlow::Continue(()) - } - }); - - waiter_on_cycle.map(|waiter_on_cycle| EntryPoint { - query_in_cycle, - waiter: Some(waiter_on_cycle), - }) + let mut entrypoint = false; + let mut query_waiting_on_cycle = None; + + // Find a direct waiter who leads to the root + for abstracted_waiter in abstracted_waiters_of(job_map, query_in_cycle) { + let Some(parent) = abstracted_waiter.parent else { + // The query in the cycle is directly connected to root. + entrypoint = true; + continue; + }; + + // Mark all the other queries in the cycle as already visited, + // so paths to the root through the cycle itself won't count. + let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); + + if connected_to_root(job_map, parent, &mut visited) { + query_waiting_on_cycle = Some((abstracted_waiter.span, parent)); + entrypoint = true; + break; + } } + + entrypoint.then_some(EntryPoint { query_in_cycle, query_waiting_on_cycle }) }) .collect::>(); // Pick an entry point, preferring ones with waiters let entry_point = entry_points .iter() - .find(|entry_point| entry_point.waiter.is_some()) + .find(|entry_point| entry_point.query_waiting_on_cycle.is_some()) .unwrap_or(&entry_points[0]); // Shift the stack so that our entry point is first @@ -289,7 +310,9 @@ fn remove_cycle<'tcx>( stack.rotate_left(pos); } - let usage = entry_point.waiter.map(|(span, job)| (span, job_map.frame_of(job).clone())); + let usage = entry_point + .query_waiting_on_cycle + .map(|(span, job)| (span, job_map.frame_of(job).clone())); // Create the cycle error let error = CycleError { @@ -300,9 +323,9 @@ fn remove_cycle<'tcx>( .collect(), }; - // We unwrap `waiter` here since there must always be one + // We unwrap `resumable` here since there must always be one // edge which is resumable / waited using a query latch - let (waitee_query, waiter_idx) = waiter.unwrap(); + let (waitee_query, waiter_idx) = resumable.unwrap(); // Extract the waiter we want to resume let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx); From 791f4f9315cdf85c5137c26070f2818d332d66b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 12 Mar 2026 15:55:28 +0100 Subject: [PATCH 09/18] Remove `value_from_cycle_error` specialization for `type_of_opaque_hir_typeck` --- compiler/rustc_query_impl/src/from_cycle_error.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 6a3155c02949e..3cca35601feb3 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -26,11 +26,6 @@ pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) { erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) }; - vtables.type_of_opaque_hir_typeck.value_from_cycle_error = |tcx, _, _, err| { - let guar = err.emit(); - erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) - }; - vtables.erase_and_anonymize_regions_ty.value_from_cycle_error = |tcx, _, _, err| { let guar = err.emit(); erase_val(Ty::new_error(tcx, guar)) From cdbe2b35b55bb38ac503e0989e69cca9aa530ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 12 Mar 2026 16:35:50 +0100 Subject: [PATCH 10/18] Remove `value_from_cycle_error` specialization for `erase_and_anonymize_regions_ty` --- compiler/rustc_query_impl/src/from_cycle_error.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 3cca35601feb3..0cc3b2cf7eec7 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -26,11 +26,6 @@ pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) { erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) }; - vtables.erase_and_anonymize_regions_ty.value_from_cycle_error = |tcx, _, _, err| { - let guar = err.emit(); - erase_val(Ty::new_error(tcx, guar)) - }; - vtables.fn_sig.value_from_cycle_error = |tcx, key, _, err| { let guar = err.delay_as_bug(); erase_val(fn_sig(tcx, key, guar)) From fed57899b93d6e17b00bf00326d7b9aebdccc6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 12 Mar 2026 17:02:49 +0100 Subject: [PATCH 11/18] Remove `value_from_cycle_error` specialization for `type_of` --- .../rustc_query_impl/src/from_cycle_error.rs | 5 -- tests/incremental/const-generic-type-cycle.rs | 2 - tests/ui/consts/issue-103790.rs | 1 - tests/ui/consts/issue-103790.stderr | 14 +---- .../ui/delegation/unsupported.current.stderr | 19 +++--- tests/ui/delegation/unsupported.next.stderr | 15 +---- tests/ui/delegation/unsupported.rs | 1 - ...ompatability-via-leakage-cycle.next.stderr | 62 +------------------ tests/ui/issues/issue-34373.rs | 1 - tests/ui/issues/issue-34373.stderr | 16 +---- ...ce-hir-wf-check-anon-const-issue-122199.rs | 1 - ...ir-wf-check-anon-const-issue-122199.stderr | 10 +-- ...ce-hir-wf-check-anon-const-issue-122989.rs | 2 - ...ir-wf-check-anon-const-issue-122989.stderr | 22 +------ 14 files changed, 20 insertions(+), 151 deletions(-) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 0cc3b2cf7eec7..8214f80cda410 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -21,11 +21,6 @@ use rustc_span::{ErrorGuaranteed, Span}; use crate::job::report_cycle; pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) { - vtables.type_of.value_from_cycle_error = |tcx, _, _, err| { - let guar = err.emit(); - erase_val(ty::EarlyBinder::bind(Ty::new_error(tcx, guar))) - }; - vtables.fn_sig.value_from_cycle_error = |tcx, key, _, err| { let guar = err.delay_as_bug(); erase_val(fn_sig(tcx, key, guar)) diff --git a/tests/incremental/const-generic-type-cycle.rs b/tests/incremental/const-generic-type-cycle.rs index 5bcbc1d5dafef..85b8092787e2c 100644 --- a/tests/incremental/const-generic-type-cycle.rs +++ b/tests/incremental/const-generic-type-cycle.rs @@ -13,7 +13,5 @@ trait Bar {} #[cfg(cfail)] trait Bar {} //[cfail]~^ ERROR cycle detected when computing type of `Bar::N` -//[cfail]~| ERROR cycle detected when computing type of `Bar::N` -//[cfail]~| ERROR `(dyn Bar<{ 2 + 1 }> + 'static)` is forbidden as the type of a const generic parameter trait BB = Bar<{ 2 + 1 }>; diff --git a/tests/ui/consts/issue-103790.rs b/tests/ui/consts/issue-103790.rs index 869a43e4018b3..a85833a565372 100644 --- a/tests/ui/consts/issue-103790.rs +++ b/tests/ui/consts/issue-103790.rs @@ -5,6 +5,5 @@ struct S; //~^ ERROR the name `S` is already used for a generic parameter in this item's generic parameters //~| ERROR missing generics for struct `S` //~| ERROR cycle detected when computing type of `S::S` -//~| ERROR `()` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/consts/issue-103790.stderr b/tests/ui/consts/issue-103790.stderr index adfac02bd0ceb..e442c64e70ce1 100644 --- a/tests/ui/consts/issue-103790.stderr +++ b/tests/ui/consts/issue-103790.stderr @@ -36,19 +36,7 @@ LL | struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: `()` is forbidden as the type of a const generic parameter - --> $DIR/issue-103790.rs:4:19 - | -LL | struct S; - | ^^ - | - = note: the only supported types are integers, `bool`, and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types - | -LL + #![feature(adt_const_params)] - | - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0107, E0391, E0403. For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/unsupported.current.stderr b/tests/ui/delegation/unsupported.current.stderr index 9bc2eec068fef..657f990271734 100644 --- a/tests/ui/delegation/unsupported.current.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -29,18 +29,13 @@ note: ...which requires comparing an impl and trait method signature, inferring LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle - = note: cycle used when checking effective visibilities - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error[E0283]: type annotations needed - --> $DIR/unsupported.rs:54:18 - | -LL | reuse Trait::foo; - | ^^^ cannot infer type +note: cycle used when checking assoc item `opaque::::opaque_ret` is compatible with trait definition + --> $DIR/unsupported.rs:33:24 | - = note: cannot satisfy `_: effects::Trait` +LL | reuse ToReuse::opaque_ret; + | ^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0283, E0391. -For more information about an error, try `rustc --explain E0283`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr index 08bc49513bad5..90b17752cd8b1 100644 --- a/tests/ui/delegation/unsupported.next.stderr +++ b/tests/ui/delegation/unsupported.next.stderr @@ -25,18 +25,9 @@ note: ...which requires comparing an impl and trait method signature, inferring LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: ...which again requires computing type of `opaque::::opaque_ret::{anon_assoc#0}`, completing the cycle - = note: cycle used when checking effective visibilities + = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0283]: type annotations needed - --> $DIR/unsupported.rs:54:18 - | -LL | reuse Trait::foo; - | ^^^ cannot infer type - | - = note: cannot satisfy `_: effects::Trait` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0283, E0391. -For more information about an error, try `rustc --explain E0283`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/unsupported.rs b/tests/ui/delegation/unsupported.rs index 1681888e34ea6..adb798aab71d0 100644 --- a/tests/ui/delegation/unsupported.rs +++ b/tests/ui/delegation/unsupported.rs @@ -52,7 +52,6 @@ mod effects { } reuse Trait::foo; - //~^ ERROR type annotations needed } fn main() {} diff --git a/tests/ui/impl-trait/in-trait/method-compatability-via-leakage-cycle.next.stderr b/tests/ui/impl-trait/in-trait/method-compatability-via-leakage-cycle.next.stderr index f0a20367a4a1a..87640517ddb20 100644 --- a/tests/ui/impl-trait/in-trait/method-compatability-via-leakage-cycle.next.stderr +++ b/tests/ui/impl-trait/in-trait/method-compatability-via-leakage-cycle.next.stderr @@ -57,66 +57,6 @@ LL | fn foo(b: bool) -> impl Sized { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0391]: cycle detected when computing type of `::foo::{anon_assoc#0}` - --> $DIR/method-compatability-via-leakage-cycle.rs:21:24 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^ - | -note: ...which requires comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires computing type of `::foo::{opaque#0}`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:24 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^ -note: ...which requires computing type of opaque `::foo::{opaque#0}`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:24 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^ -note: ...which requires borrow-checking `::foo`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires promoting constants in MIR for `::foo`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires checking if `::foo` contains FFI-unwind calls... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires building MIR for `::foo`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires match-checking `::foo`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: ...which requires type-checking `::foo`... - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires computing type of `::foo::{anon_assoc#0}`, completing the cycle -note: cycle used when checking assoc item `::foo` is compatible with trait definition - --> $DIR/method-compatability-via-leakage-cycle.rs:21:5 - | -LL | fn foo(b: bool) -> impl Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/issues/issue-34373.rs b/tests/ui/issues/issue-34373.rs index 765bfc20a4517..a9bd79f6830b8 100644 --- a/tests/ui/issues/issue-34373.rs +++ b/tests/ui/issues/issue-34373.rs @@ -6,7 +6,6 @@ trait Trait { pub struct Foo>>; //~^ ERROR cycle detected when computing type of `Foo::T` -//~| ERROR type parameter `T` is never used type DefaultFoo = Foo; fn main() { diff --git a/tests/ui/issues/issue-34373.stderr b/tests/ui/issues/issue-34373.stderr index 6d68de8fb3b8a..995f2f4022b62 100644 --- a/tests/ui/issues/issue-34373.stderr +++ b/tests/ui/issues/issue-34373.stderr @@ -5,7 +5,7 @@ LL | pub struct Foo>>; | ^^^^^^^^^^ | note: ...which requires expanding type alias `DefaultFoo`... - --> $DIR/issue-34373.rs:10:19 + --> $DIR/issue-34373.rs:9:19 | LL | type DefaultFoo = Foo; | ^^^ @@ -17,16 +17,6 @@ LL | pub struct Foo>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error[E0392]: type parameter `T` is never used - --> $DIR/issue-34373.rs:7:16 - | -LL | pub struct Foo>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unused type parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0391, E0392. -For more information about an error, try `rustc --explain E0391`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs index 072a699a6b5b9..732a5c334e50d 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs @@ -4,7 +4,6 @@ trait Trait { fn fnc(&self) -> dyn Trait { //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters //~| ERROR expected value, found builtin type `u32` - //~| ERROR defaults for generic parameters are not allowed here bar //~^ ERROR cannot find value `bar` in this scope } diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index 47f3e83fae21a..ce567b9043f06 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -20,7 +20,7 @@ LL | fn fnc(&self) -> dyn Trait { | ^^^ not a value error[E0425]: cannot find value `bar` in this scope - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:8:9 + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:7:9 | LL | bar | ^^^ not found in this scope @@ -39,13 +39,7 @@ LL | trait Trait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: defaults for generic parameters are not allowed here - --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:4:12 - | -LL | fn fnc(&self) -> dyn Trait { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0391, E0403, E0423, E0425. For more information about an error, try `rustc --explain E0391`. diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs index 13d7a800c51fd..f2b9f037ea5f3 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs @@ -4,13 +4,11 @@ trait Foo> { //~^ WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! //~| ERROR cycle detected when computing type of `Foo::N` - //~| ERROR `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter fn func() {} } trait Bar> {} //~^ WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! -//~| ERROR `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic parameter fn main() {} diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr index f9a855d3b93b7..4024f57af4ffd 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr @@ -13,7 +13,7 @@ LL | trait Foo> { | +++ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:20 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:10:20 | LL | trait Bar> {} | ^^^^^^ @@ -32,7 +32,7 @@ LL | trait Foo> { | ^^^^^^^^^^^^^^^ | note: ...which requires computing type of `Bar::M`... - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:11 + --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:10:11 | LL | trait Bar> {} | ^^^^^^^^^^^^^^^ @@ -44,22 +44,6 @@ LL | trait Foo> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:3:20 - | -LL | trait Foo> { - | ^^^^^^ - | - = note: the only supported types are integers, `bool`, and `char` - -error: `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic parameter - --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:20 - | -LL | trait Bar> {} - | ^^^^^^ - | - = note: the only supported types are integers, `bool`, and `char` - -error: aborting due to 3 previous errors; 2 warnings emitted +error: aborting due to 1 previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0391`. From 1867653c81ac31900ae623e3970dba8f833bf870 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Mar 2026 07:37:29 +0000 Subject: [PATCH 12/18] Allow calling const closures in const items --- .../src/collect/predicates_of.rs | 3 +++ .../src/ty/context/impl_interner.rs | 5 +++++ compiler/rustc_middle/src/ty/mod.rs | 6 +----- .../src/solve/assembly/structural_traits.rs | 21 ++++++++++++++----- .../src/solve/effect_goals.rs | 3 ++- compiler/rustc_type_ir/src/interner.rs | 1 + tests/ui/traits/const-traits/call.rs | 3 +-- tests/ui/traits/const-traits/call.stderr | 9 -------- 8 files changed, 29 insertions(+), 22 deletions(-) delete mode 100644 tests/ui/traits/const-traits/call.stderr diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index e16fa5492979f..98999232a7435 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -1069,6 +1069,9 @@ pub(super) fn const_conditions<'tcx>( }, // N.B. Tuple ctors are unconditionally constant. Node::Ctor(hir::VariantData::Tuple { .. }) => return Default::default(), + Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_), .. }) => { + (hir::Generics::empty(), None, tcx.is_conditionally_const(tcx.local_parent(def_id))) + } _ => bug!("const_conditions called on wrong item: {def_id:?}"), }; diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index e59573976af52..5179a77d6cb6f 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -402,6 +402,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_conditionally_const(def_id) } + fn closure_is_const(self, def_id: DefId) -> bool { + debug_assert_matches!(self.def_kind(def_id), DefKind::Closure); + self.constness(def_id) == hir::Constness::Const + } + fn alias_has_const_conditions(self, def_id: DefId) -> bool { debug_assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::OpaqueTy); self.is_conditionally_const(def_id) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6abe7d1466990..01c351de93198 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2118,11 +2118,7 @@ impl<'tcx> TyCtxt<'tcx> { // FIXME(const_trait_impl): ATPITs could be conditionally const? hir::OpaqueTyOrigin::TyAlias { .. } => false, }, - DefKind::Closure => { - // Closures and RPITs will eventually have const conditions - // for `[const]` bounds. - false - } + DefKind::Closure => self.constness(def_id) == hir::Constness::Const, DefKind::Ctor(_, CtorKind::Const) | DefKind::Mod | DefKind::Struct diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 05ea217c1de08..cd74e87b670f1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -661,10 +661,11 @@ fn coroutine_closure_to_ambiguous_coroutine( /// /// Doing so on all calls to `extract_tupled_inputs_and_output_from_callable` /// would be wasteful. +#[instrument(level = "trace", skip(cx), ret)] pub(in crate::solve) fn extract_fn_def_from_const_callable( cx: I, self_ty: I::Ty, -) -> Result<(ty::Binder, I::FunctionId, I::GenericArgs), NoSolution> { +) -> Result<(ty::Binder, I::DefId, I::GenericArgs), NoSolution> { match self_ty.kind() { ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); @@ -675,7 +676,7 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( Ok(( sig.instantiate(cx, args) .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), - def_id, + def_id.into(), args, )) } else { @@ -686,9 +687,19 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable( ty::FnPtr(..) => { return Err(NoSolution); } - // `Closure`s are not const for now. - ty::Closure(..) => { - return Err(NoSolution); + ty::Closure(def, args) => { + if cx.closure_is_const(def) { + let closure_args = args.as_closure(); + Ok(( + closure_args + .sig() + .map_bound(|sig| (sig.inputs().get(0).unwrap(), sig.output())), + def.into(), + args, + )) + } else { + return Err(NoSolution); + } } // `CoroutineClosure`s are not const for now. ty::CoroutineClosure(..) => { diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 4da76b88b5de0..4b1e4b2de571d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -272,6 +272,7 @@ where todo!("Fn* are not yet const") } + #[instrument(level = "trace", skip_all, ret)] fn consider_builtin_fn_trait_candidates( ecx: &mut EvalCtxt<'_, D>, goal: Goal, @@ -289,7 +290,7 @@ where let output_is_sized_pred = ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); let requirements = cx - .const_conditions(def_id.into()) + .const_conditions(def_id) .iter_instantiated(cx, args) .map(|trait_ref| { ( diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index e77e7af071b90..25f7c36e9985d 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -308,6 +308,7 @@ pub trait Interner: fn impl_is_const(self, def_id: Self::ImplId) -> bool; fn fn_is_const(self, def_id: Self::FunctionId) -> bool; + fn closure_is_const(self, def_id: Self::ClosureId) -> bool; fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool; fn const_conditions( self, diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index e93ea4ccbd708..41883f648513d 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -1,11 +1,10 @@ -// FIXME(const_trait_impl) check-pass +//@ check-pass //@ compile-flags: -Znext-solver #![feature(const_closures, const_trait_impl)] #![allow(incomplete_features)] const _: () = { assert!((const || true)()); - //~^ ERROR }: const Fn()` is not satisfied }; fn main() {} diff --git a/tests/ui/traits/const-traits/call.stderr b/tests/ui/traits/const-traits/call.stderr deleted file mode 100644 index 3503938327a9f..0000000000000 --- a/tests/ui/traits/const-traits/call.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: const Fn()` is not satisfied - --> $DIR/call.rs:7:13 - | -LL | assert!((const || true)()); - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. From 60bd8e36cd8f2b534ba2979ddc04cef7a34dca32 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 Mar 2026 07:37:12 +0000 Subject: [PATCH 13/18] Enable the const_closures feature gate to silence an unrelated error --- .../ice-112822-expected-type-for-param.rs | 5 +++-- .../ice-112822-expected-type-for-param.stderr | 19 ++++--------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs index 8ee3db445d072..d20abdd57f5c6 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs @@ -1,8 +1,9 @@ -#![feature(const_trait_impl)] +#![feature(const_trait_impl, const_closures)] +#![allow(incomplete_features)] const fn test() -> impl [const] Fn() { //~^ ERROR: }: [const] Fn()` is not satisfied - const move || { //~ ERROR const closures are experimental + const move || { let sl: &[u8] = b"foo"; match sl { diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr index abbe0a0070aa2..eebfd5c11e66e 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr @@ -1,20 +1,9 @@ -error[E0658]: const closures are experimental - --> $DIR/ice-112822-expected-type-for-param.rs:5:5 - | -LL | const move || { - | ^^^^^ - | - = note: see issue #106003 for more information - = help: add `#![feature(const_closures)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0277]: the trait bound `{closure@$DIR/ice-112822-expected-type-for-param.rs:5:5: 5:18}: [const] Fn()` is not satisfied - --> $DIR/ice-112822-expected-type-for-param.rs:3:20 +error[E0277]: the trait bound `{closure@$DIR/ice-112822-expected-type-for-param.rs:6:5: 6:18}: [const] Fn()` is not satisfied + --> $DIR/ice-112822-expected-type-for-param.rs:4:20 | LL | const fn test() -> impl [const] Fn() { | ^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0277, E0658. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. From 8629126289a5d631ccae4d96384de18afde587eb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 Mar 2026 07:47:24 +0000 Subject: [PATCH 14/18] Allow calling const closures on the old solver --- .../src/traits/effects.rs | 17 +++++++++--- tests/ui/traits/const-traits/call.rs | 3 ++- .../const-closure-parse-not-item.rs | 3 +-- .../const-closure-parse-not-item.stderr | 9 ------- tests/ui/traits/const-traits/gate.rs | 3 ++- tests/ui/traits/const-traits/gate.stderr | 26 +++++++++++++++---- .../ice-112822-expected-type-for-param.rs | 5 ++-- .../ice-112822-expected-type-for-param.stderr | 14 ++++++---- 8 files changed, 51 insertions(+), 29 deletions(-) delete mode 100644 tests/ui/traits/const-traits/const-closure-parse-not-item.stderr diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index 66c949a38cea7..026ab3a527a1c 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -519,10 +519,21 @@ fn evaluate_host_effect_for_fn_goal<'tcx>( // We may support function pointers at some point in the future ty::FnPtr(..) => return Err(EvaluationFailure::NoSolution), - // Closures could implement `[const] Fn`, + // Coroutines could implement `[const] Fn`, // but they don't really need to right now. - ty::Closure(..) | ty::CoroutineClosure(_, _) => { - return Err(EvaluationFailure::NoSolution); + ty::CoroutineClosure(_, _) => return Err(EvaluationFailure::NoSolution), + + ty::Closure(def, args) => { + // For now we limit ourselves to closures without binders. The next solver can handle them. + let sig = + args.as_closure().sig().no_bound_vars().ok_or(EvaluationFailure::NoSolution)?; + ( + def, + tcx.mk_args_from_iter( + [ty::GenericArg::from(*sig.inputs().get(0).unwrap()), sig.output().into()] + .into_iter(), + ), + ) } // Everything else needs explicit impls or cannot have an impl diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index 41883f648513d..0a76de9072328 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -1,5 +1,6 @@ //@ check-pass -//@ compile-flags: -Znext-solver +//@[next] compile-flags: -Znext-solver +//@revisions: next old #![feature(const_closures, const_trait_impl)] #![allow(incomplete_features)] diff --git a/tests/ui/traits/const-traits/const-closure-parse-not-item.rs b/tests/ui/traits/const-traits/const-closure-parse-not-item.rs index 35127eda5c039..22ce427b6ab92 100644 --- a/tests/ui/traits/const-traits/const-closure-parse-not-item.rs +++ b/tests/ui/traits/const-traits/const-closure-parse-not-item.rs @@ -1,5 +1,4 @@ -//@ known-bug: #110395 -// FIXME check-pass +//@check-pass #![feature(const_trait_impl, const_closures)] #![allow(incomplete_features)] diff --git a/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr b/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr deleted file mode 100644 index 1d8d5ff1b4f2e..0000000000000 --- a/tests/ui/traits/const-traits/const-closure-parse-not-item.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0277]: the trait bound `{closure@$DIR/const-closure-parse-not-item.rs:8:5: 8:18}: [const] Fn()` is not satisfied - --> $DIR/const-closure-parse-not-item.rs:7:20 - | -LL | const fn test() -> impl [const] Fn() { - | ^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/gate.rs b/tests/ui/traits/const-traits/gate.rs index 0821a45edb5e5..c9f56df35531f 100644 --- a/tests/ui/traits/const-traits/gate.rs +++ b/tests/ui/traits/const-traits/gate.rs @@ -3,7 +3,8 @@ fn main() { const { (const || {})() }; //~^ ERROR: const closures are experimental - //~| ERROR: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: const Fn()` is not satisfied + //~| ERROR: cannot call conditionally-const closure in constants + //~| ERROR: `Fn` is not yet stable as a const trait } macro_rules! e { diff --git a/tests/ui/traits/const-traits/gate.stderr b/tests/ui/traits/const-traits/gate.stderr index af5905524437d..788878292b6ff 100644 --- a/tests/ui/traits/const-traits/gate.stderr +++ b/tests/ui/traits/const-traits/gate.stderr @@ -9,7 +9,7 @@ LL | const { (const || {})() }; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: const closures are experimental - --> $DIR/gate.rs:13:5 + --> $DIR/gate.rs:14:5 | LL | e!((const || {})); | ^^^^^ @@ -18,13 +18,29 @@ LL | e!((const || {})); = help: add `#![feature(const_closures)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0277]: the trait bound `{closure@$DIR/gate.rs:4:14: 4:22}: const Fn()` is not satisfied +error[E0658]: cannot call conditionally-const closure in constants --> $DIR/gate.rs:4:13 | LL | const { (const || {})() }; | ^^^^^^^^^^^^^^^ + | + = note: closures need an RFC before allowed to be called in constants + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: `Fn` is not yet stable as a const trait + --> $DIR/gate.rs:4:13 + | +LL | const { (const || {})() }; + | ^^^^^^^^^^^^^^^ + | +help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + | +LL + #![feature(const_trait_impl)] + | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0277, E0658. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs index d20abdd57f5c6..832e88c189d60 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs @@ -1,15 +1,14 @@ -#![feature(const_trait_impl, const_closures)] +#![feature(const_trait_impl, const_closures, const_cmp)] #![allow(incomplete_features)] const fn test() -> impl [const] Fn() { - //~^ ERROR: }: [const] Fn()` is not satisfied const move || { let sl: &[u8] = b"foo"; match sl { [first, remainder @ ..] => { assert_eq!(first, &b'f'); - // FIXME(const_closures) ^ ERROR cannot call non-const function + //~^ ERROR cannot call non-const function } [] => panic!(), } diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr index eebfd5c11e66e..7c6bec43ff40d 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr @@ -1,9 +1,13 @@ -error[E0277]: the trait bound `{closure@$DIR/ice-112822-expected-type-for-param.rs:6:5: 6:18}: [const] Fn()` is not satisfied - --> $DIR/ice-112822-expected-type-for-param.rs:4:20 +error[E0015]: cannot call non-const function `core::panicking::assert_failed::<&u8, &u8>` in constant functions + --> $DIR/ice-112822-expected-type-for-param.rs:10:17 | -LL | const fn test() -> impl [const] Fn() { - | ^^^^^^^^^^^^^^^^^ +LL | assert_eq!(first, &b'f'); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: function `assert_failed` is not const + --> $SRC_DIR/core/src/panicking.rs:LL:COL + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0015`. From c521b8084fb910e4a809c0611944fc9404138615 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 Mar 2026 10:23:50 +0000 Subject: [PATCH 15/18] Remove the incomplete marker from const closures --- compiler/rustc_feature/src/unstable.rs | 2 +- tests/ui/traits/const-traits/call-const-closure.rs | 1 - tests/ui/traits/const-traits/call-const-closure.stderr | 2 +- tests/ui/traits/const-traits/call.rs | 1 - .../ui/traits/const-traits/const-closure-issue-125866-error.rs | 1 - .../traits/const-traits/const-closure-issue-125866-error.stderr | 2 +- tests/ui/traits/const-traits/const-closure-parse-not-item.rs | 1 - .../const-traits/const_closure-const_trait_impl-ice-113381.rs | 1 - .../const_closure-const_trait_impl-ice-113381.stderr | 2 +- .../traits/const-traits/ice-112822-expected-type-for-param.rs | 1 - .../const-traits/ice-112822-expected-type-for-param.stderr | 2 +- .../const-traits/non-const-op-const-closure-non-const-outer.rs | 1 - .../non-const-op-const-closure-non-const-outer.stderr | 2 +- 13 files changed, 6 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 39e886227d946..ae356118c93ec 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -429,7 +429,7 @@ declare_features! ( /// Allows defining and calling c-variadic functions in const contexts. (unstable, const_c_variadic, "1.95.0", Some(151787)), /// Allows `const || {}` closures in const contexts. - (incomplete, const_closures, "1.68.0", Some(106003)), + (unstable, const_closures, "1.68.0", Some(106003)), /// Allows using `[const] Destruct` bounds and calling drop impls in const contexts. (unstable, const_destruct, "1.85.0", Some(133214)), /// Allows `for _ in _` loops in const contexts. diff --git a/tests/ui/traits/const-traits/call-const-closure.rs b/tests/ui/traits/const-traits/call-const-closure.rs index a26f7be1148dd..c819df5d1d08d 100644 --- a/tests/ui/traits/const-traits/call-const-closure.rs +++ b/tests/ui/traits/const-traits/call-const-closure.rs @@ -2,7 +2,6 @@ //@ edition:2021 #![feature(const_trait_impl, const_closures)] -#![allow(incomplete_features)] const trait Bar { fn foo(&self); diff --git a/tests/ui/traits/const-traits/call-const-closure.stderr b/tests/ui/traits/const-traits/call-const-closure.stderr index 16b936d58aaf6..7f4728b7e3218 100644 --- a/tests/ui/traits/const-traits/call-const-closure.stderr +++ b/tests/ui/traits/const-traits/call-const-closure.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): const Bar` is not satisfied - --> $DIR/call-const-closure.rs:16:18 + --> $DIR/call-const-closure.rs:15:18 | LL | (const || ().foo())(); | ^^^ diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index 0a76de9072328..71dea1ef4e049 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -2,7 +2,6 @@ //@[next] compile-flags: -Znext-solver //@revisions: next old #![feature(const_closures, const_trait_impl)] -#![allow(incomplete_features)] const _: () = { assert!((const || true)()); diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs index e13ee39631a3a..55fae27eb6dd7 100644 --- a/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs +++ b/tests/ui/traits/const-traits/const-closure-issue-125866-error.rs @@ -1,4 +1,3 @@ -#![allow(incomplete_features)] #![feature(const_closures, const_trait_impl)] const fn create_array(mut f: impl FnMut(usize) -> u32 + Copy) -> [u32; N] { diff --git a/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr b/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr index 1eadd1d842691..448b343e2caa4 100644 --- a/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr +++ b/tests/ui/traits/const-traits/const-closure-issue-125866-error.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `impl FnMut(usize) -> u32 + Copy: [const] FnMut(usize)` is not satisfied - --> $DIR/const-closure-issue-125866-error.rs:8:22 + --> $DIR/const-closure-issue-125866-error.rs:7:22 | LL | array[i] = f(i); | - ^ diff --git a/tests/ui/traits/const-traits/const-closure-parse-not-item.rs b/tests/ui/traits/const-traits/const-closure-parse-not-item.rs index 22ce427b6ab92..ce20b05a8133c 100644 --- a/tests/ui/traits/const-traits/const-closure-parse-not-item.rs +++ b/tests/ui/traits/const-traits/const-closure-parse-not-item.rs @@ -1,7 +1,6 @@ //@check-pass #![feature(const_trait_impl, const_closures)] -#![allow(incomplete_features)] const fn test() -> impl [const] Fn() { const move || {} diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs index e355ee724d1bd..85613d9147a3e 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs @@ -1,7 +1,6 @@ //@ known-bug: #110395 //@ compile-flags: -Znext-solver #![feature(const_closures, const_trait_impl)] -#![allow(incomplete_features)] trait Foo { fn foo(&self); diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr index bbe736b94b5ba..2bafddd7cf788 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr @@ -1,5 +1,5 @@ error: cannot use `const` closures outside of const contexts - --> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:6 + --> $DIR/const_closure-const_trait_impl-ice-113381.rs:14:6 | LL | (const || (()).foo())(); | ^^^^^ diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs index 832e88c189d60..1f35abbb9d6af 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.rs @@ -1,5 +1,4 @@ #![feature(const_trait_impl, const_closures, const_cmp)] -#![allow(incomplete_features)] const fn test() -> impl [const] Fn() { const move || { diff --git a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr index 7c6bec43ff40d..73340f15cbb8a 100644 --- a/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr +++ b/tests/ui/traits/const-traits/ice-112822-expected-type-for-param.stderr @@ -1,5 +1,5 @@ error[E0015]: cannot call non-const function `core::panicking::assert_failed::<&u8, &u8>` in constant functions - --> $DIR/ice-112822-expected-type-for-param.rs:10:17 + --> $DIR/ice-112822-expected-type-for-param.rs:9:17 | LL | assert_eq!(first, &b'f'); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs index a13fab3b21db2..58402cbaec81b 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs @@ -1,5 +1,4 @@ #![feature(const_closures, const_trait_impl)] -#![allow(incomplete_features)] trait Foo { fn foo(&self); diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index 43c98ec7c661c..f5521b90cc2d9 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -1,5 +1,5 @@ error: cannot use `const` closures outside of const contexts - --> $DIR/non-const-op-const-closure-non-const-outer.rs:15:6 + --> $DIR/non-const-op-const-closure-non-const-outer.rs:14:6 | LL | (const || { (()).foo() })(); | ^^^^^ From 9503c31c8b132333e96ec9c40b4bb620643dd3d8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 13 Mar 2026 11:01:54 +0000 Subject: [PATCH 16/18] Some test cleanups --- ...sure.stderr => call-const-closure.next.stderr} | 2 +- .../const-traits/call-const-closure.old.stderr | 14 ++++++++++++++ .../ui/traits/const-traits/call-const-closure.rs | 3 ++- .../const_closure-const_trait_impl-ice-113381.rs | 15 --------------- ...nst_closure-const_trait_impl-ice-113381.stderr | 8 -------- 5 files changed, 17 insertions(+), 25 deletions(-) rename tests/ui/traits/const-traits/{call-const-closure.stderr => call-const-closure.next.stderr} (89%) create mode 100644 tests/ui/traits/const-traits/call-const-closure.old.stderr delete mode 100644 tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs delete mode 100644 tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr diff --git a/tests/ui/traits/const-traits/call-const-closure.stderr b/tests/ui/traits/const-traits/call-const-closure.next.stderr similarity index 89% rename from tests/ui/traits/const-traits/call-const-closure.stderr rename to tests/ui/traits/const-traits/call-const-closure.next.stderr index 7f4728b7e3218..16b936d58aaf6 100644 --- a/tests/ui/traits/const-traits/call-const-closure.stderr +++ b/tests/ui/traits/const-traits/call-const-closure.next.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `(): const Bar` is not satisfied - --> $DIR/call-const-closure.rs:15:18 + --> $DIR/call-const-closure.rs:16:18 | LL | (const || ().foo())(); | ^^^ diff --git a/tests/ui/traits/const-traits/call-const-closure.old.stderr b/tests/ui/traits/const-traits/call-const-closure.old.stderr new file mode 100644 index 0000000000000..16b936d58aaf6 --- /dev/null +++ b/tests/ui/traits/const-traits/call-const-closure.old.stderr @@ -0,0 +1,14 @@ +error[E0277]: the trait bound `(): const Bar` is not satisfied + --> $DIR/call-const-closure.rs:16:18 + | +LL | (const || ().foo())(); + | ^^^ + | +help: make the `impl` of trait `Bar` `const` + | +LL | impl const Bar for () { + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/call-const-closure.rs b/tests/ui/traits/const-traits/call-const-closure.rs index c819df5d1d08d..2cf561ae6db14 100644 --- a/tests/ui/traits/const-traits/call-const-closure.rs +++ b/tests/ui/traits/const-traits/call-const-closure.rs @@ -1,4 +1,5 @@ -//@ compile-flags: -Znext-solver +//@[next] compile-flags: -Znext-solver +//@ revisions: next old //@ edition:2021 #![feature(const_trait_impl, const_closures)] diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs deleted file mode 100644 index 85613d9147a3e..0000000000000 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #110395 -//@ compile-flags: -Znext-solver -#![feature(const_closures, const_trait_impl)] - -trait Foo { - fn foo(&self); -} - -impl Foo for () { - fn foo(&self) {} -} - -fn main() { - (const || (()).foo())(); -} diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr deleted file mode 100644 index 2bafddd7cf788..0000000000000 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: cannot use `const` closures outside of const contexts - --> $DIR/const_closure-const_trait_impl-ice-113381.rs:14:6 - | -LL | (const || (()).foo())(); - | ^^^^^ - -error: aborting due to 1 previous error - From 6699c13683a302e6457be7f9fb930ef8102b1982 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 12 Mar 2026 21:29:15 +1100 Subject: [PATCH 17/18] Move the big `rustc_query_impl` macro into a physical `query_impl.rs` Moving the macro and its expansion into the same physical file resolves a lot of tension in the current module arrangement. Code in the macro is now free to use plain imports in the same file, and there is no longer any question of whether `mod query_impl` should be declared inside the macro, or surrounding a separate expansion site. --- compiler/rustc_query_impl/src/execution.rs | 2 +- compiler/rustc_query_impl/src/lib.rs | 14 +- compiler/rustc_query_impl/src/plumbing.rs | 263 +---------------- .../rustc_query_impl/src/profiling_support.rs | 2 +- compiler/rustc_query_impl/src/query_impl.rs | 266 ++++++++++++++++++ 5 files changed, 275 insertions(+), 272 deletions(-) create mode 100644 compiler/rustc_query_impl/src/query_impl.rs diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 2b535d64cf706..eae91a2b2e566 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -18,9 +18,9 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::warn; use crate::dep_graph::{DepNode, DepNodeIndex}; -use crate::for_each_query_vtable; use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; use crate::plumbing::{current_query_job, next_job_id, start_query}; +use crate::query_impl::for_each_query_vtable; #[inline] fn equivalent_key(k: K) -> impl Fn(&(K, V)) -> bool { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 44c97d19914c9..c62ea3e289fcb 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -10,26 +10,24 @@ use rustc_data_structures::sync::AtomicU64; use rustc_middle::dep_graph; -use rustc_middle::queries::{self, ExternProviders, Providers, TaggedQueryKey}; +use rustc_middle::queries::{ExternProviders, Providers}; +use rustc_middle::query::QueryCache; use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_middle::query::plumbing::{QuerySystem, QueryVTable}; -use rustc_middle::query::{AsLocalQueryKey, QueryCache, QueryMode}; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_jobs_from_all_queries}; pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; -#[macro_use] -mod plumbing; - mod dep_kind_vtables; mod error; mod execution; mod from_cycle_error; mod job; +mod plumbing; mod profiling_support; +mod query_impl; /// Trait that knows how to look up the [`QueryVTable`] for a particular query. /// @@ -51,7 +49,7 @@ pub fn query_system<'tcx>( on_disk_cache: Option, incremental: bool, ) -> QuerySystem<'tcx> { - let mut query_vtables = make_query_vtables(incremental); + let mut query_vtables = query_impl::make_query_vtables(incremental); from_cycle_error::specialize_query_vtables(&mut query_vtables); QuerySystem { arenas: Default::default(), @@ -63,8 +61,6 @@ pub fn query_system<'tcx>( } } -rustc_middle::rustc_with_all_queries! { define_queries! } - pub fn provide(providers: &mut rustc_middle::util::Providers) { providers.hooks.alloc_self_profile_query_strings = profiling_support::alloc_self_profile_query_strings; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index bece2e56c0a86..a346836057d93 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -1,7 +1,3 @@ -//! The implementation of the query system itself. This defines the macros that -//! generate the actual methods on tcx which find and execute the provider, -//! manage the caches, and so forth. - use std::num::NonZero; use rustc_data_structures::sync::{DynSend, DynSync}; @@ -29,10 +25,8 @@ use rustc_span::def_id::LOCAL_CRATE; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; use crate::job::find_dep_kind_root; -use crate::{ - CollectActiveJobsKind, GetQueryVTable, collect_active_jobs_from_all_queries, - for_each_query_vtable, -}; +use crate::query_impl::for_each_query_vtable; +use crate::{CollectActiveJobsKind, GetQueryVTable, collect_active_jobs_from_all_queries}; fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) { let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::Full); @@ -283,256 +277,3 @@ pub(crate) fn force_from_dep_node_inner<'tcx, Q: GetQueryVTable<'tcx>>( false } } - -macro_rules! define_queries { - ( - // Note: `$K` and `$V` are unused but present so this can be called by - // `rustc_with_all_queries`. - queries { - $( - $(#[$attr:meta])* - fn $name:ident($K:ty) -> $V:ty - { - // Search for (QMODLIST) to find all occurrences of this query modifier list. - anon: $anon:literal, - arena_cache: $arena_cache:literal, - cache_on_disk: $cache_on_disk:literal, - depth_limit: $depth_limit:literal, - eval_always: $eval_always:literal, - feedable: $feedable:literal, - no_hash: $no_hash:literal, - returns_error_guaranteed: $returns_error_guaranteed:literal, - separate_provide_extern: $separate_provide_extern:literal, - } - )* - } - // Non-queries are unused here. - non_queries { $($_:tt)* } - ) => { - pub(crate) mod query_impl { $(pub(crate) mod $name { - use super::super::*; - use ::rustc_middle::query::erase::{self, Erased}; - - // It seems to be important that every query has its own monomorphic - // copy of `execute_query_incr` and `execute_query_non_incr`. - // Trying to inline these wrapper functions into their generic - // "inner" helpers tends to break `tests/run-make/short-ice`. - - pub(crate) mod execute_query_incr { - use super::*; - - // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames - // when `RUST_BACKTRACE=1`, add a new mod with `$name` here is to allow duplicate naming - #[inline(never)] - pub(crate) fn __rust_end_short_backtrace<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - key: queries::$name::Key<'tcx>, - mode: QueryMode, - ) -> Option>> { - #[cfg(debug_assertions)] - let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - execution::execute_query_incr_inner( - &tcx.query_system.query_vtables.$name, - tcx, - span, - key, - mode - ) - } - } - - pub(crate) mod execute_query_non_incr { - use super::*; - - #[inline(never)] - pub(crate) fn __rust_end_short_backtrace<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - key: queries::$name::Key<'tcx>, - __mode: QueryMode, - ) -> Option>> { - Some(execution::execute_query_non_incr_inner( - &tcx.query_system.query_vtables.$name, - tcx, - span, - key, - )) - } - } - - /// Defines an `invoke_provider` function that calls the query's provider, - /// to be used as a function pointer in the query's vtable. - /// - /// To mark a short-backtrace boundary, the function's actual name - /// (after demangling) must be `__rust_begin_short_backtrace`. - mod invoke_provider_fn { - use super::*; - use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased}; - - #[inline(never)] - pub(crate) fn __rust_begin_short_backtrace<'tcx>( - tcx: TyCtxt<'tcx>, - key: Key<'tcx>, - ) -> Erased> { - #[cfg(debug_assertions)] - let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - - // Call the actual provider function for this query. - - #[cfg($separate_provide_extern)] - let provided_value = if let Some(local_key) = key.as_local_key() { - (tcx.query_system.local_providers.$name)(tcx, local_key) - } else { - (tcx.query_system.extern_providers.$name)(tcx, key) - }; - - #[cfg(not($separate_provide_extern))] - let provided_value = (tcx.query_system.local_providers.$name)(tcx, key); - - rustc_middle::ty::print::with_reduced_queries!({ - tracing::trace!(?provided_value); - }); - - // Erase the returned value, because `QueryVTable` uses erased values. - // For queries with `arena_cache`, this also arena-allocates the value. - provided_to_erased(tcx, provided_value) - } - } - - pub(crate) fn make_query_vtable<'tcx>(incremental: bool) - -> QueryVTable<'tcx, queries::$name::Cache<'tcx>> - { - QueryVTable { - name: stringify!($name), - anon: $anon, - eval_always: $eval_always, - depth_limit: $depth_limit, - feedable: $feedable, - dep_kind: dep_graph::DepKind::$name, - state: Default::default(), - cache: Default::default(), - - invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, - - #[cfg($cache_on_disk)] - will_cache_on_disk_for_key_fn: - rustc_middle::queries::_cache_on_disk_if_fns::$name, - #[cfg(not($cache_on_disk))] - will_cache_on_disk_for_key_fn: |_, _| false, - - #[cfg($cache_on_disk)] - try_load_from_disk_fn: |tcx, key, prev_index, index| { - // Check the `cache_on_disk_if` condition for this key. - if !rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { - return None; - } - - let value: queries::$name::ProvidedValue<'tcx> = - $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; - - // Arena-alloc the value if appropriate, and erase it. - Some(queries::$name::provided_to_erased(tcx, value)) - }, - #[cfg(not($cache_on_disk))] - try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, - - #[cfg($cache_on_disk)] - is_loadable_from_disk_fn: |tcx, key, index| -> bool { - rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && - $crate::plumbing::loadable_from_disk(tcx, index) - }, - #[cfg(not($cache_on_disk))] - is_loadable_from_disk_fn: |_tcx, _key, _index| false, - - // The default just emits `err` and then aborts. - // `from_cycle_error::specialize_query_vtables` overwrites this default for - // certain queries. - value_from_cycle_error: |_tcx, _key, _cycle, err| { - $crate::from_cycle_error::default(err) - }, - - #[cfg($no_hash)] - hash_value_fn: None, - #[cfg(not($no_hash))] - hash_value_fn: Some(|hcx, erased_value: &erase::Erased>| { - let value = erase::restore_val(*erased_value); - rustc_middle::dep_graph::hash_result(hcx, &value) - }), - - format_value: |value| format!("{:?}", erase::restore_val::>(*value)), - create_tagged_key: TaggedQueryKey::$name, - execute_query_fn: if incremental { - query_impl::$name::execute_query_incr::__rust_end_short_backtrace - } else { - query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace - }, - } - } - - /// Marker type that implements [`GetQueryVTable`] for this query. - pub(crate) enum VTableGetter {} - - impl<'tcx> GetQueryVTable<'tcx> for VTableGetter { - type Cache = rustc_middle::queries::$name::Cache<'tcx>; - - #[inline(always)] - fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache> { - &tcx.query_system.query_vtables.$name - } - } - })*} - - pub fn make_query_vtables<'tcx>(incremental: bool) -> queries::QueryVTables<'tcx> { - queries::QueryVTables { - $( - $name: query_impl::$name::make_query_vtable(incremental), - )* - } - } - - /// Given a filter condition (e.g. `ALL` or `CACHE_ON_DISK`), a `tcx`, - /// and a closure expression that accepts `&QueryVTable`, this macro - /// calls that closure with each query vtable that satisfies the filter - /// condition. - /// - /// This needs to be a macro, because the vtables can have different - /// key/value/cache types for different queries. - /// - /// This macro's argument syntax is specifically intended to look like - /// plain Rust code, so that `for_each_query_vtable!(..)` calls will be - /// formatted by rustfmt. - /// - /// To avoid too much nested-macro complication, filter conditions are - /// implemented by hand as needed. - macro_rules! for_each_query_vtable { - // Call with all queries. - (ALL, $tcx:expr, $closure:expr) => {{ - let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx; - $( - let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> = - &tcx.query_system.query_vtables.$name; - $closure(query); - )* - }}; - - // Only call with queries that can potentially cache to disk. - // - // This allows the use of trait bounds that only need to be satisfied - // by the subset of queries that actually cache to disk. - (CACHE_ON_DISK, $tcx:expr, $closure:expr) => {{ - let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx; - $( - #[cfg($cache_on_disk)] - { - let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> = - &tcx.query_system.query_vtables.$name; - $closure(query); - } - )* - }} - } - - pub(crate) use for_each_query_vtable; - } -} diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index c88bc8845ebdb..c34938cdb3866 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -10,7 +10,7 @@ use rustc_middle::query::QueryCache; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::ty::TyCtxt; -use crate::for_each_query_vtable; +use crate::query_impl::for_each_query_vtable; pub(crate) struct QueryKeyStringCache { def_id_cache: FxHashMap, diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs new file mode 100644 index 0000000000000..f833ec3871042 --- /dev/null +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -0,0 +1,266 @@ +use rustc_middle::queries::TaggedQueryKey; +use rustc_middle::query::plumbing::QueryVTable; +use rustc_middle::query::{AsLocalQueryKey, QueryMode}; +use rustc_middle::ty::TyCtxt; +use rustc_middle::{dep_graph, queries}; +use rustc_span::Span; + +use crate::{GetQueryVTable, execution, query_impl}; + +macro_rules! define_queries { + ( + // Note: `$K` and `$V` are unused but present so this can be called by + // `rustc_with_all_queries`. + queries { + $( + $(#[$attr:meta])* + fn $name:ident($K:ty) -> $V:ty + { + // Search for (QMODLIST) to find all occurrences of this query modifier list. + anon: $anon:literal, + arena_cache: $arena_cache:literal, + cache_on_disk: $cache_on_disk:literal, + depth_limit: $depth_limit:literal, + eval_always: $eval_always:literal, + feedable: $feedable:literal, + no_hash: $no_hash:literal, + returns_error_guaranteed: $returns_error_guaranteed:literal, + separate_provide_extern: $separate_provide_extern:literal, + } + )* + } + // Non-queries are unused here. + non_queries { $($_:tt)* } + ) => { + // This macro expects to be expanded into `crate::query_impl`, which is this file. + $( + pub(crate) mod $name { + use super::*; + use ::rustc_middle::query::erase::{self, Erased}; + + // It seems to be important that every query has its own monomorphic + // copy of `execute_query_incr` and `execute_query_non_incr`. + // Trying to inline these wrapper functions into their generic + // "inner" helpers tends to break `tests/run-make/short-ice`. + + pub(crate) mod execute_query_incr { + use super::*; + + // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames + // when `RUST_BACKTRACE=1`, add a new mod with `$name` here is to allow duplicate naming + #[inline(never)] + pub(crate) fn __rust_end_short_backtrace<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + key: queries::$name::Key<'tcx>, + mode: QueryMode, + ) -> Option>> { + #[cfg(debug_assertions)] + let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); + execution::execute_query_incr_inner( + &tcx.query_system.query_vtables.$name, + tcx, + span, + key, + mode + ) + } + } + + pub(crate) mod execute_query_non_incr { + use super::*; + + #[inline(never)] + pub(crate) fn __rust_end_short_backtrace<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + key: queries::$name::Key<'tcx>, + __mode: QueryMode, + ) -> Option>> { + Some(execution::execute_query_non_incr_inner( + &tcx.query_system.query_vtables.$name, + tcx, + span, + key, + )) + } + } + + /// Defines an `invoke_provider` function that calls the query's provider, + /// to be used as a function pointer in the query's vtable. + /// + /// To mark a short-backtrace boundary, the function's actual name + /// (after demangling) must be `__rust_begin_short_backtrace`. + mod invoke_provider_fn { + use super::*; + use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased}; + + #[inline(never)] + pub(crate) fn __rust_begin_short_backtrace<'tcx>( + tcx: TyCtxt<'tcx>, + key: Key<'tcx>, + ) -> Erased> { + #[cfg(debug_assertions)] + let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); + + // Call the actual provider function for this query. + + #[cfg($separate_provide_extern)] + let provided_value = if let Some(local_key) = key.as_local_key() { + (tcx.query_system.local_providers.$name)(tcx, local_key) + } else { + (tcx.query_system.extern_providers.$name)(tcx, key) + }; + + #[cfg(not($separate_provide_extern))] + let provided_value = (tcx.query_system.local_providers.$name)(tcx, key); + + rustc_middle::ty::print::with_reduced_queries!({ + tracing::trace!(?provided_value); + }); + + // Erase the returned value, because `QueryVTable` uses erased values. + // For queries with `arena_cache`, this also arena-allocates the value. + provided_to_erased(tcx, provided_value) + } + } + + pub(crate) fn make_query_vtable<'tcx>(incremental: bool) + -> QueryVTable<'tcx, queries::$name::Cache<'tcx>> + { + QueryVTable { + name: stringify!($name), + anon: $anon, + eval_always: $eval_always, + depth_limit: $depth_limit, + feedable: $feedable, + dep_kind: dep_graph::DepKind::$name, + state: Default::default(), + cache: Default::default(), + + invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, + + #[cfg($cache_on_disk)] + will_cache_on_disk_for_key_fn: + rustc_middle::queries::_cache_on_disk_if_fns::$name, + #[cfg(not($cache_on_disk))] + will_cache_on_disk_for_key_fn: |_, _| false, + + #[cfg($cache_on_disk)] + try_load_from_disk_fn: |tcx, key, prev_index, index| { + // Check the `cache_on_disk_if` condition for this key. + if !rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { + return None; + } + + let value: queries::$name::ProvidedValue<'tcx> = + $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; + + // Arena-alloc the value if appropriate, and erase it. + Some(queries::$name::provided_to_erased(tcx, value)) + }, + #[cfg(not($cache_on_disk))] + try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, + + #[cfg($cache_on_disk)] + is_loadable_from_disk_fn: |tcx, key, index| -> bool { + rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && + $crate::plumbing::loadable_from_disk(tcx, index) + }, + #[cfg(not($cache_on_disk))] + is_loadable_from_disk_fn: |_tcx, _key, _index| false, + + // The default just emits `err` and then aborts. + // `from_cycle_error::specialize_query_vtables` overwrites this default for + // certain queries. + value_from_cycle_error: |_tcx, _key, _cycle, err| { + $crate::from_cycle_error::default(err) + }, + + #[cfg($no_hash)] + hash_value_fn: None, + #[cfg(not($no_hash))] + hash_value_fn: Some(|hcx, erased_value: &erase::Erased>| { + let value = erase::restore_val(*erased_value); + rustc_middle::dep_graph::hash_result(hcx, &value) + }), + + format_value: |value| format!("{:?}", erase::restore_val::>(*value)), + create_tagged_key: TaggedQueryKey::$name, + execute_query_fn: if incremental { + query_impl::$name::execute_query_incr::__rust_end_short_backtrace + } else { + query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace + }, + } + } + + /// Marker type that implements [`GetQueryVTable`] for this query. + pub(crate) enum VTableGetter {} + + impl<'tcx> GetQueryVTable<'tcx> for VTableGetter { + type Cache = rustc_middle::queries::$name::Cache<'tcx>; + + #[inline(always)] + fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache> { + &tcx.query_system.query_vtables.$name + } + } + } + )* + + pub(crate) fn make_query_vtables<'tcx>(incremental: bool) -> queries::QueryVTables<'tcx> { + queries::QueryVTables { + $( + $name: query_impl::$name::make_query_vtable(incremental), + )* + } + } + + /// Given a filter condition (e.g. `ALL` or `CACHE_ON_DISK`), a `tcx`, + /// and a closure expression that accepts `&QueryVTable`, this macro + /// calls that closure with each query vtable that satisfies the filter + /// condition. + /// + /// This needs to be a macro, because the vtables can have different + /// key/value/cache types for different queries. + /// + /// This macro's argument syntax is specifically intended to look like + /// plain Rust code, so that `for_each_query_vtable!(..)` calls will be + /// formatted by rustfmt. + /// + /// To avoid too much nested-macro complication, filter conditions are + /// implemented by hand as needed. + macro_rules! for_each_query_vtable { + // Call with all queries. + (ALL, $tcx:expr, $closure:expr) => {{ + let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx; + $( + let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> = + &tcx.query_system.query_vtables.$name; + $closure(query); + )* + }}; + + // Only call with queries that can potentially cache to disk. + // + // This allows the use of trait bounds that only need to be satisfied + // by the subset of queries that actually cache to disk. + (CACHE_ON_DISK, $tcx:expr, $closure:expr) => {{ + let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx; + $( + #[cfg($cache_on_disk)] + { + let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> = + &tcx.query_system.query_vtables.$name; + $closure(query); + } + )* + }} + } + + pub(crate) use for_each_query_vtable; + } +} + +rustc_middle::rustc_with_all_queries! { define_queries! } From 69ea0c506d3be9f53809feb631c62d327d4b617e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 12 Mar 2026 21:29:15 +1100 Subject: [PATCH 18/18] Use more fully-qualified paths and local imports in `query_impl` --- compiler/rustc_query_impl/src/query_impl.rs | 51 ++++++++++++--------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index f833ec3871042..d5fb90871e76e 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -1,11 +1,11 @@ use rustc_middle::queries::TaggedQueryKey; +use rustc_middle::query::erase::{self, Erased}; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{AsLocalQueryKey, QueryMode}; use rustc_middle::ty::TyCtxt; -use rustc_middle::{dep_graph, queries}; use rustc_span::Span; -use crate::{GetQueryVTable, execution, query_impl}; +use crate::GetQueryVTable; macro_rules! define_queries { ( @@ -36,7 +36,6 @@ macro_rules! define_queries { $( pub(crate) mod $name { use super::*; - use ::rustc_middle::query::erase::{self, Erased}; // It seems to be important that every query has its own monomorphic // copy of `execute_query_incr` and `execute_query_non_incr`. @@ -45,6 +44,7 @@ macro_rules! define_queries { pub(crate) mod execute_query_incr { use super::*; + use rustc_middle::queries::$name::{Key, Value}; // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames // when `RUST_BACKTRACE=1`, add a new mod with `$name` here is to allow duplicate naming @@ -52,12 +52,12 @@ macro_rules! define_queries { pub(crate) fn __rust_end_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, span: Span, - key: queries::$name::Key<'tcx>, + key: Key<'tcx>, mode: QueryMode, - ) -> Option>> { + ) -> Option>> { #[cfg(debug_assertions)] let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered(); - execution::execute_query_incr_inner( + crate::execution::execute_query_incr_inner( &tcx.query_system.query_vtables.$name, tcx, span, @@ -69,15 +69,16 @@ macro_rules! define_queries { pub(crate) mod execute_query_non_incr { use super::*; + use rustc_middle::queries::$name::{Key, Value}; #[inline(never)] pub(crate) fn __rust_end_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, span: Span, - key: queries::$name::Key<'tcx>, + key: Key<'tcx>, __mode: QueryMode, - ) -> Option>> { - Some(execution::execute_query_non_incr_inner( + ) -> Option>> { + Some(crate::execution::execute_query_non_incr_inner( &tcx.query_system.query_vtables.$name, tcx, span, @@ -93,7 +94,7 @@ macro_rules! define_queries { /// (after demangling) must be `__rust_begin_short_backtrace`. mod invoke_provider_fn { use super::*; - use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased}; + use rustc_middle::queries::$name::{Key, Value, provided_to_erased}; #[inline(never)] pub(crate) fn __rust_begin_short_backtrace<'tcx>( @@ -126,15 +127,17 @@ macro_rules! define_queries { } pub(crate) fn make_query_vtable<'tcx>(incremental: bool) - -> QueryVTable<'tcx, queries::$name::Cache<'tcx>> + -> QueryVTable<'tcx, rustc_middle::queries::$name::Cache<'tcx>> { + use rustc_middle::queries::$name::Value; + QueryVTable { name: stringify!($name), anon: $anon, eval_always: $eval_always, depth_limit: $depth_limit, feedable: $feedable, - dep_kind: dep_graph::DepKind::$name, + dep_kind: rustc_middle::dep_graph::DepKind::$name, state: Default::default(), cache: Default::default(), @@ -148,16 +151,18 @@ macro_rules! define_queries { #[cfg($cache_on_disk)] try_load_from_disk_fn: |tcx, key, prev_index, index| { + use rustc_middle::queries::$name::{ProvidedValue, provided_to_erased}; + // Check the `cache_on_disk_if` condition for this key. if !rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { return None; } - let value: queries::$name::ProvidedValue<'tcx> = + let loaded_value: ProvidedValue<'tcx> = $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?; // Arena-alloc the value if appropriate, and erase it. - Some(queries::$name::provided_to_erased(tcx, value)) + Some(provided_to_erased(tcx, loaded_value)) }, #[cfg(not($cache_on_disk))] try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, @@ -180,17 +185,19 @@ macro_rules! define_queries { #[cfg($no_hash)] hash_value_fn: None, #[cfg(not($no_hash))] - hash_value_fn: Some(|hcx, erased_value: &erase::Erased>| { + hash_value_fn: Some(|hcx, erased_value: &erase::Erased>| { let value = erase::restore_val(*erased_value); rustc_middle::dep_graph::hash_result(hcx, &value) }), - format_value: |value| format!("{:?}", erase::restore_val::>(*value)), + format_value: |erased_value: &erase::Erased>| { + format!("{:?}", erase::restore_val(*erased_value)) + }, create_tagged_key: TaggedQueryKey::$name, execute_query_fn: if incremental { - query_impl::$name::execute_query_incr::__rust_end_short_backtrace + crate::query_impl::$name::execute_query_incr::__rust_end_short_backtrace } else { - query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace + crate::query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace }, } } @@ -209,10 +216,12 @@ macro_rules! define_queries { } )* - pub(crate) fn make_query_vtables<'tcx>(incremental: bool) -> queries::QueryVTables<'tcx> { - queries::QueryVTables { + pub(crate) fn make_query_vtables<'tcx>(incremental: bool) + -> rustc_middle::queries::QueryVTables<'tcx> + { + rustc_middle::queries::QueryVTables { $( - $name: query_impl::$name::make_query_vtable(incremental), + $name: crate::query_impl::$name::make_query_vtable(incremental), )* } }