Implement FusedIterator for gen block

This commit is contained in:
Shoyu Vanilla 2024-03-22 00:48:36 +09:00
parent 03994e498d
commit ae4c5c891e
10 changed files with 105 additions and 7 deletions

View File

@ -214,6 +214,7 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0);
FusedIterator, sym::fused_iterator, fused_iterator_trait, Target::Trait, GenericRequirement::Exact(0);
Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0);

View File

@ -207,6 +207,7 @@
FromResidual,
FsOpenOptions,
FsPermissions,
FusedIterator,
Future,
FutureOutput,
GlobalAlloc,
@ -885,6 +886,7 @@
fsub_algebraic,
fsub_fast,
fundamental,
fused_iterator,
future,
future_trait,
gdb_script_file,

View File

@ -215,6 +215,13 @@ fn consider_builtin_iterator_candidate(
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
/// A coroutine (that comes from a `gen` desugaring) is known to implement
/// `FusedIterator`
fn consider_builtin_fused_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
@ -497,6 +504,8 @@ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
G::consider_builtin_future_candidate(self, goal)
} else if lang_items.iterator_trait() == Some(trait_def_id) {
G::consider_builtin_iterator_candidate(self, goal)
} else if lang_items.fused_iterator_trait() == Some(trait_def_id) {
G::consider_builtin_fused_iterator_candidate(self, goal)
} else if lang_items.async_iterator_trait() == Some(trait_def_id) {
G::consider_builtin_async_iterator_candidate(self, goal)
} else if lang_items.coroutine_trait() == Some(trait_def_id) {

View File

@ -647,6 +647,13 @@ fn consider_builtin_iterator_candidate(
)
}
fn consider_builtin_fused_iterator_candidate(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
bug!("`FusedIterator` does not have an associated type: {:?}", goal);
}
fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,

View File

@ -456,6 +456,28 @@ fn consider_builtin_iterator_candidate(
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_fused_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
if goal.predicate.polarity != ty::ImplPolarity::Positive {
return Err(NoSolution);
}
let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
return Err(NoSolution);
};
// Coroutines are not iterators unless they come from `gen` desugaring
let tcx = ecx.tcx();
if !tcx.coroutine_is_gen(def_id) {
return Err(NoSolution);
}
// Gen coroutines unconditionally implement `FusedIterator`
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,

View File

@ -118,6 +118,8 @@ pub(super) fn assemble_candidates<'o>(
self.assemble_future_candidates(obligation, &mut candidates);
} else if lang_items.iterator_trait() == Some(def_id) {
self.assemble_iterator_candidates(obligation, &mut candidates);
} else if lang_items.fused_iterator_trait() == Some(def_id) {
self.assemble_fused_iterator_candidates(obligation, &mut candidates);
} else if lang_items.async_iterator_trait() == Some(def_id) {
self.assemble_async_iterator_candidates(obligation, &mut candidates);
} else if lang_items.async_fn_kind_helper() == Some(def_id) {
@ -302,14 +304,31 @@ fn assemble_iterator_candidates(
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
if let ty::Coroutine(did, ..) = self_ty.kind() {
// gen constructs get lowered to a special kind of coroutine that
// should directly `impl Iterator`.
if self.tcx().coroutine_is_gen(*did) {
debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
// gen constructs get lowered to a special kind of coroutine that
// should directly `impl Iterator`.
if let ty::Coroutine(did, ..) = self_ty.kind()
&& self.tcx().coroutine_is_gen(*did)
{
debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
candidates.vec.push(IteratorCandidate);
}
candidates.vec.push(IteratorCandidate);
}
}
fn assemble_fused_iterator_candidates(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
// gen constructs get lowered to a special kind of coroutine that
// should directly `impl FusedIterator`.
if let ty::Coroutine(did, ..) = self_ty.kind()
&& self.tcx().coroutine_is_gen(*did)
{
debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",);
candidates.vec.push(BuiltinCandidate { has_nested: false });
}
}

View File

@ -267,6 +267,8 @@ fn confirm_builtin_candidate(
self.copy_clone_conditions(obligation)
} else if Some(trait_def) == lang_items.clone_trait() {
self.copy_clone_conditions(obligation)
} else if Some(trait_def) == lang_items.fused_iterator_trait() {
self.fused_iterator_conditions(obligation)
} else {
bug!("unexpected builtin trait {:?}", trait_def)
};

View File

@ -2259,6 +2259,20 @@ fn copy_clone_conditions(
}
}
fn fused_iterator_conditions(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
if let ty::Coroutine(did, ..) = *self_ty.kind()
&& self.tcx().coroutine_is_gen(did)
{
BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new()))
} else {
BuiltinImplConditions::None
}
}
/// For default impls, we need to break apart a type into its
/// "constituent types" -- meaning, the types that it contains.
///

View File

@ -28,6 +28,7 @@ pub unsafe trait TrustedFused {}
#[rustc_unsafe_specialization_marker]
// FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused
// but that ICEs iter::Fuse specializations.
#[cfg_attr(not(bootstrap), lang = "fused_iterator")]
pub trait FusedIterator: Iterator {}
#[stable(feature = "fused", since = "1.26.0")]

View File

@ -0,0 +1,21 @@
//@ revisions: next old
//@compile-flags: --edition 2024 -Zunstable-options
//@[next] compile-flags: -Znext-solver
//@ check-pass
#![feature(gen_blocks)]
use std::iter::FusedIterator;
fn foo() -> impl FusedIterator {
gen { yield 42 }
}
fn bar() -> impl FusedIterator<Item = u16> {
gen { yield 42 }
}
fn baz() -> impl FusedIterator + Iterator<Item = i64> {
gen { yield 42 }
}
fn main() {}