From accbfccd597b332a36d6d22335d1d7d8e9507619 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 11 Mar 2026 07:22:26 +0000 Subject: [PATCH 01/13] 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 02/13] 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 03/13] 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 04/13] 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 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 05/13] 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 06/13] 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 07/13] 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 08/13] 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 09/13] 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 10/13] 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 11/13] 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 12/13] 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 13/13] 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 -