From a851949e790f03f0fb8547941621c21c93fc5a58 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Wed, 19 Aug 2026 19:07:33 +0200 Subject: [PATCH 01/13] Rename parameters of `Extend` trait to be more consistent with common implementations --- library/core/src/iter/traits/collect.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index e7e5e30b4eff5..9f5a1edf652dc 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -394,7 +394,7 @@ const impl IntoIterator for I { /// assert_eq!("MyCollection([5, 6, 7, 1, 2, 3])", format!("{c:?}")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub trait Extend { +pub trait Extend { /// Extends a collection with the contents of an iterator. /// /// As this is the only required method for this trait, the [trait-level] docs @@ -413,11 +413,11 @@ pub trait Extend { /// assert_eq!("abcdef", &message); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - fn extend>(&mut self, iter: T); + fn extend>(&mut self, iter: I); /// Extends a collection with exactly one element. #[unstable(feature = "extend_one", issue = "72631")] - fn extend_one(&mut self, item: A) { + fn extend_one(&mut self, item: T) { self.extend(Some(item)); } @@ -442,7 +442,7 @@ pub trait Extend { // This method is for internal usage only. It is only on the trait because of specialization's limitations. #[unstable(feature = "extend_one_unchecked", issue = "none")] #[doc(hidden)] - unsafe fn extend_one_unchecked(&mut self, item: A) + unsafe fn extend_one_unchecked(&mut self, item: T) where Self: Sized, { From db9ab1a7b5aa3f05a35d24f43358d2df84f46551 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Wed, 19 Aug 2026 19:12:32 +0200 Subject: [PATCH 02/13] Rename parameters of `FromIterator` as well --- library/core/src/iter/traits/collect.rs | 32 +++++++++---------- tests/ui/suggestions/missing-assoc-fn.stderr | 2 +- ...stion-when-stmt-and-expr-span-equal.stderr | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 9f5a1edf652dc..f965076f7b165 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -97,41 +97,41 @@ use super::TrustedLen; #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( - Self = "&[{A}]", + Self = "&[{T}]", message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", - label = "try explicitly collecting into a `Vec<{A}>`", + label = "try explicitly collecting into a `Vec<{T}>`", ), on( - all(A = "{integer}", any(Self = "&[{integral}]",)), + all(T = "{integer}", any(Self = "&[{integral}]",)), message = "a slice of type `{Self}` cannot be built since we need to store the elements somewhere", - label = "try explicitly collecting into a `Vec<{A}>`", + label = "try explicitly collecting into a `Vec<{T}>`", ), on( - Self = "[{A}]", + Self = "[{T}]", message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size", - label = "try explicitly collecting into a `Vec<{A}>`", + label = "try explicitly collecting into a `Vec<{T}>`", ), on( - all(A = "{integer}", any(Self = "[{integral}]",)), + all(T = "{integer}", any(Self = "[{integral}]",)), message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size", - label = "try explicitly collecting into a `Vec<{A}>`", + label = "try explicitly collecting into a `Vec<{T}>`", ), on( - Self = "[{A}; _]", + Self = "[{T}; _]", message = "an array of type `{Self}` cannot be built directly from an iterator", - label = "try collecting into a `Vec<{A}>`, then using `.try_into()`", + label = "try collecting into a `Vec<{T}>`, then using `.try_into()`", ), on( - all(A = "{integer}", any(Self = "[{integral}; _]",)), + all(T = "{integer}", any(Self = "[{integral}; _]",)), message = "an array of type `{Self}` cannot be built directly from an iterator", - label = "try collecting into a `Vec<{A}>`, then using `.try_into()`", + label = "try collecting into a `Vec<{T}>`, then using `.try_into()`", ), message = "a value of type `{Self}` cannot be built from an iterator \ - over elements of type `{A}`", - label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" + over elements of type `{T}`", + label = "value of type `{Self}` cannot be built from `std::iter::Iterator`" )] #[rustc_diagnostic_item = "FromIterator"] -pub trait FromIterator: Sized { +pub trait FromIterator: Sized { /// Creates a value from an iterator. /// /// See the [module-level documentation] for more. @@ -149,7 +149,7 @@ pub trait FromIterator: Sized { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "from_iter_fn"] - fn from_iter>(iter: T) -> Self; + fn from_iter>(iter: I) -> Self; } /// Conversion into an [`Iterator`]. diff --git a/tests/ui/suggestions/missing-assoc-fn.stderr b/tests/ui/suggestions/missing-assoc-fn.stderr index d819f7e8bd2c4..ac04f6653866e 100644 --- a/tests/ui/suggestions/missing-assoc-fn.stderr +++ b/tests/ui/suggestions/missing-assoc-fn.stderr @@ -19,7 +19,7 @@ error[E0046]: not all trait items implemented, missing: `from_iter` LL | impl FromIterator<()> for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation | - = help: implement the missing item: `fn from_iter>(_: T) -> Self { todo!() }` + = help: implement the missing item: `fn from_iter>(_: I) -> Self { todo!() }` error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr index 6dd4b1e5d5110..1f225767472ad 100644 --- a/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr +++ b/tests/ui/suggestions/semi-suggestion-when-stmt-and-expr-span-equal.stderr @@ -20,7 +20,7 @@ LL | .collect::(); | required by a bound introduced by this call | = help: the trait `FromIterator<()>` is not implemented for `String` - = help: `String` implements trait `FromIterator`: + = help: `String` implements trait `FromIterator`: FromIterator<&char> FromIterator<&std::ascii::Char> FromIterator<&str> From 81698d22b49f36b25dd81abb87b2facf079a6a89 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Fri, 28 Aug 2026 10:08:02 -0300 Subject: [PATCH 03/13] Preserve normalized alias rigidity in borrowck HIR writeback already normalizes closure types. Reuse that result instead of normalizing again in a temporary inference context that would drop region constraints. --- .../rustc_borrowck/src/universal_regions.rs | 39 +++++++------------ compiler/rustc_type_ir/src/ty_kind.rs | 5 +++ 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 27f811cbaee9c..537cfa550fe86 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -21,19 +21,16 @@ use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; -use rustc_infer::infer::{NllRegionVariableOrigin, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; +use rustc_infer::infer::NllRegionVariableOrigin; use rustc_macros::extension; use rustc_middle::mir::RETURN_PLACE; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, BoundVariableKind, GenericArgs, GenericArgsRef, InlineConstArgs, InlineConstArgsParts, - List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, TypingMode, - fold_regions, + List, RegionExt, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions, }; use rustc_middle::{bug, span_bug}; use rustc_span::{ErrorGuaranteed, kw, sym}; -use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; use crate::BorrowckInferCtxt; @@ -136,32 +133,22 @@ pub(crate) enum DefiningTy<'tcx> { GlobalAsm(DefId), } -fn normalized_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { - let ty = tcx.type_of(def_id).instantiate_identity(); - if !tcx.next_trait_solver_globally() { - return ty.skip_normalization(); - } - - let typing_mode = if tcx.use_typing_mode_post_typeck_until_borrowck() { - TypingMode::borrowck(tcx, def_id) - } else { - TypingMode::analysis_in_body(tcx, def_id) - }; - let infcx = tcx.infer_ctxt().build(typing_mode); - let ocx = ObligationCtxt::new(&infcx); - let span = tcx.def_span(def_id); - let cause = ObligationCause::misc(span, def_id); - ocx.deeply_normalize(&cause, tcx.param_env(def_id), ty).unwrap() -} - impl<'tcx> DefiningTy<'tcx> { #[instrument(level = "debug", skip(tcx), ret)] fn new(tcx: TyCtxt<'tcx>, body_def_id: LocalDefId) -> DefiningTy<'tcx> { match tcx.hir_body_owner_kind(body_def_id) { BodyOwnerKind::Closure | BodyOwnerKind::Fn => { - // Normalize after instantiation so coroutine yield/resume - // types in the args are rigid under the next solver. - let defining_ty = normalized_type_of(tcx, body_def_id); + let defining_ty = + tcx.type_of(body_def_id).instantiate_identity().skip_normalization(); + let defining_ty = if tcx.next_trait_solver_globally() { + // Closure types come from HIR typeck results, where they were already + // normalized during writeback. Wrapping them in an `EarlyBinder` + // conservatively makes aliases non-rigid, so restore their rigidness + // instead of normalizing them again during borrowck. + ty::set_aliases_to_rigid(tcx, defining_ty) + } else { + defining_ty + }; match *defining_ty.kind() { ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args), ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args), diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 94e6be03c766f..36aee055459e1 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -112,6 +112,11 @@ impl AliasTyKind { /// with `IsRigid::Yes`. At this point we no longer have to try and renormalize this alias /// later on. /// +/// Rigidness is shared across inference contexts and compiler phases. For example, aliases +/// normalized during HIR typeck can remain rigid when the resulting type is later used by +/// borrowck. They must be made non-rigid again if they enter a typing mode or parameter +/// environment in which further normalization may be possible. +/// /// FIXME(#155345): Alias handling is currently still in flux for the new trait /// solver and this is currently somewhat messy. Please reach out on /// #t-types/trait-system-refactor-initiative if you encounter this and it isn't From a7b87de814a26c69579d1169b62ce314ebd63752 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Sat, 29 Aug 2026 11:12:00 +0800 Subject: [PATCH 04/13] remove unused `perform_locally_with_next_solver` --- .../src/traits/implied_outlives_bounds.rs | 8 -------- .../traits/query/type_op/ascribe_user_type.rs | 8 -------- .../src/traits/query/type_op/mod.rs | 14 +------------- .../src/traits/query/type_op/normalize.rs | 16 ---------------- .../src/traits/query/type_op/outlives.rs | 14 +------------- .../src/traits/query/type_op/prove_predicate.rs | 15 --------------- 6 files changed, 2 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs index 79f630860235a..c0df53db4ab34 100644 --- a/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/implied_outlives_bounds.rs @@ -39,14 +39,6 @@ impl<'tcx> QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { ) -> Result, NoSolution> { tcx.implied_outlives_bounds((canonicalized, false)) } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - _span: Span, - ) -> Result { - query_compute_implied_outlives_bounds(ocx, key.param_env, key.value.ty, false) - } } pub fn compute_implied_outlives_bounds_inner<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index e8814c56c5016..239cbc5aa7622 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -29,14 +29,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { ) -> Result, NoSolution> { tcx.type_op_ascribe_user_type(canonicalized) } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - type_op_ascribe_user_type_with_span(ocx, key, span) - } } /// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 62d636f2de046..250579a2b064a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -12,7 +12,7 @@ use crate::infer::canonical::{ QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::{ObligationCause, ObligationCtxt}; +use crate::traits::ObligationCause; pub mod ascribe_user_type; pub mod custom; @@ -83,18 +83,6 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Result, NoSolution>; - /// In the new trait solver, we already do caching in the solver itself, - /// so there's no need to canonicalize and cache via the query system. - /// Additionally, even if we were to canonicalize, we'd still need to - /// make sure to feed it predefined opaque types and the defining anchor - /// and that would require duplicating all of the tcx queries. Instead, - /// just perform these ops locally. - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result; - fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 7b330c942807c..66a2d44699634 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -1,13 +1,10 @@ use std::fmt; -use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<'tcx, T> where @@ -29,19 +26,6 @@ where ) -> Result, NoSolution> { T::type_op_method(tcx, canonicalized) } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - ocx.deeply_normalize( - &ObligationCause::dummy_with_span(span), - key.param_env, - key.value.value, - ) - .map_err(|_| NoSolution) - } } pub trait Normalizable<'tcx>: diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 99a2779aa8212..fd06dd4403c12 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,12 +1,8 @@ use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; -use crate::traits::ObligationCtxt; -use crate::traits::query::dropck_outlives::{ - compute_dropck_outlives_inner, trivial_dropck_outlives, -}; +use crate::traits::query::dropck_outlives::trivial_dropck_outlives; use crate::traits::query::type_op::DropckOutlives; impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { @@ -25,12 +21,4 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { ) -> Result, NoSolution> { tcx.dropck_outlives(canonicalized) } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span) - } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 8601371c7ebf2..5dedd9af40561 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -3,7 +3,6 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; -use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::{ObligationCtxt, sizedness_fast_path}; @@ -35,20 +34,6 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { ) -> Result, NoSolution> { tcx.type_op_prove_predicate(canonicalized) } - - fn perform_locally_with_next_solver( - ocx: &ObligationCtxt<'_, 'tcx>, - key: ParamEnvAnd<'tcx, Self>, - span: Span, - ) -> Result { - ocx.register_obligation(Obligation::new( - ocx.infcx.tcx, - ObligationCause::dummy_with_span(span), - key.param_env, - key.value.predicate, - )); - Ok(()) - } } /// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors, From ac73a907eb26d57ea452aaeeab7b13f6c40a5f98 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sat, 29 Aug 2026 12:11:18 -0300 Subject: [PATCH 05/13] Reuse HIR-normalized types in MIR build HIR writeback already normalizes closure types. Restore alias rigidity after identity instantiation instead of running the solver again while building MIR. --- compiler/rustc_mir_build/src/builder/mod.rs | 26 +++++++++------------ 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 223653232ba34..65c37562724de 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -33,7 +33,6 @@ use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node, find_ use rustc_index::bit_set::GrowableBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCause; use rustc_middle::hir::place::PlaceBase as HirPlaceBase; use rustc_middle::middle::region; use rustc_middle::mir::*; @@ -41,7 +40,6 @@ use rustc_middle::thir::{self, ExprId, LocalVarId, Param, ParamId, PatKind, Thir use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::traits::ObligationCtxt; use crate::builder::expr::as_place::PlaceBuilder; use crate::builder::scope::LintLevel; @@ -508,7 +506,17 @@ fn construct_fn<'tcx>( let infcx = tcx.infer_ctxt().build(typing_mode); - let coroutine = match normalized_type_of(&infcx, fn_def).kind() { + let defining_ty = tcx.type_of(fn_def).instantiate_identity().skip_normalization(); + let defining_ty = if infcx.next_trait_solver() { + // Closure types come from HIR typeck results, where they were already + // normalized during writeback. Wrapping them in an `EarlyBinder` + // conservatively makes aliases non-rigid, so restore their rigidness + // instead of normalizing them again during MIR build. + ty::set_aliases_to_rigid(tcx, defining_ty) + } else { + defining_ty + }; + let coroutine = match defining_ty.kind() { ty::Coroutine(_, args) => Some(Box::new(CoroutineInfo::initial( tcx.coroutine_kind(fn_def).unwrap(), args.as_coroutine().yield_ty(), @@ -566,18 +574,6 @@ fn construct_fn<'tcx>( body } -fn normalized_type_of<'tcx>(infcx: &InferCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { - let tcx = infcx.tcx; - let ty = tcx.type_of(def_id).instantiate_identity(); - if !infcx.next_trait_solver() { - return ty.skip_normalization(); - } - - let ocx = ObligationCtxt::new(infcx); - let cause = ObligationCause::misc(tcx.def_span(def_id), def_id); - ocx.deeply_normalize(&cause, tcx.param_env(def_id), ty).unwrap() -} - fn construct_const<'a, 'tcx>( tcx: TyCtxt<'tcx>, def: LocalDefId, From dad53fdf5dc6057f7bc4a228cc7c937aaca7dac0 Mon Sep 17 00:00:00 2001 From: Joao Roberto Date: Sat, 29 Aug 2026 12:11:42 -0300 Subject: [PATCH 06/13] Clarify when alias rigidity remains valid Document when changes to the typing mode or parameter environment make rigidness stale, and when compatible mode groups may reuse it. --- compiler/rustc_type_ir/src/ty_kind.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 36aee055459e1..0a9eaf360e3ef 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -112,10 +112,11 @@ impl AliasTyKind { /// with `IsRigid::Yes`. At this point we no longer have to try and renormalize this alias /// later on. /// -/// Rigidness is shared across inference contexts and compiler phases. For example, aliases -/// normalized during HIR typeck can remain rigid when the resulting type is later used by -/// borrowck. They must be made non-rigid again if they enter a typing mode or parameter -/// environment in which further normalization may be possible. +/// Rigidness becomes outdated when the surrounding typing mode or param env changes, +/// because further normalization might be possible. +/// We should also note that rigidness can be shared within some typing mode groups +/// if the param env is the same, e.g., `Typeck/PostTypeckUntilBorrowck` and +/// `PostAnalysis/Codegen`. /// /// FIXME(#155345): Alias handling is currently still in flux for the new trait /// solver and this is currently somewhat messy. Please reach out on From 44febdf5d84f0f00498d7485cf95420dfbaf7be6 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 31 Aug 2026 03:41:11 +0900 Subject: [PATCH 07/13] Emit delayed bug instead of ICEing when `TypeOutlives` goal fails --- .../traits/fulfillment_errors.rs | 9 ++++++++ ...ror-due-to-normalization-failure-no-ice.rs | 13 +++++++++++ ...due-to-normalization-failure-no-ice.stderr | 23 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.rs create mode 100644 tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1c4f0ca5069df..11b051b530198 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -657,6 +657,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) + if self.next_trait_solver() => + { + // We normalize `TypeOutlives` in the next solver, which is fallible + return self.dcx().span_delayed_bug( + span, + "type outlives claues errored outside borrowck without any other error", + ); + } ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => { span_bug!( diff --git a/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.rs b/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.rs new file mode 100644 index 0000000000000..61a9a1df8e8ee --- /dev/null +++ b/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Znext-solver + +// Regression test for + +trait Z<'a, T: ?Sized> +where + T: Z<'a, ()>, //~ ERROR: the trait bound `(): Z<'a, ()>` is not satisfied + for<'b> >::W: 'a, +{ + type W; +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.stderr b/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.stderr new file mode 100644 index 0000000000000..80f1a9328d5e0 --- /dev/null +++ b/tests/ui/traits/next-solver/outlives-goal-error-due-to-normalization-failure-no-ice.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `(): Z<'a, ()>` is not satisfied + --> $DIR/outlives-goal-error-due-to-normalization-failure-no-ice.rs:7:8 + | +LL | T: Z<'a, ()>, + | ^^^^^^^^^ the trait `Z<'a, ()>` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/outlives-goal-error-due-to-normalization-failure-no-ice.rs:5:1 + | +LL | trait Z<'a, T: ?Sized> + | ^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `Z` + --> $DIR/outlives-goal-error-due-to-normalization-failure-no-ice.rs:7:8 + | +LL | trait Z<'a, T: ?Sized> + | - required by a bound in this trait +LL | where +LL | T: Z<'a, ()>, + | ^^^^^^^^^ required by this bound in `Z` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 9bca736808532de04ed95ab73e1062dbe049b909 Mon Sep 17 00:00:00 2001 From: Frank Steffahn Date: Wed, 19 Aug 2026 19:52:29 +0200 Subject: [PATCH 08/13] More consistent use of variable names for trait implementations --- library/alloc/src/boxed/iter.rs | 52 +++++++++++----------- library/alloc/src/bstr.rs | 12 ++--- library/alloc/src/collections/btree/map.rs | 4 +- library/alloc/src/string.rs | 6 +-- library/alloc/src/wtf8/mod.rs | 4 +- library/core/src/iter/traits/collect.rs | 6 +-- library/core/src/option.rs | 6 +-- library/core/src/result.rs | 6 +-- library/proc_macro/src/lib.rs | 2 +- library/std/src/collections/hash/map.rs | 6 +-- library/std/src/ffi/os_str.rs | 6 +-- 11 files changed, 55 insertions(+), 55 deletions(-) diff --git a/library/alloc/src/boxed/iter.rs b/library/alloc/src/boxed/iter.rs index 0e6afcf062354..adaa85d7d9eec 100644 --- a/library/alloc/src/boxed/iter.rs +++ b/library/alloc/src/boxed/iter.rs @@ -93,55 +93,55 @@ impl AsyncIterator for Box { } } -/// This implementation is required to make sure that the `Box<[I]>: IntoIterator` +/// This implementation is required to make sure that the `Box<[T]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl !Iterator for Box<[I], A> {} +impl !Iterator for Box<[T], A> {} -/// This implementation is required to make sure that the `&Box<[I]>: IntoIterator` +/// This implementation is required to make sure that the `&Box<[T]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {} +impl<'a, T, A: Allocator> !Iterator for &'a Box<[T], A> {} -/// This implementation is required to make sure that the `&mut Box<[I]>: IntoIterator` +/// This implementation is required to make sure that the `&mut Box<[T]>: IntoIterator` /// implementation doesn't overlap with `IntoIterator for T where T: Iterator` blanket. #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {} +impl<'a, T, A: Allocator> !Iterator for &'a mut Box<[T], A> {} // Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator` // hides this implementation from explicit `.into_iter()` calls on editions < 2024, // so those calls will still resolve to the slice implementation, by reference. #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl IntoIterator for Box<[I], A> { - type IntoIter = vec::IntoIter; - type Item = I; - fn into_iter(self) -> vec::IntoIter { +impl IntoIterator for Box<[T], A> { + type IntoIter = vec::IntoIter; + type Item = T; + fn into_iter(self) -> vec::IntoIter { self.into_vec().into_iter() } } #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl<'a, I, A: Allocator> IntoIterator for &'a Box<[I], A> { - type IntoIter = slice::Iter<'a, I>; - type Item = &'a I; - fn into_iter(self) -> slice::Iter<'a, I> { +impl<'a, T, A: Allocator> IntoIterator for &'a Box<[T], A> { + type IntoIter = slice::Iter<'a, T>; + type Item = &'a T; + fn into_iter(self) -> slice::Iter<'a, T> { self.iter() } } #[stable(feature = "boxed_slice_into_iter", since = "1.80.0")] -impl<'a, I, A: Allocator> IntoIterator for &'a mut Box<[I], A> { - type IntoIter = slice::IterMut<'a, I>; - type Item = &'a mut I; - fn into_iter(self) -> slice::IterMut<'a, I> { +impl<'a, T, A: Allocator> IntoIterator for &'a mut Box<[T], A> { + type IntoIter = slice::IterMut<'a, T>; + type Item = &'a mut T; + fn into_iter(self) -> slice::IterMut<'a, T> { self.iter_mut() } } #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_slice_from_iter", since = "1.32.0")] -impl FromIterator for Box<[I]> { - fn from_iter>(iter: T) -> Self { +impl FromIterator for Box<[T]> { + fn from_iter>(iter: I) -> Self { iter.into_iter().collect::>().into_boxed_slice() } } @@ -149,7 +149,7 @@ impl FromIterator for Box<[I]> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } @@ -157,7 +157,7 @@ impl FromIterator for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a char> for Box { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } @@ -165,7 +165,7 @@ impl<'a> FromIterator<&'a char> for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator<&'a str> for Box { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } @@ -173,7 +173,7 @@ impl<'a> FromIterator<&'a str> for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator for Box { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } @@ -181,7 +181,7 @@ impl FromIterator for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl FromIterator> for Box { - fn from_iter>>(iter: T) -> Self { + fn from_iter>>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } @@ -189,7 +189,7 @@ impl FromIterator> for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "boxed_str_from_iter", since = "1.80.0")] impl<'a> FromIterator> for Box { - fn from_iter>>(iter: T) -> Self { + fn from_iter>>(iter: I) -> Self { String::from_iter(iter).into_boxed_str() } } diff --git a/library/alloc/src/bstr.rs b/library/alloc/src/bstr.rs index 9aa3064da886f..d45c06427aabf 100644 --- a/library/alloc/src/bstr.rs +++ b/library/alloc/src/bstr.rs @@ -281,7 +281,7 @@ impl<'a> From<&'a ByteString> for Cow<'a, ByteStr> { #[unstable(feature = "bstr", issue = "134915")] impl FromIterator for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { ByteString(iter.into_iter().collect::().into_bytes()) } } @@ -289,7 +289,7 @@ impl FromIterator for ByteString { #[unstable(feature = "bstr", issue = "134915")] impl FromIterator for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { ByteString(iter.into_iter().collect()) } } @@ -297,7 +297,7 @@ impl FromIterator for ByteString { #[unstable(feature = "bstr", issue = "134915")] impl<'a> FromIterator<&'a str> for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { ByteString(iter.into_iter().collect::().into_bytes()) } } @@ -305,7 +305,7 @@ impl<'a> FromIterator<&'a str> for ByteString { #[unstable(feature = "bstr", issue = "134915")] impl<'a> FromIterator<&'a [u8]> for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { let mut buf = Vec::new(); for b in iter { buf.extend_from_slice(b); @@ -317,7 +317,7 @@ impl<'a> FromIterator<&'a [u8]> for ByteString { #[unstable(feature = "bstr", issue = "134915")] impl<'a> FromIterator<&'a ByteStr> for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { let mut buf = Vec::new(); for b in iter { buf.extend_from_slice(&b.0); @@ -329,7 +329,7 @@ impl<'a> FromIterator<&'a ByteStr> for ByteString { #[unstable(feature = "bstr", issue = "134915")] impl FromIterator for ByteString { #[inline] - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { let mut buf = Vec::new(); for mut b in iter { buf.append(&mut b.0); diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index d8421d3c3f70a..a9b3a3787f057 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2541,7 +2541,7 @@ impl FromIterator<(K, V)> for BTreeMap { /// /// If the iterator produces any pairs with equal keys, /// all but one of the corresponding values will be dropped. - fn from_iter>(iter: T) -> BTreeMap { + fn from_iter>(iter: I) -> BTreeMap { let mut inputs: Vec<_> = iter.into_iter().collect(); if inputs.is_empty() { @@ -2557,7 +2557,7 @@ impl FromIterator<(K, V)> for BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] impl Extend<(K, V)> for BTreeMap { #[inline] - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { iter.into_iter().for_each(move |(k, v)| { self.insert(k, v); }); diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index cc321660e6ea4..a528bb948af5e 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -2480,7 +2480,7 @@ impl<'a> FromIterator> for String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "ascii_char", issue = "110998")] impl FromIterator for String { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { let buf = iter.into_iter().map(core::ascii::Char::to_u8).collect(); // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type // only contains ASCII values (0x00-0x7F), which are valid UTF-8. @@ -2491,7 +2491,7 @@ impl FromIterator for String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "ascii_char", issue = "110998")] impl<'a> FromIterator<&'a core::ascii::Char> for String { - fn from_iter>(iter: T) -> Self { + fn from_iter>(iter: I) -> Self { let buf = iter.into_iter().copied().map(core::ascii::Char::to_u8).collect(); // SAFETY: `buf` is guaranteed to be valid UTF-8 because the `core::ascii::Char` type // only contains ASCII values (0x00-0x7F), which are valid UTF-8. @@ -3332,7 +3332,7 @@ impl<'a> FromIterator for Cow<'a, str> { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "ascii_char", issue = "110998")] impl<'a> FromIterator for Cow<'a, str> { - fn from_iter>(it: T) -> Self { + fn from_iter>(it: I) -> Self { Cow::Owned(FromIterator::from_iter(it)) } } diff --git a/library/alloc/src/wtf8/mod.rs b/library/alloc/src/wtf8/mod.rs index 36ec32c549763..be9228b181d3a 100644 --- a/library/alloc/src/wtf8/mod.rs +++ b/library/alloc/src/wtf8/mod.rs @@ -423,7 +423,7 @@ impl Wtf8Buf { /// This replaces surrogate code point pairs with supplementary code points, /// like concatenating ill-formed UTF-16 strings effectively would. impl FromIterator for Wtf8Buf { - fn from_iter>(iter: T) -> Wtf8Buf { + fn from_iter>(iter: I) -> Wtf8Buf { let mut string = Wtf8Buf::new(); string.extend(iter); string @@ -435,7 +435,7 @@ impl FromIterator for Wtf8Buf { /// This replaces surrogate code point pairs with supplementary code points, /// like concatenating ill-formed UTF-16 strings effectively would. impl Extend for Wtf8Buf { - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { let iterator = iter.into_iter(); let (low, _high) = iterator.size_hint(); // Lower bound of one byte per code point (ASCII only) diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index f965076f7b165..cf79ceadf0540 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -371,7 +371,7 @@ const impl IntoIterator for I { /// // This is a bit simpler with the concrete type signature: we can call /// // extend on anything which can be turned into an Iterator which gives /// // us i32s. Because we need i32s to put into MyCollection. -/// fn extend>(&mut self, iter: T) { +/// fn extend>(&mut self, iter: I) { /// /// // The implementation is very straightforward: loop through the /// // iterator, and add() each element to ourselves. @@ -452,7 +452,7 @@ pub trait Extend { #[stable(feature = "extend_for_unit", since = "1.28.0")] impl Extend<()> for () { - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { iter.into_iter().for_each(drop) } fn extend_one(&mut self, _item: ()) {} @@ -620,7 +620,7 @@ macro_rules! impl_extend_tuple { where $($extend_ty: Extend<$ty>,)+ { - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: Iter) { default_extend(self, iter) } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 5d86f851dbd1d..6fa56a707de4e 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -465,7 +465,7 @@ //! [`Option`] of a collection of each contained value of the original //! [`Option`] values, or [`None`] if any of the elements was [`None`]. //! -//! [impl-FromIterator]: Option#impl-FromIterator%3COption%3CA%3E%3E-for-Option%3CV%3E +//! [impl-FromIterator]: Option#impl-FromIterator%3COption%3CT%3E%3E-for-Option%3CV%3E //! //! ``` //! let v = [Some(2), Some(4), None, Some(8)]; @@ -2786,7 +2786,7 @@ unsafe impl TrustedLen for OptionFlatten {} ///////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -impl> FromIterator> for Option { +impl> FromIterator> for Option { /// Takes each element in the [`Iterator`]: if it is [`None`][Option::None], /// no further elements are taken, and the [`None`][Option::None] is /// returned. Should no [`None`][Option::None] occur, a container of type @@ -2848,7 +2848,7 @@ impl> FromIterator> for Option { /// Since the third element caused an underflow, no further elements were taken, /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. #[inline] - fn from_iter>>(iter: I) -> Option { + fn from_iter>>(iter: I) -> Option { iter::try_process(iter.into_iter(), |i| i.collect()) } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index b257cd8c82a0e..24bbf37f3278e 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -511,7 +511,7 @@ //! [`Result`] of a collection of each contained value of the original //! [`Result`] values, or [`Err`] if any of the elements was [`Err`]. //! -//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CA,+E%3E%3E-for-Result%3CV,+E%3E +//! [impl-FromIterator]: Result#impl-FromIterator%3CResult%3CT,+E%3E%3E-for-Result%3CV,+E%3E //! //! ``` //! let v = [Ok(2), Ok(4), Err("err!"), Ok(8)]; @@ -2112,7 +2112,7 @@ unsafe impl TrustedLen for IntoIter {} ///////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -impl> FromIterator> for Result { +impl> FromIterator> for Result { /// Takes each element in the `Iterator`: if it is an `Err`, no further /// elements are taken, and the `Err` is returned. Should no `Err` occur, a /// container with the values of each `Result` is returned. @@ -2156,7 +2156,7 @@ impl> FromIterator> for Result { /// Since the third element caused an underflow, no further elements were taken, /// so the final value of `shared` is 6 (= `3 + 2 + 1`), not 16. #[inline] - fn from_iter>>(iter: I) -> Result { + fn from_iter>>(iter: I) -> Result { iter::try_process(iter.into_iter(), |i| i.collect()) } } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 2f026fb81ee1b..596e548b57e14 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -516,7 +516,7 @@ macro_rules! extend_items { $( #[stable(feature = "token_stream_extend_ts_items", since = "1.92.0")] impl Extend<$item> for TokenStream { - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().map(TokenTree::$item)); } } diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index fef0b1b4df88e..1ac914cda0e4e 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -3010,7 +3010,7 @@ where /// /// If the iterator produces any pairs with equal keys, /// all but one of the corresponding values will be dropped. - fn from_iter>(iter: T) -> HashMap { + fn from_iter>(iter: I) -> HashMap { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); map @@ -3027,7 +3027,7 @@ where A: Allocator, { #[inline] - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { self.base.extend(iter) } @@ -3051,7 +3051,7 @@ where A: Allocator, { #[inline] - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { self.base.extend(iter) } diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 27039f0d3d2eb..36f56bf76131e 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -1815,7 +1815,7 @@ impl FromStr for OsString { #[stable(feature = "osstring_extend", since = "1.52.0")] impl Extend for OsString { #[inline] - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { for s in iter { self.push(&s); } @@ -1825,7 +1825,7 @@ impl Extend for OsString { #[stable(feature = "osstring_extend", since = "1.52.0")] impl<'a> Extend<&'a OsStr> for OsString { #[inline] - fn extend>(&mut self, iter: T) { + fn extend>(&mut self, iter: I) { for s in iter { self.push(s); } @@ -1835,7 +1835,7 @@ impl<'a> Extend<&'a OsStr> for OsString { #[stable(feature = "osstring_extend", since = "1.52.0")] impl<'a> Extend> for OsString { #[inline] - fn extend>>(&mut self, iter: T) { + fn extend>>(&mut self, iter: I) { for s in iter { self.push(&s); } From 27f7c65c12d5629df79d121b6242a1a8e8dd8320 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 31 Aug 2026 10:26:14 +1000 Subject: [PATCH 09/13] Remove dead `LLVMOpaquePass`/`LLVMPassRef` decls --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 6fd78c6bde4be..e77d63d91703b 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -66,10 +66,8 @@ using namespace llvm; static codegen::RegisterCodeGenFlags CGF; -typedef struct LLVMOpaquePass *LLVMPassRef; typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; -DEFINE_STDCXX_CONVERSION_FUNCTIONS(Pass, LLVMPassRef) DEFINE_STDCXX_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) extern "C" void LLVMRustTimeTraceProfilerInitialize() { From 1d7375898dacaaeed8046891066b0e2b58f488c4 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 30 Aug 2026 20:47:08 -0400 Subject: [PATCH 10/13] Make the LLVM version mismatch ICE a fatal error --- compiler/rustc_codegen_llvm/src/diagnostics.rs | 14 +++++++++++++- compiler/rustc_codegen_llvm/src/llvm_util.rs | 13 +++++-------- compiler/rustc_macros/src/diagnostics/message.rs | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/diagnostics.rs b/compiler/rustc_codegen_llvm/src/diagnostics.rs index 54f8ffbb881da..fb43b36fe39b9 100644 --- a/compiler/rustc_codegen_llvm/src/diagnostics.rs +++ b/compiler/rustc_codegen_llvm/src/diagnostics.rs @@ -1,4 +1,4 @@ -use std::ffi::CString; +use std::ffi::{CString, c_uint}; use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; @@ -265,3 +265,15 @@ pub(crate) struct IntrinsicWrongArch<'a> { pub(crate) struct UnknownLlvmTargetFeaturePrefix<'a> { pub feature: &'a str, } + +#[derive(Diagnostic)] +#[diag( + "LLVM version mismatch: this compiler was built for LLVM {$expected_version}, but LLVM {$llvm_major}.{$llvm_minor}.{$llvm_patch} was found{$dll_loc}" +)] +pub(crate) struct LlvmVersionMismatch<'a> { + pub expected_version: c_uint, + pub llvm_major: c_uint, + pub llvm_minor: c_uint, + pub llvm_patch: c_uint, + pub dll_loc: &'a str, +} diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9819699ca5228..82ddcca3e1530 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -56,20 +56,17 @@ unsafe fn configure_llvm(sess: &Session) { llvm::LLVMGetVersion(&mut llvm_major, &mut llvm_minor, &mut llvm_patch); let expected_version = llvm::LLVMRustVersionMajor(); if llvm_major != expected_version { - panic!( - concat!( - "LLVM version mismatch: this compiler was built for LLVM {}, ", - "but LLVM {}.{}.{} was found{}" - ), + sess.dcx().emit_fatal(diagnostics::LlvmVersionMismatch { expected_version, llvm_major, llvm_minor, llvm_patch, - match rustc_session::filesearch::dll_path(llvm::LLVMGetVersion as *mut _) { + dll_loc: &match rustc_session::filesearch::dll_path(llvm::LLVMGetVersion as *mut _) + { Ok(path) => format!(" at {}", path.display()), Err(_) => String::new(), - } - ); + }, + }) } } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 63561e409b0ae..8eff9a4fa9e8a 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -133,6 +133,7 @@ const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[ "Cargo", "Ferris", "GCC", + "LLVM", "MIR", "NaNs", "OK", From 969c1cf191843eee26d9ea59ebddc680d57efc41 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 31 Aug 2026 10:45:29 +1000 Subject: [PATCH 11/13] Remove unnecessary `Twine`/`SMDiagnostic` typedefs Unlike `LLVMTargetMachineRef`, `LLVMTwineRef` and `LLVMSMDiagnosticRef` are not LLVM-C types and so don't need the wrap/unwrap conversions. --- .../rustc_llvm/llvm-wrapper/LLVMWrapper.h | 2 -- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 20 ++++++++----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index 0cbda23f384cc..65dda8eb94853 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -19,8 +19,6 @@ extern "C" void LLVMRustSetLastError(const char *); enum class LLVMRustResult { Success, Failure }; typedef struct OpaqueRustString *RustStringRef; -typedef struct LLVMOpaqueTwine *LLVMTwineRef; -typedef struct LLVMOpaqueSMDiagnostic *LLVMSMDiagnosticRef; extern "C" void LLVMRustStringWriteImpl(RustStringRef buf, const char *slice_ptr, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index c928282596cdd..189296dc9c4c8 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1199,11 +1199,9 @@ extern "C" void LLVMRustWriteValueToString(LLVMValueRef V, RustStringRef Str) { } } -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Twine, LLVMTwineRef) - -extern "C" void LLVMRustWriteTwineToString(LLVMTwineRef T, RustStringRef Str) { +extern "C" void LLVMRustWriteTwineToString(const Twine *T, RustStringRef Str) { auto OS = RawRustStringOstream(Str); - unwrap(T)->print(OS); + T->print(OS); } extern "C" void LLVMRustUnpackOptimizationDiagnostic( @@ -1239,13 +1237,13 @@ enum class LLVMRustDiagnosticLevel { extern "C" void LLVMRustUnpackInlineAsmDiagnostic( LLVMDiagnosticInfoRef DI, LLVMRustDiagnosticLevel *LevelOut, - uint64_t *CookieOut, LLVMTwineRef *MessageOut) { + uint64_t *CookieOut, const Twine **MessageOut) { // Undefined to call this not on an inline assembly diagnostic! llvm::DiagnosticInfoInlineAsm *IA = static_cast(unwrap(DI)); *CookieOut = IA->getLocCookie(); - *MessageOut = wrap(&IA->getMsgStr()); + *MessageOut = &IA->getMsgStr(); switch (IA->getSeverity()) { case DS_Error: @@ -1334,22 +1332,20 @@ LLVMRustGetDiagInfoKind(LLVMDiagnosticInfoRef DI) { return toRust((DiagnosticKind)unwrap(DI)->getKind()); } -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef) - -extern "C" LLVMSMDiagnosticRef LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI, +extern "C" const SMDiagnostic *LLVMRustGetSMDiagnostic(LLVMDiagnosticInfoRef DI, uint64_t *Cookie) { llvm::DiagnosticInfoSrcMgr *SM = static_cast(unwrap(DI)); *Cookie = SM->getLocCookie(); - return wrap(&SM->getSMDiag()); + return &SM->getSMDiag(); } extern "C" bool -LLVMRustUnpackSMDiagnostic(LLVMSMDiagnosticRef DRef, RustStringRef MessageOut, +LLVMRustUnpackSMDiagnostic(const SMDiagnostic *DRef, RustStringRef MessageOut, RustStringRef BufferOut, LLVMRustDiagnosticLevel *LevelOut, unsigned *LocOut, unsigned *RangesOut, size_t *NumRanges) { - SMDiagnostic &D = *unwrap(DRef); + const SMDiagnostic &D = *DRef; auto MessageOS = RawRustStringOstream(MessageOut); MessageOS << D.getMessage(); From f12defbd75e29385eb56aeedbbe7d55e2a5e66ea Mon Sep 17 00:00:00 2001 From: Jonathan Keller Date: Sun, 30 Aug 2026 22:51:51 -0700 Subject: [PATCH 12/13] _ an unused parameter --- compiler/rustc_session/src/filesearch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 6ec1466500a86..10c62d932ba26 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -258,7 +258,7 @@ pub unsafe fn dll_path(function: *mut std::ffi::c_void) -> Result Result { +pub unsafe fn dll_path(_function: *mut std::ffi::c_void) -> Result { Err("dll_path is not supported on WASI".to_string()) } From 7f0cd463543d4cc9213517619dd8f784e87896f7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 18 Aug 2026 15:45:56 +0200 Subject: [PATCH 13/13] libcore: expose volatile atomic operations --- library/core/src/lib.rs | 1 + library/core/src/ptr/mod.rs | 26 +- library/core/src/sync/atomic.rs | 319 ++++++++++++++++++ library/core/src/sync/atomic_load_volatile.md | 29 ++ .../core/src/sync/atomic_store_volatile.md | 28 ++ library/coretests/tests/atomic.rs | 23 ++ library/coretests/tests/lib.rs | 1 + 7 files changed, 415 insertions(+), 12 deletions(-) create mode 100644 library/core/src/sync/atomic_load_volatile.md create mode 100644 library/core/src/sync/atomic_store_volatile.md diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9857a3b8cfae6..ba6d786fbbd89 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -108,6 +108,7 @@ #![feature(adt_const_params)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(arbitrary_self_types_pointers)] #![feature(auto_traits)] #![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index aedab87449ff5..f1b81de50942a 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2067,12 +2067,13 @@ pub const unsafe fn write_unaligned(dst: *mut T, src: T) { /// are two cases of usage that need to be distinguished: /// /// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like -/// [`read`], except for the additional guarantee that it won't be elided or reordered (see -/// above). This implies that the operation will actually access memory and not e.g. be lowered to -/// reusing data from a previous read. Other than that, all the usual rules for memory accesses -/// apply (including provenance). In particular, just like in C, whether an operation is volatile -/// has no bearing whatsoever on questions involving concurrent accesses from multiple threads. -/// Volatile accesses behave exactly like non-atomic accesses in that regard. +/// [`read`], except for the additional guarantee that it won't be elided or reordered across +/// other externally observable events (see above). This implies that the operation will actually +/// access memory and not e.g. be lowered to reusing data from a previous read. Other than that, +/// all the usual rules for memory accesses apply (including provenance). In particular, just +/// like in C, whether an operation is volatile has no bearing whatsoever on questions involving +/// concurrent accesses from multiple threads. Volatile accesses behave exactly like non-atomic +/// accesses in that regard. /// /// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust /// allocation. In this use-case, the pointer does *not* have to be [valid] for reads. This is @@ -2174,11 +2175,12 @@ pub const unsafe fn read_volatile(src: *const T) -> T { /// /// - When a volatile operation is used for memory inside an [allocation], it behaves exactly like /// [`write`][write()], except for the additional guarantee that it won't be elided or reordered -/// (see above). This implies that the operation will actually access memory and not e.g. be -/// lowered to a register access. Other than that, all the usual rules for memory accesses apply -/// (including provenance). In particular, just like in C, whether an operation is volatile has no -/// bearing whatsoever on questions involving concurrent access from multiple threads. Volatile -/// accesses behave exactly like non-atomic accesses in that regard. +/// across other externally observable events (see above). This implies that the operation will +/// actually access memory and not e.g. be lowered to a register access. Other than that, all the +/// usual rules for memory accesses apply (including provenance). In particular, just like in C, +/// whether an operation is volatile has no bearing whatsoever on questions involving concurrent +/// access from multiple threads. Volatile accesses behave exactly like non-atomic accesses in +/// that regard. /// /// - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust /// allocation. In this use-case, the pointer does *not* have to be [valid] for writes. This is @@ -2187,7 +2189,7 @@ pub const unsafe fn read_volatile(src: *const T) -> T { /// semantics associated to their manipulation, and cannot be used as general purpose memory. /// Here, any address value is possible, including 0 and [`usize::MAX`], so long as the semantics /// of such a write are well-defined by the target hardware. The provenance of the pointer is -/// irrelevant, and it can be created with [`without_provenance`]. The access must not trap. It +/// irrelevant, and it can be created with [`without_provenance_mut`]. The access must not trap. It /// can cause side-effects, but those must not affect Rust-allocated memory in any way. This /// access is still not considered [atomic], and as such it cannot be used for inter-thread /// synchronization. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 12208b95307ee..8b770528a1736 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -606,6 +606,16 @@ impl AtomicBool { unsafe { &*ptr.cast() } } + /// Creates a new pointer to `AtomicBool` from a pointer. + /// + /// This is useful if you want to do volatile atomic accesses, and thus avoid creating + /// a reference to the destination. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + pub const fn from_ptr_raw(ptr: *mut bool) -> *const AtomicBool { + ptr.cast_const().cast() + } + /// Returns a mutable reference to the underlying [`bool`]. /// /// This is safe because the mutable reference guarantees that no other threads are @@ -765,6 +775,40 @@ impl AtomicBool { } } + /// Perform a volatile atomic load from the bool. + /// + /// `load_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_load_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be [valid] for reads, or `self` must point to memory + /// outside of all Rust allocations and reading from that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// * Reading from `self` must produce a properly initialized value of type `bool`. + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Release`] or [`AcqRel`]. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub const unsafe fn load_volatile(self: *const Self, order: Ordering) -> bool { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_load::<_, /* VOLATILE */ true>(self.cast::(), order) != 0 + } + } + /// Stores a value into the bool. /// /// `store` takes an [`Ordering`] argument which describes the memory ordering @@ -797,6 +841,39 @@ impl AtomicBool { } } + /// Performs a volatile atomic store into the bool. + /// + /// `store_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_store_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be either [valid] for writes, or `self` must point to memory + /// outside of all Rust allocations and writing to that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Acquire`] or [`AcqRel`]. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_should_not_be_called_on_const_items] + pub const unsafe fn store_volatile(self: *const Self, val: bool, order: Ordering) { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_store::<_, /* VOLATILE */ true>(self.cast::().cast_mut(), val as u8, order); + } + } + /// Stores a value into the bool, returning the previous value. /// /// `swap` takes an [`Ordering`] argument which describes the memory ordering @@ -1567,6 +1644,16 @@ impl AtomicPtr { unsafe { &*ptr.cast() } } + /// Creates a new pointer to `AtomicPtr` from a pointer. + /// + /// This is useful if you want to do volatile atomic accesses, and thus avoid creating + /// a reference to the destination. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + pub const fn from_ptr_raw(ptr: *mut *mut T) -> *const AtomicPtr { + ptr.cast_const().cast() + } + /// Creates a new `AtomicPtr` initialized with a null pointer. /// /// # Examples @@ -1770,6 +1857,71 @@ impl AtomicPtr { } } + /// Perform a volatile atomic load from the pointer. + /// + /// `load_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_load_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be [valid] for reads, or `self` must point to memory + /// outside of all Rust allocations and reading from that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// * `self` must be aligned to `align_of::>()` (note that on some platforms this + /// can be bigger than `align_of::<*mut T>()`). + /// + /// * Reading from `self` must produce a properly initialized value of type `*mut T`. + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Release`] or [`AcqRel`]. + /// + /// # Examples + /// + /// Assuming an MMIO region at `MMIO_ADDR` that belongs to a device with direct memory + /// access, we may receive a buffer in shared memory from that device as follows: + /// + /// ```rust,no_run + /// #![feature(atomic_volatile)] + /// use std::sync::atomic::{fence, AtomicPtr, Ordering}; + /// use std::ptr; + /// + /// const MMIO_ADDR: *mut *mut u8 = ptr::without_provenance_mut(0xCAF0); + /// let atomic_ptr = AtomicPtr::::from_ptr_raw(MMIO_ADDR); + /// + /// // Spin until we see a non-zero value. + /// let buf = 'buf: loop { + /// let buf = unsafe { atomic_ptr.load_volatile(Ordering::Relaxed) }; + /// if !buf.is_null() { + /// break 'buf buf; + /// } + /// }; + /// // Synchronize with the store whose value we just read. + /// // Note: a standard acquire fence may not be sufficient to synchronize with DMA devices. + /// // Depending on your target, you may have to use inline assembly to emit a special fence. + /// fence(Ordering::Acquire); + /// + /// // Now process the data in `buf`. + /// ``` + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub const unsafe fn load_volatile(self: *const Self, order: Ordering) -> *mut T { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_load::<_, /* VOLATILE */ true>(self.cast::<*mut T>(), order) + } + } + /// Stores a value into the pointer. /// /// `store` takes an [`Ordering`] argument which describes the memory ordering @@ -1803,6 +1955,67 @@ impl AtomicPtr { } } + /// Performs a volatile atomic store into the pointer. + /// + /// `store_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_store_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be either [valid] for writes, or `self` must point to memory + /// outside of all Rust allocations and writing to that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// * `self` must be aligned to `align_of::>()` (note that on some platforms this + /// can be bigger than `align_of::<*mut T>()`). + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Acquire`] or [`AcqRel`]. + /// + /// # Examples + /// + /// Assuming an MMIO region at `MMIO_ADDR` that belongs to a device with direct memory + /// access, we may submit a buffer in shared memory to that device as follows: + /// + /// ```rust,no_run + /// #![feature(atomic_volatile)] + /// use std::sync::atomic::{fence, AtomicPtr, Ordering}; + /// use std::ptr; + /// + /// const MMIO_ADDR: *mut *mut u8 = ptr::without_provenance_mut(0xCAF0); + /// let atomic_ptr = AtomicPtr::::from_ptr_raw(MMIO_ADDR); + /// + /// // Prepare some data for the DMA device. + /// # fn get_dma_buffer() -> *mut u8 { panic!() } + /// let buf = get_dma_buffer(); + /// + /// // Ensure the other side can synchronize with the store we do below. + /// // Note: a standard release fence may not be sufficient to synchronize with DMA devices. + /// // Depending on your target, you may have to use inline assembly to emit a special fence. + /// fence(Ordering::Release); + /// + /// unsafe { atomic_ptr.store_volatile(buf, Ordering::Relaxed) }; + /// ``` + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_should_not_be_called_on_const_items] + pub const unsafe fn store_volatile(self: *const Self, ptr: *mut T, order: Ordering) { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_store::<_, /* VOLATILE */ true>(self.cast::<*mut T>().cast_mut(), ptr, order); + } + } + /// Stores a value into the pointer, returning the previous value. /// /// `swap` takes an [`Ordering`] argument which describes the memory ordering @@ -2733,6 +2946,16 @@ macro_rules! atomic_int { unsafe { &*ptr.cast() } } + /// Creates a new pointer to an atomic integer from a pointer. + /// + /// This is useful if you want to do volatile atomic accesses, and thus avoid creating + /// a reference to the destination. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + pub const fn from_ptr_raw(ptr: *mut $int_type) -> *const $atomic_type { + ptr.cast_const().cast() + } + /// Returns a mutable reference to the underlying integer. /// /// This is safe because the mutable reference guarantees that no other threads are @@ -2921,6 +3144,55 @@ macro_rules! atomic_int { unsafe { atomic_load::<_, /* VOLATILE */ false>(self.as_ptr(), order) } } + /// Perform a volatile load from the atomic integer. + /// + /// `load_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_load_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be [valid] for reads, or `self` must point to memory + /// outside of all Rust allocations and reading from that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// * `self` must be aligned to + #[doc = concat!(" `align_of::<", stringify!($atomic_type), ">()`")] + #[doc = if_8_bit!{ + $int_type, + yes = [ + " (note that this is always true, since `align_of::<", + stringify!($atomic_type), ">() == 1`)." + ], + no = [ + " (note that on some platforms this can be bigger than `align_of::<", + stringify!($int_type), ">()`)." + ], + }] + /// + /// * Reading from `self` must produce a properly initialized value of the underlying + /// integer type. + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Release`] or [`AcqRel`]. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub const unsafe fn load_volatile(self: *const Self, order: Ordering) -> $int_type { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_load::<_, /* VOLATILE */ true>(self.cast::<$int_type>(), order) + } + } + /// Stores a value into the atomic integer. /// /// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation. @@ -2951,6 +3223,53 @@ macro_rules! atomic_int { unsafe { atomic_store::<_, /* VOLATILE */ false>(self.as_ptr(), val, order); } } + /// Performs a volatile store into the atomic integer. + /// + /// `store_volatile` takes an [`Ordering`] argument which describes the memory ordering + /// of this operation. Possible values are [`SeqCst`], [`Release`] and [`Relaxed`]. + /// + #[doc = include_str!("./atomic_store_volatile.md")] + /// + /// # Safety + /// + /// Behavior is undefined if any of the following conditions are violated: + /// + /// * `self` must be either [valid] for writes, or `self` must point to memory + /// outside of all Rust allocations and writing to that memory must: + /// - not trap, and + /// - not cause any memory inside a Rust allocation to be modified. + /// + /// * `self` must be aligned to + #[doc = concat!(" `align_of::<", stringify!($atomic_type), ">()`")] + #[doc = if_8_bit!{ + $int_type, + yes = [ + " (note that this is always true, since `align_of::<", + stringify!($atomic_type), ">() == 1`)." + ], + no = [ + " (note that on some platforms this can be bigger than `align_of::<", + stringify!($int_type), ">()`)." + ], + }] + /// + /// [valid]: core::ptr#safety + /// + /// # Panics + /// + /// Panics if `order` is [`Acquire`] or [`AcqRel`]. + #[inline] + #[unstable(feature = "atomic_volatile", issue = "158947")] + #[rustc_const_unstable(feature = "atomic_volatile", issue = "158947")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_should_not_be_called_on_const_items] + pub const unsafe fn store_volatile(self: *const Self, val: $int_type, order: Ordering) { + // SAFETY: follows from our own safety requirements. + unsafe { + atomic_store::<_, /* VOLATILE */ true>(self.cast::<$int_type>().cast_mut(), val, order); + } + } + /// Stores a value into the atomic integer, returning the previous value. /// /// `swap` takes an [`Ordering`] argument which describes the memory ordering diff --git a/library/core/src/sync/atomic_load_volatile.md b/library/core/src/sync/atomic_load_volatile.md new file mode 100644 index 0000000000000..be02c4bcfeccf --- /dev/null +++ b/library/core/src/sync/atomic_load_volatile.md @@ -0,0 +1,29 @@ +Volatile operations are intended to act on I/O memory. As such, they are considered externally +observable events (just like syscalls, but less opaque), and are guaranteed to not be elided or +reordered by the compiler across other externally observable events. With this in mind, there +are two cases of usage that need to be distinguished: + +- When a volatile operation is used for memory inside an [allocation], it behaves exactly + like [`load`][Self::load], except for the additional guarantee that it won't be elided or + reordered across other externally observable events (see above). This implies that the + operation will actually access memory and not e.g. be lowered to reusing data from a + previous load. Other than that, all the usual rules for memory accesses apply (including + provenance). + +- Volatile operations, however, may also be used to access memory that is _outside_ of any Rust + allocation. In this use-case, the pointer does *not* have to be [valid] for reads. This is + typically used for CPU and peripheral registers that must be accessed via an I/O memory mapping, + most commonly at fixed addresses reserved by the hardware. These often have special semantics + associated to their manipulation, and cannot be used as general purpose memory. Here, any address + value is possible, including 0 and [`usize::MAX`], so long as the semantics of such a read are + well-defined by the target hardware. The provenance of the pointer is irrelevant, and it can be + created with [`without_provenance`][crate::ptr::without_provenance]. The access must not trap. It + can cause side-effects, but those must not affect Rust-allocated memory in any way. + +In both cases, the access is also considered atomic with the given `order`. This allows +synchronization with other threads or devices that share memory with this program. + +When invoked during const evaluation, this behaves like a regular atomic load. In +particular, such reads must always follow the first of the two cases above. + +[allocation]: crate::ptr#allocated-object diff --git a/library/core/src/sync/atomic_store_volatile.md b/library/core/src/sync/atomic_store_volatile.md new file mode 100644 index 0000000000000..bb78099020c15 --- /dev/null +++ b/library/core/src/sync/atomic_store_volatile.md @@ -0,0 +1,28 @@ +Volatile operations are intended to act on I/O memory. As such, they are considered externally +observable events (just like syscalls), and are guaranteed to not be elided or reordered by the +compiler across other externally observable events. With this in mind, there are two cases of +usage that need to be distinguished: + +- When a volatile operation is used for memory inside an [allocation], it behaves exactly like + [`store`][Self::store], except for the additional guarantee that it won't be elided or reordered + across other externally observable events (see above). This implies that the operation will + actually access memory and not e.g. be lowered to a register access. Other than that, all the + usual rules for memory accesses apply (including provenance). + +- Volatile operations, however, may also be used to access memory that is _outside_ of any Rust + allocation. In this use-case, the pointer does *not* have to be [valid] for writes. This is + typically used for CPU and peripheral registers that must be accessed via an I/O memory mapping, + most commonly at fixed addresses reserved by the hardware. These often have special semantics + associated to their manipulation, and cannot be used as general purpose memory. Here, any address + value is possible, including 0 and [`usize::MAX`], so long as the semantics of such a write are + well-defined by the target hardware. The provenance of the pointer is irrelevant, and it can be + created with [`without_provenance_mut`][crate::ptr::without_provenance_mut]. The access must not + trap. It can cause side-effects, but those must not affect Rust-allocated memory in any way. + +In both cases, the access is also considered atomic with the given `order`. This allows +synchronization with other threads or devices that share memory with this program. + +When invoked during const evaluation, this behaves like a regular atomic store. In +particular, such reads must always follow the first of the two cases above. + +[allocation]: crate::ptr#allocated-object diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs index d888bd0f55a11..31fe7980510d3 100644 --- a/library/coretests/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs @@ -538,6 +538,29 @@ fn atomic_umin() { assert_eq!(ATOMIC.load(Relaxed), 0); } +#[test] +fn atomic_volatile() { + use Ordering::*; + + let mut b = true; + let atomic = AtomicBool::from_ptr_raw(&raw mut b); + assert!(unsafe { atomic.load_volatile(Relaxed) }); + unsafe { atomic.store_volatile(false, Relaxed) }; + assert!(!b); + + let mut ptr = std::ptr::null_mut::(); + let atomic = AtomicPtr::from_ptr_raw(&raw mut ptr); + assert!(unsafe { atomic.load_volatile(Relaxed) }.is_null()); + unsafe { atomic.store_volatile(std::ptr::without_provenance_mut(16), Relaxed) }; + assert!(ptr.addr() == 16); + + let mut int = 0i32; + let atomic = AtomicI32::from_ptr_raw(&raw mut int); + assert!(unsafe { atomic.load_volatile(Relaxed) } == 0); + unsafe { atomic.store_volatile(16, Relaxed) }; + assert!(int == 16); +} + /* FIXME(#110395) #[test] fn atomic_const_from() { diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index e81cae69e1852..142df37c2b7fe 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,6 +8,7 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] +#![feature(atomic_volatile)] #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(casefold)]