Skip to content

Commit ef3ef6f

Browse files
committed
py/parse: Recognise const int assignments with type hints.
This commit extends the parser's behaviour to also recognise constant integer assignments even when the assignment itself has an "int" type annotation. Now declarations like "var: int = const(val)" can be treated as constants by the compiler and thus be folded in the rest of the program as it sees fit. Before this change, that line would generate a variable creation and its value assignment, without any folding being performed. This fixes #15608. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
1 parent 05342b0 commit ef3ef6f

File tree

5 files changed

+108
-7
lines changed

5 files changed

+108
-7
lines changed

py/parse.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,15 @@ static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *nu
705705
return false;
706706
}
707707

708+
#if MICROPY_COMP_CONST
709+
static bool is_const_atom_expr_normal(mp_parse_node_t node) {
710+
return MP_PARSE_NODE_IS_STRUCT_KIND(node, RULE_atom_expr_normal)
711+
&& MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t *)node)->nodes[0])
712+
&& MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)node)->nodes[0]) == MP_QSTR_const
713+
&& MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t *)node)->nodes[1], RULE_trailer_paren);
714+
}
715+
#endif
716+
708717
static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
709718
// this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4
710719
// it does not do partial folding, eg 1 + 2 + x -> 3 + x
@@ -797,21 +806,26 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
797806
if (!MP_PARSE_NODE_IS_NULL(pn1)
798807
&& !(MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_augassign)
799808
|| MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_expr_stmt_assign_list))) {
800-
// this node is of the form <x> = <y>
809+
// this node is of the form <x> = <y> or <x> : int = <y>
801810
mp_parse_node_t pn0 = peek_result(parser, 1);
802811
if (MP_PARSE_NODE_IS_ID(pn0)
803-
&& MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_atom_expr_normal)
804-
&& MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t *)pn1)->nodes[0])
805-
&& MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn1)->nodes[0]) == MP_QSTR_const
806-
&& MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t *)pn1)->nodes[1], RULE_trailer_paren)
807-
) {
812+
&& ((MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_annassign) &&
813+
MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t *)pn1)->nodes[0]) &&
814+
MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t *)pn1)->nodes[0]) == MP_QSTR_int &&
815+
is_const_atom_expr_normal(((mp_parse_node_struct_t *)pn1)->nodes[1]))
816+
|| is_const_atom_expr_normal(pn1))) {
808817
// code to assign dynamic constants: id = const(value)
809818

810819
// get the id
811820
qstr id = MP_PARSE_NODE_LEAF_ARG(pn0);
812821

813822
// get the value
814-
mp_parse_node_t pn_value = ((mp_parse_node_struct_t *)((mp_parse_node_struct_t *)pn1)->nodes[1])->nodes[0];
823+
mp_parse_node_t pn_value;
824+
if (MP_PARSE_NODE_IS_STRUCT_KIND(pn1, RULE_annassign)) {
825+
pn_value = ((mp_parse_node_struct_t *)(((mp_parse_node_struct_t *)(((mp_parse_node_struct_t *)pn1)->nodes[1]))->nodes[1]))->nodes[0];
826+
} else {
827+
pn_value = ((mp_parse_node_struct_t *)((mp_parse_node_struct_t *)pn1)->nodes[1])->nodes[0];
828+
}
815829
if (!mp_parse_node_is_const(pn_value)) {
816830
mp_obj_t exc = mp_obj_new_exception_msg(&mp_type_SyntaxError,
817831
MP_ERROR_TEXT("not a constant"));

tests/micropython/const2_annotated.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# check that consts are not replaced in anything except standalone identifiers
2+
3+
from micropython import const
4+
5+
X: int = const(1)
6+
Y: int = const(2)
7+
Z: int = const(3)
8+
9+
# import that uses a constant
10+
import micropython as X
11+
12+
print(globals()["X"])
13+
14+
15+
# function name that matches a constant
16+
def X():
17+
print("function X", X)
18+
19+
20+
globals()["X"]()
21+
22+
23+
# arguments that match a constant
24+
def f(X, *Y, **Z):
25+
pass
26+
27+
28+
f(1)
29+
30+
31+
# class name that matches a constant
32+
class X:
33+
def f(self):
34+
print("class X", X)
35+
36+
37+
globals()["X"]().f()
38+
39+
40+
# constant within a class
41+
class A:
42+
C1: int = const(4)
43+
44+
def X(self):
45+
print("method X", Y, C1, self.C1)
46+
47+
48+
A().X()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<module 'micropython'>
2+
function X 1
3+
class X 1
4+
method X 2 4 4

tests/micropython/const_annotated.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# test constant optimisation
2+
# This test will only work when MICROPY_COMP_CONST is enabled.
3+
4+
from micropython import const
5+
6+
X: int = const(123)
7+
Y: int = const(X + 456)
8+
9+
print(X, Y + 1)
10+
11+
12+
def f():
13+
print(X, Y + 1)
14+
15+
16+
f()
17+
18+
_X: int = const(12)
19+
_Y: int = const(_X + 34)
20+
21+
print(_X, _Y)
22+
23+
24+
class A:
25+
Z: int = const(1)
26+
_Z: int = const(2)
27+
print(Z, _Z)
28+
29+
30+
print(hasattr(A, "Z"), hasattr(A, "_Z"))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
123 580
2+
123 580
3+
12 46
4+
1 2
5+
True False

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy