Skip to content
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

Add iter macro #137725

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
15 changes: 8 additions & 7 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::assert_matches::assert_matches;
use std::ops::ControlFlow;
use std::sync::Arc;

@@ -1190,11 +1189,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
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 =
@@ -1238,7 +1239,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// 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)
7 changes: 4 additions & 3 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -468,11 +468,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
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();
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/type_check/input_output.rs
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What i didn't do yet was grep for the async enum variants to see if there are any non exhaustive matches we need to change

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may also want to grep for usages of ty::CoroutineClosure to make sure that things like AsyncFn* impls (see comment below) are properly gated on checking the coroutine_kind. I think there's a more than a few places where I hard-coded the assumption that ty::CoroutineClosure are only async closures...

hir::CoroutineSource::Closure
)),
"this needs to be modified if we're lowering non-async closures"
53 changes: 53 additions & 0 deletions compiler/rustc_builtin_macros/src/iter.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions compiler/rustc_builtin_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ mod errors;
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,
2 changes: 0 additions & 2 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -1899,9 +1899,7 @@ impl CoroutineKind {
CoroutineKind::Coroutine(mov) => mov,
}
}
}

impl CoroutineKind {
pub fn is_fn_like(self) -> bool {
matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
}
13 changes: 9 additions & 4 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
@@ -199,14 +199,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}
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.
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
@@ -685,7 +685,7 @@ impl<'a> Parser<'a> {

/// 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,
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/messages.ftl
Original file line number Diff line number Diff line change
@@ -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] {""}
Original file line number Diff line number Diff line change
@@ -37,9 +37,7 @@ use super::{
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::{
@@ -904,9 +902,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
&& let ty::FnPtr(sig_tys, _) = by_ref_captures.kind()
&& !sig_tys.skip_binder().output().is_unit()
{
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());
5 changes: 3 additions & 2 deletions compiler/rustc_trait_selection/src/errors.rs
Original file line number Diff line number Diff line change
@@ -170,11 +170,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)]
2 changes: 2 additions & 0 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
@@ -420,6 +420,8 @@ pub use self::adapters::{Intersperse, IntersperseWith};
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 changes: 3 additions & 0 deletions library/core/src/iter/sources.rs
Original file line number Diff line number Diff line change
@@ -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::empty::{Empty, empty};
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")]
29 changes: 29 additions & 0 deletions library/core/src/iter/sources/generator.rs
Original file line number Diff line number Diff line change
@@ -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 */
}
4 changes: 2 additions & 2 deletions tests/ui/coroutine/gen_block.none.stderr
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions tests/ui/feature-gates/feature-gate-coroutines.e2024.stderr
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions tests/ui/feature-gates/feature-gate-coroutines.none.stderr
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/ui/feature-gates/feature-gate-yield-expr.stderr
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading