Skip to content

Commit 47410c6

Browse files
oli-obkeholk
authored andcommitted
Make iter a builtin macro
1 parent 02e5d97 commit 47410c6

File tree

15 files changed

+366
-18
lines changed

15 files changed

+366
-18
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 8 additions & 7 deletions
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

@@ -1199,11 +1198,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
11991198
let closure_def_id = self.local_def_id(closure_id);
12001199
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
12011200

1202-
assert_matches!(
1203-
coroutine_kind,
1204-
CoroutineKind::Async { .. },
1205-
"only async closures are supported currently"
1206-
);
1201+
let coroutine_desugaring = match coroutine_kind {
1202+
CoroutineKind::Async { .. } => hir::CoroutineDesugaring::Async,
1203+
CoroutineKind::Gen { .. } => hir::CoroutineDesugaring::Gen,
1204+
CoroutineKind::AsyncGen { span, .. } => {
1205+
span_bug!(span, "only async closures and `iter!` closures are supported currently")
1206+
}
1207+
};
12071208

12081209
let body = self.with_new_scopes(fn_decl_span, |this| {
12091210
let inner_decl =
@@ -1247,7 +1248,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
12471248
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
12481249
// knows that a `FnDecl` output type like `-> &str` actually means
12491250
// "coroutine that returns &str", rather than directly returning a `&str`.
1250-
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
1251+
kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring),
12511252
constness: hir::Constness::NotConst,
12521253
});
12531254
hir::ExprKind::Closure(c)

compiler/rustc_borrowck/src/type_check/input_output.rs

Lines changed: 1 addition & 1 deletion
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"
Lines changed: 78 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ mod errors;
4949
mod format;
5050
mod format_foreign;
5151
mod global_allocator;
52+
mod iter;
5253
mod log_syntax;
5354
mod pattern_type;
5455
mod source_util;
@@ -97,6 +98,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
9798
include: source_util::expand_include,
9899
include_bytes: source_util::expand_include_bytes,
99100
include_str: source_util::expand_include_str,
101+
iter: iter::expand,
100102
line: source_util::expand_line,
101103
log_syntax: log_syntax::expand_log_syntax,
102104
module_path: source_util::expand_mod,

compiler/rustc_hir_typeck/src/closure.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
204204
)
205205
}
206206
hir::ClosureKind::CoroutineClosure(kind) => {
207-
// async closures always return the type ascribed after the `->` (if present),
208-
// and yield `()`.
209207
let (bound_return_ty, bound_yield_ty) = match kind {
208+
hir::CoroutineDesugaring::Gen => {
209+
// `iter!` closures always return unit and yield the `Iterator::Item` type
210+
// that we have to infer.
211+
(tcx.types.unit, self.infcx.next_ty_var(expr_span))
212+
}
210213
hir::CoroutineDesugaring::Async => {
214+
// async closures always return the type ascribed after the `->` (if present),
215+
// and yield `()`.
211216
(bound_sig.skip_binder().output(), tcx.types.unit)
212217
}
213-
hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => {
214-
todo!("`gen` and `async gen` closures not supported yet")
218+
hir::CoroutineDesugaring::AsyncGen => {
219+
todo!("`async gen` closures not supported yet")
215220
}
216221
};
217222
// Compute all of the variables that will be used to populate the coroutine.

compiler/rustc_parse/src/parser/stmt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ impl<'a> Parser<'a> {
714714

715715
/// Parses the rest of a block expression or function body.
716716
/// Precondition: already parsed the '{'.
717-
pub(crate) fn parse_block_tail(
717+
pub fn parse_block_tail(
718718
&mut self,
719719
lo: Span,
720720
s: BlockCheckMode,

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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 2 additions & 4 deletions
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

Lines changed: 26 additions & 0 deletions
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+
}
Lines changed: 29 additions & 0 deletions
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+
}

0 commit comments

Comments
 (0)