Skip to content

Commit f19b5d3

Browse files
authored
[mypyc] Fix compilation of unreachable comprehensions (#15721)
Fixes mypyc/mypyc#816. Admittedly hacky.
1 parent 9f1c90a commit f19b5d3

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

mypyc/irbuild/expression.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
from mypyc.irbuild.constant_fold import constant_fold_expr
8181
from mypyc.irbuild.for_helpers import (
8282
comprehension_helper,
83+
raise_error_if_contains_unreachable_names,
8384
translate_list_comprehension,
8485
translate_set_comprehension,
8586
)
@@ -1020,6 +1021,9 @@ def transform_set_comprehension(builder: IRBuilder, o: SetComprehension) -> Valu
10201021

10211022

10221023
def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value:
1024+
if raise_error_if_contains_unreachable_names(builder, o):
1025+
return builder.none()
1026+
10231027
d = builder.maybe_spill(builder.call_c(dict_new_op, [], o.line))
10241028
loop_params = list(zip(o.indices, o.sequences, o.condlists, o.is_async))
10251029

mypyc/irbuild/for_helpers.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
from mypy.nodes import (
1313
ARG_POS,
1414
CallExpr,
15+
DictionaryComprehension,
1516
Expression,
1617
GeneratorExpr,
1718
Lvalue,
1819
MemberExpr,
20+
NameExpr,
1921
RefExpr,
2022
SetExpr,
2123
TupleExpr,
@@ -28,6 +30,7 @@
2830
IntOp,
2931
LoadAddress,
3032
LoadMem,
33+
RaiseStandardError,
3134
Register,
3235
TupleGet,
3336
TupleSet,
@@ -229,6 +232,9 @@ def set_item(item_index: Value) -> None:
229232

230233

231234
def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value:
235+
if raise_error_if_contains_unreachable_names(builder, gen):
236+
return builder.none()
237+
232238
# Try simplest list comprehension, otherwise fall back to general one
233239
val = sequence_from_generator_preallocate_helper(
234240
builder,
@@ -251,7 +257,30 @@ def gen_inner_stmts() -> None:
251257
return builder.read(list_ops)
252258

253259

260+
def raise_error_if_contains_unreachable_names(
261+
builder: IRBuilder, gen: GeneratorExpr | DictionaryComprehension
262+
) -> bool:
263+
"""Raise a runtime error and return True if generator contains unreachable names.
264+
265+
False is returned if the generator can be safely transformed without crashing.
266+
(It may still be unreachable!)
267+
"""
268+
if any(isinstance(s, NameExpr) and s.node is None for s in gen.indices):
269+
error = RaiseStandardError(
270+
RaiseStandardError.RUNTIME_ERROR,
271+
"mypyc internal error: should be unreachable",
272+
gen.line,
273+
)
274+
builder.add(error)
275+
return True
276+
277+
return False
278+
279+
254280
def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value:
281+
if raise_error_if_contains_unreachable_names(builder, gen):
282+
return builder.none()
283+
255284
set_ops = builder.maybe_spill(builder.new_set_op([], gen.line))
256285
loop_params = list(zip(gen.indices, gen.sequences, gen.condlists, gen.is_async))
257286

mypyc/test-data/run-misc.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,8 +1097,10 @@ B = sys.platform == 'x' and sys.foobar
10971097
C = sys.platform == 'x' and f(a, -b, 'y') > [c + e, g(y=2)]
10981098
C = sys.platform == 'x' and cast(a, b[c])
10991099
C = sys.platform == 'x' and (lambda x: y + x)
1100-
# TODO: This still doesn't work
1101-
# C = sys.platform == 'x' and (x for y in z)
1100+
C = sys.platform == 'x' and (x for y in z)
1101+
C = sys.platform == 'x' and [x for y in z]
1102+
C = sys.platform == 'x' and {x: x for y in z}
1103+
C = sys.platform == 'x' and {x for y in z}
11021104

11031105
assert not A
11041106
assert not B

0 commit comments

Comments
 (0)