Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-126835: Disable tuple folding in the AST optimizer#128802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
2f475e17a96d476d9334302a38a35c05d6905f3e62a3d97909f1feb4ac50aad2b39a1317dffaa46845f0e9631d853dfa939754820477c7846731dfbaad9fb3be400935aec965ee69f0f0801463a268315File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -153,22 +153,6 @@ def test_optimization_levels__debug__(self): | ||
| self.assertIsInstance(res.body[0].value, ast.Name) | ||
| self.assertEqual(res.body[0].value.id, expected) | ||
| def test_optimization_levels_const_folding(self): | ||
| folded = ('Expr', (1, 0, 1, 6), ('Constant', (1, 0, 1, 6), (1, 2), None)) | ||
| not_folded = ('Expr', (1, 0, 1, 6), | ||
| ('Tuple', (1, 0, 1, 6), | ||
| [('Constant', (1, 1, 1, 2), 1, None), | ||
| ('Constant', (1, 4, 1, 5), 2, None)], ('Load',))) | ||
| cases = [(-1, not_folded), (0, not_folded), (1, folded), (2, folded)] | ||
| for (optval, expected) in cases: | ||
| with self.subTest(optval=optval): | ||
| tree1 = ast.parse("(1, 2)", optimize=optval) | ||
| tree2 = ast.parse(ast.parse("(1, 2)"), optimize=optval) | ||
| for tree in [tree1, tree2]: | ||
| res = to_tuple(tree.body[0]) | ||
| self.assertEqual(res, expected) | ||
| def test_invalid_position_information(self): | ||
| invalid_linenos = [ | ||
| (10, 1), (-10, -11), (10, -11), (-5, -2), (-5, 1) | ||
| @@ -3138,101 +3122,6 @@ def test_folding_format(self): | ||
| self.assert_ast(code, non_optimized_target, optimized_target) | ||
| def test_folding_tuple(self): | ||
| code = "(1,)" | ||
| non_optimized_target = self.wrap_expr(ast.Tuple(elts=[ast.Constant(1)])) | ||
| optimized_target = self.wrap_expr(ast.Constant(value=(1,))) | ||
| self.assert_ast(code, non_optimized_target, optimized_target) | ||
| def test_folding_type_param_in_function_def(self): | ||
| code = "def foo[%s = (1, 2)](): pass" | ||
| unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) | ||
| unoptimized_type_params = [ | ||
| ("T", "T", ast.TypeVar), | ||
| ("**P", "P", ast.ParamSpec), | ||
| ("*Ts", "Ts", ast.TypeVarTuple), | ||
| ] | ||
| for type, name, type_param in unoptimized_type_params: | ||
| result_code = code % type | ||
| optimized_target = self.wrap_statement( | ||
| ast.FunctionDef( | ||
| name='foo', | ||
| args=ast.arguments(), | ||
| body=[ast.Pass()], | ||
| type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))] | ||
| ) | ||
| ) | ||
| non_optimized_target = self.wrap_statement( | ||
| ast.FunctionDef( | ||
| name='foo', | ||
| args=ast.arguments(), | ||
| body=[ast.Pass()], | ||
| type_params=[type_param(name=name, default_value=unoptimized_tuple)] | ||
| ) | ||
| ) | ||
| self.assert_ast(result_code, non_optimized_target, optimized_target) | ||
| def test_folding_type_param_in_class_def(self): | ||
| code = "class foo[%s = (1, 2)]: pass" | ||
| unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) | ||
| unoptimized_type_params = [ | ||
| ("T", "T", ast.TypeVar), | ||
| ("**P", "P", ast.ParamSpec), | ||
| ("*Ts", "Ts", ast.TypeVarTuple), | ||
| ] | ||
| for type, name, type_param in unoptimized_type_params: | ||
| result_code = code % type | ||
| optimized_target = self.wrap_statement( | ||
| ast.ClassDef( | ||
| name='foo', | ||
| body=[ast.Pass()], | ||
| type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))] | ||
| ) | ||
| ) | ||
| non_optimized_target = self.wrap_statement( | ||
| ast.ClassDef( | ||
| name='foo', | ||
| body=[ast.Pass()], | ||
| type_params=[type_param(name=name, default_value=unoptimized_tuple)] | ||
| ) | ||
| ) | ||
| self.assert_ast(result_code, non_optimized_target, optimized_target) | ||
| def test_folding_type_param_in_type_alias(self): | ||
| code = "type foo[%s = (1, 2)] = 1" | ||
| unoptimized_tuple = ast.Tuple(elts=[ast.Constant(1), ast.Constant(2)]) | ||
| unoptimized_type_params = [ | ||
| ("T", "T", ast.TypeVar), | ||
| ("**P", "P", ast.ParamSpec), | ||
| ("*Ts", "Ts", ast.TypeVarTuple), | ||
| ] | ||
| for type, name, type_param in unoptimized_type_params: | ||
| result_code = code % type | ||
| optimized_target = self.wrap_statement( | ||
| ast.TypeAlias( | ||
| name=ast.Name(id='foo', ctx=ast.Store()), | ||
| type_params=[type_param(name=name, default_value=ast.Constant((1, 2)))], | ||
| value=ast.Constant(value=1), | ||
| ) | ||
| ) | ||
| non_optimized_target = self.wrap_statement( | ||
| ast.TypeAlias( | ||
| name=ast.Name(id='foo', ctx=ast.Store()), | ||
| type_params=[type_param(name=name, default_value=unoptimized_tuple)], | ||
| value=ast.Constant(value=1), | ||
| ) | ||
| ) | ||
| self.assert_ast(result_code, non_optimized_target, optimized_target) | ||
Comment on lines
-3150
to
-3234
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as with | ||
| def test_folding_match_case_allowed_expressions(self): | ||
| def get_match_case_values(node): | ||
| result = [] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -154,18 +154,33 @@ def test_folding_of_tuples_of_constants(self): | ||
| for line, elem in ( | ||
| ('a = 1,2,3', (1, 2, 3)), | ||
| ('("a","b","c")', ('a', 'b', 'c')), | ||
| ('a,b,c = 1,2,3', (1, 2, 3)), | ||
| ('a,b,c,d = 1,2,3,4', (1, 2, 3, 4)), | ||
| ('(None, 1, None)', (None, 1, None)), | ||
| ('((1, 2), 3, 4)', ((1, 2), 3, 4)), | ||
| ('(1, 2, (3, 4))', (1, 2, (3, 4))), | ||
| ('()', ()), | ||
| ('(1, (2, (3, (4, (5,)))))', (1, (2, (3, (4, (5,)))))), | ||
| ): | ||
| with self.subTest(line=line): | ||
| code = compile(line,'','single') | ||
| self.assertInBytecode(code, 'LOAD_CONST', elem) | ||
| self.assertNotInBytecode(code, 'BUILD_TUPLE') | ||
| self.check_lnotab(code) | ||
| # Long tuples should be folded too. | ||
| code = compile(repr(tuple(range(10000))),'','single') | ||
| for expr, length in ( | ||
| ('(1, a)', 2), | ||
| ('(a, b, c)', 3), | ||
| ('(a, (b, c))', 2), | ||
| ('(1, [], {})', 3), | ||
| ): | ||
| with self.subTest(expr=expr, length=length): | ||
| code = compile(expr, '', 'single') | ||
| self.assertInBytecode(code, 'BUILD_TUPLE', length) | ||
| self.check_lnotab(code) | ||
| # Long tuples should be folded too, but their length should not | ||
| # exceed the `STACK_USE_GUIDELINE` | ||
MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we perhaps add a test that tuples longer than Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about it. At the moment, for constant tuples which are longer than Shall we assert that this intrinsic is presented in bytecode? | ||
| code = compile(repr(tuple(range(30))),'','single') | ||
Comment on lines
+181
to
+183
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How much of a concern is that we can no longer create constant tuples beyond length of MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably not ideal in case you have some kind of large constant lookup table for instance. As Kirill pointed out, this would get compiled to a bunch of Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not a hard pattern to detect, right? Maybe we could fold it anyway? @Eclips4 MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems quite predictable: >>>deffoo(): ... return (1,2,3, ... ,31) ... >>>dis.dis(foo) 1RESUME02BUILD_LIST0LOAD_SMALL_INT1LIST_APPEND1LOAD_SMALL_INT2LIST_APPEND1 ...
LOAD_SMALL_INT31LIST_APPEND1CALL_INTRINSIC_16 (INTRINSIC_LIST_TO_TUPLE)(Could also be Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly. I think we can easily fold it. MemberAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could check with Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's a good idea. We would be creating dependency between both.
| ||
| self.assertNotInBytecode(code, 'BUILD_TUPLE') | ||
| # One LOAD_CONST for the tuple, one for the None return value | ||
| load_consts = [instr for instr in dis.get_instructions(code) | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as with
test_compile_ast. Maybe add test for__debug__instead of removing test entirely?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there is
test_optimization_levels__debug__right above this one, so this one can indeed be gone.