Add iter macro

This adds an `iter!` macro that can be used to create movable
generators.

This also adds a yield_expr feature so the `yield` keyword can be used
within iter! macro bodies. This was needed because several unstable
features each need `yield` expressions, so this allows us to stabilize
them separately from any individual feature.

Co-authored-by: Oli Scherer <github35764891676564198441@oli-obk.de>
Co-authored-by: Jieyou Xu <jieyouxu@outlook.com>
Co-authored-by: Travis Cross <tc@traviscross.com>
This commit is contained in:
Oli Scherer
2025-02-27 09:51:23 +00:00
committed by Eric Holk
parent aae43c4532
commit 5fbdfc3e10
43 changed files with 826 additions and 61 deletions
+8 -7
View File
@@ -1,4 +1,3 @@
use std::assert_matches::assert_matches;
use std::ops::ControlFlow;
use std::sync::Arc;
@@ -1199,11 +1198,13 @@ fn lower_expr_coroutine_closure(
let closure_def_id = self.local_def_id(closure_id);
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
assert_matches!(
coroutine_kind,
CoroutineKind::Async { .. },
"only async closures are supported currently"
);
let coroutine_desugaring = match coroutine_kind {
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
CoroutineKind::AsyncGen { span, .. } => {
span_bug!(span, "only async closures and `iter!` closures are supported currently")
}
};
let body = self.with_new_scopes(fn_decl_span, |this| {
let inner_decl =
@@ -1247,7 +1248,7 @@ fn lower_expr_coroutine_closure(
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
// knows that a `FnDecl` output type like `-> &str` actually means
// "coroutine that returns &str", rather than directly returning a `&str`.
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
constness: hir::Constness::NotConst,
});
hir::ExprKind::Closure(c)
@@ -477,11 +477,12 @@ macro_rules! gate_all {
for span in spans {
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
#[allow(rustc::untranslatable_diagnostic)]
// Don't know which of the two features to include in the
// error message, so I am arbitrarily picking one.
feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental")
// Emit yield_expr as the error, since that will be sufficient. You can think of it
// as coroutines and gen_blocks imply yield_expr.
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
.emit();
}
}
@@ -52,7 +52,7 @@ pub(super) fn check_signature_annotation(&mut self) {
assert_matches!(
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
Some(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen,
hir::CoroutineSource::Closure
)),
"this needs to be modified if we're lowering non-async closures"
+53
View File
@@ -0,0 +1,53 @@
use rustc_ast::ptr::P;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token};
use rustc_errors::PResult;
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
use rustc_span::Span;
pub(crate) fn expand<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> MacroExpanderResult<'cx> {
let closure = match parse_closure(cx, sp, tts) {
Ok(parsed) => parsed,
Err(err) => {
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
}
};
ExpandResult::Ready(base::MacEager::expr(closure))
}
fn parse_closure<'a>(
cx: &mut ExtCtxt<'a>,
span: Span,
stream: TokenStream,
) -> PResult<'a, P<Expr>> {
let mut closure_parser = cx.new_parser_from_tts(stream);
let coroutine_kind = Some(CoroutineKind::Gen {
span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
let mut closure = closure_parser.parse_expr()?;
match &mut closure.kind {
ast::ExprKind::Closure(c) => {
if let Some(kind) = c.coroutine_kind {
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
}
c.coroutine_kind = coroutine_kind;
if closure_parser.token != token::Eof {
closure_parser.unexpected()?;
}
Ok(closure)
}
_ => {
cx.dcx().span_err(closure.span, "`iter!` body must be a closure");
Err(closure_parser.unexpected().unwrap_err())
}
}
}
+2
View File
@@ -47,6 +47,7 @@
mod format;
mod format_foreign;
mod global_allocator;
mod iter;
mod log_syntax;
mod pattern_type;
mod source_util;
@@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
include: source_util::expand_include,
include_bytes: source_util::expand_include_bytes,
include_str: source_util::expand_include_str,
iter: iter::expand,
line: source_util::expand_line,
log_syntax: log_syntax::expand_log_syntax,
module_path: source_util::expand_mod,
@@ -589,12 +589,7 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
Rvalue::Aggregate(kind, ..) => {
if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
&& let Some(
coroutine_kind @ hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
_,
),
) = self.tcx.coroutine_kind(def_id)
&& let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
{
self.check_op(ops::Coroutine(coroutine_kind));
}
@@ -486,24 +486,25 @@ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
pub(crate) struct Coroutine(pub hir::CoroutineKind);
impl<'tcx> NonConstOp<'tcx> for Coroutine {
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
if let hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Block,
) = self.0
{
Status::Unstable {
match self.0 {
hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Block,
)
// FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a
// different feature.
| hir::CoroutineKind::Coroutine(_) => Status::Unstable {
gate: sym::const_async_blocks,
gate_already_checked: false,
safe_to_expose_on_stable: false,
is_function_call: false,
}
} else {
Status::Forbidden
},
_ => Status::Forbidden,
}
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
} else {
+9 -2
View File
@@ -2061,12 +2061,19 @@ pub fn movability(self) -> Movability {
CoroutineKind::Coroutine(mov) => mov,
}
}
}
impl CoroutineKind {
pub fn is_fn_like(self) -> bool {
matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
}
pub fn to_plural_string(&self) -> String {
match self {
CoroutineKind::Desugared(d, CoroutineSource::Fn) => format!("{d:#}fn bodies"),
CoroutineKind::Desugared(d, CoroutineSource::Block) => format!("{d:#}blocks"),
CoroutineKind::Desugared(d, CoroutineSource::Closure) => format!("{d:#}closure bodies"),
CoroutineKind::Coroutine(_) => "coroutines".to_string(),
}
}
}
impl fmt::Display for CoroutineKind {
+15 -5
View File
@@ -204,14 +204,19 @@ pub(crate) fn check_expr_closure(
)
}
hir::ClosureKind::CoroutineClosure(kind) => {
// async closures always return the type ascribed after the `->` (if present),
// and yield `()`.
let (bound_return_ty, bound_yield_ty) = match kind {
hir::CoroutineDesugaring::Gen => {
// `iter!` closures always return unit and yield the `Iterator::Item` type
// that we have to infer.
(tcx.types.unit, self.infcx.next_ty_var(expr_span))
}
hir::CoroutineDesugaring::Async => {
// async closures always return the type ascribed after the `->` (if present),
// and yield `()`.
(bound_sig.skip_binder().output(), tcx.types.unit)
}
hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => {
todo!("`gen` and `async gen` closures not supported yet")
hir::CoroutineDesugaring::AsyncGen => {
todo!("`async gen` closures not supported yet")
}
};
// Compute all of the variables that will be used to populate the coroutine.
@@ -465,7 +470,12 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
if let Some(trait_def_id) = trait_def_id {
let found_kind = match closure_kind {
hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
hir::ClosureKind::Closure
// FIXME(iter_macro): Someday we'll probably want iterator closures instead of
// just using Fn* for iterators.
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => {
self.tcx.fn_trait_kind_from_def_id(trait_def_id)
}
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self
.tcx
.async_fn_trait_kind_from_def_id(trait_def_id)
+1 -1
View File
@@ -713,7 +713,7 @@ pub(super) fn parse_block_common(
/// Parses the rest of a block expression or function body.
/// Precondition: already parsed the '{'.
pub(crate) fn parse_block_tail(
pub fn parse_block_tail(
&mut self,
lo: Span,
s: BlockCheckMode,
+2 -2
View File
@@ -72,8 +72,6 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur
trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds
trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment
trait_selection_await_both_futures = consider `await`ing on both `Future`s
trait_selection_await_future = consider `await`ing on the `Future`
trait_selection_await_note = calling an async function returns a future
@@ -123,6 +121,8 @@ trait_selection_closure_kind_requirement = the requirement to implement `{$trait
trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait
trait_selection_consider_specifying_length = consider specifying the actual array length
trait_selection_coro_closure_not_fn = {$coro_kind}closure does not implement `{$kind}` because it captures state from its environment
trait_selection_data_flows = ...but data{$label_var1_exists ->
[true] {" "}from `{$label_var1}`
*[false] {""}
@@ -42,9 +42,7 @@
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::TyCategory;
use crate::error_reporting::traits::report_dyn_incompatibility;
use crate::errors::{
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
};
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn};
use crate::infer::{self, InferCtxt, InferCtxtExt as _};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{
@@ -886,9 +884,18 @@ fn emit_specialized_closure_kind_error(
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
// if they have captures.
if has_self_borrows && expected_kind != ty::ClosureKind::FnOnce {
let mut err = self.dcx().create_err(AsyncClosureNotFn {
let coro_kind = match self
.tcx
.coroutine_kind(self.tcx.coroutine_for_closure(closure_def_id))
.unwrap()
{
rustc_hir::CoroutineKind::Desugared(desugaring, _) => desugaring.to_string(),
coro => coro.to_string(),
};
let mut err = self.dcx().create_err(CoroClosureNotFn {
span: self.tcx.def_span(closure_def_id),
kind: expected_kind.as_str(),
coro_kind,
});
self.note_obligation_cause(&mut err, &obligation);
return Some(err.emit());
+3 -2
View File
@@ -201,11 +201,12 @@ pub struct ClosureFnMutLabel {
}
#[derive(Diagnostic)]
#[diag(trait_selection_async_closure_not_fn)]
pub(crate) struct AsyncClosureNotFn {
#[diag(trait_selection_coro_closure_not_fn)]
pub(crate) struct CoroClosureNotFn {
#[primary_span]
pub span: Span,
pub kind: &'static str,
pub coro_kind: String,
}
#[derive(Diagnostic)]
@@ -11,7 +11,7 @@
use hir::LangItem;
use hir::def_id::DefId;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_hir as hir;
use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind};
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
use rustc_middle::ty::fast_reject::DeepRejectCtxt;
use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode, elaborate};
@@ -438,6 +438,7 @@ fn assemble_closure_candidates(
}
}
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_async_closure_candidates(
&mut self,
obligation: &PolyTraitObligation<'tcx>,
@@ -446,15 +447,30 @@ fn assemble_async_closure_candidates(
let goal_kind =
self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();
debug!("self_ty = {:?}", obligation.self_ty().skip_binder().kind());
match *obligation.self_ty().skip_binder().kind() {
ty::CoroutineClosure(_, args) => {
ty::CoroutineClosure(def_id, args) => {
if let Some(closure_kind) =
args.as_coroutine_closure().kind_ty().to_opt_closure_kind()
&& !closure_kind.extends(goal_kind)
{
return;
}
candidates.vec.push(AsyncClosureCandidate);
// Make sure this is actually an async closure.
let Some(coroutine_kind) =
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(def_id))
else {
bug!("coroutine with no kind");
};
debug!(?coroutine_kind);
match coroutine_kind {
CoroutineKind::Desugared(CoroutineDesugaring::Async, _) => {
candidates.vec.push(AsyncClosureCandidate);
}
_ => (),
}
}
// Closures and fn pointers implement `AsyncFn*` if their return types
// implement `Future`, which is checked later.
+2
View File
@@ -420,6 +420,8 @@ fn $fold<AAA, FFF>(mut self, init: AAA, fold: FFF) -> AAA
issue = "42168"
)]
pub use self::range::Step;
#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")]
pub use self::sources::iter;
#[stable(feature = "iter_empty", since = "1.2.0")]
pub use self::sources::{Empty, empty};
#[unstable(
+3
View File
@@ -1,6 +1,7 @@
mod empty;
mod from_coroutine;
mod from_fn;
mod generator;
mod once;
mod once_with;
mod repeat;
@@ -18,6 +19,8 @@
pub use self::from_coroutine::{FromCoroutine, from_coroutine};
#[stable(feature = "iter_from_fn", since = "1.34.0")]
pub use self::from_fn::{FromFn, from_fn};
#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")]
pub use self::generator::iter;
#[stable(feature = "iter_once", since = "1.2.0")]
pub use self::once::{Once, once};
#[stable(feature = "iter_once_with", since = "1.43.0")]
@@ -0,0 +1,29 @@
/// Creates a new closure that returns an iterator where each iteration steps the given
/// generator to the next `yield` statement.
///
/// Similar to [`iter::from_fn`], but allows arbitrary control flow.
///
/// [`iter::from_fn`]: crate::iter::from_fn
///
/// # Examples
///
/// ```
/// #![feature(iter_macro, coroutines)]
/// # #[cfg(not(bootstrap))]
/// # {
///
/// let it = std::iter::iter!{|| {
/// yield 1;
/// yield 2;
/// yield 3;
/// } }();
/// let v: Vec<_> = it.collect();
/// assert_eq!(v, [1, 2, 3]);
/// # }
/// ```
#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")]
#[allow_internal_unstable(coroutines, iter_from_coroutine)]
#[cfg_attr(not(bootstrap), rustc_builtin_macro)]
pub macro iter($($t:tt)*) {
/* compiler-builtin */
}
+22
View File
@@ -0,0 +1,22 @@
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = iter! { || {
yield 0;
for x in 5..10 {
yield x * 2;
}
} };
let mut i = i();
assert_eq!(i.next(), Some(0));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
+2
View File
@@ -4,8 +4,10 @@
const gen fn a() {}
//~^ ERROR functions cannot be both `const` and `gen`
//~^^ ERROR `gen` fn bodies are not allowed in constant functions
const async gen fn b() {}
//~^ ERROR functions cannot be both `const` and `async gen`
//~^^ ERROR `async gen` fn bodies are not allowed in constant functions
fn main() {}
+14 -2
View File
@@ -8,7 +8,7 @@ LL | const gen fn a() {}
| `const` because of this
error: functions cannot be both `const` and `async gen`
--> $DIR/const_gen_fn.rs:8:1
--> $DIR/const_gen_fn.rs:9:1
|
LL | const async gen fn b() {}
| ^^^^^-^^^^^^^^^----------
@@ -16,5 +16,17 @@ LL | const async gen fn b() {}
| | `async gen` because of this
| `const` because of this
error: aborting due to 2 previous errors
error: `gen` fn bodies are not allowed in constant functions
--> $DIR/const_gen_fn.rs:5:18
|
LL | const gen fn a() {}
| ^^
error: `async gen` fn bodies are not allowed in constant functions
--> $DIR/const_gen_fn.rs:9:24
|
LL | const async gen fn b() {}
| ^^
error: aborting due to 4 previous errors
+2 -2
View File
@@ -31,7 +31,7 @@ LL | let _ = || yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -41,7 +41,7 @@ LL | let _ = #[coroutine] || yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the `#[coroutine]` attribute is an experimental feature
@@ -5,7 +5,7 @@ LL | yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -15,7 +15,7 @@ LL | let _ = || yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -25,7 +25,7 @@ LL | yield;
| ^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -35,7 +35,7 @@ LL | yield 0;
| ^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -5,7 +5,7 @@ LL | yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -15,7 +15,7 @@ LL | let _ = || yield true;
| ^^^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -25,7 +25,7 @@ LL | yield;
| ^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -35,7 +35,7 @@ LL | yield 0;
| ^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
@@ -5,7 +5,7 @@ LL | yield ();
| ^^^^^^^^
|
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
= help: add `#![feature(coroutines)]` to the crate attributes to enable
= help: add `#![feature(yield_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: yield syntax is experimental
+24
View File
@@ -0,0 +1,24 @@
//@ run-pass
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = iter! { || {
yield 0;
for x in 5..10 {
yield x * 2;
}
} };
let mut i = i();
assert_eq!(i.next(), Some(0));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
+24
View File
@@ -0,0 +1,24 @@
//@ run-pass
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = iter! {|foo| {
yield foo;
for x in 5..10 {
yield x * 2;
}
}};
let mut i = i(3);
assert_eq!(i.next(), Some(3));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
+27
View File
@@ -0,0 +1,27 @@
//@ run-pass
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = {
let s = String::new();
iter! { move || {
yield s.len();
for x in 5..10 {
yield x * 2;
}
}}
};
let mut i = i();
assert_eq!(i.next(), Some(0));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
+26
View File
@@ -0,0 +1,26 @@
// This test exercises lending behavior for iterator closures which is not yet supported.
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let f = {
let s = "foo".to_string();
iter! { move || {
for c in s.chars() {
yield c;
}
}}
};
let mut i = f();
assert_eq!(i.next(), Some('f'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), None);
let mut i = f(); //~ ERROR use of moved value: `f`
assert_eq!(i.next(), Some('f'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), None);
}
@@ -0,0 +1,25 @@
error[E0382]: use of moved value: `f`
--> $DIR/generator_capture_.rs:21:17
|
LL | let f = {
| - move occurs because `f` has type `{gen closure@$DIR/generator_capture_.rs:10:17: 10:24}`, which does not implement the `Copy` trait
...
LL | let mut i = f();
| --- `f` moved due to this call
...
LL | let mut i = f();
| ^ value used here after move
|
note: this value implements `FnOnce`, which causes it to be moved when called
--> $DIR/generator_capture_.rs:16:17
|
LL | let mut i = f();
| ^
help: consider cloning the value if the performance cost is acceptable
|
LL | let mut i = f.clone()();
| ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.
@@ -0,0 +1,25 @@
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = {
let s = String::new();
iter! { || { //~ ERROR `s` does not live long enough
yield s.len();
for x in 5..10 {
yield x * 2;
}
} }
};
let mut i = i();
assert_eq!(i.next(), Some(0));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
@@ -0,0 +1,20 @@
error[E0597]: `s` does not live long enough
--> $DIR/generator_capture_fail.rs:8:17
|
LL | let i = {
| - borrow later stored here
LL | let s = String::new();
LL | iter! { || {
| _________________^
LL | | yield s.len();
LL | | for x in 5..10 {
LL | | yield x * 2;
LL | | }
LL | | } }
| |_________^ borrowed value does not live long enough
LL | };
| - `s` dropped here while still borrowed
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0597`.
@@ -0,0 +1,32 @@
//@ run-pass
#![feature(iter_macro, yield_expr)]
use std::iter::iter;
fn main() {
let i = {
let s = String::new();
iter! { move || {
yield s.len();
for x in 5..10 {
yield x * 2;
}
}}
};
test_iterator(i);
}
/// Exercise the iterator in a separate function to ensure it's not capturing anything it shoudln't.
fn test_iterator<I: Iterator<Item = usize>>(i: impl FnOnce() -> I) {
let mut i = i();
assert_eq!(i.next(), Some(0));
assert_eq!(i.next(), Some(10));
assert_eq!(i.next(), Some(12));
assert_eq!(i.next(), Some(14));
assert_eq!(i.next(), Some(16));
assert_eq!(i.next(), Some(18));
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
assert_eq!(i.next(), None);
}
@@ -0,0 +1,30 @@
//@ run-pass
#![feature(iter_macro, yield_expr)]
// This test creates an iterator that captures a reference and ensure that doesn't force the
// iterator to become lending.
use std::iter::iter;
fn main() {
let s = "foo".to_string();
let f = iter! { || {
for c in s.chars() {
yield c;
}
}};
let mut i = f();
let mut j = f();
assert_eq!(i.next(), Some('f'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), Some('o'));
assert_eq!(i.next(), None);
assert_eq!(j.next(), Some('f'));
assert_eq!(j.next(), Some('o'));
assert_eq!(j.next(), Some('o'));
assert_eq!(j.next(), None);
}
@@ -0,0 +1,63 @@
#![feature(iter_macro, impl_trait_in_fn_trait_return, yield_expr)]
use std::iter::iter;
fn plain() -> impl Fn() -> impl Iterator<Item = u32> {
iter! { || {
yield 0;
for x in 5..10 {
yield x * 2;
}
} }
}
fn arg() -> impl Fn(u32) -> impl Iterator<Item = u32> {
iter! { |arg| {
yield arg;
for x in 5..10 {
yield x * 2;
}
} }
}
fn capture<'a>(a: &'a u32) -> impl Fn() -> (impl Iterator<Item = u32> + 'a) {
iter! { || { //~ ERROR cannot return reference to function parameter `a`
yield *a;
for x in 5..10 {
yield x * 2;
}
} }
}
fn capture_move(a: &u32) -> impl Fn() -> impl Iterator<Item = u32> {
iter! { move || { //~ ERROR does not implement `Fn` because it captures
yield *a;
for x in 5..10 {
yield x * 2;
}
} }
}
fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
iter! { move || {
//~^ ERROR captures lifetime
//~| ERROR: captures lifetime
yield *a;
for x in 5..10 {
yield x * 2;
}
} }
}
fn capture_move_once_lifetimes<'a>(
a: &'a u32,
) -> impl FnOnce() -> (impl Iterator<Item = u32> + 'a) {
iter! { move || {
yield *a;
for x in 5..10 {
yield x * 2;
}
} }
}
fn main() {}
@@ -0,0 +1,70 @@
error[E0515]: cannot return reference to function parameter `a`
--> $DIR/generator_returned_from_fn.rs:24:13
|
LL | iter! { || {
| _____________^
LL | | yield *a;
LL | | for x in 5..10 {
LL | | yield x * 2;
LL | | }
LL | | } }
| |_____^ returns a reference to data owned by the current function
error: gen closure does not implement `Fn` because it captures state from its environment
--> $DIR/generator_returned_from_fn.rs:33:13
|
LL | iter! { move || {
| _____________-^^^^^^
LL | | yield *a;
LL | | for x in 5..10 {
LL | | yield x * 2;
LL | | }
LL | | } }
| |_____- return type was inferred to be `{gen closure@$DIR/generator_returned_from_fn.rs:33:13: 33:20}` here
error[E0700]: hidden type for `impl FnOnce() -> impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
--> $DIR/generator_returned_from_fn.rs:42:13
|
LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
| ---- ------------------------------------------ opaque type defined here
| |
| hidden type `{gen closure@$DIR/generator_returned_from_fn.rs:42:13: 42:20}` captures the anonymous lifetime defined here
LL | iter! { move || {
| _____________^
LL | |
LL | |
LL | | yield *a;
... |
LL | | } }
| |_____^
|
help: add a `use<...>` bound to explicitly capture `'_`
|
LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> + use<'_> {
| +++++++++
error[E0700]: hidden type for `impl Iterator<Item = u32>` captures lifetime that does not appear in bounds
--> $DIR/generator_returned_from_fn.rs:42:13
|
LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
| ---- ------------------------- opaque type defined here
| |
| hidden type `{gen closure body@$DIR/generator_returned_from_fn.rs:42:21: 49:6}` captures the anonymous lifetime defined here
LL | iter! { move || {
| _____________^
LL | |
LL | |
LL | | yield *a;
... |
LL | | } }
| |_____^
|
help: add a `use<...>` bound to explicitly capture `'_`
|
LL | fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> + use<'_> {
| +++++++++
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0515, E0700.
For more information about an error, try `rustc --explain E0515`.
@@ -0,0 +1,18 @@
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure-simplified.rs:27:21
|
LL | call_async_once(f);
| --------------- ^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure-simplified.rs:18:28
|
LL | ...pl AsyncFnOnce()) {}
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,29 @@
// This test ensures iterators created with the `iter!` macro are not
// accidentally async closures.
//
// We test this both in a `narrow` and `wide` configuration because
// the way that the diagnostic is emitted varies depending on the
// diagnostic width. If it's too narrow to fit the explanation, that
// explanation is moved to the `help` instead of the span label.
//
//@ edition: 2024
//@ revisions: narrow wide
//@[narrow] compile-flags: --diagnostic-width=20
//@[wide] compile-flags: --diagnostic-width=300
#![feature(yield_expr, iter_macro)]
use std::iter::iter;
fn call_async_once(_: impl AsyncFnOnce()) {}
fn main() {
let f = iter! { move || {
for i in 0..10 {
yield i;
}
}};
call_async_once(f);
//~^ ERROR AsyncFnOnce()` is not satisfied
}
@@ -0,0 +1,17 @@
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure-simplified.rs:27:21
|
LL | call_async_once(f);
| --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure-simplified.rs:21:21: 21:28}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure-simplified.rs:18:28
|
LL | fn call_async_once(_: impl AsyncFnOnce()) {}
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,72 @@
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:32:34
|
LL | ...n!(call_async_once(f));
| --------------- ^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:21:34
|
LL | ...pl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:32:18
|
LL | ...n!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:21:34
|
LL | ...pl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:32:13
|
LL | ... = pin!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:21:34
|
LL | ...pl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
= note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:32:13
|
LL | ... = pin!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:21:34
|
LL | ...pl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
= note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:37:5
|
LL | ...::noop()));
| ...^^^^^^^^^^ unsatisfied trait bound
|
= help: the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:26:21: 26:28}`
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:21:34
|
LL | ...pl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,32 @@
// This test ensures iterators created with the `iter!` macro are not
// accidentally async closures.
//
//@ edition: 2024
//@ remap-src-base
#![feature(yield_expr, iter_macro)]
use std::task::{Waker, Context};
use std::iter::iter;
use std::pin::pin;
use std::future::Future;
async fn call_async_once(f: impl AsyncFnOnce()) {
f().await
}
fn main() {
let f = iter! { move || {
for i in 0..10 {
yield i;
}
}};
let x = pin!(call_async_once(f));
//~^ ERROR AsyncFnOnce()` is not satisfied
//~^^ ERROR AsyncFnOnce()` is not satisfied
//~^^^ ERROR AsyncFnOnce()` is not satisfied
//~^^^^ ERROR AsyncFnOnce()` is not satisfied
x.poll(&mut Context::from_waker(Waker::noop()));
//~^ ERROR AsyncFnOnce()` is not satisfied
}
@@ -0,0 +1,67 @@
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:25:34
|
LL | let x = pin!(call_async_once(f));
| --------------- ^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}`
| |
| required by a bound introduced by this call
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:14:34
|
LL | async fn call_async_once(f: impl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:25:18
|
LL | let x = pin!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}`
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:14:34
|
LL | async fn call_async_once(f: impl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:25:13
|
LL | let x = pin!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}`
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:14:34
|
LL | async fn call_async_once(f: impl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
= note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:25:13
|
LL | let x = pin!(call_async_once(f));
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}`
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:14:34
|
LL | async fn call_async_once(f: impl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
= note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}: AsyncFnOnce()` is not satisfied
--> $DIR/iter-macro-not-async-closure.rs:30:5
|
LL | x.poll(&mut Context::from_waker(Waker::noop()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsyncFnOnce()` is not implemented for `{gen closure@$DIR/iter-macro-not-async-closure.rs:19:21: 19:28}`
|
note: required by a bound in `call_async_once`
--> $DIR/iter-macro-not-async-closure.rs:14:34
|
LL | async fn call_async_once(f: impl AsyncFnOnce()) {
| ^^^^^^^^^^^^^ required by this bound in `call_async_once`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0277`.
+1 -1
View File
@@ -1,7 +1,7 @@
// Test that we do not suggest to add type annotations for unnamable types.
#![crate_type="lib"]
#![feature(coroutines, stmt_expr_attributes)]
#![feature(coroutines, stmt_expr_attributes, const_async_blocks)]
const A = 5;
//~^ ERROR: missing type for `const` item
@@ -1,4 +1,4 @@
#![feature(coroutines, coroutine_trait, rustc_attrs)]
#![feature(coroutines, coroutine_trait, rustc_attrs, const_async_blocks)]
#![feature(type_alias_impl_trait)]
//@ check-pass