[Patches] [ python-Patches-1531113 ] "a += yield foo" compiles as "a += yield (yield foo)"
SourceForge.net
noreply at sourceforge.net
Sun Jul 30 05:56:16 CEST 2006
Patches item #1531113, was opened at 2006-07-29 23:56
Message generated for change (Tracker Item Submitted) made by Item Submitter
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=305470&aid=1531113&group_id=5470
Please note that this message will contain a full copy of the comment thread,
including the initial issue submission, for this request,
not just the latest update.
Category: Parser/Compiler
Group: Python 2.5
Status: Open
Resolution: None
Priority: 5
Submitted By: Christopher Tur Lesniewski-Laas (ctl)
Assigned to: Nobody/Anonymous (nobody)
Summary: "a += yield foo" compiles as "a += yield (yield foo)"
Initial Comment:
[Note: my apologies for not attaching the patches using
the file upload; the file upload button is not showing
up in my Mozilla-based browsers. I'd be happy to email
the patches in MIME attachments if you provide an email
address.]
While working on a new async framework package based on
Python 2.5's new coroutine generators, I discovered a
"this could never have worked" bug in the AST compiler
or augmented assigns using the new yield operator.
The bug is that "a += yield foo" is compiled as "a +=
yield (yield foo)". A simple program that demonstrates
this bug is:
def coroutine():
a = 0
while a < 200:
a += yield
print a
c = coroutine()
c.next()
c.send(10)
c.send(20)
c.send(30)
c.send(40)
c.send(50)
This prints out:
20
60
instead of the expected:
10
30
60
100
150
The disassembler shows that two Yield instructions are
getting inserted where
there should be just one:
2 0 LOAD_CONST 1 (0)
3 STORE_FAST 0 (a)
3 6 SETUP_LOOP 35 (to 44)
>> 9 LOAD_FAST 0 (a)
12 LOAD_CONST 2 (200)
15 COMPARE_OP 0 (<)
18 JUMP_IF_FALSE 21 (to 42)
21 POP_TOP
4 22 LOAD_FAST 0 (a)
25 LOAD_CONST 0 (None)
28 YIELD_VALUE
29 YIELD_VALUE
30 INPLACE_ADD
31 STORE_FAST 0 (a)
5 34 LOAD_FAST 0 (a)
37 PRINT_ITEM
38 PRINT_NEWLINE
39 JUMP_ABSOLUTE 9
>> 42 POP_TOP
43 POP_BLOCK
>> 44 LOAD_CONST 0 (None)
47 RETURN_VALUE
The offending code is in Python/ast.c, function
ast_for_expr_stmt():
expr2 = Yield(ast_for_expr(c, ch), LINENO(ch),
ch->n_col_offset, c->c_arena);
since ast_for_expr(c, ch) processes the Yield node
again. The following patch resolves the problem for me:
Index: Python/ast.c
===================================================================
--- Python/ast.c (revision 50930)
+++ Python/ast.c (working copy)
@@ -1964,7 +1964,7 @@
if (TYPE(ch) == testlist)
expr2 = ast_for_testlist(c, ch);
else
- expr2 = Yield(ast_for_expr(c, ch),
LINENO(ch), ch->n_col_offset, c->c_arena);
+ expr2 = ast_for_expr(c, ch);
if (!expr2)
return NULL;
--------------------------------------------------------------------
A separate, minor issue I discovered while looking at
this code: the ast_for_expr_stmt function seems to
contain code to handle the case:
(yield foo) += bar
even though it correctly fails on the case:
(yield foo) = bar
This is just confusing; the two cases should match.
The following patch is one way of solving this problem:
Index: Python/ast.c
===================================================================
--- Python/ast.c (revision 50930)
+++ Python/ast.c (working copy)
@@ -1928,11 +1928,11 @@
operator_ty newoperator;
node *ch = CHILD(n, 0);
- if (TYPE(ch) == testlist)
- expr1 = ast_for_testlist(c, ch);
- else
- expr1 = Yield(ast_for_expr(c, CHILD(ch,
0)), LINENO(ch), n->n_col_offset,
- c->c_arena);
+ if (TYPE(ch) == yield_expr) {
+ ast_error(ch, "assignment to yield
expression not possible");
+ return NULL;
+ }
+ expr1 = ast_for_testlist(c, ch);
if (!expr1)
return NULL;
Cheers,
Chris Lesniewski
ctl mit edu
----------------------------------------------------------------------
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=305470&aid=1531113&group_id=5470
More information about the Patches
mailing list