Skip to content

Commit 93f4140

Browse files
oli-obkeholk
authored andcommitted
Make iter a builtin macro
1 parent d0c9fed commit 93f4140

File tree

15 files changed

+366
-18
lines changed

15 files changed

+366
-18
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::assert_matches::assert_matches;
21
use std::ops::ControlFlow;
32
use std::sync::Arc;
43

@@ -1190,11 +1189,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
11901189
let closure_def_id = self.local_def_id(closure_id);
11911190
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
11921191

1193-
assert_matches!(
1194-
coroutine_kind,
1195-
CoroutineKind::Async { .. },
1196-
"only async closures are supported currently"
1197-
);
1192+
let coroutine_desugaring = match coroutine_kind {
1193+
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
1194+
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
1195+
CoroutineKind::AsyncGen { span, .. } => {
1196+
span_bug!(span, "only async closures and `iter!` closures are supported currently")
1197+
}
1198+
};
11981199

11991200
let body = self.with_new_scopes(fn_decl_span, |this| {
12001201
let inner_decl =
@@ -1238,7 +1239,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
12381239
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
12391240
// knows that a `FnDecl` output type like `-> &str` actually means
12401241
// "coroutine that returns &str", rather than directly returning a `&str`.
1241-
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
1242+
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
12421243
constness: hir::Constness::NotConst,
12431244
});
12441245
hir::ExprKind::Closure(c)

compiler/rustc_borrowck/src/type_check/input_output.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
5252
assert_matches!(
5353
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
5454
Some(hir::CoroutineKind::Desugared(
55-
hir::CoroutineDesugaring::Async,
55+
hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::Gen,
5656
hir::CoroutineSource::Closure
5757
)),
5858
"this needs to be modified if we're lowering non-async closures"
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use rustc_ast::ptr::P;
2+
use rustc_ast::tokenstream::TokenStream;
3+
use rustc_ast::{
4+
CaptureBy, ClosureBinder, Const, CoroutineKind, DUMMY_NODE_ID, Expr, ExprKind, ast, token,
5+
};
6+
use rustc_errors::PResult;
7+
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
8+
use rustc_span::Span;
9+
10+
pub(crate) fn expand<'cx>(
11+
cx: &'cx mut ExtCtxt<'_>,
12+
sp: Span,
13+
tts: TokenStream,
14+
) -> MacroExpanderResult<'cx> {
15+
let closure = match parse_closure(cx, sp, tts) {
16+
Ok(parsed) => parsed,
17+
Err(err) => {
18+
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
19+
}
20+
};
21+
22+
ExpandResult::Ready(base::MacEager::expr(closure))
23+
}
24+
25+
fn parse_closure<'a>(
26+
cx: &mut ExtCtxt<'a>,
27+
span: Span,
28+
stream: TokenStream,
29+
) -> PResult<'a, P<Expr>> {
30+
let mut parser = cx.new_parser_from_tts(stream);
31+
let mut closure_parser = parser.clone();
32+
33+
let coroutine_kind = Some(CoroutineKind::Gen {
34+
span,
35+
closure_id: DUMMY_NODE_ID,
36+
return_impl_trait_id: DUMMY_NODE_ID,
37+
});
38+
39+
match closure_parser.parse_expr() {
40+
Ok(mut closure) => {
41+
if let ast::ExprKind::Closure(c) = &mut closure.kind {
42+
if let Some(kind) = c.coroutine_kind {
43+
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
44+
}
45+
c.coroutine_kind = coroutine_kind;
46+
if closure_parser.token != token::Eof {
47+
closure_parser.unexpected()?;
48+
}
49+
return Ok(closure);
50+
}
51+
}
52+
Err(diag) => diag.cancel(),
53+
}
54+
55+
let lo = parser.token.span.shrink_to_lo();
56+
let block = parser.parse_block_tail(
57+
lo,
58+
ast::BlockCheckMode::Default,
59+
rustc_parse::parser::AttemptLocalParseRecovery::No,
60+
)?;
61+
let fn_decl = cx.fn_decl(Default::default(), ast::FnRetTy::Default(span));
62+
let closure = ast::Closure {
63+
binder: ClosureBinder::NotPresent,
64+
capture_clause: CaptureBy::Ref,
65+
constness: Const::No,
66+
coroutine_kind,
67+
movability: ast::Movability::Movable,
68+
fn_decl,
69+
body: cx.expr_block(block),
70+
fn_decl_span: span,
71+
fn_arg_span: span,
72+
};
73+
if parser.token != token::Eof {
74+
parser.unexpected()?;
75+
}
76+
let span = lo.to(parser.token.span);
77+
Ok(cx.expr(span, ExprKind::Closure(Box::new(closure))))
78+
}

compiler/rustc_builtin_macros/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ mod errors;
4747
mod format;
4848
mod format_foreign;
4949
mod global_allocator;
50+
mod iter;
5051
mod log_syntax;
5152
mod pattern_type;
5253
mod source_util;
@@ -95,6 +96,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
9596
include: source_util::expand_include,
9697
include_bytes: source_util::expand_include_bytes,
9798
include_str: source_util::expand_include_str,
99+
iter: iter::expand,
98100
line: source_util::expand_line,
99101
log_syntax: log_syntax::expand_log_syntax,
100102
module_path: source_util::expand_mod,

compiler/rustc_hir_typeck/src/closure.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
199199
)
200200
}
201201
hir::ClosureKind::CoroutineClosure(kind) => {
202-
// async closures always return the type ascribed after the `->` (if present),
203-
// and yield `()`.
204202
let (bound_return_ty, bound_yield_ty) = match kind {
203+
hir::CoroutineDesugaring::Gen => {
204+
// `iter!` closures always return unit and yield the `Iterator::Item` type
205+
// that we have to infer.
206+
(tcx.types.unit, self.infcx.next_ty_var(expr_span))
207+
}
205208
hir::CoroutineDesugaring::Async => {
209+
// async closures always return the type ascribed after the `->` (if present),
210+
// and yield `()`.
206211
(bound_sig.skip_binder().output(), tcx.types.unit)
207212
}
208-
hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => {
209-
todo!("`gen` and `async gen` closures not supported yet")
213+
hir::CoroutineDesugaring::AsyncGen => {
214+
todo!("`async gen` closures not supported yet")
210215
}
211216
};
212217
// Compute all of the variables that will be used to populate the coroutine.

compiler/rustc_parse/src/parser/stmt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ impl<'a> Parser<'a> {
685685

686686
/// Parses the rest of a block expression or function body.
687687
/// Precondition: already parsed the '{'.
688-
pub(crate) fn parse_block_tail(
688+
pub fn parse_block_tail(
689689
&mut self,
690690
lo: Span,
691691
s: BlockCheckMode,

library/core/src/iter/sources/generator.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
/// ```
2121
#[unstable(feature = "iter_macro", issue = "none", reason = "generators are unstable")]
2222
#[allow_internal_unstable(coroutines, iter_from_coroutine)]
23+
#[cfg_attr(not(bootstrap), rustc_builtin_macro)]
2324
pub macro iter($($t:tt)*) {
24-
|| $crate::iter::from_coroutine(#[coroutine] || { $($t)* })
25+
/* compiler-builtin */
2526
}

tests/ui/iterators/generator.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ fn main() {
2121
assert_eq!(i.next(), Some(16));
2222
assert_eq!(i.next(), Some(18));
2323
assert_eq!(i.next(), None);
24-
// FIXME(iter_macro): desugar to `gen` instead of coroutines,
25-
// as the latter panic after resuming iteration.
26-
//assert_eq!(i.next(), None);
27-
//assert_eq!(i.next(), None);
24+
assert_eq!(i.next(), None);
25+
assert_eq!(i.next(), None);
2826
}

tests/ui/iterators/generator_args.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ run-pass
2+
3+
#![feature(iter_macro)]
4+
// FIXME(iter_macro): make `yield` within it legal
5+
#![feature(coroutines)]
6+
7+
use std::iter::iter;
8+
9+
fn main() {
10+
let i = iter! {|foo| {
11+
yield foo;
12+
for x in 5..10 {
13+
yield x * 2;
14+
}
15+
}};
16+
let mut i = i(3);
17+
assert_eq!(i.next(), Some(3));
18+
assert_eq!(i.next(), Some(10));
19+
assert_eq!(i.next(), Some(12));
20+
assert_eq!(i.next(), Some(14));
21+
assert_eq!(i.next(), Some(16));
22+
assert_eq!(i.next(), Some(18));
23+
assert_eq!(i.next(), None);
24+
assert_eq!(i.next(), None);
25+
assert_eq!(i.next(), None);
26+
}
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ run-pass
2+
3+
#![feature(iter_macro)]
4+
// FIXME(iter_macro): make `yield` within it legal
5+
#![feature(coroutines)]
6+
7+
use std::iter::iter;
8+
9+
fn main() {
10+
let i = {
11+
let s = String::new();
12+
iter! { move || {
13+
yield s.len();
14+
for x in 5..10 {
15+
yield x * 2;
16+
}
17+
}}
18+
};
19+
let mut i = i();
20+
assert_eq!(i.next(), Some(0));
21+
assert_eq!(i.next(), Some(10));
22+
assert_eq!(i.next(), Some(12));
23+
assert_eq!(i.next(), Some(14));
24+
assert_eq!(i.next(), Some(16));
25+
assert_eq!(i.next(), Some(18));
26+
assert_eq!(i.next(), None);
27+
assert_eq!(i.next(), None);
28+
assert_eq!(i.next(), None);
29+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ run-pass
2+
3+
#![feature(iter_macro)]
4+
// FIXME(iter_macro): make `yield` within it legal
5+
#![feature(coroutines)]
6+
7+
use std::iter::iter;
8+
9+
fn main() {
10+
let f = {
11+
let s = "foo".to_string();
12+
iter! { move || {
13+
for c in s.chars() {
14+
yield c;
15+
}
16+
}}
17+
};
18+
let mut i = f();
19+
assert_eq!(i.next(), Some('f'));
20+
assert_eq!(i.next(), Some('o'));
21+
assert_eq!(i.next(), Some('o'));
22+
assert_eq!(i.next(), None);
23+
let mut i = f();
24+
assert_eq!(i.next(), Some('f'));
25+
assert_eq!(i.next(), Some('o'));
26+
assert_eq!(i.next(), Some('o'));
27+
assert_eq!(i.next(), None);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![feature(iter_macro)]
2+
// FIXME(iter_macro): make `yield` within it legal
3+
#![feature(coroutines)]
4+
5+
use std::iter::iter;
6+
7+
fn main() {
8+
let i = {
9+
let s = String::new();
10+
iter! {
11+
yield s.len(); //~ ERROR `s` does not live long enough
12+
for x in 5..10 {
13+
yield x * 2;
14+
}
15+
}
16+
};
17+
let mut i = i();
18+
assert_eq!(i.next(), Some(0));
19+
assert_eq!(i.next(), Some(10));
20+
assert_eq!(i.next(), Some(12));
21+
assert_eq!(i.next(), Some(14));
22+
assert_eq!(i.next(), Some(16));
23+
assert_eq!(i.next(), Some(18));
24+
assert_eq!(i.next(), None);
25+
assert_eq!(i.next(), None);
26+
assert_eq!(i.next(), None);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0597]: `s` does not live long enough
2+
--> $DIR/generator_capture_fail.rs:11:13
3+
|
4+
LL | let i = {
5+
| - borrow later stored here
6+
...
7+
LL | / yield s.len();
8+
LL | | for x in 5..10 {
9+
LL | | yield x * 2;
10+
LL | | }
11+
| |_____________^ borrowed value does not live long enough
12+
LL | }
13+
LL | };
14+
| - `s` dropped here while still borrowed
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0597`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#![feature(iter_macro, impl_trait_in_fn_trait_return)]
2+
// FIXME(iter_macro): make `yield` within it legal
3+
#![feature(coroutines)]
4+
5+
use std::iter::iter;
6+
7+
fn plain() -> impl Fn() -> impl Iterator<Item = u32> {
8+
iter! {
9+
yield 0;
10+
for x in 5..10 {
11+
yield x * 2;
12+
}
13+
}
14+
}
15+
16+
fn arg() -> impl Fn(u32) -> impl Iterator<Item = u32> {
17+
iter! { |arg| {
18+
yield arg;
19+
for x in 5..10 {
20+
yield x * 2;
21+
}
22+
} }
23+
}
24+
25+
fn capture<'a>(a: &'a u32) -> impl Fn() -> (impl Iterator<Item = u32> + 'a) {
26+
iter! { || { //~ ERROR cannot return reference to function parameter `a`
27+
yield *a;
28+
for x in 5..10 {
29+
yield x * 2;
30+
}
31+
} }
32+
}
33+
34+
fn capture_move(a: &u32) -> impl Fn() -> impl Iterator<Item = u32> {
35+
iter! { move || { //~ ERROR does not implement `Fn` because it captures
36+
yield *a;
37+
for x in 5..10 {
38+
yield x * 2;
39+
}
40+
} }
41+
}
42+
43+
fn capture_move_once(a: &u32) -> impl FnOnce() -> impl Iterator<Item = u32> {
44+
iter! { move || {
45+
//~^ ERROR captures lifetime
46+
//~| ERROR: captures lifetime
47+
yield *a;
48+
for x in 5..10 {
49+
yield x * 2;
50+
}
51+
} }
52+
}
53+
54+
fn capture_move_once_lifetimes<'a>(
55+
a: &'a u32,
56+
) -> impl FnOnce() -> (impl Iterator<Item = u32> + 'a) {
57+
iter! { move || {
58+
yield *a;
59+
for x in 5..10 {
60+
yield x * 2;
61+
}
62+
} }
63+
}
64+
65+
fn main() {}

0 commit comments

Comments
 (0)