From 180e599d56c325513f1bcba038952b3cda17575c Mon Sep 17 00:00:00 2001 From: Adwin White Date: Sat, 27 Jun 2026 15:56:22 +0800 Subject: [PATCH 1/9] use correct typing mode in mir building for the next solver --- compiler/rustc_middle/src/ty/mod.rs | 17 ++++++++++++ compiler/rustc_mir_build/src/builder/mod.rs | 24 ++++++++++++----- compiler/rustc_mir_build/src/builder/scope.rs | 6 +++-- .../rustc_mir_build/src/check_tail_calls.rs | 3 +-- .../rustc_mir_build/src/check_unsafety.rs | 3 +-- compiler/rustc_mir_build/src/thir/cx/mod.rs | 4 +-- .../src/thir/pattern/check_match.rs | 3 +-- .../opaques/wrong-typing-mode-ice-1.rs | 27 +++++++++++++++++++ .../opaques/wrong-typing-mode-ice-1.stderr | 9 +++++++ .../opaques/wrong-typing-mode-ice-2.rs | 15 +++++++++++ 10 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.rs create mode 100644 tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.stderr create mode 100644 tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-2.rs diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3ea798ee45fb2..b86a51b28b022 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1127,6 +1127,23 @@ impl<'tcx> TypingEnv<'tcx> { Self::new(tcx.param_env(def_id), TypingMode::non_body_analysis()) } + /// Ideally we just use `TypingMode::PostTypeckUntilBorrowck`. + /// But that's not compatible with the old solver yet. + /// + /// FIXME: this should not be needed in the long term. + pub fn post_typeck_until_borrowck_for_mir_build( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + ) -> TypingEnv<'tcx> { + if tcx.use_typing_mode_post_typeck_until_borrowck() { + TypingEnv::new(tcx.param_env(def_id.to_def_id()), ty::TypingMode::borrowck(tcx, def_id)) + } else { + // FIXME(#132279): We're in a body, we should use a typing + // mode which reveals the opaque types defined by that body. + TypingEnv::non_body_analysis(tcx, def_id) + } + } + pub fn post_analysis(tcx: TyCtxt<'tcx>, def_id: impl IntoQueryKey) -> TypingEnv<'tcx> { TypingEnv::new(tcx.param_env_normalized_for_post_analysis(def_id), TypingMode::PostAnalysis) } diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 854a378bb993d..34a9e573a508f 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -505,9 +505,15 @@ fn construct_fn<'tcx>( ); } - // FIXME(#132279): This should be able to reveal opaque - // types defined during HIR typeck. - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let typing_mode = if tcx.use_typing_mode_post_typeck_until_borrowck() { + TypingMode::borrowck(tcx, fn_def) + } else { + // FIXME(#132279): This should be able to reveal opaque + // types defined during HIR typeck. + TypingMode::non_body_analysis() + }; + + let infcx = tcx.infer_ctxt().build(typing_mode); let mut builder = Builder::new( thir, infcx, @@ -586,9 +592,15 @@ fn construct_const<'a, 'tcx>( _ => span_bug!(tcx.def_span(def), "can't build MIR for {:?}", def), }; - // FIXME(#132279): We likely want to be able to use the hidden types of - // opaques used by this function here. - let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let typing_mode = if tcx.use_typing_mode_post_typeck_until_borrowck() { + TypingMode::borrowck(tcx, def) + } else { + // FIXME(#132279): This should be able to reveal opaque + // types defined during HIR typeck. + TypingMode::non_body_analysis() + }; + + let infcx = tcx.infer_ctxt().build(typing_mode); let mut builder = Builder::new(thir, infcx, def, hir_id, span, 0, const_ty, const_ty_span, None); diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 84abb6ca70e70..4810418c4bb10 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -969,8 +969,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { tcx: self.tcx, typeck_results, module: self.tcx.parent_module(self.hir_id).to_def_id(), - // FIXME(#132279): We're in a body, should handle opaques. - typing_env: rustc_middle::ty::TypingEnv::non_body_analysis(self.tcx, self.def_id), + typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build( + self.tcx, + self.def_id, + ), dropless_arena: &dropless_arena, match_lint_level: self.hir_id, whole_match_span: Some(rustc_span::Span::default()), diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index ac9f6e384cf04..26deba3f58c5a 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -27,8 +27,7 @@ pub(crate) fn check_tail_calls(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), E tcx, thir, found_errors: Ok(()), - // FIXME(#132279): we're clearly in a body here. - typing_env: ty::TypingEnv::non_body_analysis(tcx, def), + typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(tcx, def), is_closure, caller_def_id: def, }; diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index a5e5b9f7699b8..70e9129ffee3f 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -1093,8 +1093,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { body_target_features, assignment_info: None, in_union_destructure: false, - // FIXME(#132279): we're clearly in a body here. - typing_env: ty::TypingEnv::non_body_analysis(tcx, def), + typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(tcx, def), inside_adt: false, warnings: &mut warnings, suggest_unsafe_block: true, diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index eb8573dd5e886..8e654032e8b71 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -99,9 +99,7 @@ impl<'tcx> ThirBuildCx<'tcx> { Self { tcx, thir: Thir::new(body_type), - // FIXME(#132279): We're in a body, we should use a typing - // mode which reveals the opaque types defined by that body. - typing_env: ty::TypingEnv::non_body_analysis(tcx, def), + typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(tcx, def), typeck_results, body_owner: def.to_def_id(), apply_adjustments: !find_attr!(tcx, hir_id, CustomMir(..)), diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 7a38d9293724d..a757c96bd7f3d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -39,8 +39,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err tcx, thir: &*thir, typeck_results, - // FIXME(#132279): We're in a body, should handle opaques. - typing_env: ty::TypingEnv::non_body_analysis(tcx, def_id), + typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(tcx, def_id), hir_source: tcx.local_def_id_to_hir_id(def_id), let_source: LetSource::None, pattern_arena: &pattern_arena, diff --git a/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.rs b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.rs new file mode 100644 index 0000000000000..700e38cc3d8a2 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.rs @@ -0,0 +1,27 @@ +//@ compile-flags: -Znext-solver +//@ edition: 2024 + +// Previously, when building MIR body, we were using typing env +// `non_body_analysis` which is wrong since we're indeed in a body. +// It caused opaque types in defining body to be not revealed. +// This caused opaque types in the defining body not to be revealed. + +#![feature(type_alias_impl_trait)] + +struct Task(F); + +impl Task { + fn spawn(&self, _: impl FnOnce() -> Foo) {} +} +type Foo = impl Sized; + +#[define_opaque(Foo)] +fn foo() { + async fn cb() {} + Task::spawn(&POOL, cb) +} + +static POOL: Task = todo!(); +//~^ ERROR: evaluation panicked + +fn main() {} diff --git a/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.stderr b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.stderr new file mode 100644 index 0000000000000..95c000394a982 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-1.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation panicked: not yet implemented + --> $DIR/wrong-typing-mode-ice-1.rs:24:26 + | +LL | static POOL: Task = todo!(); + | ^^^^^^^ evaluation of `POOL` failed here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-2.rs b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-2.rs new file mode 100644 index 0000000000000..e27858b6b6fa0 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/wrong-typing-mode-ice-2.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Previously, when building MIR body, we were using typing env +// `non_body_analysis` which is wrong since we're indeed in a body. +// It caused opaque types in defining body to be not revealed. +// This caused opaque types in the defining body not to be revealed. + +#![feature(type_alias_impl_trait)] +fn main() { + struct Foo(U); + type U = impl Copy; + let foo: _ = Foo(()); + let Foo(()) = foo; +} From 1316aa8049b15926165ce7f6631fce51f545bd77 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 29 Jun 2026 13:00:05 +0900 Subject: [PATCH 2/9] Instatiate binder instead of skipping when suggesting functiom arg error --- .../src/error_reporting/traits/suggestions.rs | 18 +++-- .../iterator-item-suggest-no-ice.rs | 22 ++++++ .../iterator-item-suggest-no-ice.stderr | 71 +++++++++++++++++++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.rs create mode 100644 tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4e3a31f0e84b5..3c702f8871c42 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4872,12 +4872,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } else if let Some(where_pred) = where_pred.as_projection_clause() && let Some(failed_pred) = failed_pred.as_projection_clause() - && let Some(found) = failed_pred.skip_binder().term.as_type() + && let Some(found) = + failed_pred.map_bound(|pred| pred.term.as_type()).transpose().map(|term| { + self.instantiate_binder_with_fresh_vars( + expr.span, + BoundRegionConversionTime::FnCall, + term, + ) + }) { type_diffs = vec![TypeError::Sorts(ty::error::ExpectedFound { - expected: where_pred - .skip_binder() - .projection_term + expected: self + .instantiate_binder_with_fresh_vars( + expr.span, + BoundRegionConversionTime::FnCall, + where_pred.map_bound(|pred| pred.projection_term), + ) .expect_ty() .to_ty(self.tcx, ty::IsRigid::No), found, diff --git a/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.rs b/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.rs new file mode 100644 index 0000000000000..89a37d8c75a31 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.rs @@ -0,0 +1,22 @@ +//! Regression test for +//@ compile-flags: -Znext-solver + +trait FooMut { + fn bar(&self, _: I) + where + for<'b> &'b I: Iterator; + + fn bar(&self, _: I) + //~^ ERROR: the name `bar` is defined multiple times + where + I: Iterator, + { + let collection = vec![_I].iter().map(|x| ()); + //~^ ERROR: cannot find value `_I` in this scope + self.bar(collection); + //~^ ERROR: `&'b _` is not an iterator + //~| ERROR: type mismatch resolving `<&_ as Iterator>::Item == &()` + } +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.stderr b/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.stderr new file mode 100644 index 0000000000000..0bd604d03fdf2 --- /dev/null +++ b/tests/ui/traits/next-solver/diagnostics/iterator-item-suggest-no-ice.stderr @@ -0,0 +1,71 @@ +error[E0428]: the name `bar` is defined multiple times + --> $DIR/iterator-item-suggest-no-ice.rs:9:5 + | +LL | / fn bar(&self, _: I) +LL | | where +LL | | for<'b> &'b I: Iterator; + | |_______________________________________________- previous definition of the value `bar` here +LL | +LL | / fn bar(&self, _: I) +LL | | +LL | | where +LL | | I: Iterator, +... | +LL | | } + | |_____^ `bar` redefined here + | + = note: `bar` must be defined only once in the value namespace of this trait + +error[E0425]: cannot find value `_I` in this scope + --> $DIR/iterator-item-suggest-no-ice.rs:14:31 + | +LL | let collection = vec![_I].iter().map(|x| ()); + | ^^ not found in this scope + +error[E0277]: `&'b _` is not an iterator + --> $DIR/iterator-item-suggest-no-ice.rs:16:18 + | +LL | self.bar(collection); + | --- ^^^^^^^^^^ `&'b _` is not an iterator + | | + | required by a bound introduced by this call + | + = help: the trait `for<'b> Iterator` is not implemented for `&'b _` +note: required by a bound in `FooMut::bar` + --> $DIR/iterator-item-suggest-no-ice.rs:7:24 + | +LL | fn bar(&self, _: I) + | --- required by a bound in this associated function +LL | where +LL | for<'b> &'b I: Iterator; + | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `FooMut::bar` + +error[E0271]: type mismatch resolving `<&_ as Iterator>::Item == &()` + --> $DIR/iterator-item-suggest-no-ice.rs:16:18 + | +LL | self.bar(collection); + | --- ^^^^^^^^^^ types differ + | | + | required by a bound introduced by this call + | +note: the method call chain might not have had the expected associated types + --> $DIR/iterator-item-suggest-no-ice.rs:14:35 + | +LL | let collection = vec![_I].iter().map(|x| ()); + | -------- ^^^^^^ ----------- `Iterator::Item` remains `<{type error} as Iterator>::Item` here + | | | + | | `Iterator::Item` is `<{type error} as Iterator>::Item` here + | this expression has type `Vec<{type error}>` +note: required by a bound in `FooMut::bar` + --> $DIR/iterator-item-suggest-no-ice.rs:7:33 + | +LL | fn bar(&self, _: I) + | --- required by a bound in this associated function +LL | where +LL | for<'b> &'b I: Iterator; + | ^^^^^^^^^^^^^ required by this bound in `FooMut::bar` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0271, E0277, E0425, E0428. +For more information about an error, try `rustc --explain E0271`. From c61c52f51de4d41929a9a20f5921afa73e5718cf Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:20:11 -0700 Subject: [PATCH 3/9] detect cycles during valtree construction for self-referential consts --- .../src/const_eval/valtrees.rs | 91 ++++++++++++------- compiler/rustc_middle/src/error.rs | 9 ++ .../rustc_middle/src/mir/interpret/error.rs | 2 + .../rustc_middle/src/mir/interpret/queries.rs | 7 ++ .../cyclic-const-generic-issue-144719.rs | 20 ++++ .../cyclic-const-generic-issue-144719.stderr | 19 ++++ .../const_in_pattern/cyclic-const-dag-pass.rs | 13 +++ .../cyclic-const-long-chain-issue-144719.rs | 24 +++++ ...yclic-const-long-chain-issue-144719.stderr | 10 ++ .../cyclic-const-mutual-issue-144719.rs | 14 +++ .../cyclic-const-mutual-issue-144719.stderr | 10 ++ .../cyclic-const-pattern-issue-144719.rs | 13 +++ .../cyclic-const-pattern-issue-144719.stderr | 10 ++ 13 files changed, 211 insertions(+), 31 deletions(-) create mode 100644 tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.rs create mode 100644 tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.stderr create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-dag-pass.rs create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.rs create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.stderr create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.rs create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.stderr create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.rs create mode 100644 tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.stderr diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 1c6b623bbf267..1b6c948657e0d 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,4 +1,5 @@ use rustc_abi::{BackendRepr, FieldIdx, VariantIdx}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; use rustc_middle::traits::ObligationCause; @@ -17,13 +18,15 @@ use crate::interpret::{ intern_const_alloc_recursive, }; -#[instrument(skip(ecx), level = "debug")] +#[instrument(skip(ecx, visited, settled), level = "debug")] fn branches<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, field_count: usize, variant: Option, num_nodes: &mut usize, + visited: &mut FxHashSet>, + settled: &mut FxHashMap, EvalToValTreeResult<'tcx>>, ) -> EvalToValTreeResult<'tcx> { let place = match variant { Some(variant) => ecx.project_downcast(place, variant).unwrap(), @@ -45,7 +48,7 @@ fn branches<'tcx>( for i in 0..field_count { let field = ecx.project_field(&place, FieldIdx::from_usize(i)).unwrap(); - let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; + let valtree = const_to_valtree_inner(ecx, &field, num_nodes, visited, settled)?; branches.push(ty::Const::new_value(*ecx.tcx, valtree, field.layout.ty)); } @@ -57,39 +60,53 @@ fn branches<'tcx>( Ok(ty::ValTree::from_branches(*ecx.tcx, branches)) } -#[instrument(skip(ecx), level = "debug")] +#[instrument(skip(ecx, visited, settled), level = "debug")] fn slice_branches<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, + visited: &mut FxHashSet>, + settled: &mut FxHashMap, EvalToValTreeResult<'tcx>>, ) -> EvalToValTreeResult<'tcx> { let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {place:?}")); let mut elems = Vec::with_capacity(n as usize); for i in 0..n { let place_elem = ecx.project_index(place, i).unwrap(); - let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; + let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes, visited, settled)?; elems.push(ty::Const::new_value(*ecx.tcx, valtree, place_elem.layout.ty)); } Ok(ty::ValTree::from_branches(*ecx.tcx, elems)) } -#[instrument(skip(ecx), level = "debug")] +#[instrument(skip(ecx, visited, settled), level = "debug")] fn const_to_valtree_inner<'tcx>( ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>, num_nodes: &mut usize, + visited: &mut FxHashSet>, + settled: &mut FxHashMap, EvalToValTreeResult<'tcx>>, ) -> EvalToValTreeResult<'tcx> { let tcx = *ecx.tcx; let ty = place.layout.ty; debug!("ty kind: {:?}", ty.kind()); + if let Some(&result) = settled.get(place) { + return result; + } + + if visited.contains(place) { + return Err(ValTreeCreationError::CyclicConst); + } + if *num_nodes >= VALTREE_MAX_NODES { return Err(ValTreeCreationError::NodesOverflow); } - match ty.kind() { + visited.insert(place.clone()); + + let result = ensure_sufficient_stack(|| match ty.kind() { ty::FnDef(..) => { *num_nodes += 1; Ok(ty::ValTree::zst(tcx)) @@ -108,7 +125,7 @@ fn const_to_valtree_inner<'tcx>( // Since the returned valtree does not contain the type or layout, we can just // switch to the base type. place.layout = ecx.layout_of(*base).unwrap(); - ensure_sufficient_stack(|| const_to_valtree_inner(ecx, &place, num_nodes)) + const_to_valtree_inner(ecx, &place, num_nodes, visited, settled) } ty::RawPtr(_, _) => { @@ -120,16 +137,16 @@ fn const_to_valtree_inner<'tcx>( // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { - return Err(ValTreeCreationError::NonSupportedType(ty)); + Err(ValTreeCreationError::NonSupportedType(ty)) + } else { + let val = val.to_scalar(); + // We are in the CTFE machine, so ptr-to-int casts will fail. + // This can only be `Ok` if `val` already is an integer. + match val.try_to_scalar_int() { + Ok(val) => Ok(ty::ValTree::from_scalar_int(tcx, val)), + Err(_) => Err(ValTreeCreationError::NonSupportedType(ty)), + } } - let val = val.to_scalar(); - // We are in the CTFE machine, so ptr-to-int casts will fail. - // This can only be `Ok` if `val` already is an integer. - let Ok(val) = val.try_to_scalar_int() else { - return Err(ValTreeCreationError::NonSupportedType(ty)); - }; - // It's just a ScalarInt! - Ok(ty::ValTree::from_scalar_int(tcx, val)) } // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to @@ -138,33 +155,39 @@ fn const_to_valtree_inner<'tcx>( ty::Ref(_, _, _) => { let derefd_place = ecx.deref_pointer(place).report_err()?; - const_to_valtree_inner(ecx, &derefd_place, num_nodes) + const_to_valtree_inner(ecx, &derefd_place, num_nodes, visited, settled) } - ty::Str | ty::Slice(_) | ty::Array(_, _) => slice_branches(ecx, place, num_nodes), + ty::Str | ty::Slice(_) | ty::Array(_, _) => { + slice_branches(ecx, place, num_nodes, visited, settled) + } // Trait objects are not allowed in type level constants, as we have no concept for // resolving their backing type, even if we can do that at const eval time. We may // hypothetically be able to allow `dyn StructuralPartialEq` trait objects in the future, // but it is unclear if this is useful. ty::Dynamic(..) => Err(ValTreeCreationError::NonSupportedType(ty)), - ty::Tuple(elem_tys) => branches(ecx, place, elem_tys.len(), None, num_nodes), + ty::Tuple(elem_tys) => { + branches(ecx, place, elem_tys.len(), None, num_nodes, visited, settled) + } ty::Adt(def, _) => { if def.is_union() { - return Err(ValTreeCreationError::NonSupportedType(ty)); + Err(ValTreeCreationError::NonSupportedType(ty)) } else if def.variants().is_empty() { bug!("uninhabited types should have errored and never gotten converted to valtree") + } else { + let variant = ecx.read_discriminant(place).report_err()?; + branches( + ecx, + place, + def.variant(variant).fields.len(), + def.is_enum().then_some(variant), + num_nodes, + visited, + settled, + ) } - - let variant = ecx.read_discriminant(place).report_err()?; - branches( - ecx, - place, - def.variant(variant).fields.len(), - def.is_enum().then_some(variant), - num_nodes, - ) } // FIXME(oli-obk): we could look behind opaque types @@ -186,7 +209,11 @@ fn const_to_valtree_inner<'tcx>( | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::UnsafeBinder(_) => Err(ValTreeCreationError::NonSupportedType(ty)), - } + }); + + visited.remove(place); + settled.insert(place.clone(), result); + result } /// Valtrees don't store the `MemPlaceMeta` that all dynamically sized values have in the interpreter. @@ -257,7 +284,9 @@ pub(crate) fn eval_to_valtree<'tcx>( debug!(?place); let mut num_nodes = 0; - const_to_valtree_inner(&ecx, &place, &mut num_nodes) + let mut visited = FxHashSet::default(); + let mut settled = FxHashMap::default(); + const_to_valtree_inner(&ecx, &place, &mut num_nodes, &mut visited, &mut settled) } /// Converts a `ValTree` to a `ConstValue`, which is needed after mir diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 2823b7ba4e22e..ae2695987ff13 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -144,6 +144,15 @@ pub(crate) struct InvalidConstInValtree { pub global_const_id: String, } +#[derive(Diagnostic)] +#[diag("constant {$global_const_id} cannot be used as pattern")] +#[note("constants whose type references itself cannot be used as patterns")] +pub(crate) struct CyclicConstInValtree { + #[primary_span] + pub span: Span, + pub global_const_id: String, +} + #[derive(Diagnostic)] #[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] pub(crate) struct Reentrant; diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 7d9f6903d3ef0..e2c67b29943d6 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -105,6 +105,8 @@ pub enum ValTreeCreationError<'tcx> { InvalidConst, /// Values of this type, or this particular value, are not supported as valtrees. NonSupportedType(Ty<'tcx>), + /// Trying to valtree this constant would cause the valtree to have cycles. + CyclicConst, /// The error has already been handled by const evaluation. ErrorHandled(ErrorHandled), } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 51cf856a2d477..5e060dfb0e137 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -232,6 +232,13 @@ impl<'tcx> TyCtxt<'tcx> { }); Err(ReportedErrorInfo::allowed_in_infallible(handled).into()) } + ValTreeCreationError::CyclicConst => { + let handled = self.dcx().emit_err(error::CyclicConstInValtree { + span, + global_const_id: cid.display(self), + }); + Err(ReportedErrorInfo::allowed_in_infallible(handled).into()) + } ValTreeCreationError::ErrorHandled(handled) => Err(handled), } } diff --git a/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.rs b/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.rs new file mode 100644 index 0000000000000..64dd496ada58d --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.rs @@ -0,0 +1,20 @@ +//! Const generic variant of #144719 + +#![feature(adt_const_params, unsized_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct Thing(&'static Thing); + +static X: Thing = Thing(&X); +const Y: &Thing = &X; + +fn foo() -> usize { 0 } + +fn main() { + foo::(); + //~^ ERROR constant main::{constant#0} cannot be used as pattern + //~| ERROR constant main::{constant#0} cannot be used as pattern +} diff --git a/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.stderr b/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.stderr new file mode 100644 index 0000000000000..2b278d6aba183 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/cyclic-const-generic-issue-144719.stderr @@ -0,0 +1,19 @@ +error: constant main::{constant#0} cannot be used as pattern + --> $DIR/cyclic-const-generic-issue-144719.rs:17:11 + | +LL | foo::(); + | ^ + | + = note: constants whose type references itself cannot be used as patterns + +error: constant main::{constant#0} cannot be used as pattern + --> $DIR/cyclic-const-generic-issue-144719.rs:17:11 + | +LL | foo::(); + | ^ + | + = note: constants whose type references itself cannot be used as patterns + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-dag-pass.rs b/tests/ui/consts/const_in_pattern/cyclic-const-dag-pass.rs new file mode 100644 index 0000000000000..cecf55d23e6b7 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-dag-pass.rs @@ -0,0 +1,13 @@ +//@ run-pass +//! Regression test: shared references to the same static (DAG) must not be +//! misidentified as cyclic during valtree construction. + +#[derive(PartialEq)] +struct Pair(&'static i32, &'static i32); + +static X: i32 = 42; +const P: Pair = Pair(&X, &X); + +fn main() { + if let P = P {} +} diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.rs b/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.rs new file mode 100644 index 0000000000000..39d22558b15dd --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.rs @@ -0,0 +1,24 @@ +//! Regression test for #144719: long reference cycles shouldn't +//! overflow the stack. +//@ rustc-env:RUST_MIN_STACK=3000000 + +#[derive(PartialEq, Copy, Clone)] +struct Thing(&'static Thing); + +const N: usize = 8000; +static A: Thing = Thing(&B[0]); +static B: [Thing; N] = { + let mut x = [Thing(&A); N]; + let mut i = 0; + while i < N - 1 { + x[i] = Thing(&B[i + 1]); + i += 1; + } + x +}; +const C: &Thing = &A; + +fn main() { + if let C = C {} + //~^ ERROR constant C cannot be used as pattern +} diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.stderr b/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.stderr new file mode 100644 index 0000000000000..a549081a26475 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-long-chain-issue-144719.stderr @@ -0,0 +1,10 @@ +error: constant C cannot be used as pattern + --> $DIR/cyclic-const-long-chain-issue-144719.rs:22:12 + | +LL | if let C = C {} + | ^ + | + = note: constants whose type references itself cannot be used as patterns + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.rs b/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.rs new file mode 100644 index 0000000000000..371a6a3811419 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.rs @@ -0,0 +1,14 @@ +//! Regression test for #144719: mutually recursive statics forming a +//! reference cycle caused a stack overflow during valtree construction. + +#[derive(PartialEq)] +struct Thing(&'static Thing); + +static A: Thing = Thing(&B); +static B: Thing = Thing(&A); +const C: &Thing = &A; + +fn main() { + if let C = C {} + //~^ ERROR constant C cannot be used as pattern +} diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.stderr b/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.stderr new file mode 100644 index 0000000000000..e26505d9ce3ef --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-mutual-issue-144719.stderr @@ -0,0 +1,10 @@ +error: constant C cannot be used as pattern + --> $DIR/cyclic-const-mutual-issue-144719.rs:12:12 + | +LL | if let C = C {} + | ^ + | + = note: constants whose type references itself cannot be used as patterns + +error: aborting due to 1 previous error + diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.rs b/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.rs new file mode 100644 index 0000000000000..c54cb254b5cd8 --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.rs @@ -0,0 +1,13 @@ +//! Regression test for #144719: using a self-referential static in a +//! pattern position caused a stack overflow during valtree construction. + +#[derive(PartialEq)] +struct Thing(&'static Thing); + +static X: Thing = Thing(&X); +const Y: &Thing = &X; + +fn main() { + if let Y = Y {} + //~^ ERROR constant Y cannot be used as pattern +} diff --git a/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.stderr b/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.stderr new file mode 100644 index 0000000000000..e89b6abcac33b --- /dev/null +++ b/tests/ui/consts/const_in_pattern/cyclic-const-pattern-issue-144719.stderr @@ -0,0 +1,10 @@ +error: constant Y cannot be used as pattern + --> $DIR/cyclic-const-pattern-issue-144719.rs:11:12 + | +LL | if let Y = Y {} + | ^ + | + = note: constants whose type references itself cannot be used as patterns + +error: aborting due to 1 previous error + From a99be5a85997d0296b30ceb4c5b5b75ad7872a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 6 Jul 2026 11:23:47 +0200 Subject: [PATCH 4/9] update perf --- src/tools/rustc-perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index c0301bc44d175..dec492af8eb74 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit c0301bc44d175b9b2c5442b25049475c39d7700c +Subproject commit dec492af8eb74903879bd356648fc42d7aaf8555 From a6bd564d1ff0902271689417269fd7a3f07a9525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 22 Jun 2026 17:21:28 +0200 Subject: [PATCH 5/9] allow new licenses for rustc_perf --- src/tools/tidy/src/deps.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 9ad70538d1956..ebe3c04ea4cc4 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -244,6 +244,10 @@ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ // tidy-alphabetical-start ("inferno", "CDDL-1.0"), ("option-ext", "MPL-2.0"), + ("terminfo", "WTFPL"), + ("wasite", "Apache-2.0 OR BSL-1.0 OR MIT"), + ("wezterm-bidi", "MIT AND Unicode-DFS-2016"), + ("whoami", "Apache-2.0 OR BSL-1.0 OR MIT"), // tidy-alphabetical-end ]; From 2740e11e40070d8baff1d1d8161839cbca5ba3d5 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 6 Jul 2026 16:09:25 +0100 Subject: [PATCH 6/9] Create simple non ICE-ing path for new trait solver --- .../src/traits/auto_trait.rs | 83 +++++++++++++++++++ src/librustdoc/clean/auto_trait.rs | 1 + 2 files changed, 84 insertions(+) diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 469eb4fc0630c..d6736852e088b 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -33,6 +33,7 @@ pub struct RegionDeps<'tcx> { } pub enum AutoTraitResult { + NoImpl, ExplicitImpl, PositiveImpl(A), NegativeImpl, @@ -80,6 +81,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { ) -> AutoTraitResult { let tcx = self.tcx; + if tcx.next_trait_solver_globally() { + return self.find_auto_trait_generics_next_solver( + ty, + typing_env, + trait_did, + auto_trait_callback, + ); + } + let trait_ref = ty::TraitRef::new(tcx, trait_did, [ty]); let (infcx, orig_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); @@ -175,6 +185,79 @@ impl<'tcx> AutoTraitFinder<'tcx> { AutoTraitResult::PositiveImpl(auto_trait_callback(info)) } + fn find_auto_trait_generics_next_solver( + &self, + ty: Ty<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + trait_did: DefId, + mut auto_trait_callback: impl FnMut(AutoTraitInfo<'tcx>) -> A, + ) -> AutoTraitResult { + // When the new solver is enabled globally we keep things deliberately + // simple. The precise auto-trait synthesis depends on old-solver + // internals, so here we only synthesize a simple field-based auto-trait + // impl for ADTs. + // + // If the self type is not an ADT we return `NoImpl` instead of trying + // to do anything fancy. To decide whether to emit a negative impl, we + // replace the ADT's generic arguments with inference variables and + // check whether the auto trait can hold. A true error from that probe + // becomes a `NegativeImpl`, otherwise we continue on to emit the + // imprecise field-based impl. + // + // This keeps rustdoc from ICE-ing while `-Znext-solver=globally` is + // used for testing, even if the generated synthetic impls are less + // precise. + let tcx = self.tcx; + let ty::Adt(adt_def, args) = *ty.kind() else { + return AutoTraitResult::NoImpl; + }; + + let mut disqualifying_impl = None; + tcx.for_each_relevant_impl(trait_did, ty, |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }); + if let Some(impl_def_id) = disqualifying_impl { + debug!( + "find_auto_trait_generics({:?}): possible manual impl {impl_def_id:?} found, bailing", + ty::TraitRef::new(tcx, trait_did, [ty]), + ); + return AutoTraitResult::ExplicitImpl; + } + + let (infcx, orig_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); + let field_clauses = adt_def + .all_fields() + .map(|field| field.ty(tcx, args).skip_norm_wip()) + .filter(|field_ty| field_ty.has_non_region_param()) + .map(|field_ty| { + ty::TraitPredicate { + trait_ref: ty::TraitRef::new(tcx, trait_did, [field_ty]), + polarity: ty::PredicatePolarity::Positive, + } + .upcast(tcx) + }) + .collect::>>(); + let full_user_env = ty::ParamEnv::new( + tcx.mk_clauses_from_iter(orig_env.caller_bounds().iter().chain(field_clauses)), + ); + + let fresh_args = infcx.fresh_args_for_item(DUMMY_SP, adt_def.did()); + let fresh_ty = ty::EarlyBinder::bind(tcx, ty).instantiate(tcx, fresh_args).skip_norm_wip(); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_bound(ObligationCause::dummy(), orig_env, fresh_ty, trait_did); + let errors = ocx.try_evaluate_obligations(); + if !errors.is_empty() { + return AutoTraitResult::NegativeImpl; + } + + let info = AutoTraitInfo { + full_user_env, + region_data: RegionConstraintData::default(), + vid_to_region: FxIndexMap::default(), + }; + AutoTraitResult::PositiveImpl(auto_trait_callback(info)) + } + /// The core logic responsible for computing the bounds for our synthesized impl. /// /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index fe7e12cf1dc80..3ed08f4c5e933 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -110,6 +110,7 @@ fn synthesize_auto_trait_impl<'tcx>( (generics, ty::ImplPolarity::Negative) } + auto_trait::AutoTraitResult::NoImpl => return None, auto_trait::AutoTraitResult::ExplicitImpl => return None, }; From 84aa27d8958f75200408eb66355b69bfcad549e0 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Mon, 6 Jul 2026 16:11:03 +0100 Subject: [PATCH 7/9] Tests for `find_auto_trait_generics_next_solver` path --- .../synthetic_auto/next-solver-ambiguity.rs | 7 +++++++ .../synthetic_auto/next-solver-possible-impl.rs | 12 ++++++++++++ .../next-solver-globally.rs | 16 ++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/rustdoc-html/synthetic_auto/next-solver-ambiguity.rs create mode 100644 tests/rustdoc-html/synthetic_auto/next-solver-possible-impl.rs create mode 100644 tests/rustdoc-ui/synthetic-auto-trait-impls/next-solver-globally.rs diff --git a/tests/rustdoc-html/synthetic_auto/next-solver-ambiguity.rs b/tests/rustdoc-html/synthetic_auto/next-solver-ambiguity.rs new file mode 100644 index 0000000000000..b48be49b29761 --- /dev/null +++ b/tests/rustdoc-html/synthetic_auto/next-solver-ambiguity.rs @@ -0,0 +1,7 @@ +//@ compile-flags: -Znext-solver=globally +//@ edition: 2021 +#![crate_name = "foo"] + +//@ has 'foo/struct.Foo.html' +//@ has - '//h3[@class="code-header"]' "impl<'a, 'b, T> Send for Foo<'a, 'b, T>where &'a T: Send, &'b T: Send" +pub struct Foo<'a, 'b, T: 'a + 'b>(&'a T, &'b T); diff --git a/tests/rustdoc-html/synthetic_auto/next-solver-possible-impl.rs b/tests/rustdoc-html/synthetic_auto/next-solver-possible-impl.rs new file mode 100644 index 0000000000000..145174a1961a5 --- /dev/null +++ b/tests/rustdoc-html/synthetic_auto/next-solver-possible-impl.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Znext-solver=globally + +#![feature(auto_traits)] +#![crate_name = "foo"] + +pub auto trait Marker {} + +//@ has 'foo/struct.MyType.html' +//@ !has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 'Marker for MyType' +pub struct MyType(T); + +impl Marker for MyType {} diff --git a/tests/rustdoc-ui/synthetic-auto-trait-impls/next-solver-globally.rs b/tests/rustdoc-ui/synthetic-auto-trait-impls/next-solver-globally.rs new file mode 100644 index 0000000000000..73dff8abf3a69 --- /dev/null +++ b/tests/rustdoc-ui/synthetic-auto-trait-impls/next-solver-globally.rs @@ -0,0 +1,16 @@ +// We used to ICE here while trying to synthesize auto trait impls +// with the next trait solver enabled globally. +//@ check-pass +//@ compile-flags: -Znext-solver=globally + +#![feature(const_default)] +#![feature(const_trait_impl)] + +pub struct Inner(T); + +pub struct Outer(Inner); + +impl Unpin for Inner +where + T: const std::default::Default, +{} From 35cbff9a4c977663e71a8d6a0c010a8a3faccdea Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Mon, 6 Jul 2026 17:18:27 +0200 Subject: [PATCH 8/9] rewrite `Align::max_for_target` in a more obvious way --- compiler/rustc_abi/src/lib.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e86c7f15e9679..c2069432e6f8e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -36,6 +36,7 @@ even other Rust compilers, such as rust-analyzer! */ +use std::cmp::min; use std::fmt; #[cfg(feature = "nightly")] use std::iter::Step; @@ -1060,14 +1061,8 @@ impl Align { /// Either `1 << (pointer_bits - 1)` or [`Align::MAX`], whichever is smaller. #[inline] pub fn max_for_target(tdl: &TargetDataLayout) -> Align { - let pointer_bits = tdl.pointer_size().bits(); - if let Ok(pointer_bits) = u8::try_from(pointer_bits) - && pointer_bits <= Align::MAX.pow2 - { - Align { pow2: pointer_bits - 1 } - } else { - Align::MAX - } + let pointer_bits = u8::try_from(tdl.pointer_size().bits()).unwrap(); + min(Align { pow2: pointer_bits - 1 }, Align::MAX) } #[inline] From 81a18e7a94b380bae6363479174ee82b6a66745f Mon Sep 17 00:00:00 2001 From: Cypher Date: Wed, 1 Jul 2026 23:07:55 -0400 Subject: [PATCH 9/9] document blocking guarantees for `std::sync` --- library/std/src/sync/mod.rs | 8 ++++++++ library/std/src/sync/mpmc/mod.rs | 19 +++++++++++++++---- library/std/src/sync/mpsc.rs | 19 ++++++++++++++----- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 439b5b09a5ac0..5ac0ff2da9af5 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -164,6 +164,14 @@ //! [`Once`]: crate::sync::Once //! [`OnceLock`]: crate::sync::OnceLock //! [`RwLock`]: crate::sync::RwLock +//! +//! ## Blocking guarantees +//! +//! Methods that are documented not to block will never block for an unbounded length of time. +//! That is, they may internally block through platform primitives but still "behave" like they are non-blocking. This difference is invisible to most programs. +//! +//! This is only of note to platforms which disallow blocking, such as multithreaded WebAssembly on the main thread. +//! None of the implementations in `std::sync` are guaranteed to be (or remain) non-blocking in this regard. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index a687f39431697..a34eabbccb1fc 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -15,8 +15,9 @@ //! //! 1. An asynchronous, infinitely buffered channel. The [`channel`] function //! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. +//! **asynchronous** (they never block for space to become available; see +//! [`std::sync`] for precise guarantees on blocking.) The channel +//! conceptually has an infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will //! return a `(Sender, Receiver)` tuple where the storage for pending @@ -26,6 +27,7 @@ //! channel where each sender atomically hands off a message to a receiver. //! //! [`send`]: Sender::send +//! [`std::sync`]: ../index.html#blocking-guarantees //! //! ## Disconnection //! @@ -374,6 +376,11 @@ impl Sender { /// If called on a zero-capacity channel, this method will wait for a receive /// operation to appear on the other side of the channel. /// + /// If called on an unbounded channel, this method will never block in order to wait for space to + /// become available. (See [`std::sync`] for precise guarantees on blocking.) + /// + /// [`std::sync`]: ../index.html#blocking-guarantees + /// /// # Examples /// /// ``` @@ -770,9 +777,11 @@ pub struct Iter<'a, T: 'a> { /// if the corresponding channel has hung up. /// /// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. +/// become available. Instead, it will return [`None`]. (See [`std::sync`] for +/// precise guarantees on blocking.) /// /// [`try_iter`]: Receiver::try_iter +/// [`std::sync`]: ../index.html#blocking-guarantees /// /// # Examples /// @@ -917,7 +926,8 @@ impl Receiver { /// /// This method will never block the caller in order to wait for data to /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. + /// possible option of pending data on the channel. (See [`std::sync`] for precise + /// guarantees on blocking.) /// /// If called on a zero-capacity channel, this method will receive a message only if there /// happens to be a send operation on the other side of the channel at the same time. @@ -929,6 +939,7 @@ impl Receiver { /// (one for disconnection, one for an empty buffer). /// /// [`recv`]: Self::recv + /// [`std::sync`]: ../index.html#blocking-guarantees /// /// # Examples /// diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index b74f84ff465c0..ad74ac2248878 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -15,8 +15,9 @@ //! //! 1. An asynchronous, infinitely buffered channel. The [`channel`] function //! will return a `(Sender, Receiver)` tuple where all sends will be -//! **asynchronous** (they never block). The channel conceptually has an -//! infinite buffer. +//! **asynchronous** (they never block for space to become available; see +//! [`std::sync`] for precise guarantees on blocking.) The channel +//! conceptually has an infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will //! return a `(SyncSender, Receiver)` tuple where the storage for pending @@ -26,6 +27,7 @@ //! channel where each sender atomically hands off a message to a receiver. //! //! [`send`]: Sender::send +//! [`std::sync`]: ../index.html#blocking-guarantees //! //! ## Disconnection //! @@ -228,9 +230,11 @@ pub struct Iter<'a, T: 'a> { /// if the corresponding channel has hung up. /// /// This iterator will never block the caller in order to wait for data to -/// become available. Instead, it will return [`None`]. +/// become available. Instead, it will return [`None`]. (See [`std::sync`] for +/// precise guarantees on blocking.) /// /// [`try_iter`]: Receiver::try_iter +/// [`std::sync`]: ../index.html#blocking-guarantees /// /// # Examples /// @@ -590,7 +594,10 @@ impl Sender { /// will be received. It is possible for the corresponding receiver to /// hang up immediately after this function returns [`Ok`]. /// - /// This method will never block the current thread. + /// This method will never block the caller in order to wait for space to + /// become available. (See [`std::sync`] for precise guarantees on blocking.) + /// + /// [`std::sync`]: ../index.html#blocking-guarantees /// /// # Examples /// @@ -798,7 +805,8 @@ impl Receiver { /// /// This method will never block the caller in order to wait for data to /// become available. Instead, this will always return immediately with a - /// possible option of pending data on the channel. + /// possible option of pending data on the channel. (See [`std::sync`] for + /// precise guarantees on blocking.) /// /// This is useful for a flavor of "optimistic check" before deciding to /// block on a receiver. @@ -807,6 +815,7 @@ impl Receiver { /// (one for disconnection, one for an empty buffer). /// /// [`recv`]: Self::recv + /// [`std::sync`]: ../index.html#blocking-guarantees /// /// # Examples ///