diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py index 1c202f82e941e8..b952c0c984b1f1 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -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) - def test_folding_match_case_allowed_expressions(self): def get_match_case_values(node): result = [] diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index d15964fe9dd88b..c6c0d844ab960d 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -555,7 +555,7 @@ def test_compile_async_generator(self): self.assertEqual(type(glob['ticker']()), AsyncGeneratorType) def test_compile_ast(self): - args = ("a*(1,2)", "f.py", "exec") + args = ("a*__debug__", "f.py", "exec") raw = compile(*args, flags = ast.PyCF_ONLY_AST).body[0] opt1 = compile(*args, flags = ast.PyCF_OPTIMIZED_AST).body[0] opt2 = compile(ast.parse(args[0]), *args[1:], flags = ast.PyCF_OPTIMIZED_AST).body[0] @@ -566,14 +566,13 @@ def test_compile_ast(self): self.assertIsInstance(tree.value.left, ast.Name) self.assertEqual(tree.value.left.id, 'a') - raw_right = raw.value.right # expect Tuple((1, 2)) - self.assertIsInstance(raw_right, ast.Tuple) - self.assertListEqual([elt.value for elt in raw_right.elts], [1, 2]) + raw_right = raw.value.right + self.assertIsInstance(raw_right, ast.Name) for opt in [opt1, opt2]: - opt_right = opt.value.right # expect Constant((1,2)) + opt_right = opt.value.right self.assertIsInstance(opt_right, ast.Constant) - self.assertEqual(opt_right.value, (1, 2)) + self.assertEqual(opt_right.value, __debug__) def test_delattr(self): sys.spam = 1 diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index e0a33b8141c709..bed91337581fa9 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -797,9 +797,9 @@ def check_same_constant(const): f1, f2 = lambda: "not a name", lambda: ("not a name",) f3 = lambda x: x in {("not a name",)} self.assertIs(f1.__code__.co_consts[0], - f2.__code__.co_consts[0][0]) + f2.__code__.co_consts[1][0]) self.assertIs(next(iter(f3.__code__.co_consts[1])), - f2.__code__.co_consts[0]) + f2.__code__.co_consts[1]) # {0} is converted to a constant frozenset({0}) by the peephole # optimizer diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index e4224b843b2e00..c55a569b570783 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -1665,8 +1665,10 @@ def to_bool_str(): @requires_specialization_ft def test_unpack_sequence(self): def unpack_sequence_two_tuple(): + + t = 1, 2 for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): - a, b = 1, 2 + a, b = t self.assertEqual(a, 1) self.assertEqual(b, 2) @@ -1677,8 +1679,8 @@ def unpack_sequence_two_tuple(): def unpack_sequence_tuple(): for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD): - a, = 1, - self.assertEqual(a, 1) + a, b, c, d = 1, 2, 3, 4 + self.assertEqual((a, b, c, d), (1, 2, 3, 4)) unpack_sequence_tuple() self.assert_specialized(unpack_sequence_tuple, "UNPACK_SEQUENCE_TUPLE") diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 98f6b29dc7fc5e..7ee8aa4de51c5d 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -154,9 +154,12 @@ 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') @@ -164,8 +167,20 @@ def test_folding_of_tuples_of_constants(self): 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` + code = compile(repr(tuple(range(30))),'','single') 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) diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 0fe8d3d3f7d8c6..18537311174dcf 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -9,19 +9,19 @@ unsigned char M_test_frozenmain[] = { 31,0,89,1,78,8,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,33,0,51,0,0,0,0,0, 0,0,80,3,44,26,0,0,0,0,0,0,0,0,0,0, - 112,5,80,4,16,0,68,24,0,0,112,6,89,2,33,0, - 80,5,89,6,12,0,80,6,89,5,89,6,44,26,0,0, + 112,5,80,6,16,0,68,24,0,0,112,6,89,2,33,0, + 80,4,89,6,12,0,80,5,89,5,89,6,44,26,0,0, 0,0,0,0,0,0,0,0,12,0,49,4,51,1,0,0, 0,0,0,0,31,0,73,26,0,0,9,0,30,0,80,0, 35,0,41,7,78,122,18,70,114,111,122,101,110,32,72,101, 108,108,111,32,87,111,114,108,100,122,8,115,121,115,46,97, - 114,103,118,218,6,99,111,110,102,105,103,41,5,218,12,112, - 114,111,103,114,97,109,95,110,97,109,101,218,10,101,120,101, - 99,117,116,97,98,108,101,218,15,117,115,101,95,101,110,118, - 105,114,111,110,109,101,110,116,218,17,99,111,110,102,105,103, - 117,114,101,95,99,95,115,116,100,105,111,218,14,98,117,102, - 102,101,114,101,100,95,115,116,100,105,111,122,7,99,111,110, - 102,105,103,32,122,2,58,32,41,7,218,3,115,121,115,218, + 114,103,118,218,6,99,111,110,102,105,103,122,7,99,111,110, + 102,105,103,32,122,2,58,32,41,5,218,12,112,114,111,103, + 114,97,109,95,110,97,109,101,218,10,101,120,101,99,117,116, + 97,98,108,101,218,15,117,115,101,95,101,110,118,105,114,111, + 110,109,101,110,116,218,17,99,111,110,102,105,103,117,114,101, + 95,99,95,115,116,100,105,111,218,14,98,117,102,102,101,114, + 101,100,95,115,116,100,105,111,41,7,218,3,115,121,115,218, 17,95,116,101,115,116,105,110,116,101,114,110,97,108,99,97, 112,105,218,5,112,114,105,110,116,218,4,97,114,103,118,218, 11,103,101,116,95,99,111,110,102,105,103,115,114,2,0,0, diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 2c6e16817f2aad..78b0626a6f9379 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -278,40 +278,6 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) return 1; } -static PyObject* -make_const_tuple(asdl_expr_seq *elts) -{ - for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { - expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - if (e->kind != Constant_kind) { - return NULL; - } - } - - PyObject *newval = PyTuple_New(asdl_seq_LEN(elts)); - if (newval == NULL) { - return NULL; - } - - for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { - expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - PyObject *v = e->v.Constant.value; - PyTuple_SET_ITEM(newval, i, Py_NewRef(v)); - } - return newval; -} - -static int -fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state) -{ - PyObject *newval; - - if (node->v.Tuple.ctx != Load) - return 1; - - newval = make_const_tuple(node->v.Tuple.elts); - return make_const(node, newval, arena); -} static int astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); static int astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state); @@ -505,7 +471,6 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state) break; case Tuple_kind: CALL_SEQ(astfold_expr, expr, node_->v.Tuple.elts); - CALL(fold_tuple, expr_ty, node_); break; case Name_kind: if (node_->v.Name.ctx == Load && diff --git a/Python/codegen.c b/Python/codegen.c index cd77b34c06296b..cb26b5307660ab 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -1693,11 +1693,26 @@ codegen_typealias(compiler *c, stmt_ty s) return SUCCESS; } +static bool +is_const_tuple(asdl_expr_seq *elts) +{ + for (Py_ssize_t i = 0; i < asdl_seq_LEN(elts); i++) { + expr_ty e = (expr_ty)asdl_seq_GET(elts, i); + if (e->kind != Constant_kind) { + return false; + } + } + return true; +} + /* Return false if the expression is a constant value except named singletons. Return true otherwise. */ static bool check_is_arg(expr_ty e) { + if (e->kind == Tuple_kind) { + return !is_const_tuple(e->v.Tuple.elts); + } if (e->kind != Constant_kind) { return true; }