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 @@ language_item_table! {
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
Iterator, sym::iterator, iterator_trait, Target::Trait, GenericRequirement::Exact(0); 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); Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0); AsyncIterator, sym::async_iterator, async_iterator_trait, Target::Trait, GenericRequirement::Exact(0);

View File

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

View File

@ -215,6 +215,13 @@ pub(super) trait GoalKind<'tcx>:
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>; ) -> 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( fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
@ -497,6 +504,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_future_candidate(self, goal) G::consider_builtin_future_candidate(self, goal)
} else if lang_items.iterator_trait() == Some(trait_def_id) { } else if lang_items.iterator_trait() == Some(trait_def_id) {
G::consider_builtin_iterator_candidate(self, goal) 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) { } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
G::consider_builtin_async_iterator_candidate(self, goal) G::consider_builtin_async_iterator_candidate(self, goal)
} else if lang_items.coroutine_trait() == Some(trait_def_id) { } else if lang_items.coroutine_trait() == Some(trait_def_id) {

View File

@ -647,6 +647,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
) )
} }
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( fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,

View File

@ -456,6 +456,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) 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( fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,

View File

@ -118,6 +118,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_future_candidates(obligation, &mut candidates); self.assemble_future_candidates(obligation, &mut candidates);
} else if lang_items.iterator_trait() == Some(def_id) { } else if lang_items.iterator_trait() == Some(def_id) {
self.assemble_iterator_candidates(obligation, &mut candidates); 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) { } else if lang_items.async_iterator_trait() == Some(def_id) {
self.assemble_async_iterator_candidates(obligation, &mut candidates); self.assemble_async_iterator_candidates(obligation, &mut candidates);
} else if lang_items.async_fn_kind_helper() == Some(def_id) { } else if lang_items.async_fn_kind_helper() == Some(def_id) {
@ -302,14 +304,31 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
candidates: &mut SelectionCandidateSet<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>,
) { ) {
let self_ty = obligation.self_ty().skip_binder(); 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
// gen constructs get lowered to a special kind of coroutine that // should directly `impl Iterator`.
// should directly `impl Iterator`. if let ty::Coroutine(did, ..) = self_ty.kind()
if self.tcx().coroutine_is_gen(*did) { && self.tcx().coroutine_is_gen(*did)
debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); {
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 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.copy_clone_conditions(obligation) self.copy_clone_conditions(obligation)
} else if Some(trait_def) == lang_items.clone_trait() { } else if Some(trait_def) == lang_items.clone_trait() {
self.copy_clone_conditions(obligation) self.copy_clone_conditions(obligation)
} else if Some(trait_def) == lang_items.fused_iterator_trait() {
self.fused_iterator_conditions(obligation)
} else { } else {
bug!("unexpected builtin trait {:?}", trait_def) bug!("unexpected builtin trait {:?}", trait_def)
}; };

View File

@ -2259,6 +2259,20 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
} }
} }
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 /// For default impls, we need to break apart a type into its
/// "constituent types" -- meaning, the types that it contains. /// "constituent types" -- meaning, the types that it contains.
/// ///

View File

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