From 0ef4e1154335de75d5c333bf61e192ec99758fb3 Mon Sep 17 00:00:00 2001 From: ajasad25 Date: Fri, 22 May 2026 15:32:48 +0500 Subject: [PATCH 01/28] style: Clarify nullary call and `()` no-break rule applies past max width --- src/doc/style-guide/src/expressions.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md index 9df5d7d18edb5..53fd7df0cc972 100644 --- a/src/doc/style-guide/src/expressions.md +++ b/src/doc/style-guide/src/expressions.md @@ -185,7 +185,9 @@ let f = Foo { ## Unit literals -Never break between the opening and closing parentheses of the `()` unit literal. +Never break between the opening and closing parentheses of the `()` unit +literal. This applies even when the closing parenthesis would fall past the +maximum line width. ## Tuple literals @@ -384,7 +386,8 @@ Prefer not to break a line in the callee expression. For a function call with no arguments (a nullary function call like `func()`), never break within the parentheses, and never put a space between the parentheses. Always write a nullary function call as a single-line call, never -a multi-line call. +a multi-line call. This applies even when the closing parenthesis would fall +past the maximum line width. ### Single-line calls From 239e4dd9e8f27fd27e6c72291ecbf534d108ee97 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 30 Mar 2026 15:02:14 +0300 Subject: [PATCH 02/28] Remove will_cache_on_disk_for_key_fn and AsLocalQueryKey --- compiler/rustc_middle/src/query/keys.rs | 33 ++++++++------------- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_middle/src/query/plumbing.rs | 15 +++++++--- compiler/rustc_query_impl/src/execution.rs | 3 +- compiler/rustc_query_impl/src/plumbing.rs | 4 +-- compiler/rustc_query_impl/src/query_impl.rs | 25 +++++----------- 6 files changed, 34 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 569c30a6067a9..346a26a685317 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -36,6 +36,8 @@ pub trait QueryKey: Sized + QueryKeyBounds { /// [`QueryCache`]: rustc_middle::query::QueryCache type Cache = DefaultCache; + type LocalQueryKey = !; + /// In the event that a cycle occurs, if no explicit span has been /// given for a query with key `self`, what span should we use? fn default_span(&self, tcx: TyCtxt<'_>) -> Span; @@ -45,14 +47,12 @@ pub trait QueryKey: Sized + QueryKeyBounds { fn key_as_def_id(&self) -> Option { None } -} - -pub trait AsLocalQueryKey: QueryKey { - type LocalQueryKey; /// Given an instance of this key, what crate is it referring to? /// This is used to find the provider. - fn as_local_key(&self) -> Option; + fn as_local_key(&self) -> Option { + None + } } impl QueryKey for () { @@ -96,13 +96,11 @@ impl<'tcx> QueryKey for ty::LitToConstInput<'tcx> { impl QueryKey for CrateNum { type Cache = VecCache; + type LocalQueryKey = LocalCrate; + fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } -} - -impl AsLocalQueryKey for CrateNum { - type LocalQueryKey = LocalCrate; #[inline(always)] fn as_local_key(&self) -> Option { @@ -136,6 +134,7 @@ impl QueryKey for LocalDefId { impl QueryKey for DefId { type Cache = DefIdCache; + type LocalQueryKey = LocalDefId; fn default_span(&self, tcx: TyCtxt<'_>) -> Span { tcx.def_span(*self) @@ -145,10 +144,6 @@ impl QueryKey for DefId { fn key_as_def_id(&self) -> Option { Some(*self) } -} - -impl AsLocalQueryKey for DefId { - type LocalQueryKey = LocalDefId; #[inline(always)] fn as_local_key(&self) -> Option { @@ -197,13 +192,11 @@ impl QueryKey for (LocalDefId, LocalDefId, Ident) { } impl QueryKey for (CrateNum, DefId) { + type LocalQueryKey = DefId; + fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) } -} - -impl AsLocalQueryKey for (CrateNum, DefId) { - type LocalQueryKey = DefId; #[inline(always)] fn as_local_key(&self) -> Option { @@ -212,13 +205,11 @@ impl AsLocalQueryKey for (CrateNum, DefId) { } impl QueryKey for (CrateNum, SimplifiedType) { + type LocalQueryKey = SimplifiedType; + fn default_span(&self, _: TyCtxt<'_>) -> Span { DUMMY_SP } -} - -impl AsLocalQueryKey for (CrateNum, SimplifiedType) { - type LocalQueryKey = SimplifiedType; #[inline(always)] fn as_local_key(&self) -> Option { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b7e5e9bcb5e32..1eafd0a17119a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::LocalDefId; pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; pub use self::into_query_key::IntoQueryKey; pub use self::job::{QueryJob, QueryJobId, QueryLatch, QueryWaiter}; -pub use self::keys::{AsLocalQueryKey, LocalCrate, QueryKey}; +pub use self::keys::{LocalCrate, QueryKey}; pub use self::plumbing::{ ActiveKeyStatus, Cycle, EnsureMode, QueryMode, QueryState, QuerySystem, QueryVTable, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 8cc5e8105949a..99d084499e4cd 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -14,7 +14,7 @@ use crate::dep_graph::{DepKind, DepNodeIndex, QuerySideEffect, SerializedDepNode use crate::ich::StableHashState; use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables, TaggedQueryKey}; use crate::query::on_disk_cache::OnDiskCache; -use crate::query::{IntoQueryKey, QueryCache, QueryJob, QueryStackFrame}; +use crate::query::{IntoQueryKey, QueryCache, QueryJob, QueryKey, QueryStackFrame}; use crate::ty::{self, TyCtxt}; /// For a particular query, keeps track of "active" keys, i.e. keys whose @@ -86,6 +86,9 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// True if this query has the `feedable` modifier. pub feedable: bool, + pub cache_on_disk_local: bool, + pub separate_provide_extern: bool, + pub dep_kind: DepKind, pub state: QueryState<'tcx, C::Key>, pub cache: C, @@ -97,8 +100,6 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// This should be the only code that calls the provider function. pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub will_cache_on_disk_for_key_fn: fn(key: C::Key) -> bool, - /// Function pointer that tries to load a query value from disk. /// /// This should only be called after a successful check of `will_cache_on_disk_for_key_fn`. @@ -133,6 +134,12 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, } +impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { + pub fn cache_on_disk(&self, key: C::Key) -> bool { + self.cache_on_disk_local && (!self.separate_provide_extern || key.as_local_key().is_some()) + } +} + impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // When debug-printing a query vtable (e.g. for ICE or tracing), @@ -328,7 +335,7 @@ macro_rules! define_callbacks { /// This query has the `separate_provide_extern` modifier. #[cfg($separate_provide_extern)] pub type LocalKey<'tcx> = - as $crate::query::AsLocalQueryKey>::LocalQueryKey; + as $crate::query::QueryKey>::LocalQueryKey; /// Key type used by provider functions in `local_providers`. #[cfg(not($separate_provide_extern))] pub type LocalKey<'tcx> = Key<'tcx>; diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index b614bc14b4539..14c52345bcd2d 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -603,8 +603,7 @@ fn ensure_can_skip_execution<'tcx, C: QueryCache>( // needed, which guarantees the query provider will never run // for this key. EnsureMode::Done => { - (query.will_cache_on_disk_for_key_fn)(key) - && loadable_from_disk(tcx, serialized_dep_node_index) + query.cache_on_disk(key) && loadable_from_disk(tcx, serialized_dep_node_index) } } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 11f960598c387..620c45d8fa3df 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -93,7 +93,7 @@ fn encode_query_values_inner<'a, 'tcx, C, V>( assert!(all_inactive(&query.state)); query.cache.for_each(&mut |key, value, dep_node| { - if (query.will_cache_on_disk_for_key_fn)(*key) { + if query.cache_on_disk(*key) { encoder.encode_query_value::(dep_node, &erase::restore_val::(*value)); } }); @@ -152,7 +152,7 @@ pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( // If the recovered key isn't eligible for cache-on-disk, then there's no // value on disk to promote. - if !(query.will_cache_on_disk_for_key_fn)(key) { + if !query.cache_on_disk(key) { return; } diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 4425acc6b86b8..09e14c2338eaf 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -1,6 +1,6 @@ use rustc_middle::queries::TaggedQueryKey; use rustc_middle::query::erase::{self, Erased}; -use rustc_middle::query::{AsLocalQueryKey, QueryMode, QueryVTable}; +use rustc_middle::query::{QueryKey, QueryMode, QueryVTable}; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -127,20 +127,6 @@ macro_rules! define_queries { } } - fn will_cache_on_disk_for_key<'tcx>( - _key: rustc_middle::queries::$name::Key<'tcx>, - ) -> bool { - cfg_select! { - // If a query has both `cache_on_disk` and `separate_provide_extern`, only - // disk-cache values for "local" keys, i.e. things in the current crate. - all($cache_on_disk, $separate_provide_extern) => { - AsLocalQueryKey::as_local_key(&_key).is_some() - } - all($cache_on_disk, not($separate_provide_extern)) => true, - not($cache_on_disk) => false, - } - } - pub(crate) fn make_query_vtable<'tcx>(incremental: bool) -> QueryVTable<'tcx, rustc_middle::queries::$name::Cache<'tcx>> { @@ -153,16 +139,19 @@ macro_rules! define_queries { dep_kind: rustc_middle::dep_graph::DepKind::$name, state: Default::default(), cache: Default::default(), + cache_on_disk_local: $cache_on_disk, + separate_provide_extern: $separate_provide_extern, invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, - will_cache_on_disk_for_key_fn: - $crate::query_impl::$name::will_cache_on_disk_for_key, - #[cfg($cache_on_disk)] try_load_from_disk_fn: |tcx, prev_index| { use rustc_middle::queries::$name::{ProvidedValue, provided_to_erased}; + // Check the cache-on-disk condition for this key. + #[cfg($separate_provide_extern)] + QueryKey::as_local_key(&_key)?; + let loaded_value: ProvidedValue<'tcx> = $crate::plumbing::try_load_from_disk(tcx, prev_index)?; From 76c099caf46e8edcbf2afe19bd857f3beb0fd9c4 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 26 May 2026 11:50:32 +0300 Subject: [PATCH 03/28] Rename helper QueryVTable method back --- compiler/rustc_middle/src/query/plumbing.rs | 2 +- compiler/rustc_query_impl/src/execution.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 99d084499e4cd..e29439b832f73 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -135,7 +135,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { } impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { - pub fn cache_on_disk(&self, key: C::Key) -> bool { + pub fn will_cache_on_disk_for_key(&self, key: C::Key) -> bool { self.cache_on_disk_local && (!self.separate_provide_extern || key.as_local_key().is_some()) } } diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 14c52345bcd2d..3e47e1242262c 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -603,7 +603,7 @@ fn ensure_can_skip_execution<'tcx, C: QueryCache>( // needed, which guarantees the query provider will never run // for this key. EnsureMode::Done => { - query.cache_on_disk(key) && loadable_from_disk(tcx, serialized_dep_node_index) + query.will_cache_on_disk_for_key(key) && loadable_from_disk(tcx, serialized_dep_node_index) } } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 620c45d8fa3df..3d93bea752252 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -93,7 +93,7 @@ fn encode_query_values_inner<'a, 'tcx, C, V>( assert!(all_inactive(&query.state)); query.cache.for_each(&mut |key, value, dep_node| { - if query.cache_on_disk(*key) { + if query.will_cache_on_disk_for_key(*key) { encoder.encode_query_value::(dep_node, &erase::restore_val::(*value)); } }); @@ -152,7 +152,7 @@ pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( // If the recovered key isn't eligible for cache-on-disk, then there's no // value on disk to promote. - if !query.cache_on_disk(key) { + if !query.will_cache_on_disk_for_key(key) { return; } From 0a36812006d3a4a58aa296998ee169531e2f20ad Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 26 May 2026 12:01:03 +0300 Subject: [PATCH 04/28] Fix and update changes --- compiler/rustc_middle/src/query/plumbing.rs | 2 +- compiler/rustc_query_impl/src/execution.rs | 2 +- compiler/rustc_query_impl/src/query_impl.rs | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index e29439b832f73..8fa05876a550c 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -102,7 +102,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// Function pointer that tries to load a query value from disk. /// - /// This should only be called after a successful check of `will_cache_on_disk_for_key_fn`. + /// This should only be called after a successful check of [`Self::will_cache_on_disk_for_key`]. pub try_load_from_disk_fn: fn(tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) -> Option, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 3e47e1242262c..c3ffe816502ae 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -498,7 +498,7 @@ fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>( debug_assert!(dep_graph_data.is_index_green(prev_index)); // First try to load the result from the on-disk cache. Some things are never cached on disk. - let try_value = if (query.will_cache_on_disk_for_key_fn)(key) { + let try_value = if query.will_cache_on_disk_for_key(key) { let prof_timer = tcx.prof.incr_cache_loading(); let value = (query.try_load_from_disk_fn)(tcx, prev_index); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 09e14c2338eaf..3720d9fd80547 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -148,10 +148,6 @@ macro_rules! define_queries { try_load_from_disk_fn: |tcx, prev_index| { use rustc_middle::queries::$name::{ProvidedValue, provided_to_erased}; - // Check the cache-on-disk condition for this key. - #[cfg($separate_provide_extern)] - QueryKey::as_local_key(&_key)?; - let loaded_value: ProvidedValue<'tcx> = $crate::plumbing::try_load_from_disk(tcx, prev_index)?; From 9d82fcee1039b021d69da39a9aab59c678639a94 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 26 May 2026 12:01:50 +0300 Subject: [PATCH 05/28] Formatter --- compiler/rustc_query_impl/src/execution.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index c3ffe816502ae..2f48583f0beed 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -603,7 +603,8 @@ fn ensure_can_skip_execution<'tcx, C: QueryCache>( // needed, which guarantees the query provider will never run // for this key. EnsureMode::Done => { - query.will_cache_on_disk_for_key(key) && loadable_from_disk(tcx, serialized_dep_node_index) + query.will_cache_on_disk_for_key(key) + && loadable_from_disk(tcx, serialized_dep_node_index) } } } From 33f59590d381606c77409820f3321df6ca976605 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 29 Apr 2026 13:29:09 +1000 Subject: [PATCH 06/28] Remove unused functions in `value_analysis.rs` And reduce visibility of functions only used within the crate. --- .../rustc_mir_dataflow/src/value_analysis.rs | 62 ++----------------- 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 2476dcd5bf87a..1e04453680e81 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -120,17 +120,6 @@ impl State { State::Reachable(StateData::new()) } - pub fn all_bottom(&self) -> bool { - match self { - State::Unreachable => false, - State::Reachable(values) => - { - #[allow(rustc::potential_query_instability)] - values.map.values().all(V::is_bottom) - } - } - } - pub fn is_reachable(&self) -> bool { matches!(self, State::Reachable(_)) } @@ -168,7 +157,7 @@ impl State { /// /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track /// as such. - pub fn flood_with_tail_elem( + fn flood_with_tail_elem( &mut self, place: PlaceRef<'_>, tail_elem: Option, @@ -238,25 +227,19 @@ impl State { } /// Retrieve the value stored for a place, or `None` if it is not tracked. - pub fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { + fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { let place = map.find(place)?; self.try_get_idx(place, map) } /// Retrieve the discriminant stored for a place, or `None` if it is not tracked. - pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { + fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { let place = map.find_discr(place)?; self.try_get_idx(place, map) } - /// Retrieve the slice length stored for a place, or `None` if it is not tracked. - pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option { - let place = map.find_len(place)?; - self.try_get_idx(place, map) - } - /// Retrieve the value stored for a place index, or `None` if it is not tracked. - pub fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option { + fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option { match self { State::Reachable(values) => { map.places[place].value_index.map(|v| values.get(v).clone()) @@ -293,20 +276,6 @@ impl State { } } - /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - /// - /// This method returns ⊥ the current state is unreachable. - pub fn get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V - where - V: HasBottom + HasTop, - { - match self { - State::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP), - // Because this is unreachable, we can return any value we want. - State::Unreachable => V::BOTTOM, - } - } - /// Retrieve the value stored for a place index, or ⊤ if it is not tracked. /// /// This method returns ⊥ the current state is unreachable. @@ -800,21 +769,6 @@ impl<'tcx> Map<'tcx> { self.places[place].value_index } - /// Locates the value corresponding to the given place. - pub fn find_value(&self, place: PlaceRef<'_>) -> Option { - self.value(self.find(place)?) - } - - /// Locates the value corresponding to the given discriminant. - pub fn find_discr_value(&self, place: PlaceRef<'_>) -> Option { - self.value(self.find_discr(place)?) - } - - /// Locates the value corresponding to the given length. - pub fn find_len_value(&self, place: PlaceRef<'_>) -> Option { - self.value(self.find_len(place)?) - } - /// Iterate over all direct children. fn children(&self, parent: PlaceIndex) -> impl Iterator { Children::new(self, parent) @@ -882,12 +836,6 @@ impl<'tcx> Map<'tcx> { } } - /// Return the range of value indices inside this place. - pub fn values_inside(&self, root: PlaceIndex) -> &[ValueIndex] { - let range = self.inner_values[root].clone(); - &self.inner_values_buffer[range] - } - /// Invoke a function on each value in the given place and all descendants. #[tracing::instrument(level = "trace", skip(self, f))] fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) { @@ -937,7 +885,7 @@ impl<'tcx> Map<'tcx> { /// Recursively iterates on each value contained in `target`, paired with matching projection /// inside `source`. - pub fn for_each_value_pair( + fn for_each_value_pair( &self, target: PlaceIndex, source: PlaceIndex, From bdf8d768d5251e60f61e2b8e2f3a23212fbfe131 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 May 2026 15:32:22 +1000 Subject: [PATCH 07/28] Reduce more visibilities --- compiler/rustc_mir_dataflow/src/framework/fmt.rs | 14 +++++++------- compiler/rustc_mir_dataflow/src/move_paths/mod.rs | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index 38599cd094933..5578b435cb33f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -41,9 +41,9 @@ pub trait DebugWithContext: Eq + fmt::Debug { } /// Implements `fmt::Debug` by deferring to `>::fmt_with`. -pub struct DebugWithAdapter<'a, T, C> { - pub this: T, - pub ctxt: &'a C, +pub(crate) struct DebugWithAdapter<'a, T, C> { + pub(crate) this: T, + pub(crate) ctxt: &'a C, } impl fmt::Debug for DebugWithAdapter<'_, T, C> @@ -56,10 +56,10 @@ where } /// Implements `fmt::Debug` by deferring to `>::fmt_diff_with`. -pub struct DebugDiffWithAdapter<'a, T, C> { - pub new: T, - pub old: T, - pub ctxt: &'a C, +pub(crate) struct DebugDiffWithAdapter<'a, T, C> { + pub(crate) new: T, + pub(crate) old: T, + pub(crate) ctxt: &'a C, } impl fmt::Debug for DebugDiffWithAdapter<'_, T, C> diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 8be7d210f8dfd..7f8872b3e3493 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -388,7 +388,7 @@ impl<'tcx> MoveData<'tcx> { /// A projection into a move path producing a child path #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum MoveSubPath { +enum MoveSubPath { Deref, Field(FieldIdx), ConstantIndex(u64), @@ -397,7 +397,7 @@ pub enum MoveSubPath { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum MoveSubPathResult { +enum MoveSubPathResult { One(MoveSubPath), Subslice { from: u64, to: u64 }, Skip, @@ -405,7 +405,7 @@ pub enum MoveSubPathResult { } impl MoveSubPath { - pub fn of(elem: ProjectionKind) -> MoveSubPathResult { + fn of(elem: ProjectionKind) -> MoveSubPathResult { let subpath = match elem { // correspond to a MoveSubPath ProjectionKind::Deref => MoveSubPath::Deref, From d2bb77279d7bb9cde825584ec143124ced907b0b Mon Sep 17 00:00:00 2001 From: Andrii Anoshyn Date: Wed, 27 May 2026 22:13:43 +0300 Subject: [PATCH 08/28] Note irrefutable while let in loop type errors --- compiler/rustc_hir_typeck/src/coercion.rs | 28 ++++++++- tests/ui/coercion/coerce-loop-issue-122561.rs | 18 ++++++ .../coercion/coerce-loop-issue-122561.stderr | 62 +++++++++++++++++-- 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 7a25b5e5e7a4e..d8881de576eb4 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1881,7 +1881,7 @@ impl<'tcx> CoerceMany<'tcx> { if let Some(expr) = expression { if let hir::ExprKind::Loop( - _, + block, _, loop_src @ (hir::LoopSource::While | hir::LoopSource::ForLoop), _, @@ -1894,6 +1894,14 @@ impl<'tcx> CoerceMany<'tcx> { }; err.note(format!("{loop_type} evaluate to unit type `()`")); + if loop_src == hir::LoopSource::While + && let Some(pat) = irrefutable_while_let_pattern(block) + { + err.span_note( + pat.span, + "this pattern always matches, so the loop condition never fails", + ); + } } fcx.emit_coerce_suggestions( @@ -2126,6 +2134,24 @@ impl<'tcx> CoerceMany<'tcx> { } } +fn irrefutable_while_let_pattern<'hir>(block: &hir::Block<'hir>) -> Option<&'hir hir::Pat<'hir>> { + let hir::ExprKind::If(cond, _, _) = block.expr?.kind else { + return None; + }; + let hir::ExprKind::Let(let_expr) = cond.kind else { + return None; + }; + simple_irrefutable_pattern(let_expr.pat).then_some(let_expr.pat) +} + +fn simple_irrefutable_pattern(pat: &hir::Pat<'_>) -> bool { + match pat.kind { + hir::PatKind::Wild | hir::PatKind::Binding(_, _, _, None) => true, + hir::PatKind::Tuple(pats, _) => pats.iter().all(simple_irrefutable_pattern), + _ => false, + } +} + /// Recursively visit goals to decide whether an unsizing is possible. /// `Break`s when it isn't, and an error should be raised. /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item. diff --git a/tests/ui/coercion/coerce-loop-issue-122561.rs b/tests/ui/coercion/coerce-loop-issue-122561.rs index d79dfa28b0daf..f116f5fcddda5 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.rs +++ b/tests/ui/coercion/coerce-loop-issue-122561.rs @@ -69,6 +69,24 @@ fn while_zero_times() -> bool { } } +fn while_let_binding() -> bool { + while let x = false { + //~^ ERROR mismatched types + if x { + return true; + } + } +} + +fn while_let_tuple() -> bool { + while let (x, _) = (false, true) { + //~^ ERROR mismatched types + if x { + return true; + } + } +} + fn while_never_type() -> ! { while true { //~^ ERROR mismatched types diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr index 3fd6671565f18..ef7a3049465ad 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.stderr +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -7,7 +7,7 @@ LL | while true { = note: `#[warn(while_true)]` on by default warning: denote infinite loops with `loop { ... }` - --> $DIR/coerce-loop-issue-122561.rs:73:5 + --> $DIR/coerce-loop-issue-122561.rs:91:5 | LL | while true { | ^^^^^^^^^^ help: use `loop` @@ -171,6 +171,56 @@ LL + /* `bool` value */ error[E0308]: mismatched types --> $DIR/coerce-loop-issue-122561.rs:73:5 | +LL | fn while_let_binding() -> bool { + | ---- expected `bool` because of return type +LL | / while let x = false { +LL | | +LL | | if x { +LL | | return true; +LL | | } +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +note: this pattern always matches, so the loop condition never fails + --> $DIR/coerce-loop-issue-122561.rs:73:15 + | +LL | while let x = false { + | ^ +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:82:5 + | +LL | fn while_let_tuple() -> bool { + | ---- expected `bool` because of return type +LL | / while let (x, _) = (false, true) { +LL | | +LL | | if x { +LL | | return true; +LL | | } +LL | | } + | |_____^ expected `bool`, found `()` + | + = note: `while` loops evaluate to unit type `()` +note: this pattern always matches, so the loop condition never fails + --> $DIR/coerce-loop-issue-122561.rs:82:15 + | +LL | while let (x, _) = (false, true) { + | ^^^^^^ +help: consider returning a value here + | +LL ~ } +LL + /* `bool` value */ + | + +error[E0308]: mismatched types + --> $DIR/coerce-loop-issue-122561.rs:91:5 + | LL | fn while_never_type() -> ! { | - expected `!` because of return type LL | / while true { @@ -188,7 +238,7 @@ LL + /* `loop {}` or `panic!("...")` */ | error[E0308]: mismatched types - --> $DIR/coerce-loop-issue-122561.rs:87:5 + --> $DIR/coerce-loop-issue-122561.rs:105:5 | LL | / for i in 0.. { LL | | @@ -203,7 +253,7 @@ LL + /* `i32` value */ | error[E0308]: mismatched types - --> $DIR/coerce-loop-issue-122561.rs:94:9 + --> $DIR/coerce-loop-issue-122561.rs:112:9 | LL | / for i in 0..5 { LL | | @@ -218,7 +268,7 @@ LL + /* `usize` value */ | error[E0308]: mismatched types - --> $DIR/coerce-loop-issue-122561.rs:100:9 + --> $DIR/coerce-loop-issue-122561.rs:118:9 | LL | / while false { LL | | @@ -233,7 +283,7 @@ LL + /* `usize` value */ | error[E0308]: mismatched types - --> $DIR/coerce-loop-issue-122561.rs:106:23 + --> $DIR/coerce-loop-issue-122561.rs:124:23 | LL | let _ = |a: &[(); for x in 0..2 {}]| {}; | ^^^^^^^^^^^^^^^^ expected `usize`, found `()` @@ -244,6 +294,6 @@ help: consider returning a value here LL | let _ = |a: &[(); for x in 0..2 {} /* `usize` value */]| {}; | +++++++++++++++++++ -error: aborting due to 14 previous errors; 2 warnings emitted +error: aborting due to 16 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0308`. From cfbe6af4f25d12f7eade4106f978363d11c4d125 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Thu, 28 May 2026 10:15:29 +0800 Subject: [PATCH 09/28] Fix tupled closure signature in AsyncFn arg mismatch diagnostic --- .../rustc_middle/src/middle/lang_items.rs | 19 +++++++++++++++ .../src/error_reporting/traits/suggestions.rs | 2 +- .../async-fn/closure-arg-type-mismatch.rs | 9 +++++++ .../async-fn/closure-arg-type-mismatch.stderr | 24 +++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs create mode 100644 tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index a0e4c288c4a85..8a2595e2938b8 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -55,6 +55,25 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Given a [`DefId`], returns whether it is one of the built-in callable + /// traits: `Fn`/`FnMut`/`FnOnce` or `AsyncFn`/`AsyncFnMut`/`AsyncFnOnce`. + /// + /// These built-in callable traits all model their inputs using the + /// `rust-call` ABI, which is tupled at the type level. + pub fn is_callable_trait(self, id: DefId) -> bool { + matches!( + self.as_lang_item(id), + Some( + LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce + ) + ) + } + /// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family /// trait, if it is defined. pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option { 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 66eaa49cbd5d4..beea76434f1b3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2346,7 +2346,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let inputs = trait_ref.args.type_at(1); let sig = match inputs.kind() { - ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id) => { + ty::Tuple(inputs) if infcx.tcx.is_callable_trait(trait_ref.def_id) => { infcx.tcx.mk_fn_sig_safe_rust_abi(*inputs, infcx.next_ty_var(DUMMY_SP)) } _ => infcx.tcx.mk_fn_sig_safe_rust_abi([inputs], infcx.next_ty_var(DUMMY_SP)), diff --git a/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs new file mode 100644 index 0000000000000..be1ad20e4c221 --- /dev/null +++ b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.rs @@ -0,0 +1,9 @@ +//! Regression test for +//@ edition:2021 + +fn foo(_: impl AsyncFn(&mut i32)) {} + +fn main() { + foo(|_: i32| async {}); + //~^ ERROR type mismatch in closure arguments +} diff --git a/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr new file mode 100644 index 0000000000000..a3b47bb448d17 --- /dev/null +++ b/tests/ui/async-await/async-fn/closure-arg-type-mismatch.stderr @@ -0,0 +1,24 @@ +error[E0631]: type mismatch in closure arguments + --> $DIR/closure-arg-type-mismatch.rs:7:5 + | +LL | foo(|_: i32| async {}); + | ^^^^--------^^^^^^^^^^ + | | | + | | found signature defined here + | expected due to this + | + = note: expected closure signature `for<'a> fn(&'a mut _) -> _` + found closure signature `fn(_) -> _` +note: required by a bound in `foo` + --> $DIR/closure-arg-type-mismatch.rs:4:16 + | +LL | fn foo(_: impl AsyncFn(&mut i32)) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `foo` +help: consider adjusting the signature so it borrows its argument + | +LL | foo(|_: &mut i32| async {}); + | ++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0631`. From e731b4e5f9da236a0525e6a815c4261aa835b334 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 27 May 2026 15:09:42 +0000 Subject: [PATCH 10/28] Allow two object files for a single CGU in CompiledModule cg_clif needs this as it passes inline assembly to an external assembler, producing a separate object file. Currently it emits multiple CompiledModules, but this is not compatible with using the codegen coordinator of cg_ssa. --- .../rustc_codegen_cranelift/src/driver/aot.rs | 66 +++++++------------ .../rustc_codegen_cranelift/src/global_asm.rs | 18 +---- compiler/rustc_codegen_ssa/src/back/link.rs | 57 ++++++++++++---- compiler/rustc_codegen_ssa/src/back/write.rs | 15 +++++ compiler/rustc_codegen_ssa/src/lib.rs | 2 + 5 files changed, 85 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 3781ad7b3b83f..323fec06bcc58 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -35,8 +35,7 @@ fn disable_incr_cache() -> bool { } struct ModuleCodegenResult { - module_regular: CompiledModule, - module_global_asm: Option, + module: CompiledModule, existing_work_product: Option<(WorkProductId, WorkProduct)>, } @@ -80,29 +79,25 @@ impl OngoingCodegen { Ok(module_codegen_result) => module_codegen_result, Err(err) => sess.dcx().fatal(err), }; - let ModuleCodegenResult { module_regular, module_global_asm, existing_work_product } = - module_codegen_result; + let ModuleCodegenResult { module, existing_work_product } = module_codegen_result; if let Some((work_product_id, work_product)) = existing_work_product { work_products.insert(work_product_id, work_product); } else { let work_product = if disable_incr_cache { None - } else if let Some(module_global_asm) = &module_global_asm { + } else if let Some(global_asm_object) = &module.global_asm_object { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, - &module_regular.name, - &[ - ("o", module_regular.object.as_ref().unwrap()), - ("asm.o", module_global_asm.object.as_ref().unwrap()), - ], + &module.name, + &[("o", module.object.as_ref().unwrap()), ("asm.o", global_asm_object)], &[], ) } else { rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, - &module_regular.name, - &[("o", module_regular.object.as_ref().unwrap())], + &module.name, + &[("o", module.object.as_ref().unwrap())], &[], ) }; @@ -111,10 +106,7 @@ impl OngoingCodegen { } } - modules.push(module_regular); - if let Some(module_global_asm) = module_global_asm { - modules.push(module_global_asm); - } + modules.push(module); } self.concurrency_limiter.finished(); @@ -163,29 +155,17 @@ fn emit_cgu( debug.emit(&mut product); } - let module_regular = emit_module( + let module = emit_module( output_filenames, prof, product.object, ModuleKind::Regular, name.clone(), + global_asm_object_file, producer, )?; - Ok(ModuleCodegenResult { - module_regular, - module_global_asm: global_asm_object_file.map(|global_asm_object_file| CompiledModule { - name: format!("{name}.asm"), - kind: ModuleKind::Regular, - object: Some(global_asm_object_file), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: Vec::new(), - }), - existing_work_product: None, - }) + Ok(ModuleCodegenResult { module, existing_work_product: None }) } fn emit_module( @@ -194,6 +174,7 @@ fn emit_module( mut object: cranelift_object::object::write::Object<'_>, kind: ModuleKind, name: String, + global_asm_object: Option, producer_str: &str, ) -> Result { if object.format() == cranelift_object::object::BinaryFormat::Elf { @@ -235,6 +216,7 @@ fn emit_module( name, kind, object: Some(tmp_file), + global_asm_object, dwarf_object: None, bytecode: None, assembly: None, @@ -265,7 +247,7 @@ fn reuse_workproduct_for_cgu( } let obj_out_global_asm = - crate::global_asm::add_file_stem_postfix(obj_out_regular.clone(), ".asm"); + tcx.output_filenames(()).temp_path_ext_for_cgu("asm.o", cgu.name().as_str()); let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") { let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(tcx.sess, asm_o); if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm) @@ -283,26 +265,21 @@ fn reuse_workproduct_for_cgu( }; Ok(ModuleCodegenResult { - module_regular: CompiledModule { + module: CompiledModule { name: cgu.name().to_string(), kind: ModuleKind::Regular, object: Some(obj_out_regular), + global_asm_object: source_file_global_asm.as_ref().map(|_| obj_out_global_asm), dwarf_object: None, bytecode: None, assembly: None, llvm_ir: None, - links_from_incr_cache: vec![source_file_regular], + links_from_incr_cache: if let Some(source_file_global_asm) = source_file_global_asm { + vec![source_file_regular, source_file_global_asm] + } else { + vec![source_file_regular] + }, }, - module_global_asm: source_file_global_asm.map(|source_file| CompiledModule { - name: cgu.name().to_string(), - kind: ModuleKind::Regular, - object: Some(obj_out_global_asm), - dwarf_object: None, - bytecode: None, - assembly: None, - llvm_ir: None, - links_from_incr_cache: vec![source_file], - }), existing_work_product: Some((cgu.work_product_id(), work_product)), }) } @@ -447,6 +424,7 @@ fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option { product.object, ModuleKind::Allocator, "allocator_shim".to_owned(), + None, &crate::debuginfo::producer(tcx.sess), ) { Ok(allocator_module) => Some(allocator_module), diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 0c5f4136a32d6..ee7e6732e6a77 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, }; -use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_session::config::OutputFilenames; use rustc_target::asm::InlineAsmArch; use crate::prelude::*; @@ -198,10 +198,7 @@ pub(crate) fn compile_global_asm( .join("\n"); global_asm.push('\n'); - let global_asm_object_file = add_file_stem_postfix( - config.output_filenames.temp_path_for_cgu(OutputType::Object, cgu_name), - ".asm", - ); + let global_asm_object_file = config.output_filenames.temp_path_ext_for_cgu("asm.o", cgu_name); // Assemble `global_asm` if option_env!("CG_CLIF_FORCE_GNU_AS").is_some() { @@ -271,14 +268,3 @@ pub(crate) fn compile_global_asm( Ok(Some(global_asm_object_file)) } - -pub(crate) fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf { - let mut new_filename = path.file_stem().unwrap().to_owned(); - new_filename.push(postfix); - if let Some(extension) = path.extension() { - new_filename.push("."); - new_filename.push(extension); - } - path.set_file_name(new_filename); - path -} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d2c82280ad2ab..0152030ea875b 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -98,8 +98,13 @@ pub fn link_binary( } sess.time("link_binary_check_files_are_writeable", || { - for obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { - check_file_is_writeable(obj, sess); + for m in &compiled_modules.modules { + if let Some(obj) = &m.object { + check_file_is_writeable(obj, sess); + } + if let Some(obj) = &m.global_asm_object { + check_file_is_writeable(obj, sess); + } } }); @@ -202,6 +207,10 @@ pub fn link_binary( ensure_removed(sess.dcx(), obj); } + if !preserve_objects && let Some(ref obj) = module.global_asm_object { + ensure_removed(sess.dcx(), obj); + } + if !preserve_dwarf_objects && let Some(ref dwo_obj) = module.dwarf_object { ensure_removed(sess.dcx(), dwo_obj); } @@ -307,6 +316,7 @@ fn link_rlib<'a>( .modules .iter() .filter_map(|m| m.object.as_ref()) + .chain(compiled_modules.modules.iter().filter_map(|m| m.global_asm_object.as_ref())) .map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string()) .collect(); @@ -352,6 +362,10 @@ fn link_rlib<'a>( ab.add_file(obj); } + if let Some(obj) = m.global_asm_object.as_ref() { + ab.add_file(obj); + } + if let Some(dwarf_obj) = m.dwarf_object.as_ref() { ab.add_file(dwarf_obj); } @@ -360,9 +374,13 @@ fn link_rlib<'a>( match flavor { RlibFlavor::Normal => {} RlibFlavor::StaticlibBase => { - let obj = compiled_modules.allocator_module.as_ref().and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - ab.add_file(obj); + if let Some(m) = &compiled_modules.allocator_module { + if let Some(obj) = &m.object { + ab.add_file(obj); + } + if let Some(obj) = &m.global_asm_object { + ab.add_file(obj); + } } } } @@ -632,8 +650,13 @@ fn link_dwarf_object( // Input objs contain .o/.dwo files from the current crate. match sess.opts.unstable_opts.split_dwarf_kind { SplitDwarfKind::Single => { - for input_obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { - package.add_input_object(input_obj)?; + for m in &compiled_modules.modules { + if let Some(input_obj) = &m.object { + package.add_input_object(input_obj)?; + } + if let Some(input_obj) = &m.global_asm_object { + package.add_input_object(input_obj)?; + } } } SplitDwarfKind::Split => { @@ -2243,8 +2266,13 @@ fn add_linked_symbol_object( /// Add object files containing code from the current crate. fn add_local_crate_regular_objects(cmd: &mut dyn Linker, compiled_modules: &CompiledModules) { - for obj in compiled_modules.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); + for m in &compiled_modules.modules { + if let Some(obj) = &m.object { + cmd.add_object(obj); + } + if let Some(obj) = &m.global_asm_object { + cmd.add_object(obj); + } } } @@ -2255,10 +2283,13 @@ fn add_local_crate_allocator_objects( crate_info: &CrateInfo, crate_type: CrateType, ) { - if needs_allocator_shim_for_linking(&crate_info.dependency_formats, crate_type) { - if let Some(obj) = - compiled_modules.allocator_module.as_ref().and_then(|m| m.object.as_ref()) - { + if needs_allocator_shim_for_linking(&crate_info.dependency_formats, crate_type) + && let Some(m) = &compiled_modules.allocator_module + { + if let Some(obj) = &m.object { + cmd.add_object(obj); + } + if let Some(obj) = &m.global_asm_object { cmd.add_object(obj); } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7b22ac231df1c..54693d0a69527 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -474,6 +474,9 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( if let Some(object_file_path) = &module.object { files.push((OutputType::Object.extension(), object_file_path.as_path())); } + if let Some(global_asm_object_file_path) = &module.global_asm_object { + files.push(("asm.o", global_asm_object_file_path.as_path())); + } if let Some(dwarf_object_file_path) = &module.dwarf_object { files.push(("dwo", dwarf_object_file_path.as_path())); } @@ -620,6 +623,10 @@ pub fn produce_final_output_artifacts( ensure_removed(sess.dcx(), path); } + if let Some(ref path) = module.global_asm_object { + ensure_removed(sess.dcx(), path); + } + if let Some(ref path) = module.dwarf_object { ensure_removed(sess.dcx(), path); } @@ -924,6 +931,13 @@ fn execute_copy_from_cache_work_item( let llvm_ir = load_from_incr_cache(module_config.emit_ir, OutputType::LlvmAssembly); let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode); let object = load_from_incr_cache(should_emit_obj, OutputType::Object); + let global_asm_object = + if should_emit_obj && let Some(saved_file) = module.source.saved_files.get("asm.o") { + let output_path = cgcx.output_filenames.temp_path_ext_for_cgu("asm.o", &module.name); + load_from_incr_comp_dir(output_path, &saved_file) + } else { + None + }; if should_emit_obj && object.is_none() { dcx.emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name }) } @@ -933,6 +947,7 @@ fn execute_copy_from_cache_work_item( kind: ModuleKind::Regular, name: module.name, object, + global_asm_object, dwarf_object, bytecode, assembly, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index c7446bd784b40..560cbe98f7497 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -109,6 +109,7 @@ impl ModuleCodegen { name: self.name, kind: self.kind, object, + global_asm_object: None, dwarf_object, bytecode, assembly, @@ -123,6 +124,7 @@ pub struct CompiledModule { pub name: String, pub kind: ModuleKind, pub object: Option, + pub global_asm_object: Option, pub dwarf_object: Option, pub bytecode: Option, pub assembly: Option, // --emit=asm From 686c8c924cc385f5addfdfbadbeadeb8d568ef6f Mon Sep 17 00:00:00 2001 From: Ilia Novoselov Date: Fri, 29 May 2026 15:25:53 +0200 Subject: [PATCH 11/28] Expanded tests for &x -> &mut x suggestions --- .../borrowck-deref-pattern-assignment.rs | 47 ++++++++++ .../borrowck-deref-pattern-assignment.stderr | 87 +++++++++++++++++++ ...rowck-for-loop-deref-pattern-assignment.rs | 10 --- ...k-for-loop-deref-pattern-assignment.stderr | 17 ---- 4 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 tests/ui/borrowck/borrowck-deref-pattern-assignment.rs create mode 100644 tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr delete mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs delete mode 100644 tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr diff --git a/tests/ui/borrowck/borrowck-deref-pattern-assignment.rs b/tests/ui/borrowck/borrowck-deref-pattern-assignment.rs new file mode 100644 index 0000000000000..9d20f8a36aae4 --- /dev/null +++ b/tests/ui/borrowck/borrowck-deref-pattern-assignment.rs @@ -0,0 +1,47 @@ +//! regression test for +//! Ensure the diagnostic suggests `&(mut x)` (parenthesized) instead of `&mut x`. + +fn for_loop() { + let nums: &[u32] = &[1, 2, 3]; + for &num in nums { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + println!("{num}"); + } +} + +fn let_deref(num_ref: &u32) -> u32 { + let &num = num_ref; + + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + + num +} + +fn deref_inside_pattern(option_num_ref: Option<&u32>) { + if let Some(&num) = option_num_ref { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + + println!("{num}"); + } +} + +/// Insides of deref pattern do not need additional parens +fn inside_of_deref(num_option_ref: &Option) { + if let &Some(num) = num_option_ref { + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + + println!("{num}"); + } +} + +/// &mut deref pattern does not need additional parens +fn let_mut_deref(num_mut_ref: &mut u32) -> u32 { + let &mut num = num_mut_ref; + + num *= 2; //~ ERROR cannot assign twice to immutable variable `num` + + num +} + + +fn main() {} diff --git a/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr new file mode 100644 index 0000000000000..e0dd39c9ef8f4 --- /dev/null +++ b/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr @@ -0,0 +1,87 @@ +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-deref-pattern-assignment.rs:7:9 + | +LL | for &num in nums { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL - for &num in nums { +LL + for &(mut num) in nums { + | + +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-deref-pattern-assignment.rs:15:5 + | +LL | let &num = num_ref; + | --- first assignment to `num` +LL | +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | let &mut num = num_ref; + | +++ +help: to modify the original value, take a borrow instead + | +LL | let &ref mut num = num_ref; + | +++++++ + +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-deref-pattern-assignment.rs:22:9 + | +LL | if let Some(&num) = option_num_ref { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | if let Some(&mut num) = option_num_ref { + | +++ +help: to modify the original value, take a borrow instead + | +LL | if let Some(&ref mut num) = option_num_ref { + | +++++++ + +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-deref-pattern-assignment.rs:31:9 + | +LL | if let &Some(num) = num_option_ref { + | --- first assignment to `num` +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | if let &Some(mut num) = num_option_ref { + | +++ +help: to modify the original value, take a borrow instead + | +LL | if let &Some(ref mut num) = num_option_ref { + | +++++++ + +error[E0384]: cannot assign twice to immutable variable `num` + --> $DIR/borrowck-deref-pattern-assignment.rs:41:5 + | +LL | let &mut num = num_mut_ref; + | --- first assignment to `num` +LL | +LL | num *= 2; + | ^^^^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +LL | let &mut mut num = num_mut_ref; + | +++ +help: to modify the original value, take a borrow instead + | +LL | let &mut ref mut num = num_mut_ref; + | +++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0384`. diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs deleted file mode 100644 index fc4f1e4eacb95..0000000000000 --- a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! regression test for -//! Ensure the diagnostic suggests `for &(mut x) ...` (parenthesized) instead of `&mut x`. - -fn main() { - let nums: &[u32] = &[1, 2, 3]; - for &num in nums { - num *= 2; //~ ERROR cannot assign twice to immutable variable `num` - println!("{num}"); - } -} diff --git a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr deleted file mode 100644 index 3c4d0e966136d..0000000000000 --- a/tests/ui/borrowck/borrowck-for-loop-deref-pattern-assignment.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0384]: cannot assign twice to immutable variable `num` - --> $DIR/borrowck-for-loop-deref-pattern-assignment.rs:7:9 - | -LL | for &num in nums { - | --- first assignment to `num` -LL | num *= 2; - | ^^^^^^^^ cannot assign twice to immutable variable - | -help: consider making this binding mutable - | -LL - for &num in nums { -LL + for &(mut num) in nums { - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0384`. From 9bef677785b802f9a7e6ba1e298a430f1b993477 Mon Sep 17 00:00:00 2001 From: Ilia Novoselov Date: Fri, 29 May 2026 15:32:44 +0200 Subject: [PATCH 12/28] Fixed &x -> &mut x suggestions for pattern matching --- .../src/diagnostics/conflict_errors.rs | 75 +++++++++---------- .../borrowck-deref-pattern-assignment.stderr | 10 ++- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 9a64a063fcf5c..993f04fe1c161 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4023,7 +4023,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && decl.can_be_made_mutable() { let mut is_for_loop = false; - let mut is_ref_pattern = false; + let mut is_immut_ref_pattern = false; if let LocalInfo::User(BindingForm::Var(VarBindingForm { opt_match_place: Some((_, match_span)), .. @@ -4031,55 +4031,52 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { { if matches!(match_span.desugaring_kind(), Some(DesugaringKind::ForLoop)) { is_for_loop = true; + } - if let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(self.mir_def_id()) { - struct RefPatternFinder<'tcx> { - tcx: TyCtxt<'tcx>, - binding_span: Span, - is_ref_pattern: bool, - } + if let Some(body) = self.infcx.tcx.hir_maybe_body_owned_by(self.mir_def_id()) { + struct RefPatternFinder<'tcx> { + tcx: TyCtxt<'tcx>, + binding_span: Span, + is_immut_ref_pattern: bool, + } - impl<'tcx> Visitor<'tcx> for RefPatternFinder<'tcx> { - type NestedFilter = OnlyBodies; + impl<'tcx> Visitor<'tcx> for RefPatternFinder<'tcx> { + type NestedFilter = OnlyBodies; - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.tcx - } + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.tcx + } - fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { - if !self.is_ref_pattern - && let hir::PatKind::Binding(_, _, ident, _) = pat.kind - && ident.span == self.binding_span - { - self.is_ref_pattern = - self.tcx.hir_parent_iter(pat.hir_id).any(|(_, node)| { - matches!( - node, - hir::Node::Pat(hir::Pat { - kind: hir::PatKind::Ref(..), - .. - }) - ) - }); - } - hir::intravisit::walk_pat(self, pat); + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + if !self.is_immut_ref_pattern + && let hir::PatKind::Binding(_, _, ident, _) = pat.kind + && ident.span == self.binding_span + && matches!( + self.tcx.parent_hir_node(pat.hir_id), + hir::Node::Pat(hir::Pat { + kind: hir::PatKind::Ref(_, _, hir::Mutability::Not), + .. + }) + ) + { + self.is_immut_ref_pattern = true; } + hir::intravisit::walk_pat(self, pat); } + } - let mut finder = RefPatternFinder { - tcx: self.infcx.tcx, - binding_span: decl.source_info.span, - is_ref_pattern: false, - }; + let mut finder = RefPatternFinder { + tcx: self.infcx.tcx, + binding_span: decl.source_info.span, + is_immut_ref_pattern: false, + }; - finder.visit_body(body); - is_ref_pattern = finder.is_ref_pattern; - } + finder.visit_body(body); + is_immut_ref_pattern = finder.is_immut_ref_pattern; } } - let (span, message) = if is_for_loop - && is_ref_pattern + let (span, message) = if is_immut_ref_pattern && let Ok(binding_name) = self.infcx.tcx.sess.source_map().span_to_snippet(decl.source_info.span) { diff --git a/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr b/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr index e0dd39c9ef8f4..65524e420d7fb 100644 --- a/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr +++ b/tests/ui/borrowck/borrowck-deref-pattern-assignment.stderr @@ -23,8 +23,9 @@ LL | num *= 2; | help: consider making this binding mutable | -LL | let &mut num = num_ref; - | +++ +LL - let &num = num_ref; +LL + let &(mut num) = num_ref; + | help: to modify the original value, take a borrow instead | LL | let &ref mut num = num_ref; @@ -40,8 +41,9 @@ LL | num *= 2; | help: consider making this binding mutable | -LL | if let Some(&mut num) = option_num_ref { - | +++ +LL - if let Some(&num) = option_num_ref { +LL + if let Some(&(mut num)) = option_num_ref { + | help: to modify the original value, take a borrow instead | LL | if let Some(&ref mut num) = option_num_ref { From 4f1630d3aadc3a4aa003134caaff25354ba1dc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 20:29:16 +0200 Subject: [PATCH 13/28] Move `compute_object_lifetime_bound` into submodule `dyn_trait` --- .../src/hir_ty_lowering/dyn_trait.rs | 42 +++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 45 +------------------ 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index a498e97403881..818c418ad7c60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -515,6 +515,48 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } + /// Given the bounds on an object, determines what single region bound (if any) we can + /// use to summarize this type. + /// + /// The basic idea is that we will use the bound the user + /// provided, if they provided one, and otherwise search the supertypes of trait bounds + /// for region bounds. It may be that we can derive no bound at all, in which case + /// we return `None`. + #[instrument(level = "debug", skip(self, span), ret)] + fn compute_object_lifetime_bound( + &self, + span: Span, + existential_predicates: &'tcx ty::List>, + ) -> Option> // if None, use the default + { + let tcx = self.tcx(); + + // No explicit region bound specified. Therefore, examine trait + // bounds and see if we can derive region bounds from those. + let derived_region_bounds = traits::wf::object_region_bounds(tcx, existential_predicates); + + // If there are no derived region bounds, then report back that we + // can find no region bound. The caller will use the default. + if derived_region_bounds.is_empty() { + return None; + } + + // If any of the derived region bounds are 'static, that is always + // the best choice. + if derived_region_bounds.iter().any(|r| r.is_static()) { + return Some(tcx.lifetimes.re_static); + } + + // Determine whether there is exactly one unique region in the set + // of derived region bounds. If so, use that. Otherwise, report an + // error. + let r = derived_region_bounds[0]; + if derived_region_bounds[1..].iter().any(|r1| r != *r1) { + self.dcx().emit_err(crate::errors::AmbiguousLifetimeBound { span }); + } + Some(r) + } + /// Prohibit or lint against *bare* trait object types depending on the edition. /// /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5a86e8186a5aa..89bf398a9d166 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -47,12 +47,11 @@ use rustc_session::errors::feature_err; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, FulfillmentError}; use tracing::{debug, instrument}; use crate::check::check_abi; -use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoFieldOnType}; +use crate::errors::{BadReturnTypeNotation, NoFieldOnType}; use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint}; use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args}; use crate::middle::resolve_bound_vars as rbv; @@ -3738,48 +3737,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Given the bounds on an object, determines what single region bound (if any) we can - /// use to summarize this type. - /// - /// The basic idea is that we will use the bound the user - /// provided, if they provided one, and otherwise search the supertypes of trait bounds - /// for region bounds. It may be that we can derive no bound at all, in which case - /// we return `None`. - #[instrument(level = "debug", skip(self, span), ret)] - fn compute_object_lifetime_bound( - &self, - span: Span, - existential_predicates: &'tcx ty::List>, - ) -> Option> // if None, use the default - { - let tcx = self.tcx(); - - // No explicit region bound specified. Therefore, examine trait - // bounds and see if we can derive region bounds from those. - let derived_region_bounds = object_region_bounds(tcx, existential_predicates); - - // If there are no derived region bounds, then report back that we - // can find no region bound. The caller will use the default. - if derived_region_bounds.is_empty() { - return None; - } - - // If any of the derived region bounds are 'static, that is always - // the best choice. - if derived_region_bounds.iter().any(|r| r.is_static()) { - return Some(tcx.lifetimes.re_static); - } - - // Determine whether there is exactly one unique region in the set - // of derived region bounds. If so, use that. Otherwise, report an - // error. - let r = derived_region_bounds[0]; - if derived_region_bounds[1..].iter().any(|r1| r != *r1) { - self.dcx().emit_err(AmbiguousLifetimeBound { span }); - } - Some(r) - } - fn construct_const_ctor_value( &self, ctor_def_id: DefId, From a866e4279db74d724eba32c2369ca0fa8606f76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 16:22:53 +0200 Subject: [PATCH 14/28] Rename `AmbiguousAssocItem` to `AmbiguityBetweenVariantAndAssocItem` and move its `Diagnostic` impl --- .../src/hir_ty_lowering/errors.rs | 52 +++++++++++++++++++ .../src/hir_ty_lowering/mod.rs | 52 +------------------ 2 files changed, 54 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 5e11819805e15..710d527146a53 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1847,6 +1847,58 @@ fn generics_args_err_extend<'a>( } } +pub(super) struct AmbiguityBetweenVariantAndAssocItem<'tcx> { + pub(super) variant_def_id: DefId, + pub(super) item_def_id: DefId, + pub(super) span: Span, + pub(super) segment_ident: Ident, + pub(super) bound_def_id: DefId, + pub(super) self_ty: Ty<'tcx>, + pub(super) tcx: TyCtxt<'tcx>, + pub(super) mode: super::LowerTypeRelativePathMode, +} + +impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAssocItem<'tcx> { + fn into_diag( + self, + dcx: rustc_errors::DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> Diag<'a, ()> { + let Self { + variant_def_id, + item_def_id, + span, + segment_ident, + bound_def_id, + self_ty, + tcx, + mode, + } = self; + let mut lint = Diag::new(dcx, level, "ambiguous associated item"); + + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + segment_ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), + Applicability::MachineApplicable, + ); + lint + } +} + pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { match assoc_tag { ty::AssocTag::Fn => "function", diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 89bf398a9d166..e7071861d2ab0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -26,7 +26,7 @@ use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, StashKey, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, StashKey, struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -1549,54 +1549,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, mode: LowerTypeRelativePathMode, ) -> Result, ErrorGuaranteed> { - struct AmbiguousAssocItem<'tcx> { - variant_def_id: DefId, - item_def_id: DefId, - span: Span, - segment_ident: Ident, - bound_def_id: DefId, - self_ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - mode: LowerTypeRelativePathMode, - } - - impl<'a, 'tcx> Diagnostic<'a, ()> for AmbiguousAssocItem<'tcx> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - let Self { - variant_def_id, - item_def_id, - span, - segment_ident, - bound_def_id, - self_ty, - tcx, - mode, - } = self; - let mut lint = Diag::new(dcx, level, "ambiguous associated item"); - - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - segment_ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); - - lint.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), - Applicability::MachineApplicable, - ); - lint - } - } - debug!(%self_ty, ?segment.ident); let tcx = self.tcx(); @@ -1676,7 +1628,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, - AmbiguousAssocItem { + errors::AmbiguityBetweenVariantAndAssocItem { variant_def_id, item_def_id, span, From fd36a801b1ea39cc9a251058aa231de09d844f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 29 May 2026 16:27:03 +0200 Subject: [PATCH 15/28] Move distractingly lengthy error reporting code into new `report_ambiguous_assoc_item` Moreover, in `probe_single_bound_for_assoc_item` unconditionally return an `Err(_)` if there's more than a single bound (aka ambiguity) instead of returning the first bound in some cases. This avoids triggering a debug assertion later on ("not enough bound vars"). See the added test for details. --- .../src/hir_ty_lowering/errors.rs | 139 ++++++++++++++++- .../src/hir_ty_lowering/mod.rs | 141 ++---------------- tests/crashes/139387.rs | 15 -- .../path-ambiguous-late-bound-vars.rs | 31 ++++ .../path-ambiguous-late-bound-vars.stderr | 26 ++++ 5 files changed, 205 insertions(+), 147 deletions(-) delete mode 100644 tests/crashes/139387.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs create mode 100644 tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 710d527146a53..b8cdd67519bef 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -403,6 +403,141 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }) } + pub(super) fn report_ambiguous_assoc_item( + &self, + bound1: ty::PolyTraitRef<'tcx>, + bound2: ty::PolyTraitRef<'tcx>, + matching_candidates: impl Iterator>, + qself: AssocItemQSelf, + assoc_tag: ty::AssocTag, + assoc_ident: Ident, + span: Span, + constraint: Option<&hir::AssocItemConstraint<'tcx>>, + ) -> ErrorGuaranteed { + let tcx = self.tcx(); + + let assoc_kind_str = assoc_tag_str(assoc_tag); + let qself_str = qself.to_string(tcx); + let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { + span, + assoc_kind: assoc_kind_str, + assoc_ident, + qself: &qself_str, + }); + // Provide a more specific error code index entry for equality bindings. + err.code( + if let Some(constraint) = constraint + && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind + { + E0222 + } else { + E0221 + }, + ); + + // FIXME(#97583): Print associated item bindings properly (i.e., not as equality + // predicates!). + // FIXME: Turn this into a structured, translatable & more actionable suggestion. + let mut where_bounds = vec![]; + for bound in [bound1, bound2].into_iter().chain(matching_candidates) { + let bound_id = bound.def_id(); + let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( + tcx, + assoc_ident, + assoc_tag, + bound_id, + ); + let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); + + if let Some(bound_span) = bound_span { + err.span_label( + bound_span, + format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), + ); + if let Some(constraint) = constraint { + match constraint.kind { + hir::AssocItemConstraintKind::Equality { term } => { + let term: ty::Term<'_> = match term { + hir::Term::Ty(ty) => self.lower_ty(ty).into(), + hir::Term::Const(ct) => { + let assoc_item = + assoc_item.expect("assoc_item should be present"); + let projection_term = bound.map_bound(|trait_ref| { + let item_segment = hir::PathSegment { + ident: constraint.ident, + hir_id: constraint.hir_id, + res: Res::Err, + args: Some(constraint.gen_args), + infer_args: false, + }; + + let alias_args = self.lower_generic_args_of_assoc_item( + constraint.ident.span, + assoc_item.def_id, + &item_segment, + trait_ref.args, + ); + ty::AliasTerm::new_from_def_id( + tcx, + assoc_item.def_id, + alias_args, + ) + }); + + // FIXME(mgca): code duplication with other places we lower + // the rhs' of associated const bindings + let ty = projection_term.map_bound(|alias| { + tcx.type_of(alias.def_id()) + .instantiate(tcx, alias.args) + .skip_norm_wip() + }); + let ty = super::bounds::check_assoc_const_binding_type( + self, + constraint.ident, + ty, + constraint.hir_id, + ); + + self.lower_const_arg(ct, ty).into() + } + }; + if term.references_error() { + continue; + } + // FIXME(#97583): This isn't syntactically well-formed! + where_bounds.push(format!( + " T: {trait}::{assoc_ident} = {term}", + trait = bound.print_only_trait_path(), + )); + } + // FIXME: Provide a suggestion. + hir::AssocItemConstraintKind::Bound { bounds: _ } => {} + } + } else { + err.span_suggestion_verbose( + span.with_hi(assoc_ident.span.lo()), + "use fully-qualified syntax to disambiguate", + format!("<{qself_str} as {}>::", bound.print_only_trait_path()), + Applicability::MaybeIncorrect, + ); + } + } else { + let trait_ = tcx.short_string(bound.print_only_trait_path(), err.long_ty_path()); + err.note(format!( + "associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`", + )); + } + } + if !where_bounds.is_empty() { + err.help(format!( + "consider introducing a new type parameter `T` and adding `where` constraints:\ + \n where\n T: {qself_str},\n{}", + where_bounds.join(",\n"), + )); + } + err.emit() + } + pub(crate) fn report_missing_self_ty_for_resolved_path( &self, trait_def_id: DefId, @@ -568,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - pub(super) fn report_ambiguous_assoc_item_path( + fn report_ambiguous_assoc_item_path( &self, span: Span, types: &[String], @@ -1899,7 +2034,7 @@ impl<'a, 'tcx> rustc_errors::Diagnostic<'a, ()> for AmbiguityBetweenVariantAndAs } } -pub(crate) fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { +fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str { match assoc_tag { ty::AssocTag::Fn => "function", ty::AssocTag::Const => "constant", diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index e7071861d2ab0..d2c236b5e0a12 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -36,7 +36,6 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::DynCompatibilityViolation; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ self, Const, FnSigKind, GenericArgKind, GenericArgsRef, GenericParamDefKind, LitToConstInput, Ty, TyCtxt, TypeSuperFoldable, TypeVisitableExt, TypingMode, Unnormalized, Upcast, @@ -1281,13 +1280,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { where I: Iterator>, { - let tcx = self.tcx(); - let mut matching_candidates = all_candidates().filter(|r| { self.probe_trait_that_defines_assoc_item(r.def_id(), assoc_tag, assoc_ident) }); - let Some(bound) = matching_candidates.next() else { + let Some(bound1) = matching_candidates.next() else { return Err(self.report_unresolved_assoc_item( all_candidates, qself, @@ -1297,137 +1294,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { constraint, )); }; - debug!(?bound); if let Some(bound2) = matching_candidates.next() { - debug!(?bound2); - - let assoc_kind_str = errors::assoc_tag_str(assoc_tag); - let qself_str = qself.to_string(tcx); - let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { - span, - assoc_kind: assoc_kind_str, + return Err(self.report_ambiguous_assoc_item( + bound1, + bound2, + matching_candidates, + qself, + assoc_tag, assoc_ident, - qself: &qself_str, - }); - // Provide a more specific error code index entry for equality bindings. - err.code( - if let Some(constraint) = constraint - && let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind - { - E0222 - } else { - E0221 - }, - ); - - // FIXME(#97583): Print associated item bindings properly (i.e., not as equality - // predicates!). - // FIXME: Turn this into a structured, translatable & more actionable suggestion. - let mut where_bounds = vec![]; - for bound in [bound, bound2].into_iter().chain(matching_candidates) { - let bound_id = bound.def_id(); - let assoc_item = tcx.associated_items(bound_id).find_by_ident_and_kind( - tcx, - assoc_ident, - assoc_tag, - bound_id, - ); - let bound_span = assoc_item.and_then(|item| tcx.hir_span_if_local(item.def_id)); - - if let Some(bound_span) = bound_span { - err.span_label( - bound_span, - format!("ambiguous `{assoc_ident}` from `{}`", bound.print_trait_sugared(),), - ); - if let Some(constraint) = constraint { - match constraint.kind { - hir::AssocItemConstraintKind::Equality { term } => { - let term: ty::Term<'_> = match term { - hir::Term::Ty(ty) => self.lower_ty(ty).into(), - hir::Term::Const(ct) => { - let assoc_item = - assoc_item.expect("assoc_item should be present"); - let projection_term = bound.map_bound(|trait_ref| { - let item_segment = hir::PathSegment { - ident: constraint.ident, - hir_id: constraint.hir_id, - res: Res::Err, - args: Some(constraint.gen_args), - infer_args: false, - }; - - let alias_args = self.lower_generic_args_of_assoc_item( - constraint.ident.span, - assoc_item.def_id, - &item_segment, - trait_ref.args, - ); - ty::AliasTerm::new_from_def_id( - tcx, - assoc_item.def_id, - alias_args, - ) - }); - - // FIXME(mgca): code duplication with other places we lower - // the rhs' of associated const bindings - let ty = projection_term.map_bound(|alias| { - tcx.type_of(alias.def_id()) - .instantiate(tcx, alias.args) - .skip_norm_wip() - }); - let ty = bounds::check_assoc_const_binding_type( - self, - constraint.ident, - ty, - constraint.hir_id, - ); - - self.lower_const_arg(ct, ty).into() - } - }; - if term.references_error() { - continue; - } - // FIXME(#97583): This isn't syntactically well-formed! - where_bounds.push(format!( - " T: {trait}::{assoc_ident} = {term}", - trait = bound.print_only_trait_path(), - )); - } - // FIXME: Provide a suggestion. - hir::AssocItemConstraintKind::Bound { bounds: _ } => {} - } - } else { - err.span_suggestion_verbose( - span.with_hi(assoc_ident.span.lo()), - "use fully-qualified syntax to disambiguate", - format!("<{qself_str} as {}>::", bound.print_only_trait_path()), - Applicability::MaybeIncorrect, - ); - } - } else { - let trait_ = - tcx.short_string(bound.print_only_trait_path(), err.long_ty_path()); - err.note(format!( - "associated {assoc_kind_str} `{assoc_ident}` could derive from `{trait_}`", - )); - } - } - if !where_bounds.is_empty() { - err.help(format!( - "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {qself_str},\n{}", - where_bounds.join(",\n"), - )); - let reported = err.emit(); - return Err(reported); - } - err.emit(); + span, + constraint, + )); } - Ok(bound) + Ok(bound1) } /// Lower a [type-relative](hir::QPath::TypeRelative) path in type position to a type. diff --git a/tests/crashes/139387.rs b/tests/crashes/139387.rs deleted file mode 100644 index 133643ad084ba..0000000000000 --- a/tests/crashes/139387.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: #139387 -//@ needs-rustc-debug-assertions - -trait A { - fn method() -> impl Sized; -} -trait B { - fn method(Hash: Wrap Epsilon<'_, SI1: Eta>>>) -> impl Sized; -} - -fn ambiguous() -where - T::method(..): Send, -{ -} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs new file mode 100644 index 0000000000000..d994e26ad03a9 --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.rs @@ -0,0 +1,31 @@ +// We used to lower the ambiguous `T::f(..)` to `::f::{type#0}` after emitting the error. +// Meaning we picked one of the candidates and proceeded instead of bailing out early. +// However, sensibly RBV doesn't register any bound vars for ambiguous RTN[^1], so later on when +// wrapping the predicate (here: WellFormed) into a Binder we would correctly fail bound var +// validation (in debug mode). +// +// We now bail out early and thus prevent nonsensical types from getting leaked to subsequent +// compiler passes. +// +// [^1]: It actually maintains its own bespoke lowering function for type-relative paths that +// relatively closely mirrors the one in HIR ty lowering. + +// issue: +//@ needs-rustc-debug-assertions +#![feature(return_type_notation)] + +trait A { + fn f() -> impl Sized; +} + +trait B { + fn f<'b>() -> impl Sized; +} + +fn f() +where + T::f(..):, //~ ERROR ambiguous associated function +{ +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr new file mode 100644 index 0000000000000..5b6804095de0d --- /dev/null +++ b/tests/ui/associated-type-bounds/return-type-notation/path-ambiguous-late-bound-vars.stderr @@ -0,0 +1,26 @@ +error[E0221]: ambiguous associated function `f` in bounds of `T` + --> $DIR/path-ambiguous-late-bound-vars.rs:27:5 + | +LL | fn f() -> impl Sized; + | --------------------- ambiguous `f` from `A` +... +LL | fn f<'b>() -> impl Sized; + | ------------------------- ambiguous `f` from `B` +... +LL | T::f(..):, + | ^^^^^^^^ ambiguous associated function `f` + | +help: use fully-qualified syntax to disambiguate + | +LL - T::f(..):, +LL + ::f(..):, + | +help: use fully-qualified syntax to disambiguate + | +LL - T::f(..):, +LL + ::f(..):, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0221`. From 4fe62be7fe8a732d91cda4ff08a85ba2e713366f Mon Sep 17 00:00:00 2001 From: Andrii Anoshyn Date: Fri, 29 May 2026 22:37:53 +0300 Subject: [PATCH 16/28] Address irrefutable while let diagnostic review --- compiler/rustc_hir_typeck/src/coercion.rs | 6 +++--- tests/ui/coercion/coerce-loop-issue-122561.stderr | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d8881de576eb4..9a20d47bda9af 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1895,11 +1895,11 @@ impl<'tcx> CoerceMany<'tcx> { err.note(format!("{loop_type} evaluate to unit type `()`")); if loop_src == hir::LoopSource::While - && let Some(pat) = irrefutable_while_let_pattern(block) + && let Some(pat) = irrefutable_if_let_expr(block) { err.span_note( pat.span, - "this pattern always matches, so the loop condition never fails", + "this pattern always matches, consider using `loop` instead", ); } } @@ -2134,7 +2134,7 @@ impl<'tcx> CoerceMany<'tcx> { } } -fn irrefutable_while_let_pattern<'hir>(block: &hir::Block<'hir>) -> Option<&'hir hir::Pat<'hir>> { +fn irrefutable_if_let_expr<'hir>(block: &hir::Block<'hir>) -> Option<&'hir hir::Pat<'hir>> { let hir::ExprKind::If(cond, _, _) = block.expr?.kind else { return None; }; diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr index ef7a3049465ad..1051823e6c01f 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.stderr +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -182,7 +182,7 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` -note: this pattern always matches, so the loop condition never fails +note: this pattern always matches, consider using `loop` instead --> $DIR/coerce-loop-issue-122561.rs:73:15 | LL | while let x = false { @@ -207,7 +207,7 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` -note: this pattern always matches, so the loop condition never fails +note: this pattern always matches, consider using `loop` instead --> $DIR/coerce-loop-issue-122561.rs:82:15 | LL | while let (x, _) = (false, true) { From 5878c571846eea48d999b1b27c43eeab2e5b6fd5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 10 May 2026 14:29:19 +0000 Subject: [PATCH 17/28] Make a dedicated coroutine mir-opt test directory. --- tests/mir-opt/building/async_await.rs | 18 --- ...await.a-{closure#0}.coroutine_resume.0.mir | 2 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 134 +++++++++++++++--- tests/mir-opt/coroutine/async_await.rs | 29 ++++ ...oo-{closure#0}-{closure#0}.built.after.mir | 0 ...-{closure#0}-{synthetic#0}.built.after.mir | 0 .../async_closure_fake_read_for_by_move.rs | 0 ...0}-{closure#0}-{closure#0}.built.after.mir | 0 ...-{closure#0}-{synthetic#0}.built.after.mir | 0 ...closure#0}.coroutine_closure_by_move.0.mir | 0 ...0}-{closure#1}-{closure#0}.built.after.mir | 0 ...-{closure#1}-{synthetic#0}.built.after.mir | 0 ...closure#1}.coroutine_closure_by_move.0.mir | 0 ...{closure#1}.coroutine_closure_by_ref.0.mir | 0 .../{ => coroutine}/async_closure_shims.rs | 0 ...#0}.coroutine_drop_async.0.panic-abort.mir | 0 ...0}.coroutine_drop_async.0.panic-unwind.mir | 0 .../{ => coroutine}/async_drop_live_dead.rs | 0 ...losure#0}.[Foo;1].MentionedItems.after.mir | 0 .../{ => coroutine}/async_drop_mir_pin.rs | 0 ....main-{closure#0}.StateTransform.after.mir | 119 +++++++++++++--- ....main-{closure#1}.StateTransform.after.mir | 119 +++++++++++++--- .../{building => coroutine}/coroutine.rs | 3 +- ...losure#0}.coroutine_drop.0.panic-abort.mir | 0 ...osure#0}.coroutine_drop.0.panic-unwind.mir | 0 .../{ => coroutine}/coroutine_drop_cleanup.rs | 0 ...e#0}.StateTransform.before.panic-abort.mir | 0 ...#0}.StateTransform.before.panic-unwind.mir | 0 .../coroutine_storage_dead_unwind.rs | 0 ...ny.main-{closure#0}.coroutine_resume.0.mir | 0 .../mir-opt/{ => coroutine}/coroutine_tiny.rs | 0 .../{ => inline}/inline_coroutine_body.rs | 0 ...y.run2-{closure#0}.Inline.panic-abort.diff | 0 ....run2-{closure#0}.Inline.panic-unwind.diff | 0 34 files changed, 347 insertions(+), 77 deletions(-) delete mode 100644 tests/mir-opt/building/async_await.rs rename tests/mir-opt/{building => coroutine}/async_await.a-{closure#0}.coroutine_resume.0.mir (96%) rename tests/mir-opt/{building => coroutine}/async_await.b-{closure#0}.coroutine_resume.0.mir (75%) create mode 100644 tests/mir-opt/coroutine/async_await.rs rename tests/mir-opt/{ => coroutine}/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_fake_read_for_by_move.rs (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir (100%) rename tests/mir-opt/{ => coroutine}/async_closure_shims.rs (100%) rename tests/mir-opt/{ => coroutine}/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir (100%) rename tests/mir-opt/{ => coroutine}/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir (100%) rename tests/mir-opt/{ => coroutine}/async_drop_live_dead.rs (100%) rename tests/mir-opt/{ => coroutine}/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir (100%) rename tests/mir-opt/{ => coroutine}/async_drop_mir_pin.rs (100%) rename tests/mir-opt/{building => coroutine}/coroutine.main-{closure#0}.StateTransform.after.mir (64%) rename tests/mir-opt/{building => coroutine}/coroutine.main-{closure#1}.StateTransform.after.mir (64%) rename tests/mir-opt/{building => coroutine}/coroutine.rs (93%) rename tests/mir-opt/{ => coroutine}/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir (100%) rename tests/mir-opt/{ => coroutine}/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir (100%) rename tests/mir-opt/{ => coroutine}/coroutine_drop_cleanup.rs (100%) rename tests/mir-opt/{ => coroutine}/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir (100%) rename tests/mir-opt/{ => coroutine}/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir (100%) rename tests/mir-opt/{ => coroutine}/coroutine_storage_dead_unwind.rs (100%) rename tests/mir-opt/{ => coroutine}/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir (100%) rename tests/mir-opt/{ => coroutine}/coroutine_tiny.rs (100%) rename tests/mir-opt/{ => inline}/inline_coroutine_body.rs (100%) rename tests/mir-opt/{ => inline}/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff (100%) rename tests/mir-opt/{ => inline}/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff (100%) diff --git a/tests/mir-opt/building/async_await.rs b/tests/mir-opt/building/async_await.rs deleted file mode 100644 index e84f8a6c561bc..0000000000000 --- a/tests/mir-opt/building/async_await.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ skip-filecheck -// This test makes sure that the coroutine MIR pass eliminates all calls to -// `get_context`, and that the MIR argument type for an async fn and all locals -// related to `yield` are `&mut Context`, and its return type is `Poll`. - -//@ edition:2018 -//@ compile-flags: -Zmir-opt-level=0 -C panic=abort - -#![crate_type = "lib"] - -// EMIT_MIR async_await.a-{closure#0}.coroutine_resume.0.mir -async fn a() {} - -// EMIT_MIR async_await.b-{closure#0}.coroutine_resume.0.mir -pub async fn b() { - a().await; - a().await -} diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir similarity index 96% rename from tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir rename to tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir index 6cad5b105d3e3..f59cdb5c27bc0 100644 --- a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir @@ -38,7 +38,7 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> } bb4: { - assert(const false, "`async fn` resumed after completion") -> [success: bb4, unwind unreachable]; + assert(const false, "`async fn` resumed after completion") -> [success: bb4, unwind continue]; } bb5: { diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir similarity index 75% rename from tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir rename to tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir index 96ee37185db16..0c130634aeac4 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -13,7 +13,7 @@ ], ), source_info: SourceInfo { - span: $DIR/async_await.rs:16:5: 16:14 (#9), + span: $DIR/async_await.rs:27:5: 27:14 (#9), scope: scope[0], }, ignore_for_traits: false, @@ -30,7 +30,7 @@ ], ), source_info: SourceInfo { - span: $DIR/async_await.rs:17:5: 17:14 (#11), + span: $DIR/async_await.rs:28:5: 28:14 (#11), scope: scope[0], }, ignore_for_traits: false, @@ -105,18 +105,18 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb0: { _39 = copy (_1.0: &mut {async fn body of b()}); _38 = discriminant((*_39)); - switchInt(move _38) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8]; + switchInt(move _38) -> [0: bb1, 1: bb47, 2: bb46, 3: bb44, 4: bb45, otherwise: bb8]; } bb1: { StorageLive(_3); StorageLive(_4); StorageLive(_5); - _5 = a() -> [return: bb2, unwind unreachable]; + _5 = a() -> [return: bb2, unwind: bb38]; } bb2: { - _4 = <{async fn body of a()} as IntoFuture>::into_future(move _5) -> [return: bb3, unwind unreachable]; + _4 = <{async fn body of a()} as IntoFuture>::into_future(move _5) -> [return: bb3, unwind: bb37]; } bb3: { @@ -135,7 +135,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_12); _12 = &mut (((*_39) as variant#3).0: {async fn body of a()}); _11 = &mut (*_12); - _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb5, unwind unreachable]; + _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb5, unwind: bb34]; } bb5: { @@ -151,7 +151,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb6: { _13 = &mut (*_14); StorageDead(_15); - _9 = <{async fn body of a()} as Future>::poll(move _10, move _13) -> [return: bb7, unwind unreachable]; + _9 = <{async fn body of a()} as Future>::poll(move _10, move _13) -> [return: bb7, unwind: bb33]; } bb7: { @@ -193,7 +193,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_12); StorageDead(_9); StorageDead(_8); - drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind unreachable]; + drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind: bb36]; } bb11: { @@ -214,11 +214,11 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_3); StorageLive(_21); StorageLive(_22); - _22 = a() -> [return: bb14, unwind unreachable]; + _22 = a() -> [return: bb14, unwind: bb31]; } bb14: { - _21 = <{async fn body of a()} as IntoFuture>::into_future(move _22) -> [return: bb15, unwind unreachable]; + _21 = <{async fn body of a()} as IntoFuture>::into_future(move _22) -> [return: bb15, unwind: bb30]; } bb15: { @@ -237,7 +237,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_28); _28 = &mut (((*_39) as variant#4).0: {async fn body of a()}); _27 = &mut (*_28); - _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb17, unwind unreachable]; + _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb17, unwind: bb27]; } bb17: { @@ -253,7 +253,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb18: { _29 = &mut (*_30); StorageDead(_31); - _25 = <{async fn body of a()} as Future>::poll(move _26, move _29) -> [return: bb19, unwind unreachable]; + _25 = <{async fn body of a()} as Future>::poll(move _26, move _29) -> [return: bb19, unwind: bb26]; } bb19: { @@ -290,7 +290,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageDead(_28); StorageDead(_25); StorageDead(_24); - drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind unreachable]; + drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind: bb29]; } bb22: { @@ -308,7 +308,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb24: { StorageDead(_21); - goto -> bb26; + goto -> bb42; } bb25: { @@ -317,11 +317,103 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> return; } - bb26: { + bb26 (cleanup): { + StorageDead(_29); + StorageDead(_26); + StorageDead(_30); + goto -> bb28; + } + + bb27 (cleanup): { + StorageDead(_27); + StorageDead(_26); + goto -> bb28; + } + + bb28 (cleanup): { + StorageDead(_28); + StorageDead(_25); + StorageDead(_24); + drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + nop; + goto -> bb32; + } + + bb30 (cleanup): { + goto -> bb31; + } + + bb31 (cleanup): { + StorageDead(_22); + goto -> bb32; + } + + bb32 (cleanup): { + StorageDead(_21); + goto -> bb40; + } + + bb33 (cleanup): { + StorageDead(_13); + StorageDead(_10); + StorageDead(_14); + goto -> bb35; + } + + bb34 (cleanup): { + StorageDead(_11); + StorageDead(_10); + goto -> bb35; + } + + bb35 (cleanup): { + StorageDead(_12); + StorageDead(_9); + StorageDead(_8); + drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb36 (cleanup): { + nop; + goto -> bb39; + } + + bb37 (cleanup): { + goto -> bb38; + } + + bb38 (cleanup): { + StorageDead(_5); + goto -> bb39; + } + + bb39 (cleanup): { + StorageDead(_4); + StorageDead(_3); + goto -> bb40; + } + + bb40 (cleanup): { + goto -> bb41; + } + + bb41 (cleanup): { + goto -> bb43; + } + + bb42: { goto -> bb25; } - bb27: { + bb43 (cleanup): { + discriminant((*_39)) = 2; + resume; + } + + bb44: { StorageLive(_3); StorageLive(_4); StorageLive(_19); @@ -330,7 +422,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> goto -> bb11; } - bb28: { + bb45: { StorageLive(_21); StorageLive(_35); StorageLive(_36); @@ -338,7 +430,11 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> goto -> bb22; } - bb29: { - assert(const false, "`async fn` resumed after completion") -> [success: bb29, unwind unreachable]; + bb46: { + assert(const false, "`async fn` resumed after panicking") -> [success: bb46, unwind continue]; + } + + bb47: { + assert(const false, "`async fn` resumed after completion") -> [success: bb47, unwind continue]; } } diff --git a/tests/mir-opt/coroutine/async_await.rs b/tests/mir-opt/coroutine/async_await.rs new file mode 100644 index 0000000000000..8334baa594720 --- /dev/null +++ b/tests/mir-opt/coroutine/async_await.rs @@ -0,0 +1,29 @@ +// This test makes sure that the coroutine MIR pass eliminates all calls to +// `get_context`, and that the MIR argument type for an async fn and all locals +// related to `yield` are `&mut Context`, and its return type is `Poll`. + +//@ edition:2018 +//@ compile-flags: -Zmir-opt-level=0 +//@ needs-unwind + +#![crate_type = "lib"] + +// EMIT_MIR async_await.a-{closure#0}.coroutine_resume.0.mir +async fn a() { + // CHECK-LABEL: fn a::{closure#0}( + // CHECK-SAME: _1: Pin<&mut {async fn body of a()}> + // CHECK-SAME: _2: &mut Context<'_> + // CHECK-SAME: -> Poll<()> + // CHECK-NOT: get_context +} + +// EMIT_MIR async_await.b-{closure#0}.coroutine_resume.0.mir +pub async fn b() { + // CHECK-LABEL: fn b::{closure#0}( + // CHECK-SAME: _1: Pin<&mut {async fn body of b()}> + // CHECK-SAME: _2: &mut Context<'_> + // CHECK-SAME: -> Poll<()> + // CHECK-NOT: get_context + a().await; + a().await +} diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.rs b/tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.rs similarity index 100% rename from tests/mir-opt/async_closure_fake_read_for_by_move.rs rename to tests/mir-opt/coroutine/async_closure_fake_read_for_by_move.rs diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir b/tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir similarity index 100% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir rename to tests/mir-opt/coroutine/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir diff --git a/tests/mir-opt/async_closure_shims.rs b/tests/mir-opt/coroutine/async_closure_shims.rs similarity index 100% rename from tests/mir-opt/async_closure_shims.rs rename to tests/mir-opt/coroutine/async_closure_shims.rs diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir b/tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir similarity index 100% rename from tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir rename to tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir similarity index 100% rename from tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir rename to tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir diff --git a/tests/mir-opt/async_drop_live_dead.rs b/tests/mir-opt/coroutine/async_drop_live_dead.rs similarity index 100% rename from tests/mir-opt/async_drop_live_dead.rs rename to tests/mir-opt/coroutine/async_drop_live_dead.rs diff --git a/tests/mir-opt/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir b/tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir similarity index 100% rename from tests/mir-opt/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir rename to tests/mir-opt/coroutine/async_drop_mir_pin.core.future-async_drop-async_drop_in_place-{closure#0}.[Foo;1].MentionedItems.after.mir diff --git a/tests/mir-opt/async_drop_mir_pin.rs b/tests/mir-opt/coroutine/async_drop_mir_pin.rs similarity index 100% rename from tests/mir-opt/async_drop_mir_pin.rs rename to tests/mir-opt/coroutine/async_drop_mir_pin.rs diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir similarity index 64% rename from tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir rename to tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir index b61215dc28cb4..7adceef744a2a 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:18:6: 18:9 (#0), + span: $DIR/coroutine.rs:19:6: 19:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,7 +22,7 @@ }, } */ -fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { +fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { debug arg => (((*_18) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; @@ -40,12 +40,12 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 let _15: &std::panic::Location<'_>; let mut _16: (); let mut _17: u32; - let mut _18: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}; bb0: { - _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}); _17 = discriminant((*_18)); - switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + switchInt(move _17) -> [0: bb1, 1: bb35, 2: bb34, 3: bb32, 4: bb33, otherwise: bb36]; } bb1: { @@ -55,13 +55,13 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 StorageLive(_5); StorageLive(_6); _6 = &(((*_18) as variant#4).0: std::string::String); - _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + _5 = ::clone(move _6) -> [return: bb2, unwind: bb23]; } bb2: { StorageDead(_6); StorageLive(_7); - _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + _7 = Location::<'_>::caller() -> [return: bb3, unwind: bb22]; } bb3: { @@ -85,7 +85,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 bb6: { StorageDead(_4); - drop(_3) -> [return: bb7, unwind unreachable]; + drop(_3) -> [return: bb7, unwind: bb26]; } bb7: { @@ -99,14 +99,14 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 StorageLive(_12); StorageLive(_13); _13 = &(((*_18) as variant#4).0: std::string::String); - _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + _12 = ::clone(move _13) -> [return: bb8, unwind: bb20]; } bb8: { StorageDead(_13); StorageLive(_14); StorageLive(_15); - _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + _15 = Location::<'_>::caller() -> [return: bb9, unwind: bb16]; } bb9: { @@ -134,7 +134,7 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 bb12: { StorageDead(_9); - drop(_8) -> [return: bb13, unwind unreachable]; + drop(_8) -> [return: bb13, unwind: bb19]; } bb13: { @@ -142,11 +142,11 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 StorageDead(_11); StorageDead(_8); _16 = const (); - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind: bb28]; } bb14: { - goto -> bb16; + goto -> bb30; } bb15: { @@ -155,18 +155,95 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 return; } - bb16: { + bb16 (cleanup): { + StorageDead(_14); + drop(_12) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + StorageDead(_12); + StorageDead(_10); + goto -> bb18; + } + + bb18 (cleanup): { + StorageDead(_9); + goto -> bb19; + } + + bb19 (cleanup): { + StorageDead(_15); + goto -> bb21; + } + + bb20 (cleanup): { + StorageDead(_13); + StorageDead(_12); + StorageDead(_10); + StorageDead(_9); + goto -> bb21; + } + + bb21 (cleanup): { + StorageDead(_11); + StorageDead(_8); + goto -> bb27; + } + + bb22 (cleanup): { + StorageDead(_7); + drop(_5) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + StorageDead(_6); + goto -> bb24; + } + + bb24 (cleanup): { + StorageDead(_5); + goto -> bb25; + } + + bb25 (cleanup): { + StorageDead(_4); + goto -> bb26; + } + + bb26 (cleanup): { + StorageDead(_3); + goto -> bb27; + } + + bb27 (cleanup): { + drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + goto -> bb29; + } + + bb29 (cleanup): { + goto -> bb31; + } + + bb30: { goto -> bb15; } - bb17: { + bb31 (cleanup): { + discriminant((*_18)) = 2; + resume; + } + + bb32: { StorageLive(_3); StorageLive(_4); _3 = move _2; goto -> bb5; } - bb18: { + bb33: { StorageLive(_8); StorageLive(_9); StorageLive(_11); @@ -175,11 +252,15 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2 goto -> bb11; } - bb19: { - assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + bb34: { + assert(const false, "coroutine resumed after panicking") -> [success: bb34, unwind continue]; + } + + bb35: { + assert(const false, "coroutine resumed after completion") -> [success: bb35, unwind continue]; } - bb20: { + bb36: { unreachable; } } diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir similarity index 64% rename from tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir rename to tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir index aac028a9e6c0e..b4fcd2051303e 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:25:6: 25:9 (#0), + span: $DIR/coroutine.rs:26:6: 26:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,7 +22,7 @@ }, } */ -fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { +fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { debug arg => (((*_18) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; @@ -40,12 +40,12 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 let _15: &std::panic::Location<'_>; let mut _16: (); let mut _17: u32; - let mut _18: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}; bb0: { - _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}); _17 = discriminant((*_18)); - switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + switchInt(move _17) -> [0: bb1, 1: bb35, 2: bb34, 3: bb32, 4: bb33, otherwise: bb36]; } bb1: { @@ -55,13 +55,13 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 StorageLive(_5); StorageLive(_6); _6 = &(((*_18) as variant#4).0: std::string::String); - _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + _5 = ::clone(move _6) -> [return: bb2, unwind: bb23]; } bb2: { StorageDead(_6); StorageLive(_7); - _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + _7 = Location::<'_>::caller() -> [return: bb3, unwind: bb22]; } bb3: { @@ -85,7 +85,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 bb6: { StorageDead(_4); - drop(_3) -> [return: bb7, unwind unreachable]; + drop(_3) -> [return: bb7, unwind: bb26]; } bb7: { @@ -99,14 +99,14 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 StorageLive(_12); StorageLive(_13); _13 = &(((*_18) as variant#4).0: std::string::String); - _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + _12 = ::clone(move _13) -> [return: bb8, unwind: bb20]; } bb8: { StorageDead(_13); StorageLive(_14); StorageLive(_15); - _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + _15 = Location::<'_>::caller() -> [return: bb9, unwind: bb16]; } bb9: { @@ -134,7 +134,7 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 bb12: { StorageDead(_9); - drop(_8) -> [return: bb13, unwind unreachable]; + drop(_8) -> [return: bb13, unwind: bb19]; } bb13: { @@ -142,11 +142,11 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 StorageDead(_11); StorageDead(_8); _16 = const (); - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind: bb28]; } bb14: { - goto -> bb16; + goto -> bb30; } bb15: { @@ -155,18 +155,95 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 return; } - bb16: { + bb16 (cleanup): { + StorageDead(_14); + drop(_12) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + StorageDead(_12); + StorageDead(_10); + goto -> bb18; + } + + bb18 (cleanup): { + StorageDead(_9); + goto -> bb19; + } + + bb19 (cleanup): { + StorageDead(_15); + goto -> bb21; + } + + bb20 (cleanup): { + StorageDead(_13); + StorageDead(_12); + StorageDead(_10); + StorageDead(_9); + goto -> bb21; + } + + bb21 (cleanup): { + StorageDead(_11); + StorageDead(_8); + goto -> bb27; + } + + bb22 (cleanup): { + StorageDead(_7); + drop(_5) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + StorageDead(_6); + goto -> bb24; + } + + bb24 (cleanup): { + StorageDead(_5); + goto -> bb25; + } + + bb25 (cleanup): { + StorageDead(_4); + goto -> bb26; + } + + bb26 (cleanup): { + StorageDead(_3); + goto -> bb27; + } + + bb27 (cleanup): { + drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + goto -> bb29; + } + + bb29 (cleanup): { + goto -> bb31; + } + + bb30: { goto -> bb15; } - bb17: { + bb31 (cleanup): { + discriminant((*_18)) = 2; + resume; + } + + bb32: { StorageLive(_3); StorageLive(_4); _3 = move _2; goto -> bb5; } - bb18: { + bb33: { StorageLive(_8); StorageLive(_9); StorageLive(_11); @@ -175,11 +252,15 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2 goto -> bb11; } - bb19: { - assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + bb34: { + assert(const false, "coroutine resumed after panicking") -> [success: bb34, unwind continue]; + } + + bb35: { + assert(const false, "coroutine resumed after completion") -> [success: bb35, unwind continue]; } - bb20: { + bb36: { unreachable; } } diff --git a/tests/mir-opt/building/coroutine.rs b/tests/mir-opt/coroutine/coroutine.rs similarity index 93% rename from tests/mir-opt/building/coroutine.rs rename to tests/mir-opt/coroutine/coroutine.rs index 77388c8786aaa..41fdbee119e14 100644 --- a/tests/mir-opt/building/coroutine.rs +++ b/tests/mir-opt/coroutine/coroutine.rs @@ -1,6 +1,7 @@ //@ skip-filecheck //@ edition:2024 -//@ compile-flags: -Zmir-opt-level=0 -C panic=abort +//@ compile-flags: -Zmir-opt-level=0 +//@ needs-unwind #![feature(stmt_expr_attributes)] #![feature(closure_track_caller)] diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir similarity index 100% rename from tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir rename to tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir b/tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir similarity index 100% rename from tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir rename to tests/mir-opt/coroutine/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir diff --git a/tests/mir-opt/coroutine_drop_cleanup.rs b/tests/mir-opt/coroutine/coroutine_drop_cleanup.rs similarity index 100% rename from tests/mir-opt/coroutine_drop_cleanup.rs rename to tests/mir-opt/coroutine/coroutine_drop_cleanup.rs diff --git a/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir similarity index 100% rename from tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir rename to tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir diff --git a/tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir similarity index 100% rename from tests/mir-opt/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir rename to tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir diff --git a/tests/mir-opt/coroutine_storage_dead_unwind.rs b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs similarity index 100% rename from tests/mir-opt/coroutine_storage_dead_unwind.rs rename to tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir similarity index 100% rename from tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir rename to tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir diff --git a/tests/mir-opt/coroutine_tiny.rs b/tests/mir-opt/coroutine/coroutine_tiny.rs similarity index 100% rename from tests/mir-opt/coroutine_tiny.rs rename to tests/mir-opt/coroutine/coroutine_tiny.rs diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline/inline_coroutine_body.rs similarity index 100% rename from tests/mir-opt/inline_coroutine_body.rs rename to tests/mir-opt/inline/inline_coroutine_body.rs diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff similarity index 100% rename from tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff rename to tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff similarity index 100% rename from tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff rename to tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff From d332a1249cf01543ee65284823ba6ecace629ce3 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Thu, 14 May 2026 12:48:24 +0000 Subject: [PATCH 18/28] Change how coroutine layout is dumped in MIR. --- compiler/rustc_middle/src/mir/pretty.rs | 77 ++++++++++++++++++- ...await.a-{closure#0}.coroutine_resume.0.mir | 17 ++-- ...await.b-{closure#0}.coroutine_resume.0.mir | 63 ++++----------- ....main-{closure#0}.StateTransform.after.mir | 34 +++----- ....main-{closure#1}.StateTransform.after.mir | 34 +++----- ...ny.main-{closure#0}.coroutine_resume.0.mir | 32 +++----- 6 files changed, 131 insertions(+), 126 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 60c829311c4b1..1f8efa72ea867 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -15,6 +15,7 @@ use crate::mir::interpret::{ }; use crate::mir::visit::Visitor; use crate::mir::*; +use crate::ty::CoroutineArgsExt; const INDENT: &str = " "; /// Alignment for lining up comments following MIR statements @@ -185,9 +186,6 @@ impl<'a, 'tcx> MirDumper<'a, 'tcx> { Some(promoted) => write!(w, "::{promoted:?}`")?, } writeln!(w, " {} {}", self.disambiguator, self.pass_name)?; - if let Some(ref layout) = body.coroutine_layout_raw() { - writeln!(w, "/* coroutine_layout = {layout:#?} */")?; - } writeln!(w)?; (self.writer.extra_data)(PassWhere::BeforeCFG, w)?; write_user_type_annotations(self.tcx(), body, w)?; @@ -429,6 +427,31 @@ fn write_scope_tree( } } + // Coroutine debuginfo. + if let Some(layout) = body.coroutine_layout_raw() { + for (field, name) in layout.field_names.iter_enumerated() { + if let Some(name) = name + && let source_info = layout.field_tys[field].source_info + && source_info.scope == parent + { + let indented_debug_info = + format!("{0:1$}coroutine debug {2} => {3:?};", INDENT, indent, name, field); + + if options.include_extra_comments { + writeln!( + w, + "{0:1$} // in {2}", + indented_debug_info, + ALIGN, + comment(tcx, source_info), + )?; + } else { + writeln!(w, "{indented_debug_info}")?; + } + } + } + } + // Local variable types. for (local, local_decl) in body.local_decls.iter_enumerated() { if (1..body.arg_count + 1).contains(&local.index()) { @@ -530,6 +553,50 @@ impl Debug for VarDebugInfo<'_> { } } +fn write_coroutine_layout<'tcx>( + tcx: TyCtxt<'tcx>, + layout: &CoroutineLayout<'_>, + w: &mut dyn io::Write, + options: PrettyPrintMirOptions, +) -> io::Result<()> { + let CoroutineLayout { + field_tys, + field_names: _, // Dumped in scope tree with debug info. + variant_fields, + variant_source_info, + storage_conflicts, + } = layout; + + writeln!(w, "{INDENT}coroutine layout {{")?; + + for (field, CoroutineSavedTy { ty, source_info, ignore_for_traits }) in + field_tys.iter_enumerated() + { + let ignore_for_traits = if *ignore_for_traits { " (ignored for traits)" } else { "" }; + let indented_body = format!("{INDENT}{INDENT}field {field:?}: {ty}{ignore_for_traits};",); + if options.include_extra_comments { + writeln!(w, "{0:ALIGN$} // in {1}", indented_body, comment(tcx, *source_info))?; + } else { + writeln!(w, "{}", indented_body)?; + } + } + + writeln!(w, "{INDENT}{INDENT}variant_fields = {{")?; + for (variant, fields) in variant_fields.iter_enumerated() { + let variant_name = ty::CoroutineArgs::variant_name(variant); + let header = format!("{INDENT}{INDENT}{INDENT}{variant_name:9}({variant:?}): {fields:?},"); + if options.include_extra_comments { + let source_info = variant_source_info[variant]; + writeln!(w, "{0:ALIGN$} // in {1}", header, comment(tcx, source_info))?; + } else { + writeln!(w, "{}", header)?; + } + } + writeln!(w, "{INDENT}{INDENT}}}")?; + writeln!(w, "{INDENT}{INDENT}storage_conflicts = {storage_conflicts:?}")?; + writeln!(w, "{INDENT}}}") +} + /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). fn write_mir_intro<'tcx>( @@ -541,6 +608,10 @@ fn write_mir_intro<'tcx>( write_mir_sig(tcx, body, w)?; writeln!(w, "{{")?; + if let Some(ref layout) = body.coroutine_layout_raw() { + write_coroutine_layout(tcx, layout, w, options)?; + } + // construct a scope tree and write it out let mut scope_tree: FxHashMap> = Default::default(); for (index, scope_data) in body.source_scopes.iter_enumerated() { diff --git a/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir index f59cdb5c27bc0..539316c9725e4 100644 --- a/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir @@ -1,15 +1,14 @@ // MIR for `a::{closure#0}` 0 coroutine_resume -/* coroutine_layout = CoroutineLayout { - field_tys: {}, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - }, - storage_conflicts: BitMatrix(0x0) {}, -} */ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { + coroutine layout { + variant_fields = { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + } + storage_conflicts = BitMatrix(0x0) {} + } debug _task_context => _2; let mut _0: std::task::Poll<()>; let mut _3: (); diff --git a/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir index 0c130634aeac4..22e4a8cee6494 100644 --- a/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -1,56 +1,21 @@ // MIR for `b::{closure#0}` 0 coroutine_resume -/* coroutine_layout = CoroutineLayout { - field_tys: { - _s0: CoroutineSavedTy { - ty: Coroutine( - DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), - [ - (), - std::future::ResumeTy, - (), - (), - (), - ], - ), - source_info: SourceInfo { - span: $DIR/async_await.rs:27:5: 27:14 (#9), - scope: scope[0], - }, - ignore_for_traits: false, - }, - _s1: CoroutineSavedTy { - ty: Coroutine( - DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), - [ - (), - std::future::ResumeTy, - (), - (), - (), - ], - ), - source_info: SourceInfo { - span: $DIR/async_await.rs:28:5: 28:14 (#11), - scope: scope[0], - }, - ignore_for_traits: false, - }, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s1], - }, - storage_conflicts: BitMatrix(2x2) { - (_s0, _s0), - (_s1, _s1), - }, -} */ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { + coroutine layout { + field _s0: {async fn body of a()}; + field _s1: {async fn body of a()}; + variant_fields = { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s1], + } + storage_conflicts = BitMatrix(2x2) {(_s0, _s0), (_s1, _s1)} + } debug _task_context => _2; + coroutine debug __awaitee => _s0; + coroutine debug __awaitee => _s1; let mut _0: std::task::Poll<()>; let _3: (); let mut _4: {async fn body of a()}; diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir index 7adceef744a2a..60e52d51829fc 100644 --- a/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir @@ -1,29 +1,19 @@ // MIR for `main::{closure#0}` after StateTransform -/* coroutine_layout = CoroutineLayout { - field_tys: { - _s0: CoroutineSavedTy { - ty: std::string::String, - source_info: SourceInfo { - span: $DIR/coroutine.rs:19:6: 19:9 (#0), - scope: scope[0], - }, - ignore_for_traits: false, - }, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s0], - }, - storage_conflicts: BitMatrix(1x1) { - (_s0, _s0), - }, -} */ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + coroutine layout { + field _s0: String; + variant_fields = { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + } + storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} + } debug arg => (((*_18) as variant#4).0: std::string::String); + coroutine debug arg => _s0; let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir index b4fcd2051303e..1abfe986f6867 100644 --- a/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir @@ -1,29 +1,19 @@ // MIR for `main::{closure#1}` after StateTransform -/* coroutine_layout = CoroutineLayout { - field_tys: { - _s0: CoroutineSavedTy { - ty: std::string::String, - source_info: SourceInfo { - span: $DIR/coroutine.rs:26:6: 26:9 (#0), - scope: scope[0], - }, - ignore_for_traits: false, - }, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s0], - }, - storage_conflicts: BitMatrix(1x1) { - (_s0, _s0), - }, -} */ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + coroutine layout { + field _s0: String; + variant_fields = { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + } + storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} + } debug arg => (((*_18) as variant#4).0: std::string::String); + coroutine debug arg => _s0; let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); diff --git a/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index 222c7144ef07d..e5667215d54fd 100644 --- a/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -1,28 +1,18 @@ // MIR for `main::{closure#0}` 0 coroutine_resume -/* coroutine_layout = CoroutineLayout { - field_tys: { - _s0: CoroutineSavedTy { - ty: HasDrop, - source_info: SourceInfo { - span: $DIR/coroutine_tiny.rs:22:13: 22:15 (#0), - scope: scope[0], - }, - ignore_for_traits: false, - }, - }, - variant_fields: { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - }, - storage_conflicts: BitMatrix(1x1) { - (_s0, _s0), - }, -} */ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}>, _2: u8) -> CoroutineState<(), ()> { + coroutine layout { + field _s0: HasDrop; + variant_fields = { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + } + storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} + } debug _x => _2; + coroutine debug _d => _s0; let mut _0: std::ops::CoroutineState<(), ()>; let _3: HasDrop; let mut _4: !; From 3b77583f46f3e1d5fb836d38c3b9a7e7a53ebaa3 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 17 May 2026 15:20:10 +0000 Subject: [PATCH 19/28] Simplify can_unwind. --- compiler/rustc_mir_transform/src/coroutine.rs | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4ae6e9a8885d1..b18c7db387401 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1173,40 +1173,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { } // Unwinds can only start at certain terminators. - for block in body.basic_blocks.iter() { - match block.terminator().kind { - // These never unwind. - TerminatorKind::Goto { .. } - | TerminatorKind::SwitchInt { .. } - | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => {} - - // Resume will *continue* unwinding, but if there's no other unwinding terminator it - // will never be reached. - TerminatorKind::UnwindResume => {} - - TerminatorKind::Yield { .. } => { - unreachable!("`can_unwind` called before coroutine transform") - } - - // These may unwind. - TerminatorKind::Drop { .. } - | TerminatorKind::Call { .. } - | TerminatorKind::InlineAsm { .. } - | TerminatorKind::Assert { .. } => return true, - - TerminatorKind::TailCall { .. } => { - unreachable!("tail calls can't be present in generators") - } - } - } - + body.basic_blocks.iter().any(|block| block.terminator().unwind().is_some()) // If we didn't find an unwinding terminator, the function cannot unwind. - false } // Poison the coroutine when it unwinds From 9e5c064fa4b2efddd6aee32e75d0e72c9e6979cb Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 17 May 2026 15:43:18 +0000 Subject: [PATCH 20/28] Put entry block at the end instead of shifting everything. --- compiler/rustc_mir_transform/src/coroutine.rs | 39 +- ...nc_await.a-{closure#0}.StateTransform.diff | 56 ++ ...await.a-{closure#0}.coroutine_resume.0.mir | 46 -- ...nc_await.b-{closure#0}.StateTransform.diff | 543 ++++++++++++++++++ ...await.b-{closure#0}.coroutine_resume.0.mir | 405 ------------- tests/mir-opt/coroutine/async_await.rs | 4 +- ....main-{closure#0}.StateTransform.after.mir | 264 --------- ...utine.main-{closure#0}.StateTransform.diff | 353 ++++++++++++ ....main-{closure#1}.StateTransform.after.mir | 264 --------- ...utine.main-{closure#1}.StateTransform.diff | 353 ++++++++++++ tests/mir-opt/coroutine/coroutine.rs | 4 +- ...e#0}.StateTransform.before.panic-abort.mir | 83 --- ...#0}.StateTransform.before.panic-unwind.mir | 118 ---- ...closure#0}.StateTransform.panic-abort.diff | 141 +++++ ...losure#0}.StateTransform.panic-unwind.diff | 201 +++++++ .../coroutine_storage_dead_unwind.rs | 2 +- ..._tiny.main-{closure#0}.StateTransform.diff | 98 ++++ ...ny.main-{closure#0}.coroutine_resume.0.mir | 79 --- tests/mir-opt/coroutine/coroutine_tiny.rs | 6 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 158 ++--- ....run2-{closure#0}.Inline.panic-unwind.diff | 168 +++--- tests/ui/force-inlining/deny-async.stderr | 16 +- .../async-closure.stdout | 28 +- 23 files changed, 1964 insertions(+), 1465 deletions(-) create mode 100644 tests/mir-opt/coroutine/async_await.a-{closure#0}.StateTransform.diff delete mode 100644 tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir create mode 100644 tests/mir-opt/coroutine/async_await.b-{closure#0}.StateTransform.diff delete mode 100644 tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir delete mode 100644 tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir create mode 100644 tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.diff delete mode 100644 tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir create mode 100644 tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.diff delete mode 100644 tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir delete mode 100644 tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir create mode 100644 tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-abort.diff create mode 100644 tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-unwind.diff create mode 100644 tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.StateTransform.diff delete mode 100644 tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index b18c7db387401..a00fad4bd599a 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1077,7 +1077,7 @@ fn compute_layout<'tcx>( /// Replaces the entry point of `body` with a block that switches on the coroutine discriminant and /// dispatches to blocks according to `cases`. /// -/// After this function, the former entry point of the function will be bb1. +/// After this function, the former entry point of the function will be the last block. fn insert_switch<'tcx>( body: &mut Body<'tcx>, cases: Vec<(usize, BasicBlock)>, @@ -1085,23 +1085,34 @@ fn insert_switch<'tcx>( default_block: BasicBlock, ) { let (assign, discr) = transform.get_discr(body); - let switch_targets = - SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block); - let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets }; - let source_info = SourceInfo::outermost(body.span); - body.basic_blocks_mut().raw.insert( - 0, - BasicBlockData::new_stmts( - vec![assign], - Some(Terminator { source_info, kind: switch }), - false, - ), + // MIR validation ensures that no block targets `ENTRY_BLOCK`. + #[cfg(debug_assertions)] + for bb in body.basic_blocks.iter() { + for target in bb.terminator().successors() { + assert_ne!(target, START_BLOCK); + } + } + + // Add the switch as entry block, and put the former entry block at the end. + let former_entry = std::mem::replace( + &mut body.basic_blocks_mut()[START_BLOCK], + BasicBlockData::new_stmts(vec![assign], None, false), ); + let former_entry = body.basic_blocks_mut().push(former_entry); - for b in body.basic_blocks_mut().iter_mut() { - b.terminator_mut().successors_mut(|target| *target += 1); + // We may point to `START_BLOCK` in our `cases`, replace it with `former_entry`. + let mut switch_targets = + SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block); + for bb in switch_targets.all_targets_mut() { + if *bb == START_BLOCK { + *bb = former_entry; + } } + + let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets }; + body.basic_blocks_mut()[START_BLOCK].terminator = + Some(Terminator { source_info: SourceInfo::outermost(body.span), kind: switch }); } fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { diff --git a/tests/mir-opt/coroutine/async_await.a-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_await.a-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..dddd66e73fd51 --- /dev/null +++ b/tests/mir-opt/coroutine/async_await.a-{closure#0}.StateTransform.diff @@ -0,0 +1,56 @@ +- // MIR for `a::{closure#0}` before StateTransform ++ // MIR for `a::{closure#0}` after StateTransform + +- fn a::{closure#0}(_1: {async fn body of a()}, _2: std::future::ResumeTy) -> () +- yields () +- { ++ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ } ++ storage_conflicts = BitMatrix(0x0) {} ++ } + debug _task_context => _2; +- let mut _0: (); ++ let mut _0: std::task::Poll<()>; ++ let mut _3: (); ++ let mut _4: u32; ++ let mut _5: &mut {async fn body of a()}; + + bb0: { +- _0 = const (); +- drop(_1) -> [return: bb1, unwind: bb2]; ++ _5 = copy (_1.0: &mut {async fn body of a()}); ++ _4 = discriminant((*_5)); ++ switchInt(move _4) -> [0: bb5, 1: bb3, otherwise: bb4]; + } + + bb1: { ++ _0 = Poll::<()>::Ready(move _3); ++ discriminant((*_5)) = 1; + return; + } + +- bb2 (cleanup): { +- resume; ++ bb2: { ++ goto -> bb1; ++ } ++ ++ bb3: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb3, unwind continue]; ++ } ++ ++ bb4: { ++ unreachable; ++ } ++ ++ bb5: { ++ _3 = const (); ++ goto -> bb2; + } + } + diff --git a/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir deleted file mode 100644 index 539316c9725e4..0000000000000 --- a/tests/mir-opt/coroutine/async_await.a-{closure#0}.coroutine_resume.0.mir +++ /dev/null @@ -1,46 +0,0 @@ -// MIR for `a::{closure#0}` 0 coroutine_resume - -fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - coroutine layout { - variant_fields = { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - } - storage_conflicts = BitMatrix(0x0) {} - } - debug _task_context => _2; - let mut _0: std::task::Poll<()>; - let mut _3: (); - let mut _4: u32; - let mut _5: &mut {async fn body of a()}; - - bb0: { - _5 = copy (_1.0: &mut {async fn body of a()}); - _4 = discriminant((*_5)); - switchInt(move _4) -> [0: bb1, 1: bb4, otherwise: bb5]; - } - - bb1: { - _3 = const (); - goto -> bb3; - } - - bb2: { - _0 = Poll::<()>::Ready(move _3); - discriminant((*_5)) = 1; - return; - } - - bb3: { - goto -> bb2; - } - - bb4: { - assert(const false, "`async fn` resumed after completion") -> [success: bb4, unwind continue]; - } - - bb5: { - unreachable; - } -} diff --git a/tests/mir-opt/coroutine/async_await.b-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_await.b-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..fdbc67d51bd34 --- /dev/null +++ b/tests/mir-opt/coroutine/async_await.b-{closure#0}.StateTransform.diff @@ -0,0 +1,543 @@ +- // MIR for `b::{closure#0}` before StateTransform ++ // MIR for `b::{closure#0}` after StateTransform + +- fn b::{closure#0}(_1: {async fn body of b()}, _2: std::future::ResumeTy) -> () +- yields () +- { ++ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ field _s0: {async fn body of a()}; ++ field _s1: {async fn body of a()}; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0], ++ Suspend1 (4): [_s1], ++ } ++ storage_conflicts = BitMatrix(2x2) {(_s0, _s0), (_s1, _s1)} ++ } + debug _task_context => _2; +- let mut _0: (); ++ coroutine debug __awaitee => _s0; ++ coroutine debug __awaitee => _s1; ++ let mut _0: std::task::Poll<()>; + let _3: (); + let mut _4: {async fn body of a()}; + let mut _5: {async fn body of a()}; + let mut _6: {async fn body of a()}; + let mut _7: (); + let _8: (); + let mut _9: std::task::Poll<()>; + let mut _10: std::pin::Pin<&mut {async fn body of a()}>; + let mut _11: &mut {async fn body of a()}; + let mut _12: &mut {async fn body of a()}; + let mut _13: &mut std::task::Context<'_>; + let mut _14: &mut std::task::Context<'_>; +- let mut _15: std::future::ResumeTy; ++ let mut _15: &mut std::task::Context<'_>; + let mut _16: isize; + let mut _18: !; +- let mut _19: std::future::ResumeTy; ++ let mut _19: &mut std::task::Context<'_>; + let mut _20: (); + let mut _21: {async fn body of a()}; + let mut _22: {async fn body of a()}; + let mut _23: {async fn body of a()}; + let _24: (); + let mut _25: std::task::Poll<()>; + let mut _26: std::pin::Pin<&mut {async fn body of a()}>; + let mut _27: &mut {async fn body of a()}; + let mut _28: &mut {async fn body of a()}; + let mut _29: &mut std::task::Context<'_>; + let mut _30: &mut std::task::Context<'_>; +- let mut _31: std::future::ResumeTy; ++ let mut _31: &mut std::task::Context<'_>; + let mut _32: isize; + let mut _34: !; +- let mut _35: std::future::ResumeTy; ++ let mut _35: &mut std::task::Context<'_>; + let mut _36: (); ++ let mut _37: (); ++ let mut _38: u32; ++ let mut _39: &mut {async fn body of b()}; + scope 1 { +- debug __awaitee => _6; ++ debug __awaitee => (((*_39) as variant#3).0: {async fn body of a()}); + let _17: (); + scope 2 { + debug result => _17; + } + } + scope 3 { +- debug __awaitee => _23; ++ debug __awaitee => (((*_39) as variant#4).0: {async fn body of a()}); + let _33: (); + scope 4 { + debug result => _33; + } + } + + bb0: { +- StorageLive(_3); +- StorageLive(_4); +- StorageLive(_5); +- _5 = a() -> [return: bb1, unwind: bb47]; ++ _39 = copy (_1.0: &mut {async fn body of b()}); ++ _38 = discriminant((*_39)); ++ switchInt(move _38) -> [0: bb47, 1: bb46, 2: bb45, 3: bb43, 4: bb44, otherwise: bb7]; + } + + bb1: { +- _4 = <{async fn body of a()} as IntoFuture>::into_future(move _5) -> [return: bb2, unwind: bb46]; ++ _4 = <{async fn body of a()} as IntoFuture>::into_future(move _5) -> [return: bb2, unwind: bb36]; + } + + bb2: { + StorageDead(_5); + PlaceMention(_4); +- StorageLive(_6); +- _6 = move _4; ++ nop; ++ (((*_39) as variant#3).0: {async fn body of a()}) = move _4; + goto -> bb3; + } + + bb3: { + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + StorageLive(_12); +- _12 = &mut _6; ++ _12 = &mut (((*_39) as variant#3).0: {async fn body of a()}); + _11 = &mut (*_12); +- _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb4, unwind: bb43]; ++ _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb4, unwind: bb33]; + } + + bb4: { + StorageDead(_11); + StorageLive(_13); + StorageLive(_14); + StorageLive(_15); + _15 = copy _2; +- _14 = std::future::get_context::<'_, '_>(move _15) -> [return: bb5, unwind: bb41]; ++ _14 = move _15; ++ goto -> bb5; + } + + bb5: { + _13 = &mut (*_14); + StorageDead(_15); +- _9 = <{async fn body of a()} as Future>::poll(move _10, move _13) -> [return: bb6, unwind: bb42]; ++ _9 = <{async fn body of a()} as Future>::poll(move _10, move _13) -> [return: bb6, unwind: bb32]; + } + + bb6: { + StorageDead(_13); + StorageDead(_10); + PlaceMention(_9); + _16 = discriminant(_9); + switchInt(move _16) -> [0: bb9, 1: bb8, otherwise: bb7]; + } + + bb7: { + unreachable; + } + + bb8: { + _8 = const (); + StorageDead(_14); + StorageDead(_12); + StorageDead(_9); + StorageDead(_8); + StorageLive(_19); + StorageLive(_20); + _20 = (); +- _19 = yield(move _20) -> [resume: bb10, drop: bb28]; ++ _0 = Poll::<()>::Pending; ++ StorageDead(_3); ++ StorageDead(_4); ++ StorageDead(_19); ++ StorageDead(_20); ++ discriminant((*_39)) = 3; ++ return; + } + + bb9: { + StorageLive(_17); + _17 = copy ((_9 as Ready).0: ()); + _3 = copy _17; + StorageDead(_17); + StorageDead(_14); + StorageDead(_12); + StorageDead(_9); + StorageDead(_8); +- drop(_6) -> [return: bb11, unwind: bb45]; ++ drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb11, unwind: bb35]; + } + + bb10: { + StorageDead(_20); + _2 = move _19; + StorageDead(_19); + _7 = const (); + goto -> bb3; + } + + bb11: { +- StorageDead(_6); ++ nop; + goto -> bb12; + } + + bb12: { + StorageDead(_4); + StorageDead(_3); + StorageLive(_21); + StorageLive(_22); +- _22 = a() -> [return: bb13, unwind: bb39]; ++ _22 = a() -> [return: bb13, unwind: bb30]; + } + + bb13: { +- _21 = <{async fn body of a()} as IntoFuture>::into_future(move _22) -> [return: bb14, unwind: bb38]; ++ _21 = <{async fn body of a()} as IntoFuture>::into_future(move _22) -> [return: bb14, unwind: bb29]; + } + + bb14: { + StorageDead(_22); + PlaceMention(_21); +- StorageLive(_23); +- _23 = move _21; ++ nop; ++ (((*_39) as variant#4).0: {async fn body of a()}) = move _21; + goto -> bb15; + } + + bb15: { + StorageLive(_24); + StorageLive(_25); + StorageLive(_26); + StorageLive(_27); + StorageLive(_28); +- _28 = &mut _23; ++ _28 = &mut (((*_39) as variant#4).0: {async fn body of a()}); + _27 = &mut (*_28); +- _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb16, unwind: bb35]; ++ _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb16, unwind: bb26]; + } + + bb16: { + StorageDead(_27); + StorageLive(_29); + StorageLive(_30); + StorageLive(_31); + _31 = copy _2; +- _30 = std::future::get_context::<'_, '_>(move _31) -> [return: bb17, unwind: bb33]; ++ _30 = move _31; ++ goto -> bb17; + } + + bb17: { + _29 = &mut (*_30); + StorageDead(_31); +- _25 = <{async fn body of a()} as Future>::poll(move _26, move _29) -> [return: bb18, unwind: bb34]; ++ _25 = <{async fn body of a()} as Future>::poll(move _26, move _29) -> [return: bb18, unwind: bb25]; + } + + bb18: { + StorageDead(_29); + StorageDead(_26); + PlaceMention(_25); + _32 = discriminant(_25); + switchInt(move _32) -> [0: bb20, 1: bb19, otherwise: bb7]; + } + + bb19: { + _24 = const (); + StorageDead(_30); + StorageDead(_28); + StorageDead(_25); + StorageDead(_24); + StorageLive(_35); + StorageLive(_36); + _36 = (); +- _35 = yield(move _36) -> [resume: bb21, drop: bb25]; ++ _0 = Poll::<()>::Pending; ++ StorageDead(_21); ++ StorageDead(_35); ++ StorageDead(_36); ++ discriminant((*_39)) = 4; ++ return; + } + + bb20: { + StorageLive(_33); + _33 = copy ((_25 as Ready).0: ()); +- _0 = copy _33; ++ _37 = copy _33; + StorageDead(_33); + StorageDead(_30); + StorageDead(_28); + StorageDead(_25); + StorageDead(_24); +- drop(_23) -> [return: bb22, unwind: bb37]; ++ drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb22, unwind: bb28]; + } + + bb21: { + StorageDead(_36); + _2 = move _35; + StorageDead(_35); + _7 = const (); + goto -> bb15; + } + + bb22: { +- StorageDead(_23); ++ nop; + goto -> bb23; + } + + bb23: { + StorageDead(_21); +- drop(_1) -> [return: bb24, unwind: bb50]; ++ goto -> bb41; + } + + bb24: { ++ _0 = Poll::<()>::Ready(move _37); ++ discriminant((*_39)) = 1; + return; + } + +- bb25: { +- StorageDead(_36); +- StorageDead(_35); +- drop(_23) -> [return: bb26, unwind: bb51]; +- } +- +- bb26: { +- StorageDead(_23); +- goto -> bb27; +- } +- +- bb27: { +- StorageDead(_21); +- goto -> bb31; +- } +- +- bb28: { +- StorageDead(_20); +- StorageDead(_19); +- drop(_6) -> [return: bb29, unwind: bb53]; +- } +- +- bb29: { +- StorageDead(_6); +- goto -> bb30; +- } +- +- bb30: { +- StorageDead(_4); +- StorageDead(_3); +- goto -> bb31; +- } +- +- bb31: { +- drop(_1) -> [return: bb32, unwind: bb50]; +- } +- +- bb32: { +- coroutine_drop; +- } +- +- bb33 (cleanup): { +- StorageDead(_31); +- goto -> bb34; +- } +- +- bb34 (cleanup): { ++ bb25 (cleanup): { + StorageDead(_29); + StorageDead(_26); + StorageDead(_30); +- goto -> bb36; ++ goto -> bb27; + } + +- bb35 (cleanup): { ++ bb26 (cleanup): { + StorageDead(_27); + StorageDead(_26); +- goto -> bb36; ++ goto -> bb27; + } + +- bb36 (cleanup): { ++ bb27 (cleanup): { + StorageDead(_28); + StorageDead(_25); + StorageDead(_24); +- drop(_23) -> [return: bb37, unwind terminate(cleanup)]; ++ drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb28, unwind terminate(cleanup)]; + } + +- bb37 (cleanup): { +- StorageDead(_23); +- goto -> bb40; ++ bb28 (cleanup): { ++ nop; ++ goto -> bb31; + } + +- bb38 (cleanup): { +- goto -> bb39; ++ bb29 (cleanup): { ++ goto -> bb30; + } + +- bb39 (cleanup): { ++ bb30 (cleanup): { + StorageDead(_22); +- goto -> bb40; ++ goto -> bb31; + } + +- bb40 (cleanup): { ++ bb31 (cleanup): { + StorageDead(_21); +- goto -> bb49; ++ goto -> bb39; + } + +- bb41 (cleanup): { +- StorageDead(_15); +- goto -> bb42; +- } +- +- bb42 (cleanup): { ++ bb32 (cleanup): { + StorageDead(_13); + StorageDead(_10); + StorageDead(_14); +- goto -> bb44; ++ goto -> bb34; + } + +- bb43 (cleanup): { ++ bb33 (cleanup): { + StorageDead(_11); + StorageDead(_10); +- goto -> bb44; ++ goto -> bb34; + } + +- bb44 (cleanup): { ++ bb34 (cleanup): { + StorageDead(_12); + StorageDead(_9); + StorageDead(_8); +- drop(_6) -> [return: bb45, unwind terminate(cleanup)]; ++ drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb35, unwind terminate(cleanup)]; + } + +- bb45 (cleanup): { +- StorageDead(_6); +- goto -> bb48; ++ bb35 (cleanup): { ++ nop; ++ goto -> bb38; + } + +- bb46 (cleanup): { +- goto -> bb47; ++ bb36 (cleanup): { ++ goto -> bb37; + } + +- bb47 (cleanup): { ++ bb37 (cleanup): { + StorageDead(_5); +- goto -> bb48; ++ goto -> bb38; + } + +- bb48 (cleanup): { ++ bb38 (cleanup): { + StorageDead(_4); + StorageDead(_3); +- goto -> bb49; ++ goto -> bb39; + } + +- bb49 (cleanup): { +- drop(_1) -> [return: bb50, unwind terminate(cleanup)]; ++ bb39 (cleanup): { ++ goto -> bb40; + } + +- bb50 (cleanup): { ++ bb40 (cleanup): { ++ goto -> bb42; ++ } ++ ++ bb41: { ++ goto -> bb24; ++ } ++ ++ bb42 (cleanup): { ++ discriminant((*_39)) = 2; + resume; + } + +- bb51 (cleanup): { +- StorageDead(_23); +- goto -> bb52; ++ bb43: { ++ StorageLive(_3); ++ StorageLive(_4); ++ StorageLive(_19); ++ StorageLive(_20); ++ _19 = move _2; ++ goto -> bb10; + } + +- bb52 (cleanup): { +- StorageDead(_21); +- goto -> bb55; ++ bb44: { ++ StorageLive(_21); ++ StorageLive(_35); ++ StorageLive(_36); ++ _35 = move _2; ++ goto -> bb21; + } + +- bb53 (cleanup): { +- StorageDead(_6); +- goto -> bb54; ++ bb45: { ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb45, unwind continue]; + } + +- bb54 (cleanup): { +- StorageDead(_4); +- StorageDead(_3); +- goto -> bb55; ++ bb46: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb46, unwind continue]; + } + +- bb55 (cleanup): { +- drop(_1) -> [return: bb50, unwind terminate(cleanup)]; ++ bb47: { ++ StorageLive(_3); ++ StorageLive(_4); ++ StorageLive(_5); ++ _5 = a() -> [return: bb1, unwind: bb37]; + } + } + diff --git a/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir deleted file mode 100644 index 22e4a8cee6494..0000000000000 --- a/tests/mir-opt/coroutine/async_await.b-{closure#0}.coroutine_resume.0.mir +++ /dev/null @@ -1,405 +0,0 @@ -// MIR for `b::{closure#0}` 0 coroutine_resume - -fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { - coroutine layout { - field _s0: {async fn body of a()}; - field _s1: {async fn body of a()}; - variant_fields = { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s1], - } - storage_conflicts = BitMatrix(2x2) {(_s0, _s0), (_s1, _s1)} - } - debug _task_context => _2; - coroutine debug __awaitee => _s0; - coroutine debug __awaitee => _s1; - let mut _0: std::task::Poll<()>; - let _3: (); - let mut _4: {async fn body of a()}; - let mut _5: {async fn body of a()}; - let mut _6: {async fn body of a()}; - let mut _7: (); - let _8: (); - let mut _9: std::task::Poll<()>; - let mut _10: std::pin::Pin<&mut {async fn body of a()}>; - let mut _11: &mut {async fn body of a()}; - let mut _12: &mut {async fn body of a()}; - let mut _13: &mut std::task::Context<'_>; - let mut _14: &mut std::task::Context<'_>; - let mut _15: &mut std::task::Context<'_>; - let mut _16: isize; - let mut _18: !; - let mut _19: &mut std::task::Context<'_>; - let mut _20: (); - let mut _21: {async fn body of a()}; - let mut _22: {async fn body of a()}; - let mut _23: {async fn body of a()}; - let _24: (); - let mut _25: std::task::Poll<()>; - let mut _26: std::pin::Pin<&mut {async fn body of a()}>; - let mut _27: &mut {async fn body of a()}; - let mut _28: &mut {async fn body of a()}; - let mut _29: &mut std::task::Context<'_>; - let mut _30: &mut std::task::Context<'_>; - let mut _31: &mut std::task::Context<'_>; - let mut _32: isize; - let mut _34: !; - let mut _35: &mut std::task::Context<'_>; - let mut _36: (); - let mut _37: (); - let mut _38: u32; - let mut _39: &mut {async fn body of b()}; - scope 1 { - debug __awaitee => (((*_39) as variant#3).0: {async fn body of a()}); - let _17: (); - scope 2 { - debug result => _17; - } - } - scope 3 { - debug __awaitee => (((*_39) as variant#4).0: {async fn body of a()}); - let _33: (); - scope 4 { - debug result => _33; - } - } - - bb0: { - _39 = copy (_1.0: &mut {async fn body of b()}); - _38 = discriminant((*_39)); - switchInt(move _38) -> [0: bb1, 1: bb47, 2: bb46, 3: bb44, 4: bb45, otherwise: bb8]; - } - - bb1: { - StorageLive(_3); - StorageLive(_4); - StorageLive(_5); - _5 = a() -> [return: bb2, unwind: bb38]; - } - - bb2: { - _4 = <{async fn body of a()} as IntoFuture>::into_future(move _5) -> [return: bb3, unwind: bb37]; - } - - bb3: { - StorageDead(_5); - PlaceMention(_4); - nop; - (((*_39) as variant#3).0: {async fn body of a()}) = move _4; - goto -> bb4; - } - - bb4: { - StorageLive(_8); - StorageLive(_9); - StorageLive(_10); - StorageLive(_11); - StorageLive(_12); - _12 = &mut (((*_39) as variant#3).0: {async fn body of a()}); - _11 = &mut (*_12); - _10 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _11) -> [return: bb5, unwind: bb34]; - } - - bb5: { - StorageDead(_11); - StorageLive(_13); - StorageLive(_14); - StorageLive(_15); - _15 = copy _2; - _14 = move _15; - goto -> bb6; - } - - bb6: { - _13 = &mut (*_14); - StorageDead(_15); - _9 = <{async fn body of a()} as Future>::poll(move _10, move _13) -> [return: bb7, unwind: bb33]; - } - - bb7: { - StorageDead(_13); - StorageDead(_10); - PlaceMention(_9); - _16 = discriminant(_9); - switchInt(move _16) -> [0: bb10, 1: bb9, otherwise: bb8]; - } - - bb8: { - unreachable; - } - - bb9: { - _8 = const (); - StorageDead(_14); - StorageDead(_12); - StorageDead(_9); - StorageDead(_8); - StorageLive(_19); - StorageLive(_20); - _20 = (); - _0 = Poll::<()>::Pending; - StorageDead(_3); - StorageDead(_4); - StorageDead(_19); - StorageDead(_20); - discriminant((*_39)) = 3; - return; - } - - bb10: { - StorageLive(_17); - _17 = copy ((_9 as Ready).0: ()); - _3 = copy _17; - StorageDead(_17); - StorageDead(_14); - StorageDead(_12); - StorageDead(_9); - StorageDead(_8); - drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb12, unwind: bb36]; - } - - bb11: { - StorageDead(_20); - _2 = move _19; - StorageDead(_19); - _7 = const (); - goto -> bb4; - } - - bb12: { - nop; - goto -> bb13; - } - - bb13: { - StorageDead(_4); - StorageDead(_3); - StorageLive(_21); - StorageLive(_22); - _22 = a() -> [return: bb14, unwind: bb31]; - } - - bb14: { - _21 = <{async fn body of a()} as IntoFuture>::into_future(move _22) -> [return: bb15, unwind: bb30]; - } - - bb15: { - StorageDead(_22); - PlaceMention(_21); - nop; - (((*_39) as variant#4).0: {async fn body of a()}) = move _21; - goto -> bb16; - } - - bb16: { - StorageLive(_24); - StorageLive(_25); - StorageLive(_26); - StorageLive(_27); - StorageLive(_28); - _28 = &mut (((*_39) as variant#4).0: {async fn body of a()}); - _27 = &mut (*_28); - _26 = Pin::<&mut {async fn body of a()}>::new_unchecked(move _27) -> [return: bb17, unwind: bb27]; - } - - bb17: { - StorageDead(_27); - StorageLive(_29); - StorageLive(_30); - StorageLive(_31); - _31 = copy _2; - _30 = move _31; - goto -> bb18; - } - - bb18: { - _29 = &mut (*_30); - StorageDead(_31); - _25 = <{async fn body of a()} as Future>::poll(move _26, move _29) -> [return: bb19, unwind: bb26]; - } - - bb19: { - StorageDead(_29); - StorageDead(_26); - PlaceMention(_25); - _32 = discriminant(_25); - switchInt(move _32) -> [0: bb21, 1: bb20, otherwise: bb8]; - } - - bb20: { - _24 = const (); - StorageDead(_30); - StorageDead(_28); - StorageDead(_25); - StorageDead(_24); - StorageLive(_35); - StorageLive(_36); - _36 = (); - _0 = Poll::<()>::Pending; - StorageDead(_21); - StorageDead(_35); - StorageDead(_36); - discriminant((*_39)) = 4; - return; - } - - bb21: { - StorageLive(_33); - _33 = copy ((_25 as Ready).0: ()); - _37 = copy _33; - StorageDead(_33); - StorageDead(_30); - StorageDead(_28); - StorageDead(_25); - StorageDead(_24); - drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb23, unwind: bb29]; - } - - bb22: { - StorageDead(_36); - _2 = move _35; - StorageDead(_35); - _7 = const (); - goto -> bb16; - } - - bb23: { - nop; - goto -> bb24; - } - - bb24: { - StorageDead(_21); - goto -> bb42; - } - - bb25: { - _0 = Poll::<()>::Ready(move _37); - discriminant((*_39)) = 1; - return; - } - - bb26 (cleanup): { - StorageDead(_29); - StorageDead(_26); - StorageDead(_30); - goto -> bb28; - } - - bb27 (cleanup): { - StorageDead(_27); - StorageDead(_26); - goto -> bb28; - } - - bb28 (cleanup): { - StorageDead(_28); - StorageDead(_25); - StorageDead(_24); - drop((((*_39) as variant#4).0: {async fn body of a()})) -> [return: bb29, unwind terminate(cleanup)]; - } - - bb29 (cleanup): { - nop; - goto -> bb32; - } - - bb30 (cleanup): { - goto -> bb31; - } - - bb31 (cleanup): { - StorageDead(_22); - goto -> bb32; - } - - bb32 (cleanup): { - StorageDead(_21); - goto -> bb40; - } - - bb33 (cleanup): { - StorageDead(_13); - StorageDead(_10); - StorageDead(_14); - goto -> bb35; - } - - bb34 (cleanup): { - StorageDead(_11); - StorageDead(_10); - goto -> bb35; - } - - bb35 (cleanup): { - StorageDead(_12); - StorageDead(_9); - StorageDead(_8); - drop((((*_39) as variant#3).0: {async fn body of a()})) -> [return: bb36, unwind terminate(cleanup)]; - } - - bb36 (cleanup): { - nop; - goto -> bb39; - } - - bb37 (cleanup): { - goto -> bb38; - } - - bb38 (cleanup): { - StorageDead(_5); - goto -> bb39; - } - - bb39 (cleanup): { - StorageDead(_4); - StorageDead(_3); - goto -> bb40; - } - - bb40 (cleanup): { - goto -> bb41; - } - - bb41 (cleanup): { - goto -> bb43; - } - - bb42: { - goto -> bb25; - } - - bb43 (cleanup): { - discriminant((*_39)) = 2; - resume; - } - - bb44: { - StorageLive(_3); - StorageLive(_4); - StorageLive(_19); - StorageLive(_20); - _19 = move _2; - goto -> bb11; - } - - bb45: { - StorageLive(_21); - StorageLive(_35); - StorageLive(_36); - _35 = move _2; - goto -> bb22; - } - - bb46: { - assert(const false, "`async fn` resumed after panicking") -> [success: bb46, unwind continue]; - } - - bb47: { - assert(const false, "`async fn` resumed after completion") -> [success: bb47, unwind continue]; - } -} diff --git a/tests/mir-opt/coroutine/async_await.rs b/tests/mir-opt/coroutine/async_await.rs index 8334baa594720..4e506edb61cf5 100644 --- a/tests/mir-opt/coroutine/async_await.rs +++ b/tests/mir-opt/coroutine/async_await.rs @@ -8,7 +8,7 @@ #![crate_type = "lib"] -// EMIT_MIR async_await.a-{closure#0}.coroutine_resume.0.mir +// EMIT_MIR async_await.a-{closure#0}.StateTransform.diff async fn a() { // CHECK-LABEL: fn a::{closure#0}( // CHECK-SAME: _1: Pin<&mut {async fn body of a()}> @@ -17,7 +17,7 @@ async fn a() { // CHECK-NOT: get_context } -// EMIT_MIR async_await.b-{closure#0}.coroutine_resume.0.mir +// EMIT_MIR async_await.b-{closure#0}.StateTransform.diff pub async fn b() { // CHECK-LABEL: fn b::{closure#0}( // CHECK-SAME: _1: Pin<&mut {async fn body of b()}> diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir deleted file mode 100644 index 60e52d51829fc..0000000000000 --- a/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.after.mir +++ /dev/null @@ -1,264 +0,0 @@ -// MIR for `main::{closure#0}` after StateTransform - -fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - coroutine layout { - field _s0: String; - variant_fields = { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s0], - } - storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} - } - debug arg => (((*_18) as variant#4).0: std::string::String); - coroutine debug arg => _s0; - let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; - let _3: std::string::String; - let mut _4: (&str, std::string::String, &std::panic::Location<'_>); - let mut _5: std::string::String; - let mut _6: &std::string::String; - let mut _7: &std::panic::Location<'_>; - let _8: std::string::String; - let mut _9: (&str, std::string::String, &std::panic::Location<'_>); - let mut _10: &str; - let _11: &str; - let mut _12: std::string::String; - let mut _13: &std::string::String; - let mut _14: &std::panic::Location<'_>; - let _15: &std::panic::Location<'_>; - let mut _16: (); - let mut _17: u32; - let mut _18: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}; - - bb0: { - _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}); - _17 = discriminant((*_18)); - switchInt(move _17) -> [0: bb1, 1: bb35, 2: bb34, 3: bb32, 4: bb33, otherwise: bb36]; - } - - bb1: { - (((*_18) as variant#4).0: std::string::String) = move _2; - StorageLive(_3); - StorageLive(_4); - StorageLive(_5); - StorageLive(_6); - _6 = &(((*_18) as variant#4).0: std::string::String); - _5 = ::clone(move _6) -> [return: bb2, unwind: bb23]; - } - - bb2: { - StorageDead(_6); - StorageLive(_7); - _7 = Location::<'_>::caller() -> [return: bb3, unwind: bb22]; - } - - bb3: { - _4 = (const "first", move _5, move _7); - StorageDead(_7); - goto -> bb4; - } - - bb4: { - StorageDead(_5); - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); - StorageDead(_3); - StorageDead(_4); - discriminant((*_18)) = 3; - return; - } - - bb5: { - goto -> bb6; - } - - bb6: { - StorageDead(_4); - drop(_3) -> [return: bb7, unwind: bb26]; - } - - bb7: { - StorageDead(_3); - StorageLive(_8); - StorageLive(_9); - StorageLive(_10); - StorageLive(_11); - _11 = const "second"; - _10 = &(*_11); - StorageLive(_12); - StorageLive(_13); - _13 = &(((*_18) as variant#4).0: std::string::String); - _12 = ::clone(move _13) -> [return: bb8, unwind: bb20]; - } - - bb8: { - StorageDead(_13); - StorageLive(_14); - StorageLive(_15); - _15 = Location::<'_>::caller() -> [return: bb9, unwind: bb16]; - } - - bb9: { - _14 = &(*_15); - _9 = (move _10, move _12, move _14); - StorageDead(_14); - goto -> bb10; - } - - bb10: { - StorageDead(_12); - StorageDead(_10); - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); - StorageDead(_8); - StorageDead(_9); - StorageDead(_11); - StorageDead(_15); - discriminant((*_18)) = 4; - return; - } - - bb11: { - goto -> bb12; - } - - bb12: { - StorageDead(_9); - drop(_8) -> [return: bb13, unwind: bb19]; - } - - bb13: { - StorageDead(_15); - StorageDead(_11); - StorageDead(_8); - _16 = const (); - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind: bb28]; - } - - bb14: { - goto -> bb30; - } - - bb15: { - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - discriminant((*_18)) = 1; - return; - } - - bb16 (cleanup): { - StorageDead(_14); - drop(_12) -> [return: bb17, unwind terminate(cleanup)]; - } - - bb17 (cleanup): { - StorageDead(_12); - StorageDead(_10); - goto -> bb18; - } - - bb18 (cleanup): { - StorageDead(_9); - goto -> bb19; - } - - bb19 (cleanup): { - StorageDead(_15); - goto -> bb21; - } - - bb20 (cleanup): { - StorageDead(_13); - StorageDead(_12); - StorageDead(_10); - StorageDead(_9); - goto -> bb21; - } - - bb21 (cleanup): { - StorageDead(_11); - StorageDead(_8); - goto -> bb27; - } - - bb22 (cleanup): { - StorageDead(_7); - drop(_5) -> [return: bb24, unwind terminate(cleanup)]; - } - - bb23 (cleanup): { - StorageDead(_6); - goto -> bb24; - } - - bb24 (cleanup): { - StorageDead(_5); - goto -> bb25; - } - - bb25 (cleanup): { - StorageDead(_4); - goto -> bb26; - } - - bb26 (cleanup): { - StorageDead(_3); - goto -> bb27; - } - - bb27 (cleanup): { - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb28, unwind terminate(cleanup)]; - } - - bb28 (cleanup): { - goto -> bb29; - } - - bb29 (cleanup): { - goto -> bb31; - } - - bb30: { - goto -> bb15; - } - - bb31 (cleanup): { - discriminant((*_18)) = 2; - resume; - } - - bb32: { - StorageLive(_3); - StorageLive(_4); - _3 = move _2; - goto -> bb5; - } - - bb33: { - StorageLive(_8); - StorageLive(_9); - StorageLive(_11); - StorageLive(_15); - _8 = move _2; - goto -> bb11; - } - - bb34: { - assert(const false, "coroutine resumed after panicking") -> [success: bb34, unwind continue]; - } - - bb35: { - assert(const false, "coroutine resumed after completion") -> [success: bb35, unwind continue]; - } - - bb36: { - unreachable; - } -} - -ALLOC0 (size: 6, align: 1) { - 73 65 63 6f 6e 64 │ second -} - -ALLOC1 (size: 5, align: 1) { - 66 69 72 73 74 │ first -} diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..17671228295e3 --- /dev/null +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#0}.StateTransform.diff @@ -0,0 +1,353 @@ +- // MIR for `main::{closure#0}` before StateTransform ++ // MIR for `main::{closure#0}` after StateTransform + +- fn main::{closure#0}(_1: {coroutine@$DIR/coroutine.rs:19:5: 19:18}, _2: String) -> () +- yields (&str, String, &Location<'_>) +- { +- debug arg => _2; +- let mut _0: (); ++ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { ++ coroutine layout { ++ field _s0: String; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0], ++ Suspend1 (4): [_s0], ++ } ++ storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} ++ } ++ debug arg => (((*_18) as variant#4).0: std::string::String); ++ coroutine debug arg => _s0; ++ let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; ++ let mut _16: (); ++ let mut _17: u32; ++ let mut _18: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}; + + bb0: { +- StorageLive(_3); +- StorageLive(_4); +- StorageLive(_5); +- StorageLive(_6); +- _6 = &_2; +- _5 = ::clone(move _6) -> [return: bb1, unwind: bb31]; ++ _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:19:5: 19:18}); ++ _17 = discriminant((*_18)); ++ switchInt(move _17) -> [0: bb36, 1: bb34, 2: bb33, 3: bb31, 4: bb32, otherwise: bb35]; + } + + bb1: { + StorageDead(_6); + StorageLive(_7); +- _7 = Location::<'_>::caller() -> [return: bb2, unwind: bb30]; ++ _7 = Location::<'_>::caller() -> [return: bb2, unwind: bb21]; + } + + bb2: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb3; + } + + bb3: { + StorageDead(_5); +- _3 = yield(move _4) -> [resume: bb4, drop: bb18]; ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); ++ StorageDead(_3); ++ StorageDead(_4); ++ discriminant((*_18)) = 3; ++ return; + } + + bb4: { + goto -> bb5; + } + + bb5: { + StorageDead(_4); +- drop(_3) -> [return: bb6, unwind: bb34]; ++ drop(_3) -> [return: bb6, unwind: bb25]; + } + + bb6: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); +- _13 = &_2; +- _12 = ::clone(move _13) -> [return: bb7, unwind: bb28]; ++ _13 = &(((*_18) as variant#4).0: std::string::String); ++ _12 = ::clone(move _13) -> [return: bb7, unwind: bb19]; + } + + bb7: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); +- _15 = Location::<'_>::caller() -> [return: bb8, unwind: bb24]; ++ _15 = Location::<'_>::caller() -> [return: bb8, unwind: bb15]; + } + + bb8: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_10); +- _8 = yield(move _9) -> [resume: bb10, drop: bb15]; ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); ++ StorageDead(_8); ++ StorageDead(_9); ++ StorageDead(_11); ++ StorageDead(_15); ++ discriminant((*_18)) = 4; ++ return; + } + + bb10: { + goto -> bb11; + } + + bb11: { + StorageDead(_9); +- drop(_8) -> [return: bb12, unwind: bb27]; ++ drop(_8) -> [return: bb12, unwind: bb18]; + } + + bb12: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); +- _0 = const (); +- drop(_2) -> [return: bb13, unwind: bb36]; ++ _16 = const (); ++ drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb13, unwind: bb27]; + } + + bb13: { +- drop(_1) -> [return: bb14, unwind: bb37]; ++ goto -> bb29; + } + + bb14: { ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); ++ discriminant((*_18)) = 1; + return; + } + +- bb15: { +- goto -> bb16; ++ bb15 (cleanup): { ++ StorageDead(_14); ++ drop(_12) -> [return: bb16, unwind terminate(cleanup)]; + } + +- bb16: { +- StorageDead(_9); ++ bb16 (cleanup): { ++ StorageDead(_12); ++ StorageDead(_10); + goto -> bb17; + } + +- bb17: { +- StorageDead(_15); +- StorageDead(_11); +- StorageDead(_8); +- goto -> bb21; ++ bb17 (cleanup): { ++ StorageDead(_9); ++ goto -> bb18; + } + +- bb18: { +- goto -> bb19; ++ bb18 (cleanup): { ++ StorageDead(_15); ++ goto -> bb20; + } + +- bb19: { +- StorageDead(_4); ++ bb19 (cleanup): { ++ StorageDead(_13); ++ StorageDead(_12); ++ StorageDead(_10); ++ StorageDead(_9); + goto -> bb20; + } + +- bb20: { +- StorageDead(_3); +- goto -> bb21; ++ bb20 (cleanup): { ++ StorageDead(_11); ++ StorageDead(_8); ++ goto -> bb26; + } + +- bb21: { +- drop(_2) -> [return: bb22, unwind: bb38]; ++ bb21 (cleanup): { ++ StorageDead(_7); ++ drop(_5) -> [return: bb23, unwind terminate(cleanup)]; + } + +- bb22: { +- drop(_1) -> [return: bb23, unwind: bb37]; ++ bb22 (cleanup): { ++ StorageDead(_6); ++ goto -> bb23; + } + +- bb23: { +- coroutine_drop; ++ bb23 (cleanup): { ++ StorageDead(_5); ++ goto -> bb24; + } + + bb24 (cleanup): { +- StorageDead(_14); +- drop(_12) -> [return: bb25, unwind terminate(cleanup)]; ++ StorageDead(_4); ++ goto -> bb25; + } + + bb25 (cleanup): { +- StorageDead(_12); +- StorageDead(_10); ++ StorageDead(_3); + goto -> bb26; + } + + bb26 (cleanup): { +- StorageDead(_9); +- goto -> bb27; ++ drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { +- StorageDead(_15); +- goto -> bb29; ++ goto -> bb28; + } + + bb28 (cleanup): { +- StorageDead(_13); +- StorageDead(_12); +- StorageDead(_10); +- StorageDead(_9); +- goto -> bb29; ++ goto -> bb30; + } + +- bb29 (cleanup): { +- StorageDead(_11); +- StorageDead(_8); +- goto -> bb35; ++ bb29: { ++ goto -> bb14; + } + + bb30 (cleanup): { +- StorageDead(_7); +- drop(_5) -> [return: bb32, unwind terminate(cleanup)]; ++ discriminant((*_18)) = 2; ++ resume; + } + +- bb31 (cleanup): { +- StorageDead(_6); +- goto -> bb32; ++ bb31: { ++ StorageLive(_3); ++ StorageLive(_4); ++ _3 = move _2; ++ goto -> bb4; + } + +- bb32 (cleanup): { +- StorageDead(_5); +- goto -> bb33; ++ bb32: { ++ StorageLive(_8); ++ StorageLive(_9); ++ StorageLive(_11); ++ StorageLive(_15); ++ _8 = move _2; ++ goto -> bb10; + } + +- bb33 (cleanup): { +- StorageDead(_4); +- goto -> bb34; ++ bb33: { ++ assert(const false, "coroutine resumed after panicking") -> [success: bb33, unwind continue]; + } + +- bb34 (cleanup): { +- StorageDead(_3); +- goto -> bb35; ++ bb34: { ++ assert(const false, "coroutine resumed after completion") -> [success: bb34, unwind continue]; + } + +- bb35 (cleanup): { +- drop(_2) -> [return: bb36, unwind terminate(cleanup)]; ++ bb35: { ++ unreachable; + } + +- bb36 (cleanup): { +- drop(_1) -> [return: bb37, unwind terminate(cleanup)]; +- } +- +- bb37 (cleanup): { +- resume; +- } +- +- bb38 (cleanup): { +- drop(_1) -> [return: bb37, unwind terminate(cleanup)]; ++ bb36: { ++ (((*_18) as variant#4).0: std::string::String) = move _2; ++ StorageLive(_3); ++ StorageLive(_4); ++ StorageLive(_5); ++ StorageLive(_6); ++ _6 = &(((*_18) as variant#4).0: std::string::String); ++ _5 = ::clone(move _6) -> [return: bb1, unwind: bb22]; + } + } + + ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second + } + + ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first + } + diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir deleted file mode 100644 index 1abfe986f6867..0000000000000 --- a/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.after.mir +++ /dev/null @@ -1,264 +0,0 @@ -// MIR for `main::{closure#1}` after StateTransform - -fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - coroutine layout { - field _s0: String; - variant_fields = { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - Suspend1 (4): [_s0], - } - storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} - } - debug arg => (((*_18) as variant#4).0: std::string::String); - coroutine debug arg => _s0; - let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; - let _3: std::string::String; - let mut _4: (&str, std::string::String, &std::panic::Location<'_>); - let mut _5: std::string::String; - let mut _6: &std::string::String; - let mut _7: &std::panic::Location<'_>; - let _8: std::string::String; - let mut _9: (&str, std::string::String, &std::panic::Location<'_>); - let mut _10: &str; - let _11: &str; - let mut _12: std::string::String; - let mut _13: &std::string::String; - let mut _14: &std::panic::Location<'_>; - let _15: &std::panic::Location<'_>; - let mut _16: (); - let mut _17: u32; - let mut _18: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}; - - bb0: { - _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}); - _17 = discriminant((*_18)); - switchInt(move _17) -> [0: bb1, 1: bb35, 2: bb34, 3: bb32, 4: bb33, otherwise: bb36]; - } - - bb1: { - (((*_18) as variant#4).0: std::string::String) = move _2; - StorageLive(_3); - StorageLive(_4); - StorageLive(_5); - StorageLive(_6); - _6 = &(((*_18) as variant#4).0: std::string::String); - _5 = ::clone(move _6) -> [return: bb2, unwind: bb23]; - } - - bb2: { - StorageDead(_6); - StorageLive(_7); - _7 = Location::<'_>::caller() -> [return: bb3, unwind: bb22]; - } - - bb3: { - _4 = (const "first", move _5, move _7); - StorageDead(_7); - goto -> bb4; - } - - bb4: { - StorageDead(_5); - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); - StorageDead(_3); - StorageDead(_4); - discriminant((*_18)) = 3; - return; - } - - bb5: { - goto -> bb6; - } - - bb6: { - StorageDead(_4); - drop(_3) -> [return: bb7, unwind: bb26]; - } - - bb7: { - StorageDead(_3); - StorageLive(_8); - StorageLive(_9); - StorageLive(_10); - StorageLive(_11); - _11 = const "second"; - _10 = &(*_11); - StorageLive(_12); - StorageLive(_13); - _13 = &(((*_18) as variant#4).0: std::string::String); - _12 = ::clone(move _13) -> [return: bb8, unwind: bb20]; - } - - bb8: { - StorageDead(_13); - StorageLive(_14); - StorageLive(_15); - _15 = Location::<'_>::caller() -> [return: bb9, unwind: bb16]; - } - - bb9: { - _14 = &(*_15); - _9 = (move _10, move _12, move _14); - StorageDead(_14); - goto -> bb10; - } - - bb10: { - StorageDead(_12); - StorageDead(_10); - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); - StorageDead(_8); - StorageDead(_9); - StorageDead(_11); - StorageDead(_15); - discriminant((*_18)) = 4; - return; - } - - bb11: { - goto -> bb12; - } - - bb12: { - StorageDead(_9); - drop(_8) -> [return: bb13, unwind: bb19]; - } - - bb13: { - StorageDead(_15); - StorageDead(_11); - StorageDead(_8); - _16 = const (); - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb14, unwind: bb28]; - } - - bb14: { - goto -> bb30; - } - - bb15: { - _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - discriminant((*_18)) = 1; - return; - } - - bb16 (cleanup): { - StorageDead(_14); - drop(_12) -> [return: bb17, unwind terminate(cleanup)]; - } - - bb17 (cleanup): { - StorageDead(_12); - StorageDead(_10); - goto -> bb18; - } - - bb18 (cleanup): { - StorageDead(_9); - goto -> bb19; - } - - bb19 (cleanup): { - StorageDead(_15); - goto -> bb21; - } - - bb20 (cleanup): { - StorageDead(_13); - StorageDead(_12); - StorageDead(_10); - StorageDead(_9); - goto -> bb21; - } - - bb21 (cleanup): { - StorageDead(_11); - StorageDead(_8); - goto -> bb27; - } - - bb22 (cleanup): { - StorageDead(_7); - drop(_5) -> [return: bb24, unwind terminate(cleanup)]; - } - - bb23 (cleanup): { - StorageDead(_6); - goto -> bb24; - } - - bb24 (cleanup): { - StorageDead(_5); - goto -> bb25; - } - - bb25 (cleanup): { - StorageDead(_4); - goto -> bb26; - } - - bb26 (cleanup): { - StorageDead(_3); - goto -> bb27; - } - - bb27 (cleanup): { - drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb28, unwind terminate(cleanup)]; - } - - bb28 (cleanup): { - goto -> bb29; - } - - bb29 (cleanup): { - goto -> bb31; - } - - bb30: { - goto -> bb15; - } - - bb31 (cleanup): { - discriminant((*_18)) = 2; - resume; - } - - bb32: { - StorageLive(_3); - StorageLive(_4); - _3 = move _2; - goto -> bb5; - } - - bb33: { - StorageLive(_8); - StorageLive(_9); - StorageLive(_11); - StorageLive(_15); - _8 = move _2; - goto -> bb11; - } - - bb34: { - assert(const false, "coroutine resumed after panicking") -> [success: bb34, unwind continue]; - } - - bb35: { - assert(const false, "coroutine resumed after completion") -> [success: bb35, unwind continue]; - } - - bb36: { - unreachable; - } -} - -ALLOC0 (size: 6, align: 1) { - 73 65 63 6f 6e 64 │ second -} - -ALLOC1 (size: 5, align: 1) { - 66 69 72 73 74 │ first -} diff --git a/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.diff b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.diff new file mode 100644 index 0000000000000..97c45da8a18f1 --- /dev/null +++ b/tests/mir-opt/coroutine/coroutine.main-{closure#1}.StateTransform.diff @@ -0,0 +1,353 @@ +- // MIR for `main::{closure#1}` before StateTransform ++ // MIR for `main::{closure#1}` after StateTransform + +- fn main::{closure#1}(_1: {coroutine@$DIR/coroutine.rs:26:5: 26:18}, _2: String) -> () +- yields (&str, String, &Location<'_>) +- { +- debug arg => _2; +- let mut _0: (); ++ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { ++ coroutine layout { ++ field _s0: String; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0], ++ Suspend1 (4): [_s0], ++ } ++ storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} ++ } ++ debug arg => (((*_18) as variant#4).0: std::string::String); ++ coroutine debug arg => _s0; ++ let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; ++ let mut _16: (); ++ let mut _17: u32; ++ let mut _18: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}; + + bb0: { +- StorageLive(_3); +- StorageLive(_4); +- StorageLive(_5); +- StorageLive(_6); +- _6 = &_2; +- _5 = ::clone(move _6) -> [return: bb1, unwind: bb31]; ++ _18 = copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:26:5: 26:18}); ++ _17 = discriminant((*_18)); ++ switchInt(move _17) -> [0: bb36, 1: bb34, 2: bb33, 3: bb31, 4: bb32, otherwise: bb35]; + } + + bb1: { + StorageDead(_6); + StorageLive(_7); +- _7 = Location::<'_>::caller() -> [return: bb2, unwind: bb30]; ++ _7 = Location::<'_>::caller() -> [return: bb2, unwind: bb21]; + } + + bb2: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb3; + } + + bb3: { + StorageDead(_5); +- _3 = yield(move _4) -> [resume: bb4, drop: bb18]; ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); ++ StorageDead(_3); ++ StorageDead(_4); ++ discriminant((*_18)) = 3; ++ return; + } + + bb4: { + goto -> bb5; + } + + bb5: { + StorageDead(_4); +- drop(_3) -> [return: bb6, unwind: bb34]; ++ drop(_3) -> [return: bb6, unwind: bb25]; + } + + bb6: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); +- _13 = &_2; +- _12 = ::clone(move _13) -> [return: bb7, unwind: bb28]; ++ _13 = &(((*_18) as variant#4).0: std::string::String); ++ _12 = ::clone(move _13) -> [return: bb7, unwind: bb19]; + } + + bb7: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); +- _15 = Location::<'_>::caller() -> [return: bb8, unwind: bb24]; ++ _15 = Location::<'_>::caller() -> [return: bb8, unwind: bb15]; + } + + bb8: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb9; + } + + bb9: { + StorageDead(_12); + StorageDead(_10); +- _8 = yield(move _9) -> [resume: bb10, drop: bb15]; ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); ++ StorageDead(_8); ++ StorageDead(_9); ++ StorageDead(_11); ++ StorageDead(_15); ++ discriminant((*_18)) = 4; ++ return; + } + + bb10: { + goto -> bb11; + } + + bb11: { + StorageDead(_9); +- drop(_8) -> [return: bb12, unwind: bb27]; ++ drop(_8) -> [return: bb12, unwind: bb18]; + } + + bb12: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); +- _0 = const (); +- drop(_2) -> [return: bb13, unwind: bb36]; ++ _16 = const (); ++ drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb13, unwind: bb27]; + } + + bb13: { +- drop(_1) -> [return: bb14, unwind: bb37]; ++ goto -> bb29; + } + + bb14: { ++ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); ++ discriminant((*_18)) = 1; + return; + } + +- bb15: { +- goto -> bb16; ++ bb15 (cleanup): { ++ StorageDead(_14); ++ drop(_12) -> [return: bb16, unwind terminate(cleanup)]; + } + +- bb16: { +- StorageDead(_9); ++ bb16 (cleanup): { ++ StorageDead(_12); ++ StorageDead(_10); + goto -> bb17; + } + +- bb17: { +- StorageDead(_15); +- StorageDead(_11); +- StorageDead(_8); +- goto -> bb21; ++ bb17 (cleanup): { ++ StorageDead(_9); ++ goto -> bb18; + } + +- bb18: { +- goto -> bb19; ++ bb18 (cleanup): { ++ StorageDead(_15); ++ goto -> bb20; + } + +- bb19: { +- StorageDead(_4); ++ bb19 (cleanup): { ++ StorageDead(_13); ++ StorageDead(_12); ++ StorageDead(_10); ++ StorageDead(_9); + goto -> bb20; + } + +- bb20: { +- StorageDead(_3); +- goto -> bb21; ++ bb20 (cleanup): { ++ StorageDead(_11); ++ StorageDead(_8); ++ goto -> bb26; + } + +- bb21: { +- drop(_2) -> [return: bb22, unwind: bb38]; ++ bb21 (cleanup): { ++ StorageDead(_7); ++ drop(_5) -> [return: bb23, unwind terminate(cleanup)]; + } + +- bb22: { +- drop(_1) -> [return: bb23, unwind: bb37]; ++ bb22 (cleanup): { ++ StorageDead(_6); ++ goto -> bb23; + } + +- bb23: { +- coroutine_drop; ++ bb23 (cleanup): { ++ StorageDead(_5); ++ goto -> bb24; + } + + bb24 (cleanup): { +- StorageDead(_14); +- drop(_12) -> [return: bb25, unwind terminate(cleanup)]; ++ StorageDead(_4); ++ goto -> bb25; + } + + bb25 (cleanup): { +- StorageDead(_12); +- StorageDead(_10); ++ StorageDead(_3); + goto -> bb26; + } + + bb26 (cleanup): { +- StorageDead(_9); +- goto -> bb27; ++ drop((((*_18) as variant#4).0: std::string::String)) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { +- StorageDead(_15); +- goto -> bb29; ++ goto -> bb28; + } + + bb28 (cleanup): { +- StorageDead(_13); +- StorageDead(_12); +- StorageDead(_10); +- StorageDead(_9); +- goto -> bb29; ++ goto -> bb30; + } + +- bb29 (cleanup): { +- StorageDead(_11); +- StorageDead(_8); +- goto -> bb35; ++ bb29: { ++ goto -> bb14; + } + + bb30 (cleanup): { +- StorageDead(_7); +- drop(_5) -> [return: bb32, unwind terminate(cleanup)]; ++ discriminant((*_18)) = 2; ++ resume; + } + +- bb31 (cleanup): { +- StorageDead(_6); +- goto -> bb32; ++ bb31: { ++ StorageLive(_3); ++ StorageLive(_4); ++ _3 = move _2; ++ goto -> bb4; + } + +- bb32 (cleanup): { +- StorageDead(_5); +- goto -> bb33; ++ bb32: { ++ StorageLive(_8); ++ StorageLive(_9); ++ StorageLive(_11); ++ StorageLive(_15); ++ _8 = move _2; ++ goto -> bb10; + } + +- bb33 (cleanup): { +- StorageDead(_4); +- goto -> bb34; ++ bb33: { ++ assert(const false, "coroutine resumed after panicking") -> [success: bb33, unwind continue]; + } + +- bb34 (cleanup): { +- StorageDead(_3); +- goto -> bb35; ++ bb34: { ++ assert(const false, "coroutine resumed after completion") -> [success: bb34, unwind continue]; + } + +- bb35 (cleanup): { +- drop(_2) -> [return: bb36, unwind terminate(cleanup)]; ++ bb35: { ++ unreachable; + } + +- bb36 (cleanup): { +- drop(_1) -> [return: bb37, unwind terminate(cleanup)]; +- } +- +- bb37 (cleanup): { +- resume; +- } +- +- bb38 (cleanup): { +- drop(_1) -> [return: bb37, unwind terminate(cleanup)]; ++ bb36: { ++ (((*_18) as variant#4).0: std::string::String) = move _2; ++ StorageLive(_3); ++ StorageLive(_4); ++ StorageLive(_5); ++ StorageLive(_6); ++ _6 = &(((*_18) as variant#4).0: std::string::String); ++ _5 = ::clone(move _6) -> [return: bb1, unwind: bb22]; + } + } + + ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second + } + + ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first + } + diff --git a/tests/mir-opt/coroutine/coroutine.rs b/tests/mir-opt/coroutine/coroutine.rs index 41fdbee119e14..15d3cbb7c99f4 100644 --- a/tests/mir-opt/coroutine/coroutine.rs +++ b/tests/mir-opt/coroutine/coroutine.rs @@ -12,8 +12,8 @@ use std::ops::{Coroutine, CoroutineState}; use std::panic::Location; use std::pin::Pin; -// EMIT_MIR coroutine.main-{closure#0}.StateTransform.after.mir -// EMIT_MIR coroutine.main-{closure#1}.StateTransform.after.mir +// EMIT_MIR coroutine.main-{closure#0}.StateTransform.diff +// EMIT_MIR coroutine.main-{closure#1}.StateTransform.diff fn main() { let simple = #[coroutine] |arg: String| { diff --git a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir deleted file mode 100644 index 4731aed335d9f..0000000000000 --- a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-abort.mir +++ /dev/null @@ -1,83 +0,0 @@ -// MIR for `main::{closure#0}` before StateTransform - -fn main::{closure#0}(_1: {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}, _2: ()) -> () -yields () - { - let mut _0: (); - let _3: Foo; - let _5: (); - let mut _6: (); - let _7: (); - let mut _8: Foo; - let _9: (); - let mut _10: Bar; - scope 1 { - debug a => _3; - let _4: Bar; - scope 2 { - debug b => _4; - } - } - - bb0: { - StorageLive(_3); - _3 = Foo(const 5_i32); - StorageLive(_4); - _4 = Bar(const 6_i32); - StorageLive(_5); - StorageLive(_6); - _6 = (); - _5 = yield(move _6) -> [resume: bb1, drop: bb6]; - } - - bb1: { - StorageDead(_6); - StorageDead(_5); - StorageLive(_7); - StorageLive(_8); - _8 = move _3; - _7 = take::(move _8) -> [return: bb2, unwind unreachable]; - } - - bb2: { - StorageDead(_8); - StorageDead(_7); - StorageLive(_9); - StorageLive(_10); - _10 = move _4; - _9 = take::(move _10) -> [return: bb3, unwind unreachable]; - } - - bb3: { - StorageDead(_10); - StorageDead(_9); - _0 = const (); - StorageDead(_4); - goto -> bb4; - } - - bb4: { - StorageDead(_3); - drop(_1) -> [return: bb5, unwind unreachable]; - } - - bb5: { - return; - } - - bb6: { - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); - drop(_3) -> [return: bb7, unwind unreachable]; - } - - bb7: { - StorageDead(_3); - drop(_1) -> [return: bb8, unwind unreachable]; - } - - bb8: { - coroutine_drop; - } -} diff --git a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir deleted file mode 100644 index 14e1782b86016..0000000000000 --- a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.panic-unwind.mir +++ /dev/null @@ -1,118 +0,0 @@ -// MIR for `main::{closure#0}` before StateTransform - -fn main::{closure#0}(_1: {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}, _2: ()) -> () -yields () - { - let mut _0: (); - let _3: Foo; - let _5: (); - let mut _6: (); - let _7: (); - let mut _8: Foo; - let _9: (); - let mut _10: Bar; - scope 1 { - debug a => _3; - let _4: Bar; - scope 2 { - debug b => _4; - } - } - - bb0: { - StorageLive(_3); - _3 = Foo(const 5_i32); - StorageLive(_4); - _4 = Bar(const 6_i32); - StorageLive(_5); - StorageLive(_6); - _6 = (); - _5 = yield(move _6) -> [resume: bb1, drop: bb6]; - } - - bb1: { - StorageDead(_6); - StorageDead(_5); - StorageLive(_7); - StorageLive(_8); - _8 = move _3; - _7 = take::(move _8) -> [return: bb2, unwind: bb10]; - } - - bb2: { - StorageDead(_8); - StorageDead(_7); - StorageLive(_9); - StorageLive(_10); - _10 = move _4; - _9 = take::(move _10) -> [return: bb3, unwind: bb9]; - } - - bb3: { - StorageDead(_10); - StorageDead(_9); - _0 = const (); - StorageDead(_4); - goto -> bb4; - } - - bb4: { - StorageDead(_3); - drop(_1) -> [return: bb5, unwind: bb14]; - } - - bb5: { - return; - } - - bb6: { - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); - drop(_3) -> [return: bb7, unwind: bb15]; - } - - bb7: { - StorageDead(_3); - drop(_1) -> [return: bb8, unwind: bb14]; - } - - bb8: { - coroutine_drop; - } - - bb9 (cleanup): { - StorageDead(_10); - StorageDead(_9); - goto -> bb12; - } - - bb10 (cleanup): { - goto -> bb11; - } - - bb11 (cleanup): { - StorageDead(_8); - StorageDead(_7); - goto -> bb12; - } - - bb12 (cleanup): { - StorageDead(_4); - goto -> bb13; - } - - bb13 (cleanup): { - StorageDead(_3); - drop(_1) -> [return: bb14, unwind terminate(cleanup)]; - } - - bb14 (cleanup): { - resume; - } - - bb15 (cleanup): { - StorageDead(_3); - drop(_1) -> [return: bb14, unwind terminate(cleanup)]; - } -} diff --git a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-abort.diff b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-abort.diff new file mode 100644 index 0000000000000..9e4fb0cb64788 --- /dev/null +++ b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-abort.diff @@ -0,0 +1,141 @@ +- // MIR for `main::{closure#0}` before StateTransform ++ // MIR for `main::{closure#0}` after StateTransform + +- fn main::{closure#0}(_1: {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}, _2: ()) -> () +- yields () +- { +- let mut _0: (); ++ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}>, _2: ()) -> CoroutineState<(), ()> { ++ coroutine layout { ++ field _s0: Foo; ++ field _s1: Bar; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0, _s1], ++ } ++ storage_conflicts = BitMatrix(2x2) {(_s0, _s0), (_s0, _s1), (_s1, _s0), (_s1, _s1)} ++ } ++ coroutine debug a => _s0; ++ let mut _0: std::ops::CoroutineState<(), ()>; + let _3: Foo; + let _5: (); + let mut _6: (); + let _7: (); + let mut _8: Foo; + let _9: (); + let mut _10: Bar; ++ let mut _11: (); ++ let mut _12: u32; ++ let mut _13: &mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}; + scope 1 { +- debug a => _3; ++ debug a => (((*_13) as variant#3).0: Foo); ++ coroutine debug b => _s1; + let _4: Bar; + scope 2 { +- debug b => _4; ++ debug b => (((*_13) as variant#3).1: Bar); + } + } + + bb0: { +- StorageLive(_3); +- _3 = Foo(const 5_i32); +- StorageLive(_4); +- _4 = Bar(const 6_i32); +- StorageLive(_5); +- StorageLive(_6); +- _6 = (); +- _5 = yield(move _6) -> [resume: bb1, drop: bb6]; ++ _13 = copy (_1.0: &mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}); ++ _12 = discriminant((*_13)); ++ switchInt(move _12) -> [0: bb10, 1: bb8, 3: bb7, otherwise: bb9]; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); +- _8 = move _3; ++ _8 = move (((*_13) as variant#3).0: Foo); + _7 = take::(move _8) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_8); + StorageDead(_7); + StorageLive(_9); + StorageLive(_10); +- _10 = move _4; ++ _10 = move (((*_13) as variant#3).1: Bar); + _9 = take::(move _10) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_10); + StorageDead(_9); +- _0 = const (); +- StorageDead(_4); ++ _11 = const (); ++ nop; + goto -> bb4; + } + + bb4: { +- StorageDead(_3); +- drop(_1) -> [return: bb5, unwind unreachable]; ++ nop; ++ goto -> bb6; + } + + bb5: { ++ _0 = CoroutineState::<(), ()>::Complete(move _11); ++ discriminant((*_13)) = 1; + return; + } + + bb6: { +- StorageDead(_6); +- StorageDead(_5); +- StorageDead(_4); +- drop(_3) -> [return: bb7, unwind unreachable]; ++ goto -> bb5; + } + + bb7: { +- StorageDead(_3); +- drop(_1) -> [return: bb8, unwind unreachable]; ++ StorageLive(_5); ++ StorageLive(_6); ++ _5 = move _2; ++ goto -> bb1; + } + + bb8: { +- coroutine_drop; ++ assert(const false, "coroutine resumed after completion") -> [success: bb8, unwind unreachable]; ++ } ++ ++ bb9: { ++ unreachable; ++ } ++ ++ bb10: { ++ nop; ++ (((*_13) as variant#3).0: Foo) = Foo(const 5_i32); ++ nop; ++ (((*_13) as variant#3).1: Bar) = Bar(const 6_i32); ++ StorageLive(_5); ++ StorageLive(_6); ++ _6 = (); ++ _0 = CoroutineState::<(), ()>::Yielded(move _6); ++ StorageDead(_5); ++ StorageDead(_6); ++ discriminant((*_13)) = 3; ++ return; + } + } + diff --git a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-unwind.diff b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-unwind.diff new file mode 100644 index 0000000000000..fa89df127094a --- /dev/null +++ b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.panic-unwind.diff @@ -0,0 +1,201 @@ +- // MIR for `main::{closure#0}` before StateTransform ++ // MIR for `main::{closure#0}` after StateTransform + +- fn main::{closure#0}(_1: {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}, _2: ()) -> () +- yields () +- { +- let mut _0: (); ++ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}>, _2: ()) -> CoroutineState<(), ()> { ++ coroutine layout { ++ field _s0: Foo; ++ field _s1: Bar; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0, _s1], ++ } ++ storage_conflicts = BitMatrix(2x2) {(_s0, _s0), (_s0, _s1), (_s1, _s0), (_s1, _s1)} ++ } ++ coroutine debug a => _s0; ++ let mut _0: std::ops::CoroutineState<(), ()>; + let _3: Foo; + let _5: (); + let mut _6: (); + let _7: (); + let mut _8: Foo; + let _9: (); + let mut _10: Bar; ++ let mut _11: (); ++ let mut _12: u32; ++ let mut _13: &mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}; + scope 1 { +- debug a => _3; ++ debug a => (((*_13) as variant#3).0: Foo); ++ coroutine debug b => _s1; + let _4: Bar; + scope 2 { +- debug b => _4; ++ debug b => (((*_13) as variant#3).1: Bar); + } + } + + bb0: { +- StorageLive(_3); +- _3 = Foo(const 5_i32); +- StorageLive(_4); +- _4 = Bar(const 6_i32); +- StorageLive(_5); +- StorageLive(_6); +- _6 = (); +- _5 = yield(move _6) -> [resume: bb1, drop: bb6]; ++ _13 = copy (_1.0: &mut {coroutine@$DIR/coroutine_storage_dead_unwind.rs:24:5: 24:7}); ++ _12 = discriminant((*_13)); ++ switchInt(move _12) -> [0: bb18, 1: bb16, 2: bb15, 3: bb14, otherwise: bb17]; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); +- _8 = move _3; +- _7 = take::(move _8) -> [return: bb2, unwind: bb10]; ++ _8 = move (((*_13) as variant#3).0: Foo); ++ _7 = take::(move _8) -> [return: bb2, unwind: bb7]; + } + + bb2: { + StorageDead(_8); + StorageDead(_7); + StorageLive(_9); + StorageLive(_10); +- _10 = move _4; +- _9 = take::(move _10) -> [return: bb3, unwind: bb9]; ++ _10 = move (((*_13) as variant#3).1: Bar); ++ _9 = take::(move _10) -> [return: bb3, unwind: bb6]; + } + + bb3: { + StorageDead(_10); + StorageDead(_9); +- _0 = const (); +- StorageDead(_4); ++ _11 = const (); ++ nop; + goto -> bb4; + } + + bb4: { +- StorageDead(_3); +- drop(_1) -> [return: bb5, unwind: bb14]; ++ nop; ++ goto -> bb12; + } + + bb5: { ++ _0 = CoroutineState::<(), ()>::Complete(move _11); ++ discriminant((*_13)) = 1; + return; + } + +- bb6: { +- StorageDead(_6); +- StorageDead(_5); +- StorageDead(_4); +- drop(_3) -> [return: bb7, unwind: bb15]; ++ bb6 (cleanup): { ++ StorageDead(_10); ++ StorageDead(_9); ++ goto -> bb9; + } + +- bb7: { +- StorageDead(_3); +- drop(_1) -> [return: bb8, unwind: bb14]; ++ bb7 (cleanup): { ++ goto -> bb8; + } + +- bb8: { +- coroutine_drop; ++ bb8 (cleanup): { ++ StorageDead(_8); ++ StorageDead(_7); ++ goto -> bb9; + } + + bb9 (cleanup): { +- StorageDead(_10); +- StorageDead(_9); +- goto -> bb12; ++ nop; ++ goto -> bb10; + } + + bb10 (cleanup): { ++ nop; + goto -> bb11; + } + + bb11 (cleanup): { +- StorageDead(_8); +- StorageDead(_7); +- goto -> bb12; ++ goto -> bb13; + } + +- bb12 (cleanup): { +- StorageDead(_4); +- goto -> bb13; ++ bb12: { ++ goto -> bb5; + } + + bb13 (cleanup): { +- StorageDead(_3); +- drop(_1) -> [return: bb14, unwind terminate(cleanup)]; ++ discriminant((*_13)) = 2; ++ resume; + } + +- bb14 (cleanup): { +- resume; ++ bb14: { ++ StorageLive(_5); ++ StorageLive(_6); ++ _5 = move _2; ++ goto -> bb1; + } + +- bb15 (cleanup): { +- StorageDead(_3); +- drop(_1) -> [return: bb14, unwind terminate(cleanup)]; ++ bb15: { ++ assert(const false, "coroutine resumed after panicking") -> [success: bb15, unwind continue]; ++ } ++ ++ bb16: { ++ assert(const false, "coroutine resumed after completion") -> [success: bb16, unwind continue]; ++ } ++ ++ bb17: { ++ unreachable; ++ } ++ ++ bb18: { ++ nop; ++ (((*_13) as variant#3).0: Foo) = Foo(const 5_i32); ++ nop; ++ (((*_13) as variant#3).1: Bar) = Bar(const 6_i32); ++ StorageLive(_5); ++ StorageLive(_6); ++ _6 = (); ++ _0 = CoroutineState::<(), ()>::Yielded(move _6); ++ StorageDead(_5); ++ StorageDead(_6); ++ discriminant((*_13)) = 3; ++ return; + } + } + diff --git a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs index 0537aedcf230b..1267f7248bc17 100644 --- a/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs +++ b/tests/mir-opt/coroutine/coroutine_storage_dead_unwind.rs @@ -18,7 +18,7 @@ struct Bar(i32); fn take(_x: T) {} -// EMIT_MIR coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.before.mir +// EMIT_MIR coroutine_storage_dead_unwind.main-{closure#0}.StateTransform.diff fn main() { let _gen = #[coroutine] || { diff --git a/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..b84e55cac4616 --- /dev/null +++ b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.StateTransform.diff @@ -0,0 +1,98 @@ +- // MIR for `main::{closure#0}` before StateTransform ++ // MIR for `main::{closure#0}` after StateTransform + +- fn main::{closure#0}(_1: {coroutine@$DIR/coroutine_tiny.rs:20:5: 20:13}, _2: u8) -> () +- yields () +- { ++ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:20:5: 20:13}>, _2: u8) -> CoroutineState<(), ()> { ++ coroutine layout { ++ field _s0: HasDrop; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0], ++ } ++ storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} ++ } + debug _x => _2; +- let mut _0: (); ++ coroutine debug _d => _s0; ++ let mut _0: std::ops::CoroutineState<(), ()>; + let _3: HasDrop; + let mut _4: !; + let mut _5: (); + let _6: u8; + let mut _7: (); + let _8: (); ++ let mut _9: (); ++ let mut _10: u32; ++ let mut _11: &mut {coroutine@$DIR/coroutine_tiny.rs:20:5: 20:13}; + scope 1 { +- debug _d => _3; ++ debug _d => (((*_11) as variant#3).0: HasDrop); + } + + bb0: { +- StorageLive(_3); +- _3 = HasDrop; +- StorageLive(_4); +- goto -> bb1; ++ _11 = copy (_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:20:5: 20:13}); ++ _10 = discriminant((*_11)); ++ switchInt(move _10) -> [0: bb6, 3: bb4, otherwise: bb5]; + } + + bb1: { + StorageLive(_6); + StorageLive(_7); + _7 = (); +- _6 = yield(move _7) -> [resume: bb2, drop: bb4]; ++ _0 = CoroutineState::<(), ()>::Yielded(move _7); ++ StorageDead(_4); ++ StorageDead(_6); ++ StorageDead(_7); ++ discriminant((*_11)) = 3; ++ return; + } + + bb2: { + StorageDead(_7); + StorageDead(_6); + StorageLive(_8); + _8 = callee() -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_8); + _5 = const (); + goto -> bb1; + } + + bb4: { +- StorageDead(_7); +- StorageDead(_6); +- StorageDead(_4); +- drop(_3) -> [return: bb5, unwind unreachable]; ++ StorageLive(_4); ++ StorageLive(_6); ++ StorageLive(_7); ++ _6 = move _2; ++ goto -> bb2; + } + + bb5: { +- StorageDead(_3); +- drop(_1) -> [return: bb6, unwind unreachable]; ++ unreachable; + } + + bb6: { +- coroutine_drop; ++ nop; ++ (((*_11) as variant#3).0: HasDrop) = HasDrop; ++ StorageLive(_4); ++ goto -> bb1; + } + } + diff --git a/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir deleted file mode 100644 index e5667215d54fd..0000000000000 --- a/tests/mir-opt/coroutine/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ /dev/null @@ -1,79 +0,0 @@ -// MIR for `main::{closure#0}` 0 coroutine_resume - -fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}>, _2: u8) -> CoroutineState<(), ()> { - coroutine layout { - field _s0: HasDrop; - variant_fields = { - Unresumed(0): [], - Returned (1): [], - Panicked (2): [], - Suspend0 (3): [_s0], - } - storage_conflicts = BitMatrix(1x1) {(_s0, _s0)} - } - debug _x => _2; - coroutine debug _d => _s0; - let mut _0: std::ops::CoroutineState<(), ()>; - let _3: HasDrop; - let mut _4: !; - let mut _5: (); - let _6: u8; - let mut _7: (); - let _8: (); - let mut _9: (); - let mut _10: u32; - let mut _11: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}; - scope 1 { - debug _d => (((*_11) as variant#3).0: HasDrop); - } - - bb0: { - _11 = copy (_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}); - _10 = discriminant((*_11)); - switchInt(move _10) -> [0: bb1, 3: bb5, otherwise: bb6]; - } - - bb1: { - nop; - (((*_11) as variant#3).0: HasDrop) = HasDrop; - StorageLive(_4); - goto -> bb2; - } - - bb2: { - StorageLive(_6); - StorageLive(_7); - _7 = (); - _0 = CoroutineState::<(), ()>::Yielded(move _7); - StorageDead(_4); - StorageDead(_6); - StorageDead(_7); - discriminant((*_11)) = 3; - return; - } - - bb3: { - StorageDead(_7); - StorageDead(_6); - StorageLive(_8); - _8 = callee() -> [return: bb4, unwind unreachable]; - } - - bb4: { - StorageDead(_8); - _5 = const (); - goto -> bb2; - } - - bb5: { - StorageLive(_4); - StorageLive(_6); - StorageLive(_7); - _6 = move _2; - goto -> bb3; - } - - bb6: { - unreachable; - } -} diff --git a/tests/mir-opt/coroutine/coroutine_tiny.rs b/tests/mir-opt/coroutine/coroutine_tiny.rs index b92628aebf96a..2f3a7744bd603 100644 --- a/tests/mir-opt/coroutine/coroutine_tiny.rs +++ b/tests/mir-opt/coroutine/coroutine_tiny.rs @@ -1,4 +1,3 @@ -//@ skip-filecheck //! Tests that coroutines that cannot return or unwind don't have unnecessary //! panic branches. @@ -15,7 +14,7 @@ impl Drop for HasDrop { fn callee() {} -// EMIT_MIR coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +// EMIT_MIR coroutine_tiny.main-{closure#0}.StateTransform.diff fn main() { let _gen = #[coroutine] |_x: u8| { @@ -26,3 +25,6 @@ fn main() { } }; } + +// CHECK-NOT: panic +// CHECK-NOT: cleanup diff --git a/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index f8ef70bd6c9ce..617f2ad463ed3 100644 --- a/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -47,42 +47,42 @@ + let _26: (); + scope 9 { + } -+ scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ scope 11 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } -+ scope 13 (inlined as Future>::poll) { -+ let mut _34: (); -+ let mut _35: std::option::Option<()>; -+ let mut _36: &mut std::option::Option<()>; -+ let mut _37: &mut std::future::Ready<()>; -+ let mut _38: &mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _39: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; -+ let mut _40: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { -+ let mut _41: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { ++ scope 12 (inlined as Future>::poll) { ++ let mut _33: (); ++ let mut _34: std::option::Option<()>; ++ let mut _35: &mut std::option::Option<()>; ++ let mut _36: &mut std::future::Ready<()>; ++ let mut _37: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 13 (inlined > as DerefMut>::deref_mut) { ++ let mut _38: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; ++ let mut _39: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 14 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { ++ let mut _40: &mut &mut std::future::Ready<()>; ++ scope 15 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } + } + } -+ scope 17 (inlined Option::<()>::take) { -+ let mut _42: std::option::Option<()>; -+ scope 18 (inlined std::mem::replace::>) { -+ scope 19 { ++ scope 16 (inlined Option::<()>::take) { ++ let mut _41: std::option::Option<()>; ++ scope 17 (inlined std::mem::replace::>) { ++ scope 18 { + } + } + } -+ scope 20 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _43: isize; -+ let mut _44: !; -+ scope 21 { ++ scope 19 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _42: isize; ++ let mut _43: !; ++ scope 20 { + } + } + } + } -+ scope 10 (inlined ready::<()>) { -+ let mut _33: std::option::Option<()>; ++ scope 10 (inlined as IntoFuture>::into_future) { + } -+ scope 11 (inlined as IntoFuture>::into_future) { ++ scope 21 (inlined ready::<()>) { ++ let mut _44: std::option::Option<()>; + } + } + } @@ -127,7 +127,7 @@ + StorageLive(_32); + _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _31 = discriminant((*_32)); -+ switchInt(move _31) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; ++ switchInt(move _31) -> [0: bb10, 1: bb9, 3: bb8, otherwise: bb4]; } - bb3: { @@ -154,25 +154,6 @@ } + bb3: { -+ (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>); -+ StorageLive(_12); -+ StorageLive(_13); -+ StorageLive(_14); -+ _14 = (); -+ StorageLive(_33); -+ _33 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _33); -+ StorageDead(_33); -+ StorageDead(_14); -+ _12 = move _13; -+ StorageDead(_13); -+ (((*_32) as variant#3).1: std::future::Ready<()>) = move _12; -+ goto -> bb4; -+ } -+ - bb4: { -- StorageDead(_2); -- return; + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); @@ -189,31 +170,33 @@ + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); -+ StorageLive(_37); -+ StorageLive(_39); -+ StorageLive(_44); ++ StorageLive(_36); ++ StorageLive(_38); ++ StorageLive(_43); ++ StorageLive(_33); + StorageLive(_34); -+ StorageLive(_35); -+ StorageLive(_40); -+ _40 = &raw mut _19; -+ _39 = copy _40 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); -+ StorageDead(_40); -+ _37 = no_retag copy ((*_39).0: &mut std::future::Ready<()>); ++ StorageLive(_39); ++ _39 = &raw mut _19; ++ _38 = copy _39 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); ++ StorageDead(_39); ++ _36 = no_retag copy ((*_38).0: &mut std::future::Ready<()>); ++ StorageLive(_41); ++ _41 = Option::<()>::None; ++ _34 = copy ((*_36).0: std::option::Option<()>); ++ ((*_36).0: std::option::Option<()>) = move _41; ++ StorageDead(_41); + StorageLive(_42); -+ _42 = Option::<()>::None; -+ _35 = copy ((*_37).0: std::option::Option<()>); -+ ((*_37).0: std::option::Option<()>) = move _42; -+ StorageDead(_42); -+ StorageLive(_43); -+ _43 = discriminant(_35); -+ switchInt(move _43) -> [0: bb11, 1: bb12, otherwise: bb5]; - } ++ _42 = discriminant(_34); ++ switchInt(move _42) -> [0: bb11, 1: bb12, otherwise: bb4]; ++ } + -+ bb5: { + bb4: { +- StorageDead(_2); +- return; + unreachable; -+ } + } + -+ bb6: { ++ bb5: { + _17 = const (); + StorageDead(_23); + StorageDead(_21); @@ -230,7 +213,7 @@ + goto -> bb2; + } + -+ bb7: { ++ bb6: { + StorageLive(_26); + _26 = copy ((_18 as Ready).0: ()); + _30 = copy _26; @@ -240,16 +223,16 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; ++ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb7, unwind unreachable]; + } + -+ bb8: { ++ bb7: { + _7 = Poll::<()>::Ready(move _30); + discriminant((*_32)) = 1; + goto -> bb2; + } + -+ bb9: { ++ bb8: { + StorageLive(_12); + StorageLive(_28); + StorageLive(_29); @@ -258,30 +241,47 @@ + _9 = move _28; + StorageDead(_28); + _16 = const (); -+ goto -> bb4; ++ goto -> bb3; ++ } ++ ++ bb9: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb9, unwind unreachable]; + } + + bb10: { -+ assert(const false, "`async fn` resumed after completion") -> [success: bb10, unwind unreachable]; ++ (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_44); ++ _44 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _44); ++ StorageDead(_44); ++ StorageDead(_14); ++ _12 = move _13; ++ StorageDead(_13); ++ (((*_32) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb3; + } + + bb11: { -+ _44 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _43 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { -+ _34 = move ((_35 as Some).0: ()); -+ StorageDead(_43); -+ StorageDead(_35); -+ _18 = Poll::<()>::Ready(move _34); ++ _33 = move ((_34 as Some).0: ()); ++ StorageDead(_42); + StorageDead(_34); -+ StorageDead(_44); -+ StorageDead(_39); -+ StorageDead(_37); ++ _18 = Poll::<()>::Ready(move _33); ++ StorageDead(_33); ++ StorageDead(_43); ++ StorageDead(_38); ++ StorageDead(_36); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); -+ switchInt(move _25) -> [0: bb7, 1: bb6, otherwise: bb5]; ++ switchInt(move _25) -> [0: bb6, 1: bb5, otherwise: bb4]; + } + } + diff --git a/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 18be10c534ec4..f12462a4d2b34 100644 --- a/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -47,42 +47,42 @@ + let _26: (); + scope 9 { + } -+ scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { ++ scope 11 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } -+ scope 13 (inlined as Future>::poll) { -+ let mut _34: (); -+ let mut _35: std::option::Option<()>; -+ let mut _36: &mut std::option::Option<()>; -+ let mut _37: &mut std::future::Ready<()>; -+ let mut _38: &mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _39: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; -+ let mut _40: *mut std::pin::Pin<&mut std::future::Ready<()>>; -+ scope 15 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { -+ let mut _41: &mut &mut std::future::Ready<()>; -+ scope 16 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { ++ scope 12 (inlined as Future>::poll) { ++ let mut _33: (); ++ let mut _34: std::option::Option<()>; ++ let mut _35: &mut std::option::Option<()>; ++ let mut _36: &mut std::future::Ready<()>; ++ let mut _37: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 13 (inlined > as DerefMut>::deref_mut) { ++ let mut _38: *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>>; ++ let mut _39: *mut std::pin::Pin<&mut std::future::Ready<()>>; ++ scope 14 (inlined > as pin::helper::PinDerefMutHelper>::deref_mut) { ++ let mut _40: &mut &mut std::future::Ready<()>; ++ scope 15 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { + } + } + } -+ scope 17 (inlined Option::<()>::take) { -+ let mut _42: std::option::Option<()>; -+ scope 18 (inlined std::mem::replace::>) { -+ scope 19 { ++ scope 16 (inlined Option::<()>::take) { ++ let mut _41: std::option::Option<()>; ++ scope 17 (inlined std::mem::replace::>) { ++ scope 18 { + } + } + } -+ scope 20 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _43: isize; -+ let mut _44: !; -+ scope 21 { ++ scope 19 (inlined #[track_caller] Option::<()>::expect) { ++ let mut _42: isize; ++ let mut _43: !; ++ scope 20 { + } + } + } + } -+ scope 10 (inlined ready::<()>) { -+ let mut _33: std::option::Option<()>; ++ scope 10 (inlined as IntoFuture>::into_future) { + } -+ scope 11 (inlined as IntoFuture>::into_future) { ++ scope 21 (inlined ready::<()>) { ++ let mut _44: std::option::Option<()>; + } + } + } @@ -127,7 +127,7 @@ + StorageLive(_32); + _32 = copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _31 = discriminant((*_32)); -+ switchInt(move _31) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; ++ switchInt(move _31) -> [0: bb15, 1: bb14, 2: bb13, 3: bb12, otherwise: bb6]; } - bb3: { @@ -165,25 +165,6 @@ - StorageDead(_2); - return; + bb5: { -+ (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>); -+ StorageLive(_12); -+ StorageLive(_13); -+ StorageLive(_14); -+ _14 = (); -+ StorageLive(_33); -+ _33 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _33); -+ StorageDead(_33); -+ StorageDead(_14); -+ _12 = move _13; -+ StorageDead(_13); -+ (((*_32) as variant#3).1: std::future::Ready<()>) = move _12; -+ goto -> bb6; - } - -- bb5 (cleanup): { -- drop(_2) -> [return: bb6, unwind terminate(cleanup)]; -+ bb6: { + StorageLive(_17); + StorageLive(_18); + StorageLive(_19); @@ -200,33 +181,35 @@ + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); -+ StorageLive(_37); -+ StorageLive(_39); -+ StorageLive(_44); ++ StorageLive(_36); ++ StorageLive(_38); ++ StorageLive(_43); ++ StorageLive(_33); + StorageLive(_34); -+ StorageLive(_35); -+ StorageLive(_40); -+ _40 = &raw mut _19; -+ _39 = copy _40 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); -+ StorageDead(_40); -+ _37 = no_retag copy ((*_39).0: &mut std::future::Ready<()>); ++ StorageLive(_39); ++ _39 = &raw mut _19; ++ _38 = copy _39 as *mut std::pin::helper::PinHelper<&mut std::future::Ready<()>> (PtrToPtr); ++ StorageDead(_39); ++ _36 = no_retag copy ((*_38).0: &mut std::future::Ready<()>); ++ StorageLive(_41); ++ _41 = Option::<()>::None; ++ _34 = copy ((*_36).0: std::option::Option<()>); ++ ((*_36).0: std::option::Option<()>) = move _41; ++ StorageDead(_41); + StorageLive(_42); -+ _42 = Option::<()>::None; -+ _35 = copy ((*_37).0: std::option::Option<()>); -+ ((*_37).0: std::option::Option<()>) = move _42; -+ StorageDead(_42); -+ StorageLive(_43); -+ _43 = discriminant(_35); -+ switchInt(move _43) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _42 = discriminant(_34); ++ switchInt(move _42) -> [0: bb16, 1: bb17, otherwise: bb6]; + } + +- bb5 (cleanup): { +- drop(_2) -> [return: bb6, unwind terminate(cleanup)]; ++ bb6: { ++ unreachable; } - bb6 (cleanup): { - resume; + bb7: { -+ unreachable; - } -+ -+ bb8: { + _17 = const (); + StorageDead(_23); + StorageDead(_21); @@ -241,9 +224,9 @@ + StorageDead(_29); + discriminant((*_32)) = 3; + goto -> bb4; -+ } + } + -+ bb9: { ++ bb8: { + StorageLive(_26); + _26 = copy ((_18 as Ready).0: ()); + _30 = copy _26; @@ -253,16 +236,16 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; ++ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb9, unwind: bb11]; + } + -+ bb10: { ++ bb9: { + _7 = Poll::<()>::Ready(move _30); + discriminant((*_32)) = 1; + goto -> bb4; + } + -+ bb11 (cleanup): { ++ bb10 (cleanup): { + StorageDead(_22); + StorageDead(_19); + StorageDead(_23); @@ -270,15 +253,15 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; ++ drop((((*_32) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb11, unwind terminate(cleanup)]; + } + -+ bb12 (cleanup): { ++ bb11 (cleanup): { + discriminant((*_32)) = 2; + goto -> bb2; + } + -+ bb13: { ++ bb12: { + StorageLive(_12); + StorageLive(_28); + StorageLive(_29); @@ -287,34 +270,51 @@ + _9 = move _28; + StorageDead(_28); + _16 = const (); -+ goto -> bb6; ++ goto -> bb5; ++ } ++ ++ bb13: { ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb13, unwind: bb2]; + } + + bb14: { -+ assert(const false, "`async fn` resumed after panicking") -> [success: bb14, unwind: bb2]; ++ assert(const false, "`async fn` resumed after completion") -> [success: bb14, unwind: bb2]; + } + + bb15: { -+ assert(const false, "`async fn` resumed after completion") -> [success: bb15, unwind: bb2]; ++ (((*_32) as variant#3).0: ActionPermit<'_, T>) = move ((*_32).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_44); ++ _44 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _44); ++ StorageDead(_44); ++ StorageDead(_14); ++ _12 = move _13; ++ StorageDead(_13); ++ (((*_32) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb5; + } + + bb16: { -+ _44 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _43 = option::expect_failed(const "`Ready` polled after completion") -> bb10; + } + + bb17: { -+ _34 = move ((_35 as Some).0: ()); -+ StorageDead(_43); -+ StorageDead(_35); -+ _18 = Poll::<()>::Ready(move _34); ++ _33 = move ((_34 as Some).0: ()); ++ StorageDead(_42); + StorageDead(_34); -+ StorageDead(_44); -+ StorageDead(_39); -+ StorageDead(_37); ++ _18 = Poll::<()>::Ready(move _33); ++ StorageDead(_33); ++ StorageDead(_43); ++ StorageDead(_38); ++ StorageDead(_36); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); -+ switchInt(move _25) -> [0: bb9, 1: bb8, otherwise: bb7]; ++ switchInt(move _25) -> [0: bb8, 1: bb7, otherwise: bb6]; + } + } + diff --git a/tests/ui/force-inlining/deny-async.stderr b/tests/ui/force-inlining/deny-async.stderr index d6516ed875c21..bd24bc694e8ae 100644 --- a/tests/ui/force-inlining/deny-async.stderr +++ b/tests/ui/force-inlining/deny-async.stderr @@ -20,14 +20,6 @@ LL | pub fn callee_justified() { | = note: incompatible due to: #[rustc_no_mir_inline] -error: `callee` could not be inlined into `async_caller::{closure#0}` but is required to be inlined - --> $DIR/deny-async.rs:22:5 - | -LL | callee(); - | ^^^^^^^^ ...`callee` called here - | - = note: could not be inlined due to: #[rustc_no_mir_inline] - error: `callee_justified` could not be inlined into `async_caller::{closure#0}` but is required to be inlined --> $DIR/deny-async.rs:24:5 | @@ -37,5 +29,13 @@ LL | callee_justified(); = note: could not be inlined due to: #[rustc_no_mir_inline] = note: `callee_justified` is required to be inlined to: the test requires it +error: `callee` could not be inlined into `async_caller::{closure#0}` but is required to be inlined + --> $DIR/deny-async.rs:22:5 + | +LL | callee(); + | ^^^^^^^^ ...`callee` called here + | + = note: could not be inlined due to: #[rustc_no_mir_inline] + error: aborting due to 4 previous errors diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout index 3aca86ae66935..6dbc1ddf2aad9 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.stdout +++ b/tests/ui/rustc_public-ir-print/async-closure.stdout @@ -48,9 +48,15 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo bb0: { _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); _6 = discriminant((*_7)); - switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; + switchInt(move _6) -> [0: bb3, 1: bb1, otherwise: bb2]; } bb1: { + assert(false, `async fn` resumed after completion) -> [success: bb1, unwind unreachable]; + } + bb2: { + unreachable; + } + bb3: { StorageLive(_3); _4 = no_retag ((*_7).0: &i32); _3 = (*_4); @@ -60,12 +66,6 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo discriminant((*_7)) = 1; return; } - bb2: { - assert(false, `async fn` resumed after completion) -> [success: bb2, unwind unreachable]; - } - bb3: { - unreachable; - } } fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}>, _2: &mut Context<'_>) -> Poll<()> { let mut _0: Poll<()>; @@ -80,9 +80,15 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c bb0: { _7 = (_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}); _6 = discriminant((*_7)); - switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; + switchInt(move _6) -> [0: bb3, 1: bb1, otherwise: bb2]; } bb1: { + assert(false, `async fn` resumed after completion) -> [success: bb1, unwind unreachable]; + } + bb2: { + unreachable; + } + bb3: { StorageLive(_3); _4 = no_retag ((*_7).0: &i32); _3 = (*_4); @@ -92,10 +98,4 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c discriminant((*_7)) = 1; return; } - bb2: { - assert(false, `async fn` resumed after completion) -> [success: bb2, unwind unreachable]; - } - bb3: { - unreachable; - } } From f6a5b0b0c07d00673e89ac537ebe850538837428 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 29 May 2026 19:59:02 +0000 Subject: [PATCH 21/28] Review nits. --- compiler/rustc_middle/src/mir/pretty.rs | 2 +- compiler/rustc_mir_transform/src/coroutine.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 1f8efa72ea867..7bae4768bf46a 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -430,8 +430,8 @@ fn write_scope_tree( // Coroutine debuginfo. if let Some(layout) = body.coroutine_layout_raw() { for (field, name) in layout.field_names.iter_enumerated() { + let source_info = layout.field_tys[field].source_info; if let Some(name) = name - && let source_info = layout.field_tys[field].source_info && source_info.scope == parent { let indented_debug_info = diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a00fad4bd599a..87db4550d4f10 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1183,9 +1183,8 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { return false; } - // Unwinds can only start at certain terminators. + // If we don't find an unwinding terminator, the function cannot unwind. body.basic_blocks.iter().any(|block| block.terminator().unwind().is_some()) - // If we didn't find an unwinding terminator, the function cannot unwind. } // Poison the coroutine when it unwinds From ad7cda0ebd0129cf580ba1a7b831715509b3c005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 6 Mar 2026 15:33:37 +0100 Subject: [PATCH 22/28] Use `trait_object_dummy_self` more & heavily fix+update related docs --- .../src/outlives/implicit_infer.rs | 117 +++++++----------- compiler/rustc_middle/src/ty/context.rs | 30 ++++- compiler/rustc_middle/src/ty/print/pretty.rs | 6 +- compiler/rustc_symbol_mangling/src/v0.rs | 8 +- compiler/rustc_type_ir/src/predicate.rs | 31 ++--- 5 files changed, 93 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 23e6b2281b370..174f702dcd0c4 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -8,11 +8,7 @@ use tracing::debug; use super::explicit::ExplicitPredicatesMap; use super::utils::*; -/// Infer predicates for the items in the crate. -/// -/// `global_inferred_outlives`: this is initially the empty map that -/// was generated by walking the items in the crate. This will -/// now be filled with inferred predicates. +/// Infer outlives-predicates for the items in the local crate. pub(super) fn infer_predicates( tcx: TyCtxt<'_>, ) -> FxIndexMap>> { @@ -154,7 +150,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( args, required_predicates, explicit_map, - None, + IgnorePredicatesReferencingSelf::No, ); } @@ -175,7 +171,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( args, required_predicates, explicit_map, - None, + IgnorePredicatesReferencingSelf::No, ); } @@ -183,23 +179,27 @@ fn insert_required_predicates_to_be_wf<'tcx>( // This corresponds to `dyn Trait<..>`. In this case, we should // use the explicit predicates as well. debug!("Dynamic"); - if let Some(ex_trait_ref) = obj.principal() { - // Here, we are passing the type `usize` as a - // placeholder value with the function - // `with_self_ty`, since there is no concrete type - // `Self` for a `dyn Trait` at this - // stage. Therefore when checking explicit - // predicates in `check_explicit_predicates` we - // need to ignore checking the explicit_map for - // Self type. - let args = ex_trait_ref.with_self_ty(tcx, tcx.types.usize).skip_binder().args; + if let Some(trait_ref) = obj.principal() { + let args = trait_ref + .with_self_ty(tcx, tcx.types.trait_object_dummy_self) + .skip_binder() + .args; + // We skip predicates that reference the `Self` type parameter since we don't + // want to leak the dummy Self to the predicates map. + // + // While filtering out bounds like `Self: 'a` as in `trait Trait<'a, T>: 'a {}` + // doesn't matter since they can't affect the lifetime / type parameters anyway, + // for bounds like `Self::AssocTy: 'b` which we of course currently also ignore + // (see also #54467) it might conceivably be better to extract the binding + // `AssocTy = U` from the trait object type (which must exist) and thus infer + // an outlives requirement that `U: 'b`. check_explicit_predicates( tcx, - ex_trait_ref.skip_binder().def_id, + trait_ref.def_id(), args, required_predicates, explicit_map, - Some(tcx.types.self_param), + IgnorePredicatesReferencingSelf::Yes, ); } } @@ -215,7 +215,7 @@ fn insert_required_predicates_to_be_wf<'tcx>( args, required_predicates, explicit_map, - None, + IgnorePredicatesReferencingSelf::No, ); } @@ -244,77 +244,44 @@ fn insert_required_predicates_to_be_wf<'tcx>( /// will give us `U: 'static` and `U: Outer`. The latter we /// can ignore, but we will want to process `U: 'static`, /// applying the instantiation as above. +#[tracing::instrument(level = "debug", skip(tcx))] fn check_explicit_predicates<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, args: &[GenericArg<'tcx>], required_predicates: &mut RequiredPredicates<'tcx>, explicit_map: &mut ExplicitPredicatesMap<'tcx>, - ignored_self_ty: Option>, + ignore_preds_refing_self: IgnorePredicatesReferencingSelf, ) { - debug!( - "check_explicit_predicates(def_id={:?}, \ - args={:?}, \ - explicit_map={:?}, \ - required_predicates={:?}, \ - ignored_self_ty={:?})", - def_id, args, explicit_map, required_predicates, ignored_self_ty, - ); let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id); - for (outlives_predicate, &span) in explicit_predicates.as_ref().skip_binder() { - debug!("outlives_predicate = {outlives_predicate:?}"); - - // Careful: If we are inferring the effects of a `dyn Trait<..>` - // type, then when we look up the predicates for `Trait`, - // we may find some that reference `Self`. e.g., perhaps the - // definition of `Trait` was: - // - // ``` - // trait Trait<'a, T> where Self: 'a { .. } - // ``` - // - // we want to ignore such predicates here, because - // there is no type parameter for them to affect. Consider - // a struct containing `dyn Trait`: - // - // ``` - // struct MyStruct<'x, X> { field: Box> } - // ``` - // - // The `where Self: 'a` predicate refers to the *existential, hidden type* - // that is represented by the `dyn Trait`, not to the `X` type parameter - // (or any other generic parameter) declared on `MyStruct`. - // - // Note that we do this check for self **before** applying `args`. In the - // case that `args` come from a `dyn Trait` type, our caller will have - // included `Self = usize` as the value for `Self`. If we were - // to apply the args, and not filter this predicate, we might then falsely - // conclude that e.g., `X: 'x` was a reasonable inferred requirement. - // - // Another similar case is where we have an inferred - // requirement like `::Foo: 'b`. We presently - // ignore such requirements as well (cc #54467)-- though - // conceivably it might be better if we could extract the `Foo - // = X` binding from the object type (there must be such a - // binding) and thus infer an outlives requirement that `X: - // 'b`. - if let Some(self_ty) = ignored_self_ty - && let GenericArgKind::Type(ty) = outlives_predicate.0.kind() - && ty.walk().any(|arg| arg == self_ty.into()) + for (&predicate @ ty::OutlivesPredicate(arg, _), &span) in + explicit_predicates.as_ref().skip_binder() + { + debug!(?predicate); + + if let IgnorePredicatesReferencingSelf::Yes = ignore_preds_refing_self + && arg.walk().any(|arg| arg == tcx.types.self_param.into()) { - debug!("skipping self ty = {ty:?}"); + debug!("ignoring predicate since it references `Self`"); continue; } - let predicate = - explicit_predicates.rebind(*outlives_predicate).instantiate(tcx, args).skip_norm_wip(); - debug!("predicate = {predicate:?}"); - insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); + let predicate @ ty::OutlivesPredicate(arg, region) = + explicit_predicates.rebind(predicate).instantiate(tcx, args).skip_norm_wip(); + debug!(?predicate); + + insert_outlives_predicate(tcx, arg, region, span, required_predicates); } } -/// Check the inferred predicates declared on the type. +#[derive(Debug)] +enum IgnorePredicatesReferencingSelf { + Yes, + No, +} + +/// Check the inferred predicates of the type. /// /// ### Example /// diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e756277b92d76..d14e10c5014ad 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -324,10 +324,32 @@ pub struct CommonTypes<'tcx> { pub never: Ty<'tcx>, pub self_param: Ty<'tcx>, - /// Dummy type used for the `Self` of a `TraitRef` created for converting - /// a trait object, and which gets removed in `ExistentialTraitRef`. - /// This type must not appear anywhere in other converted types. - /// `Infer(ty::FreshTy(0))` does the job. + /// A dummy type that can be used as the self type of trait object types outside of + /// [`ty::ExistentialTraitRef`], [`ty::ExistentialProjection`], etc. + /// + /// This is most useful or even necessary when you want to manipulate existential predicates + /// together with normal predicates or if you want to pass them to an API that only expects + /// normal predicates. + /// + /// Indeed, you can sometimes use the trait object type itself as the self type instead of this + /// dummy type. However, that's not always correct: For example, if said trait object type can + /// also appear "naturally" in whatever type system entity you're working with (like predicates) + /// but you still need to be able to identify the erased self type later on. + /// That's when this dummy type comes in handy. + /// + /// HIR ty lowering guarantees / has to guarantee that this dummy type doesn't appear in the + /// lowered types, so you can "freely" use it (see warning below). + /// + ///
+ /// + /// Under the hood, this type is just `ty::Infer(ty::FreshTy(0))`. Consequently, you must be + /// sure that fresh types cannot appear by other means in whatever type system entity you're + /// working with. + /// + /// Keep uses of this dummy type as local as possible and try not to leak it to subsequent + /// passes! + /// + ///
pub trait_object_dummy_self: Ty<'tcx>, /// Pre-interned `Infer(ty::TyVar(n))` for small values of `n`. diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index daead99b977c1..026b6a09a8bb7 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3265,9 +3265,9 @@ define_print! { } ty::ExistentialTraitRef<'tcx> { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = Ty::new_fresh(p.tcx(), 0); - let trait_ref = self.with_self_ty(p.tcx(), dummy_self); + // Dummy Self is safe to use as it can't appear in generic param defaults which is important + // later on for correctly eliding generic args that coincide with their default. + let trait_ref = self.with_self_ty(p.tcx(), p.tcx().types.trait_object_dummy_self); trait_ref.print_only_trait_path().print(p)?; } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 61c1c83c3f8f6..a9573cdf1bf75 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -646,9 +646,11 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // could have different bound vars *anyways*. match predicate.as_ref().skip_binder() { ty::ExistentialPredicate::Trait(trait_ref) => { - // Use a type that can't appear in defaults of type parameters. - let dummy_self = Ty::new_fresh(p.tcx, 0); - let trait_ref = trait_ref.with_self_ty(p.tcx, dummy_self); + // Dummy Self is safe to use as it can't appear in generic param defaults + // which is important later on for correctly eliding generic args that + // coincide with their default. + let trait_ref = + trait_ref.with_self_ty(p.tcx, p.tcx.types.trait_object_dummy_self); p.print_def_path(trait_ref.def_id, trait_ref.args)?; } ty::ExistentialPredicate::Projection(projection) => { diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 301cf7dbf1087..e220676aef29c 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -388,14 +388,13 @@ impl ty::Binder> { } } -/// An existential reference to a trait, where `Self` is erased. +/// An existential reference to a trait where the self type `Self` is erased. /// -/// For example, the trait object `Trait<'a, 'b, X, Y>` is: +/// For example, the trait object type `Trait<'a, T, N>` can be understood as: /// ```ignore (illustrative) -/// exists T. T: Trait<'a, 'b, X, Y> +/// exists X: Trait<'a, T, N> /// ``` -/// The generic parameters don't include the erased `Self`, only trait -/// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). +/// The generic arguments don't include the erased self type (so it's only `['a, T, N]`). #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] #[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( @@ -438,13 +437,18 @@ impl ExistentialTraitRef { } } - /// Object types don't have a self type specified. Therefore, when - /// we convert the principal trait-ref into a normal trait-ref, - /// you must give *some* self type. A common choice is `mk_err()` - /// or some placeholder type. + /// Convert the *existential* trait ref into a normal one by providing a self type. + /// + /// Existential trait refs don't contain a self type, it's erased. + /// Therefore, you must specify *some* self type to perform the conversion. + /// A common choice is the trait object type itself or some kind of dummy type. pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> TraitRef { + // FIXME(#157122): This assertion was accidentally commented out in refactoring PR #53816 + // back in 2018 but nowadays it can actually trigger. Either remove this + // comment entirely if the assertion is incorrect or uncomment it and fix + // the fallout! // otherwise the escaping vars would be captured by the binder - // debug_assert!(!self_ty.has_escaping_bound_vars()); + //debug_assert!(!self_ty.has_escaping_bound_vars()); TraitRef::new(interner, self.def_id, [self_ty.into()].into_iter().chain(self.args.iter())) } @@ -455,10 +459,9 @@ impl ty::Binder> { self.skip_binder().def_id } - /// Object types don't have a self type specified. Therefore, when - /// we convert the principal trait-ref into a normal trait-ref, - /// you must give *some* self type. A common choice is `mk_err()` - /// or some placeholder type. + /// Convert the *existential* polymorphic trait ref into a normal one by providing a self type. + /// + /// See also [`ExistentialTraitRef::with_self_ty`]. pub fn with_self_ty(&self, cx: I, self_ty: I::Ty) -> ty::Binder> { self.map_bound(|trait_ref| trait_ref.with_self_ty(cx, self_ty)) } From 57393b76a774b6ee9b1e09e68dbb9038a8ee9010 Mon Sep 17 00:00:00 2001 From: Andrii Anoshyn Date: Fri, 29 May 2026 23:38:26 +0300 Subject: [PATCH 23/28] Label irrefutable while let pattern diagnostic --- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../coercion/coerce-loop-issue-122561.stderr | 20 ++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 9a20d47bda9af..2b47b9fc9f093 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1897,7 +1897,7 @@ impl<'tcx> CoerceMany<'tcx> { if loop_src == hir::LoopSource::While && let Some(pat) = irrefutable_if_let_expr(block) { - err.span_note( + err.span_label( pat.span, "this pattern always matches, consider using `loop` instead", ); diff --git a/tests/ui/coercion/coerce-loop-issue-122561.stderr b/tests/ui/coercion/coerce-loop-issue-122561.stderr index 1051823e6c01f..da69098dfe04a 100644 --- a/tests/ui/coercion/coerce-loop-issue-122561.stderr +++ b/tests/ui/coercion/coerce-loop-issue-122561.stderr @@ -173,7 +173,10 @@ error[E0308]: mismatched types | LL | fn while_let_binding() -> bool { | ---- expected `bool` because of return type -LL | / while let x = false { +LL | while let x = false { + | ^ - this pattern always matches, consider using `loop` instead + | _____| + | | LL | | LL | | if x { LL | | return true; @@ -182,11 +185,6 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` -note: this pattern always matches, consider using `loop` instead - --> $DIR/coerce-loop-issue-122561.rs:73:15 - | -LL | while let x = false { - | ^ help: consider returning a value here | LL ~ } @@ -198,7 +196,10 @@ error[E0308]: mismatched types | LL | fn while_let_tuple() -> bool { | ---- expected `bool` because of return type -LL | / while let (x, _) = (false, true) { +LL | while let (x, _) = (false, true) { + | ^ ------ this pattern always matches, consider using `loop` instead + | _____| + | | LL | | LL | | if x { LL | | return true; @@ -207,11 +208,6 @@ LL | | } | |_____^ expected `bool`, found `()` | = note: `while` loops evaluate to unit type `()` -note: this pattern always matches, consider using `loop` instead - --> $DIR/coerce-loop-issue-122561.rs:82:15 - | -LL | while let (x, _) = (false, true) { - | ^^^^^^ help: consider returning a value here | LL ~ } From f7cf361f3cdda167130295789b0a38350aa35a5d Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 29 May 2026 21:57:45 -0700 Subject: [PATCH 24/28] =?UTF-8?q?compiler:=20`ops::RangeInclusive`=20?= =?UTF-8?q?=E2=86=92=20`range::RangeInclusive`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type's been stable for over 6 weeks now, so let's use it! It's better for cases like this one where it's stored in a data structure. Probably won't be materially faster, but does make the variant slightly smaller and lets some more things be `Copy`. --- compiler/rustc_abi/src/layout.rs | 9 ++++++--- compiler/rustc_abi/src/lib.rs | 5 +++-- compiler/rustc_codegen_cranelift/src/discriminant.rs | 8 ++++---- .../src/debuginfo/metadata/enums/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/mir/place.rs | 2 +- .../rustc_const_eval/src/interpret/discriminant.rs | 10 +++++----- compiler/rustc_data_structures/src/stable_hash.rs | 6 +++--- compiler/rustc_middle/src/ty/layout.rs | 2 +- compiler/rustc_public/src/unstable/convert/mod.rs | 8 ++++---- compiler/rustc_transmute/src/layout/tree.rs | 8 ++++---- 11 files changed, 36 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 8ab80c77ff9a5..733a1956231b1 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,6 +1,7 @@ use std::collections::BTreeSet; use std::fmt::{self, Write}; use std::ops::Deref; +use std::range::RangeInclusive; use std::{cmp, iter}; use rustc_hashes::Hash64; @@ -631,11 +632,13 @@ impl LayoutCalculator { let all_indices = variants.indices(); let needs_disc = |index: VariantIdx| index != largest_variant_index && !absent(&variants[index]); - let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap() - ..=all_indices.rev().find(|v| needs_disc(*v)).unwrap(); + let niche_variants = RangeInclusive { + start: all_indices.clone().find(|v| needs_disc(*v)).unwrap(), + last: all_indices.rev().find(|v| needs_disc(*v)).unwrap(), + }; let count = - (niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1; + (niche_variants.last.index() as u128 - niche_variants.start.index() as u128) + 1; // Use the largest niche in the largest variant. let niche = variant_layouts[largest_variant_index].largest_niche?; diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5e1a95d620f2a..166c8bea6f354 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -40,7 +40,8 @@ use std::fmt; #[cfg(feature = "nightly")] use std::iter::Step; use std::num::{NonZeroUsize, ParseIntError}; -use std::ops::{Add, AddAssign, Deref, Mul, RangeFull, RangeInclusive, Sub}; +use std::ops::{Add, AddAssign, Deref, Mul, RangeFull, Sub}; +use std::range::RangeInclusive; use std::str::FromStr; use bitflags::bitflags; @@ -1958,7 +1959,7 @@ pub enum Variants { } // NOTE: This struct is generic over the VariantIdx for rust-analyzer usage. -#[derive(PartialEq, Eq, Hash, Clone, Debug)] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] #[cfg_attr(feature = "nightly", derive(StableHash))] pub enum TagEncoding { /// The tag directly stores the discriminant, but possibly with a smaller layout diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs index a08b0e0cbfc59..8818e8634952e 100644 --- a/compiler/rustc_codegen_cranelift/src/discriminant.rs +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -55,7 +55,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>( if variant_index != untagged_variant { let niche = place.place_field(fx, tag_field); let niche_type = fx.clif_type(niche.layout().ty).unwrap(); - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = variant_index.as_u32() - niche_variants.start.as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); let niche_value = match niche_type { types::I128 => { @@ -133,7 +133,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( dest.write_cvalue(fx, res); } TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { - let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + let relative_max = niche_variants.last.as_u32() - niche_variants.start.as_u32(); // We have a subrange `niche_start..=niche_end` inside `range`. // If the value of the tag is inside this subrange, it's a @@ -162,7 +162,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( // } let is_niche = codegen_icmp_imm(fx, IntCC::Equal, tag, niche_start as i128); let tagged_discr = - fx.bcx.ins().iconst(cast_to, niche_variants.start().as_u32() as i64); + fx.bcx.ins().iconst(cast_to, niche_variants.start.as_u32() as i64); (is_niche, tagged_discr, 0) } else { // The special cases don't apply, so we'll have to go with @@ -184,7 +184,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>( relative_discr, i128::from(relative_max), ); - (is_niche, cast_tag, niche_variants.start().as_u32() as u128) + (is_niche, cast_tag, niche_variants.start.as_u32() as u128) }; let tagged_discr = if delta == 0 { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index f7fe0eb8cb3b8..86060f068eaf0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -426,7 +426,7 @@ fn compute_discriminant_value<'ll, 'tcx>( DiscrResult::Range(min, max) } else { let value = (variant_index.as_u32() as u128) - .wrapping_sub(niche_variants.start().as_u32() as u128) + .wrapping_sub(niche_variants.start.as_u32() as u128) .wrapping_add(niche_start); let value = tag.size(cx).truncate(value); DiscrResult::Value(value) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 83fce5a5c8deb..c0c71edd4d905 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -494,7 +494,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { // `layout_sanity_check` ensures that we only get here for cases where the discriminant // value and the variant index match, since that's all `Niche` can encode. - let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + let relative_max = niche_variants.last.as_u32() - niche_variants.start.as_u32(); let niche_start_const = bx.cx().const_uint_big(tag_llty, niche_start); // We have a subrange `niche_start..=niche_end` inside `range`. @@ -523,7 +523,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { // } let is_niche = bx.icmp(IntPredicate::IntEQ, tag, niche_start_const); let tagged_discr = - bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64); + bx.cx().const_uint(cast_to, niche_variants.start.as_u32() as u64); (is_niche, tagged_discr, 0) } else { // Thanks to parameter attributes and load metadata, LLVM already knows @@ -549,7 +549,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { { let impossible = niche_start .wrapping_add(u128::from(untagged_variant.as_u32())) - .wrapping_sub(u128::from(niche_variants.start().as_u32())); + .wrapping_sub(u128::from(niche_variants.start.as_u32())); let impossible = bx.cx().const_uint_big(tag_llty, impossible); let ne = bx.icmp(IntPredicate::IntNE, tag, impossible); bx.assume(ne); @@ -633,7 +633,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { ) }; - (is_niche, cast_tag, niche_variants.start().as_u32() as u128) + (is_niche, cast_tag, niche_variants.start.as_u32() as u128) }; let tagged_discr = if delta == 0 { diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 53518fd816f31..ebc993f14ea03 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -500,7 +500,7 @@ pub(super) fn codegen_tag_value<'tcx, V>( // around the `niche`'s type. // The easiest way to do that is to do wrapping arithmetic on `u128` and then // masking off any extra bits that occur because we did the arithmetic with too many bits. - let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = variant_index.as_u32() - niche_variants.start.as_u32(); let niche_value = (niche_value as u128).wrapping_add(niche_start); let niche_value = niche_value & niche_layout.size.unsigned_int_max(); diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index c50d9db8be639..a1776c6ba3d13 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -142,8 +142,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let tag_val = tag_val.to_scalar(); // Compute the variant this niche value/"tag" corresponds to. With niche layout, // discriminant (encoded in niche/tag) and variant index are the same. - let variants_start = niche_variants.start().as_u32(); - let variants_end = niche_variants.end().as_u32(); + let variants_start = niche_variants.start.as_u32(); + let variants_last = niche_variants.last.as_u32(); let variant = match tag_val.try_to_scalar_int() { Err(dbg_val) => { // So this is a pointer then, and casting to an int failed. @@ -151,7 +151,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // The niche must be just 0, and the ptr not null, then we know this is // okay. Everything else, we conservatively reject. let ptr_valid = niche_start == 0 - && variants_start == variants_end + && variants_start == variants_last && !self.scalar_may_be_null(tag_val)?; if !ptr_valid { throw_ub!(InvalidTag(dbg_val)) @@ -169,7 +169,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let variant_index_relative = variant_index_relative_val.to_scalar().to_bits(tag_val.layout.size)?; // Check if this is in the range that indicates an actual discriminant. - if variant_index_relative <= u128::from(variants_end - variants_start) { + if variant_index_relative <= u128::from(variants_last - variants_start) { let variant_index_relative = u32::try_from(variant_index_relative) .expect("we checked that this fits into a u32"); // Then computing the absolute variant idx should not overflow any more. @@ -309,7 +309,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { niche_variants.contains(&variant_index), "invalid variant index for this enum" ); - let variants_start = niche_variants.start().as_u32(); + let variants_start = niche_variants.start.as_u32(); let variant_index_relative = variant_index.as_u32().strict_sub(variants_start); // We need to use machine arithmetic when taking into account `niche_start`: // tag_val = variant_index_relative + niche_start_val diff --git a/compiler/rustc_data_structures/src/stable_hash.rs b/compiler/rustc_data_structures/src/stable_hash.rs index 07a5e06c6bdde..79d45121226f7 100644 --- a/compiler/rustc_data_structures/src/stable_hash.rs +++ b/compiler/rustc_data_structures/src/stable_hash.rs @@ -527,14 +527,14 @@ impl StableHash for ::std::mem::Discriminant { } } -impl StableHash for ::std::ops::RangeInclusive +impl StableHash for ::std::range::RangeInclusive where T: StableHash, { #[inline] fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - self.start().stable_hash(hcx, hasher); - self.end().stable_hash(hcx, hasher); + self.start.stable_hash(hcx, hasher); + self.last.stable_hash(hcx, hasher); } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 168aa5e9b0219..98f2a6a2603e7 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1097,7 +1097,7 @@ where } else { VariantIdx::from_u32(0) }; - assert_eq!(tagged_variant, *niche_variants.start()); + assert_eq!(tagged_variant, niche_variants.start); if *niche_start == 0 { // The other variant is encoded as "null", so we can recurse searching for // a pointer here. This relies on the fact that the codegen backend diff --git a/compiler/rustc_public/src/unstable/convert/mod.rs b/compiler/rustc_public/src/unstable/convert/mod.rs index f20406631e3ff..1ec0b9a30534d 100644 --- a/compiler/rustc_public/src/unstable/convert/mod.rs +++ b/compiler/rustc_public/src/unstable/convert/mod.rs @@ -6,7 +6,7 @@ //! For contributors, please make sure to avoid calling rustc's internal functions and queries. //! These should be done via `rustc_public_bridge` APIs, but it's possible to access ADT fields directly. -use std::ops::RangeInclusive; +use std::{ops, range}; use rustc_public_bridge::Tables; use rustc_public_bridge::context::CompilerCtxt; @@ -95,16 +95,16 @@ where } } -impl<'tcx, T> Stable<'tcx> for RangeInclusive +impl<'tcx, T> Stable<'tcx> for range::RangeInclusive where T: Stable<'tcx>, { - type T = RangeInclusive; + type T = ops::RangeInclusive; fn stable<'cx>( &self, tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - RangeInclusive::new(self.start().stable(tables, cx), self.end().stable(tables, cx)) + ops::RangeInclusive::new(self.start.stable(tables, cx), self.last.stable(tables, cx)) } } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index b63baa71a312f..d7aeb6b06b82f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -421,12 +421,12 @@ pub(crate) mod rustc { ) }; - match layout.variants() { + match *layout.variants() { Variants::Empty => Ok(Self::uninhabited()), Variants::Single { index } => { // `Variants::Single` on enums with variants denotes that // the enum delegates its layout to the variant at `index`. - layout_of_variant(*index, None) + layout_of_variant(index, None) } Variants::Multiple { tag: _, tag_encoding, tag_field, .. } => { // `Variants::Multiple` denotes an enum with multiple @@ -435,12 +435,12 @@ pub(crate) mod rustc { // For enums (but not coroutines), the tag field is // currently always the first field of the layout. - assert_eq!(*tag_field, FieldIdx::ZERO); + assert_eq!(tag_field, FieldIdx::ZERO); let variants = def.discriminants(cx.tcx()).try_fold( Self::uninhabited(), |variants, (idx, _discriminant)| { - let variant = layout_of_variant(idx, Some(tag_encoding.clone()))?; + let variant = layout_of_variant(idx, Some(tag_encoding))?; Result::::Ok(variants.or(variant)) }, )?; From e83d369a29f225ec7470e616c90b9ee0d923a96b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 29 May 2026 10:24:51 +0200 Subject: [PATCH 25/28] Use default field values to avoid some churn --- compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/ty/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index a9e547b5862a6..a018951a94770 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -37,6 +37,7 @@ #![feature(core_intrinsics)] #![feature(debug_closure_helpers)] #![feature(decl_macro)] +#![feature(default_field_values)] #![feature(deref_patterns)] #![feature(discriminant_kind)] #![feature(extern_types)] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 28e18331d7127..9e3c9b18626e0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -205,7 +205,7 @@ pub struct ResolverGlobalCtxt { #[derive(Debug)] pub struct PerOwnerResolverData { - pub node_id_to_def_id: NodeMap, + pub node_id_to_def_id: NodeMap = Default::default(), /// The id of the owner pub id: ast::NodeId, /// The `DefId` of the owner, can't be found in `node_id_to_def_id`. @@ -214,7 +214,7 @@ pub struct PerOwnerResolverData { impl PerOwnerResolverData { pub fn new(id: ast::NodeId, def_id: LocalDefId) -> PerOwnerResolverData { - PerOwnerResolverData { node_id_to_def_id: Default::default(), id, def_id } + PerOwnerResolverData { id, def_id, .. } } } From fec454558b4974bb237f5513ab193f9f4fcef439 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 27 May 2026 13:22:12 +0200 Subject: [PATCH 26/28] Track `lifetime_elision_allowed` per owner, and discard fn ptr and `Fn()` syntax registrations of it, they are never used --- compiler/rustc_ast_lowering/src/lib.rs | 8 +++----- compiler/rustc_middle/src/ty/mod.rs | 7 ++++--- compiler/rustc_resolve/src/late.rs | 4 +++- compiler/rustc_resolve/src/lib.rs | 3 --- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9e855f305ae84..3cb36792bfeb7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -321,10 +321,6 @@ impl<'tcx> ResolverAstLowering<'tcx> { fn owner_def_id(&self, id: NodeId) -> LocalDefId { self.owners[&id].def_id } - - fn lifetime_elision_allowed(&self, id: NodeId) -> bool { - self.lifetime_elision_allowed.contains(&id) - } } /// How relaxed bounds `?Trait` should be treated. @@ -1853,7 +1849,9 @@ impl<'hir> LoweringContext<'_, 'hir> { _ => hir::ImplicitSelfKind::None, } })) - .set_lifetime_elision_allowed(self.resolver.lifetime_elision_allowed(fn_node_id)) + .set_lifetime_elision_allowed( + self.owner.id == fn_node_id && self.owner.lifetime_elision_allowed, + ) .set_c_variadic(c_variadic); self.arena.alloc(hir::FnDecl { inputs, output, fn_decl_kind }) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9e3c9b18626e0..d8626567aef67 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -31,7 +31,7 @@ use rustc_ast as ast; use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::steal::Steal; @@ -206,6 +206,9 @@ pub struct ResolverGlobalCtxt { #[derive(Debug)] pub struct PerOwnerResolverData { pub node_id_to_def_id: NodeMap = Default::default(), + /// Whether lifetime elision was successful. + pub lifetime_elision_allowed: bool = false, + /// The id of the owner pub id: ast::NodeId, /// The `DefId` of the owner, can't be found in `node_id_to_def_id`. @@ -238,8 +241,6 @@ pub struct ResolverAstLowering<'tcx> { pub owners: NodeMap, pub trait_map: NodeMap<&'tcx [hir::TraitCandidate<'tcx>]>, - /// List functions and methods for which lifetime elision was successful. - pub lifetime_elision_allowed: FxHashSet, /// Lints that were emitted by the resolver and early lints. pub lint_buffer: Steal, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index d7ff0ebb3c993..ae6043530c919 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2434,7 +2434,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let outer_failures = take(&mut this.diag_metadata.current_elision_failures); let output_rib = if let Ok(res) = elision_lifetime.as_ref() { - this.r.lifetime_elision_allowed.insert(fn_id); + if fn_id == this.r.current_owner.id { + this.r.current_owner.lifetime_elision_allowed = true; + } LifetimeRibKind::Elided(*res) } else { LifetimeRibKind::ElisionFailure diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 0889a29571292..542211f060572 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1522,8 +1522,6 @@ pub struct Resolver<'ra, 'tcx> { /// they are declared in the static array generated by proc_macro_harness. proc_macros: Vec = Vec::new(), confused_type_with_std_module: FxIndexMap, - /// Whether lifetime elision was successful. - lifetime_elision_allowed: FxHashSet = default::fx_hash_set(), /// Names of items that were stripped out via cfg with their corresponding cfg meta item. stripped_cfg_items: Vec> = Vec::new(), @@ -2011,7 +2009,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { next_node_id: self.next_node_id, owners: self.owners, trait_map: self.trait_map, - lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), delegation_infos: self.delegation_infos, disambiguators, From 5b37028e31515790b4c45056ed07a03a58018304 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 28 May 2026 10:23:14 +0200 Subject: [PATCH 27/28] Track `label_res_map` per-owner --- compiler/rustc_ast_lowering/src/expr.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 5 ----- compiler/rustc_middle/src/ty/mod.rs | 10 ++++++++-- compiler/rustc_resolve/src/late.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 3 --- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 87e1d9aa7a114..ce20aaf4e0276 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1549,7 +1549,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_loop_destination(&mut self, destination: Option<(NodeId, Label)>) -> hir::Destination { let target_id = match destination { Some((id, _)) => { - if let Some(loop_id) = self.resolver.get_label_res(id) { + if let Some(loop_id) = self.owner.get_label_res(id) { let local_id = self.ident_and_label_to_local_id[&loop_id]; let loop_hir_id = HirId { owner: self.current_hir_id_owner, local_id }; Ok(loop_hir_id) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 3cb36792bfeb7..f19a1fc95a495 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -293,11 +293,6 @@ impl<'tcx> ResolverAstLowering<'tcx> { self.import_res_map.get(&id).copied().unwrap_or_default() } - /// Obtains resolution for a label with the given `NodeId`. - fn get_label_res(&self, id: NodeId) -> Option { - self.label_res_map.get(&id).copied() - } - /// Obtains resolution for a lifetime with the given `NodeId`. fn get_lifetime_res(&self, id: NodeId) -> Option { self.lifetimes_res_map.get(&id).copied() diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d8626567aef67..300da2a0828e4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -208,6 +208,9 @@ pub struct PerOwnerResolverData { pub node_id_to_def_id: NodeMap = Default::default(), /// Whether lifetime elision was successful. pub lifetime_elision_allowed: bool = false, + /// Resolutions for labels. + /// Maps from NodeId of the break/continue expression to the NodeId of their corresponding blocks or loops. + pub label_res_map: NodeMap = Default::default(), /// The id of the owner pub id: ast::NodeId, @@ -219,6 +222,11 @@ impl PerOwnerResolverData { pub fn new(id: ast::NodeId, def_id: LocalDefId) -> PerOwnerResolverData { PerOwnerResolverData { id, def_id, .. } } + + /// Obtains resolution for a label with the given `NodeId`. + pub fn get_label_res(&self, id: ast::NodeId) -> Option { + self.label_res_map.get(&id).copied() + } } /// Resolutions that should only be used for lowering. @@ -229,8 +237,6 @@ pub struct ResolverAstLowering<'tcx> { pub partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. pub import_res_map: NodeMap>>>, - /// Resolutions for labels (node IDs of their corresponding blocks or loops). - pub label_res_map: NodeMap, /// Resolutions for lifetimes. pub lifetimes_res_map: NodeMap, /// Lifetime parameters that lowering will have to introduce. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ae6043530c919..7030859916fbe 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5199,7 +5199,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match self.resolve_label(label.ident) { Ok((node_id, _)) => { // Since this res is a label, it is never read. - self.r.label_res_map.insert(expr.id, node_id); + self.r.current_owner.label_res_map.insert(expr.id, node_id); self.diag_metadata.unused_labels.swap_remove(&node_id); } Err(error) => { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 542211f060572..7c80cfa878b89 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1369,8 +1369,6 @@ pub struct Resolver<'ra, 'tcx> { import_res_map: NodeMap>> = Default::default(), /// An import will be inserted into this map if it has been used. import_use_map: FxHashMap, Used> = default::fx_hash_map(), - /// Resolutions for labels (node IDs of their corresponding blocks or loops). - label_res_map: NodeMap = Default::default(), /// Resolutions for lifetimes. lifetimes_res_map: NodeMap = Default::default(), /// Lifetime parameters that lowering will have to introduce. @@ -2003,7 +2001,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ast_lowering = ty::ResolverAstLowering { partial_res_map: self.partial_res_map, import_res_map: self.import_res_map, - label_res_map: self.label_res_map, lifetimes_res_map: self.lifetimes_res_map, extra_lifetime_params_map: self.extra_lifetime_params_map, next_node_id: self.next_node_id, From b7ea5d8ab76b32ed111b24fa4bfdbc45a8cc7bc3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 28 May 2026 10:34:07 +0200 Subject: [PATCH 28/28] Track `lifetimes_res_map` per owner --- compiler/rustc_ast_lowering/src/lib.rs | 22 ++++++++-------------- compiler/rustc_ast_lowering/src/path.rs | 4 ++-- compiler/rustc_middle/src/ty/mod.rs | 9 +++++++-- compiler/rustc_resolve/src/late.rs | 6 +++--- compiler/rustc_resolve/src/lib.rs | 6 +----- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index f19a1fc95a495..4045f08c053ed 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -293,11 +293,6 @@ impl<'tcx> ResolverAstLowering<'tcx> { self.import_res_map.get(&id).copied().unwrap_or_default() } - /// Obtains resolution for a lifetime with the given `NodeId`. - fn get_lifetime_res(&self, id: NodeId) -> Option { - self.lifetimes_res_map.get(&id).copied() - } - /// Obtain the list of lifetimes parameters to add to an item. /// /// Extra lifetime parameters should only be added in places that can appear @@ -1603,7 +1598,7 @@ impl<'hir> LoweringContext<'_, 'hir> { None => { let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) = - self.resolver.get_lifetime_res(t.id) + self.owner.get_lifetime_res(t.id) { assert_eq!(start.plus(1), end); start @@ -2012,7 +2007,7 @@ impl<'hir> LoweringContext<'_, 'hir> { source: LifetimeSource, syntax: LifetimeSyntax, ) -> &'hir hir::Lifetime { - let res = if let Some(res) = self.resolver.get_lifetime_res(id) { + let res = if let Some(res) = self.owner.get_lifetime_res(id) { match res { LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param), LifetimeRes::Fresh { param, .. } => { @@ -2098,13 +2093,12 @@ impl<'hir> LoweringContext<'_, 'hir> { // AST resolution emitted an error on those parameters, so we lower them using // `ParamName::Error`. let ident = self.lower_ident(param.ident); - let param_name = if let Some(LifetimeRes::Error(..)) = - self.resolver.get_lifetime_res(param.id) - { - ParamName::Error(ident) - } else { - ParamName::Plain(ident) - }; + let param_name = + if let Some(LifetimeRes::Error(..)) = self.owner.get_lifetime_res(param.id) { + ParamName::Error(ident) + } else { + ParamName::Plain(ident) + }; let kind = hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index ef60dc6fc4f1c..f5a306aa9140d 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -17,7 +17,7 @@ use super::errors::{ }; use super::{ AllowReturnTypeNotation, GenericArgsCtor, GenericArgsMode, ImplTraitContext, ImplTraitPosition, - LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt, + LifetimeRes, LoweringContext, ParamMode, }; impl<'hir> LoweringContext<'_, 'hir> { @@ -422,7 +422,7 @@ impl<'hir> LoweringContext<'_, 'hir> { segment_ident_span: Span, generic_args: &mut GenericArgsCtor<'hir>, ) { - let (start, end) = match self.resolver.get_lifetime_res(segment_id) { + let (start, end) = match self.owner.get_lifetime_res(segment_id) { Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end), None => return, Some(res) => { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 300da2a0828e4..6df1ed82d260a 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -211,6 +211,8 @@ pub struct PerOwnerResolverData { /// Resolutions for labels. /// Maps from NodeId of the break/continue expression to the NodeId of their corresponding blocks or loops. pub label_res_map: NodeMap = Default::default(), + /// Resolutions for lifetimes. + pub lifetimes_res_map: NodeMap = Default::default(), /// The id of the owner pub id: ast::NodeId, @@ -227,6 +229,11 @@ impl PerOwnerResolverData { pub fn get_label_res(&self, id: ast::NodeId) -> Option { self.label_res_map.get(&id).copied() } + + /// Obtains resolution for a lifetime with the given `NodeId`. + pub fn get_lifetime_res(&self, id: ast::NodeId) -> Option { + self.lifetimes_res_map.get(&id).copied() + } } /// Resolutions that should only be used for lowering. @@ -237,8 +244,6 @@ pub struct ResolverAstLowering<'tcx> { pub partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. pub import_res_map: NodeMap>>>, - /// Resolutions for lifetimes. - pub lifetimes_res_map: NodeMap, /// Lifetime parameters that lowering will have to introduce. pub extra_lifetime_params_map: NodeMap>, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 7030859916fbe..58899ffd53ab9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2406,7 +2406,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Define a new lifetime (e.g. in generics) #[instrument(level = "debug", skip(self))] fn record_lifetime_def(&mut self, id: NodeId, res: LifetimeRes) { - if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { + if let Some(prev_res) = self.r.current_owner.lifetimes_res_map.insert(id, res) { panic!( "lifetime parameter {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)" ) @@ -2611,11 +2611,11 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let lt_id = if let Some(lt) = lt { lt.id } else { - let res = self.r.lifetimes_res_map[&ty.id]; + let res = self.r.current_owner.lifetimes_res_map[&ty.id]; let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() }; start }; - let lt_res = self.r.lifetimes_res_map[<_id]; + let lt_res = self.r.current_owner.lifetimes_res_map[<_id]; trace!("FindReferenceVisitor inserting res={:?}", lt_res); self.lifetime.insert(lt_res); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7c80cfa878b89..ffb2181bae3a9 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -53,8 +53,7 @@ use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ - self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, - PerNS, + self, CtorOf, DefKind, DocLinkResMap, MacroKinds, NonMacroAttrKind, PartialRes, PerNS, }; use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::{PerParentDisambiguatorState, PerParentDisambiguatorsMap}; @@ -1369,8 +1368,6 @@ pub struct Resolver<'ra, 'tcx> { import_res_map: NodeMap>> = Default::default(), /// An import will be inserted into this map if it has been used. import_use_map: FxHashMap, Used> = default::fx_hash_map(), - /// Resolutions for lifetimes. - lifetimes_res_map: NodeMap = Default::default(), /// Lifetime parameters that lowering will have to introduce. extra_lifetime_params_map: NodeMap> = Default::default(), @@ -2001,7 +1998,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ast_lowering = ty::ResolverAstLowering { partial_res_map: self.partial_res_map, import_res_map: self.import_res_map, - lifetimes_res_map: self.lifetimes_res_map, extra_lifetime_params_map: self.extra_lifetime_params_map, next_node_id: self.next_node_id, owners: self.owners,