Skip to content

Commit 27a894f

Browse files
authored
Rollup merge of #137725 - oli-obk:i-want-to-move-it-move-it, r=compiler-errors,traviscross
Add `iter` macro See related discussion in https://rust-lang.zulipchat.com/#narrow/channel/481571-t-lang.2Fgen/topic/iter!.20macro/near/500784563 very little error case testing so far, but the success path works. There is also no `IterFn` trait yet, as T-lang didn't consider it something urgently needed I think we can implement it in follow-up PRs. r? lang for the tests, `@compiler-errors` for the impl
2 parents a124fb3 + 5fbdfc3 commit 27a894f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+826
-61
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_ast_passes/src/feature_gate.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -477,11 +477,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
477477
for span in spans {
478478
if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines))
479479
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
480+
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
480481
{
481482
#[allow(rustc::untranslatable_diagnostic)]
482-
// Don't know which of the two features to include in the
483-
// error message, so I am arbitrarily picking one.
484-
feature_err(&visitor.sess, sym::coroutines, *span, "yield syntax is experimental")
483+
// Emit yield_expr as the error, since that will be sufficient. You can think of it
484+
// as coroutines and gen_blocks imply yield_expr.
485+
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
485486
.emit();
486487
}
487488
}

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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use rustc_ast::ptr::P;
2+
use rustc_ast::tokenstream::TokenStream;
3+
use rustc_ast::{CoroutineKind, DUMMY_NODE_ID, Expr, ast, token};
4+
use rustc_errors::PResult;
5+
use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult};
6+
use rustc_span::Span;
7+
8+
pub(crate) fn expand<'cx>(
9+
cx: &'cx mut ExtCtxt<'_>,
10+
sp: Span,
11+
tts: TokenStream,
12+
) -> MacroExpanderResult<'cx> {
13+
let closure = match parse_closure(cx, sp, tts) {
14+
Ok(parsed) => parsed,
15+
Err(err) => {
16+
return ExpandResult::Ready(DummyResult::any(sp, err.emit()));
17+
}
18+
};
19+
20+
ExpandResult::Ready(base::MacEager::expr(closure))
21+
}
22+
23+
fn parse_closure<'a>(
24+
cx: &mut ExtCtxt<'a>,
25+
span: Span,
26+
stream: TokenStream,
27+
) -> PResult<'a, P<Expr>> {
28+
let mut closure_parser = cx.new_parser_from_tts(stream);
29+
30+
let coroutine_kind = Some(CoroutineKind::Gen {
31+
span,
32+
closure_id: DUMMY_NODE_ID,
33+
return_impl_trait_id: DUMMY_NODE_ID,
34+
});
35+
36+
let mut closure = closure_parser.parse_expr()?;
37+
match &mut closure.kind {
38+
ast::ExprKind::Closure(c) => {
39+
if let Some(kind) = c.coroutine_kind {
40+
cx.dcx().span_err(kind.span(), "only plain closures allowed in `iter!`");
41+
}
42+
c.coroutine_kind = coroutine_kind;
43+
if closure_parser.token != token::Eof {
44+
closure_parser.unexpected()?;
45+
}
46+
Ok(closure)
47+
}
48+
_ => {
49+
cx.dcx().span_err(closure.span, "`iter!` body must be a closure");
50+
Err(closure_parser.unexpected().unwrap_err())
51+
}
52+
}
53+
}

compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 2 additions & 0 deletions
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_const_eval/src/check_consts/check.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -589,12 +589,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
589589

590590
Rvalue::Aggregate(kind, ..) => {
591591
if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref()
592-
&& let Some(
593-
coroutine_kind @ hir::CoroutineKind::Desugared(
594-
hir::CoroutineDesugaring::Async,
595-
_,
596-
),
597-
) = self.tcx.coroutine_kind(def_id)
592+
&& let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
598593
{
599594
self.check_op(ops::Coroutine(coroutine_kind));
600595
}

compiler/rustc_const_eval/src/check_consts/ops.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -486,24 +486,25 @@ impl<'tcx> NonConstOp<'tcx> for IntrinsicUnstable {
486486
pub(crate) struct Coroutine(pub hir::CoroutineKind);
487487
impl<'tcx> NonConstOp<'tcx> for Coroutine {
488488
fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
489-
if let hir::CoroutineKind::Desugared(
490-
hir::CoroutineDesugaring::Async,
491-
hir::CoroutineSource::Block,
492-
) = self.0
493-
{
494-
Status::Unstable {
489+
match self.0 {
490+
hir::CoroutineKind::Desugared(
491+
hir::CoroutineDesugaring::Async,
492+
hir::CoroutineSource::Block,
493+
)
494+
// FIXME(coroutines): eventually we want to gate const coroutine coroutines behind a
495+
// different feature.
496+
| hir::CoroutineKind::Coroutine(_) => Status::Unstable {
495497
gate: sym::const_async_blocks,
496498
gate_already_checked: false,
497499
safe_to_expose_on_stable: false,
498500
is_function_call: false,
499-
}
500-
} else {
501-
Status::Forbidden
501+
},
502+
_ => Status::Forbidden,
502503
}
503504
}
504505

505506
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
506-
let msg = format!("{:#}s are not allowed in {}s", self.0, ccx.const_kind());
507+
let msg = format!("{} are not allowed in {}s", self.0.to_plural_string(), ccx.const_kind());
507508
if let Status::Unstable { gate, .. } = self.status_in_item(ccx) {
508509
ccx.tcx.sess.create_feature_err(errors::UnallowedOpInConstContext { span, msg }, gate)
509510
} else {

compiler/rustc_hir/src/hir.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,12 +2061,19 @@ impl CoroutineKind {
20612061
CoroutineKind::Coroutine(mov) => mov,
20622062
}
20632063
}
2064-
}
20652064

2066-
impl CoroutineKind {
20672065
pub fn is_fn_like(self) -> bool {
20682066
matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
20692067
}
2068+
2069+
pub fn to_plural_string(&self) -> String {
2070+
match self {
2071+
CoroutineKind::Desugared(d, CoroutineSource::Fn) => format!("{d:#}fn bodies"),
2072+
CoroutineKind::Desugared(d, CoroutineSource::Block) => format!("{d:#}blocks"),
2073+
CoroutineKind::Desugared(d, CoroutineSource::Closure) => format!("{d:#}closure bodies"),
2074+
CoroutineKind::Coroutine(_) => "coroutines".to_string(),
2075+
}
2076+
}
20702077
}
20712078

20722079
impl fmt::Display for CoroutineKind {

compiler/rustc_hir_typeck/src/closure.rs

Lines changed: 15 additions & 5 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.
@@ -465,7 +470,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
465470

466471
if let Some(trait_def_id) = trait_def_id {
467472
let found_kind = match closure_kind {
468-
hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
473+
hir::ClosureKind::Closure
474+
// FIXME(iter_macro): Someday we'll probably want iterator closures instead of
475+
// just using Fn* for iterators.
476+
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => {
477+
self.tcx.fn_trait_kind_from_def_id(trait_def_id)
478+
}
469479
hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self
470480
.tcx
471481
.async_fn_trait_kind_from_def_id(trait_def_id)

compiler/rustc_parse/src/parser/stmt.rs

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

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

0 commit comments

Comments
 (0)