diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 5e319449290c..d6d672c859b0 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -111,7 +111,7 @@ vec_api_by_item_type, ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional -from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op +from mypyc.irbuild.builder import IRBuilder, expr_has_suspend, int_borrow_friendly_op from mypyc.irbuild.constant_fold import constant_fold_expr from mypyc.irbuild.for_helpers import ( comprehension_helper, @@ -1613,10 +1613,25 @@ def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: if isinstance(o.left_expr, StarExpr): builder.error("Unpacking in comprehensions is not supported yet", o.line) return builder.none() + if is_async_generator_expr(o): + builder.error("Async generator expressions are not supported", o.line) + return builder.none() builder.warning("Treating generator comprehension as list", o.line) return builder.primitive_op(iter_op, [translate_generator_to_list(builder, o)], o.line) +def is_async_generator_expr(o: GeneratorExpr) -> bool: + """Is this an async generator expression (it has 'async for' or 'await')? + + The first iterable is evaluated in the enclosing scope, so an 'await' there doesn't + make the generator expression async. + """ + if any(o.is_async): + return True + parts = [o.left_expr, *o.sequences[1:]] + [cond for conds in o.condlists for cond in conds] + return any(expr_has_suspend(e) for e in parts) + + def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value: value = builder.accept(o.value) target = builder.get_assignment_target(o.target) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 83b2a16be791..af1859e3a110 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3868,3 +3868,17 @@ def get_length(): L0: r0 = 'https://w3id.org/cwl/salad#' return 54 + +[case testAsyncGeneratorExprNotSupported] +from typing import AsyncIterator + +async def h(x: int) -> int: + return x + +async def f(xs: list[int], a: AsyncIterator[int]) -> None: + g1 = (await h(x) for x in xs) # E: Async generator expressions are not supported + g2 = (x async for x in a) # E: Async generator expressions are not supported + g3 = (x for x in xs if [y async for y in a]) # E: Async generator expressions are not supported + # An 'await' in the first iterable is evaluated in the enclosing function. + g4 = (x for x in [await h(1)]) # W: Treating generator comprehension as list +[typing fixtures/typing-full.pyi]