Skip to content

Use gen blocks in the compiler instead of from_coroutine #142801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 64 additions & 68 deletions compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2177,84 +2177,80 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
/// Walk the generics of the item for a trait bound whose self type
/// corresponds to the expected res, and return the trait def id.
fn for_each_trait_bound_on_res(&self, expected_res: Res) -> impl Iterator<Item = DefId> {
std::iter::from_coroutine(
#[coroutine]
move || {
let mut scope = self.scope;
loop {
let hir_id = match *scope {
Scope::Binder { hir_id, .. } => Some(hir_id),
Scope::Root { opt_parent_item: Some(parent_def_id) } => {
Some(self.tcx.local_def_id_to_hir_id(parent_def_id))
}
Scope::Body { .. }
| Scope::ObjectLifetimeDefault { .. }
| Scope::Supertrait { .. }
| Scope::TraitRefBoundary { .. }
| Scope::LateBoundary { .. }
| Scope::Opaque { .. }
| Scope::Root { opt_parent_item: None } => None,
};
gen move {
let mut scope = self.scope;
loop {
let hir_id = match *scope {
Scope::Binder { hir_id, .. } => Some(hir_id),
Scope::Root { opt_parent_item: Some(parent_def_id) } => {
Some(self.tcx.local_def_id_to_hir_id(parent_def_id))
}
Scope::Body { .. }
| Scope::ObjectLifetimeDefault { .. }
| Scope::Supertrait { .. }
| Scope::TraitRefBoundary { .. }
| Scope::LateBoundary { .. }
| Scope::Opaque { .. }
| Scope::Root { opt_parent_item: None } => None,
};

if let Some(hir_id) = hir_id {
let node = self.tcx.hir_node(hir_id);
// If this is a `Self` bound in a trait, yield the trait itself.
// Specifically, we don't need to look at any supertraits since
// we already do that in `BoundVarContext::supertrait_hrtb_vars`.
if let Res::SelfTyParam { trait_: _ } = expected_res
&& let hir::Node::Item(item) = node
&& let hir::ItemKind::Trait(..) = item.kind
{
// Yield the trait's def id. Supertraits will be
// elaborated from that.
yield item.owner_id.def_id.to_def_id();
} else if let Some(generics) = node.generics() {
for pred in generics.predicates {
let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind
else {
continue;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
pred.bounded_ty.kind
else {
continue;
};
// Match the expected res.
if bounded_path.res != expected_res {
continue;
}
for pred in pred.bounds {
match pred {
hir::GenericBound::Trait(poly_trait_ref) => {
if let Some(def_id) =
poly_trait_ref.trait_ref.trait_def_id()
{
yield def_id;
}
if let Some(hir_id) = hir_id {
let node = self.tcx.hir_node(hir_id);
// If this is a `Self` bound in a trait, yield the trait itself.
// Specifically, we don't need to look at any supertraits since
// we already do that in `BoundVarContext::supertrait_hrtb_vars`.
if let Res::SelfTyParam { trait_: _ } = expected_res
&& let hir::Node::Item(item) = node
&& let hir::ItemKind::Trait(..) = item.kind
{
// Yield the trait's def id. Supertraits will be
// elaborated from that.
yield item.owner_id.def_id.to_def_id();
} else if let Some(generics) = node.generics() {
for pred in generics.predicates {
let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else {
continue;
};
let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) =
pred.bounded_ty.kind
else {
continue;
};
// Match the expected res.
if bounded_path.res != expected_res {
continue;
}
for pred in pred.bounds {
match pred {
hir::GenericBound::Trait(poly_trait_ref) => {
if let Some(def_id) =
poly_trait_ref.trait_ref.trait_def_id()
{
yield def_id;
}
hir::GenericBound::Outlives(_)
| hir::GenericBound::Use(_, _) => {}
}
hir::GenericBound::Outlives(_)
| hir::GenericBound::Use(_, _) => {}
}
}
}
}
}

match *scope {
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s }
| Scope::LateBoundary { s, .. }
| Scope::Opaque { s, .. } => {
scope = s;
}
Scope::Root { .. } => break,
match *scope {
Scope::Binder { s, .. }
| Scope::Body { s, .. }
| Scope::ObjectLifetimeDefault { s, .. }
| Scope::Supertrait { s, .. }
| Scope::TraitRefBoundary { s }
| Scope::LateBoundary { s, .. }
| Scope::Opaque { s, .. } => {
scope = s;
}
Scope::Root { .. } => break,
}
},
)
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
#![feature(gen_blocks)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(iter_intersperse)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
#![allow(internal_features)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(coroutines)]
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(file_buffered)]
#![feature(gen_blocks)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(macro_metavar_expr)]
Expand Down
44 changes: 20 additions & 24 deletions compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::iter::TrustedLen;
use std::path::{Path, PathBuf};
use std::sync::{Arc, OnceLock};
use std::{io, iter, mem};
use std::{io, mem};

pub(super) use cstore_impl::provide;
use rustc_ast as ast;
Expand Down Expand Up @@ -1272,34 +1272,30 @@ impl<'a> CrateMetadataRef<'a> {
id: DefIndex,
sess: &'a Session,
) -> impl Iterator<Item = ModChild> {
iter::from_coroutine(
#[coroutine]
move || {
if let Some(data) = &self.root.proc_macro_data {
// If we are loading as a proc macro, we want to return
// the view of this crate as a proc macro crate.
if id == CRATE_DEF_INDEX {
for child_index in data.macros.decode(self) {
yield self.get_mod_child(child_index, sess);
}
}
} else {
// Iterate over all children.
let non_reexports =
self.root.tables.module_children_non_reexports.get(self, id);
for child_index in non_reexports.unwrap().decode(self) {
gen move {
if let Some(data) = &self.root.proc_macro_data {
// If we are loading as a proc macro, we want to return
// the view of this crate as a proc macro crate.
if id == CRATE_DEF_INDEX {
for child_index in data.macros.decode(self) {
yield self.get_mod_child(child_index, sess);
}
}
} else {
// Iterate over all children.
let non_reexports = self.root.tables.module_children_non_reexports.get(self, id);
for child_index in non_reexports.unwrap().decode(self) {
yield self.get_mod_child(child_index, sess);
}

let reexports = self.root.tables.module_children_reexports.get(self, id);
if !reexports.is_default() {
for reexport in reexports.decode((self, sess)) {
yield reexport;
}
let reexports = self.root.tables.module_children_reexports.get(self, id);
if !reexports.is_default() {
for reexport in reexports.decode((self, sess)) {
yield reexport;
}
}
},
)
}
}
}

fn is_ctfe_mir_available(self, id: DefIndex) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
#![feature(box_patterns)]
#![feature(closure_track_caller)]
#![feature(core_intrinsics)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
#![feature(decl_macro)]
#![feature(discriminant_kind)]
#![feature(extern_types)]
#![feature(file_buffered)]
#![feature(gen_blocks)]
#![feature(if_let_guard)]
#![feature(intra_doc_pointers)]
#![feature(iter_from_coroutine)]
Expand Down
82 changes: 39 additions & 43 deletions compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,53 +422,49 @@ pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>(
child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T,
) -> impl Iterator<Item = T> {
std::iter::from_coroutine(
#[coroutine]
move || {
let mut child_captures = child_captures.into_iter().enumerate().peekable();

// One parent capture may correspond to several child captures if we end up
// refining the set of captures via edition-2021 precise captures. We want to
// match up any number of child captures with one parent capture, so we keep
// peeking off this `Peekable` until the child doesn't match anymore.
for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
// Make sure we use every field at least once, b/c why are we capturing something
// if it's not used in the inner coroutine.
let mut field_used_at_least_once = false;

// A parent matches a child if they share the same prefix of projections.
// The child may have more, if it is capturing sub-fields out of
// something that is captured by-move in the parent closure.
while child_captures.peek().is_some_and(|(_, child_capture)| {
child_prefix_matches_parent_projections(parent_capture, child_capture)
}) {
let (child_field_idx, child_capture) = child_captures.next().unwrap();
// This analysis only makes sense if the parent capture is a
// prefix of the child capture.
assert!(
child_capture.place.projections.len()
>= parent_capture.place.projections.len(),
"parent capture ({parent_capture:#?}) expected to be prefix of \
gen move {
let mut child_captures = child_captures.into_iter().enumerate().peekable();

// One parent capture may correspond to several child captures if we end up
// refining the set of captures via edition-2021 precise captures. We want to
// match up any number of child captures with one parent capture, so we keep
// peeking off this `Peekable` until the child doesn't match anymore.
for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
// Make sure we use every field at least once, b/c why are we capturing something
// if it's not used in the inner coroutine.
let mut field_used_at_least_once = false;

// A parent matches a child if they share the same prefix of projections.
// The child may have more, if it is capturing sub-fields out of
// something that is captured by-move in the parent closure.
while child_captures.peek().is_some_and(|(_, child_capture)| {
child_prefix_matches_parent_projections(parent_capture, child_capture)
}) {
let (child_field_idx, child_capture) = child_captures.next().unwrap();
// This analysis only makes sense if the parent capture is a
// prefix of the child capture.
assert!(
child_capture.place.projections.len() >= parent_capture.place.projections.len(),
"parent capture ({parent_capture:#?}) expected to be prefix of \
child capture ({child_capture:#?})"
);

yield for_each(
(parent_field_idx, parent_capture),
(child_field_idx, child_capture),
);

field_used_at_least_once = true;
}
);

// Make sure the field was used at least once.
assert!(
field_used_at_least_once,
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
yield for_each(
(parent_field_idx, parent_capture),
(child_field_idx, child_capture),
);

field_used_at_least_once = true;
}
assert_eq!(child_captures.next(), None, "leftover child captures?");
},
)

// Make sure the field was used at least once.
assert!(
field_used_at_least_once,
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
);
}
assert_eq!(child_captures.next(), None, "leftover child captures?");
}
}

fn child_prefix_matches_parent_projections(
Expand Down
29 changes: 13 additions & 16 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2087,23 +2087,20 @@ impl<'tcx> TyCtxt<'tcx> {
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);

let definitions = &self.untracked.definitions;
std::iter::from_coroutine(
#[coroutine]
|| {
let mut i = 0;

// Recompute the number of definitions each time, because our caller may be creating
// new ones.
while i < { definitions.read().num_definitions() } {
let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
yield LocalDefId { local_def_index };
i += 1;
}
gen {
let mut i = 0;

// Recompute the number of definitions each time, because our caller may be creating
// new ones.
while i < { definitions.read().num_definitions() } {
let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
yield LocalDefId { local_def_index };
i += 1;
}

// Freeze definitions once we finish iterating on them, to prevent adding new ones.
definitions.freeze();
},
)
// Freeze definitions once we finish iterating on them, to prevent adding new ones.
definitions.freeze();
}
}

pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
Expand Down
Loading