Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
71d656e
LinkedGraph: Use IndexVec instead of Vec in LinkedGraph nodes
petrochenkov Feb 14, 2026
96697d4
LinkedGraph: support adding nodes and edges in arbitrary order
petrochenkov Feb 14, 2026
accbfcc
Unconditionally error when trying to create a const coroutine
oli-obk Mar 11, 2026
ae4ddd2
Reject const closures outside const contexts
oli-obk Mar 12, 2026
e8a4611
Const closure's const context is the same as their parent
oli-obk Mar 12, 2026
8459d6b
Fix std doctest build for SGX target.
sardok Mar 12, 2026
86c5da6
delete some duplicated tests
cyrgani Mar 13, 2026
1f44a11
Replace `visit_waiters` with `abstracted_waiters_of`
Zoxc Mar 3, 2026
791f4f9
Remove `value_from_cycle_error` specialization for `type_of_opaque_hi…
Zoxc Mar 12, 2026
cdbe2b3
Remove `value_from_cycle_error` specialization for `erase_and_anonymi…
Zoxc Mar 12, 2026
fed5789
Remove `value_from_cycle_error` specialization for `type_of`
Zoxc Mar 12, 2026
1867653
Allow calling const closures in const items
oli-obk Mar 11, 2026
60bd8e3
Enable the const_closures feature gate to silence an unrelated error
oli-obk Mar 13, 2026
8629126
Allow calling const closures on the old solver
oli-obk Mar 13, 2026
c521b80
Remove the incomplete marker from const closures
oli-obk Mar 13, 2026
9503c31
Some test cleanups
oli-obk Mar 13, 2026
6699c13
Move the big `rustc_query_impl` macro into a physical `query_impl.rs`
Zalathar Mar 12, 2026
69ea0c5
Use more fully-qualified paths and local imports in `query_impl`
Zalathar Mar 12, 2026
76b769c
Rollup merge of #152621 - petrochenkov:graph2, r=Zalathar
Zalathar Mar 14, 2026
1d49de5
Rollup merge of #153376 - Zoxc:cycle-waiters-iter, r=nnethercote
Zalathar Mar 14, 2026
e0bb94e
Rollup merge of #153760 - Zalathar:query-impl, r=nnethercote
Zalathar Mar 14, 2026
4625821
Rollup merge of #153818 - oli-obk:const-closures, r=fee1-dead
Zalathar Mar 14, 2026
4b35702
Rollup merge of #153774 - sardok:fix_sgx_std_doctest_build, r=WaffleL…
Zalathar Mar 14, 2026
22409b4
Rollup merge of #153786 - Zoxc:rm-from-cycle-error-specs, r=nnethercote
Zalathar Mar 14, 2026
00ae089
Rollup merge of #153819 - cyrgani:dupe-tests, r=adwinwhite
Zalathar Mar 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
e.id,
expr_hir_id,
*coroutine_kind,
*constness,
fn_decl,
body,
*fn_decl_span,
Expand Down Expand Up @@ -1060,7 +1061,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
constness: Const,
mut constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
Expand All @@ -1070,11 +1071,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;
Expand Down Expand Up @@ -1157,6 +1165,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,
Expand Down Expand Up @@ -1203,6 +1212,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,
Expand All @@ -1216,7 +1229,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)
}
Expand Down
24 changes: 20 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::mem;

use rustc_abi::ExternAbi;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
Expand Down Expand Up @@ -345,6 +347,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
body.as_deref(),
attrs,
contract.as_deref(),
header.constness,
);

let itctx = ImplTraitContext::Universal;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand All @@ -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)));

Expand All @@ -1389,16 +1400,20 @@ 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 {
self.lower_body(|this| {
(
&[],
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")),
},
)
Expand All @@ -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 {
Expand All @@ -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| {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct LoweringContext<'a, 'hir> {
loop_scope: Option<HirId>,
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,
Expand Down Expand Up @@ -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,
Expand Down
51 changes: 37 additions & 14 deletions compiler/rustc_data_structures/src/graph/linked_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -45,13 +46,13 @@ mod tests;
/// and does not implement those traits, so it has its own implementations of a
/// few basic graph algorithms.
pub struct LinkedGraph<N, E> {
nodes: Vec<Node<N>>,
nodes: IndexVec<NodeIndex, Node<N>>,
edges: Vec<Edge<E>>,
}

pub struct Node<N> {
first_edge: [EdgeIndex; 2], // see module comment
pub data: N,
pub data: Option<N>,
}

#[derive(Debug)]
Expand All @@ -62,7 +63,7 @@ pub struct Edge<E> {
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)]
Expand All @@ -87,19 +88,29 @@ impl NodeIndex {
}
}

impl Idx for NodeIndex {
fn new(idx: usize) -> NodeIndex {
NodeIndex(idx)
}

fn index(self) -> usize {
self.0
}
}

impl<N: Debug, E: Debug> LinkedGraph<N, E> {
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<N>] {
pub fn all_nodes(&self) -> &IndexSlice<NodeIndex, Node<N>> {
&self.nodes
}

Expand All @@ -124,22 +135,34 @@ impl<N: Debug, E: Debug> LinkedGraph<N, E> {
NodeIndex(self.nodes.len())
}

fn ensure_node(&mut self, idx: NodeIndex) -> &mut Node<N> {
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.0].data
self.nodes[idx].data.as_mut().unwrap()
}

pub fn node_data(&self, idx: NodeIndex) -> &N {
&self.nodes[idx.0].data
self.nodes[idx].data.as_ref().unwrap()
}

pub fn node(&self, idx: NodeIndex) -> &Node<N> {
&self.nodes[idx.0]
&self.nodes[idx]
}

// # Edge construction and queries
Expand All @@ -154,16 +177,16 @@ impl<N: Debug, E: Debug> LinkedGraph<N, E> {
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.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
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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,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.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/collect/predicates_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:?}"),
};

Expand Down
17 changes: 5 additions & 12 deletions compiler/rustc_middle/src/dep_graph/retained.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand All @@ -13,7 +12,6 @@ use super::{DepNode, DepNodeIndex};
pub struct RetainedDepGraph {
pub inner: LinkedGraph<DepNode, ()>,
pub indices: FxHashMap<DepNode, NodeIndex>,
pub dep_index_to_index: IndexVec<DepNodeIndex, Option<NodeIndex>>,
}

impl RetainedDepGraph {
Expand All @@ -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)> {
Expand Down
Loading
Loading