Require parenthesis around yield expressions

If yield is used in an expression context parenthesis are now required.
This ensures that the code is unambiguos.

Yield statements can still be used without parenthesis (which should be
the most common case).

Also yield expressions without value can be used without parenthesis,
too (this should be the most common case for coroutines).

If the yield expression is used in a context where parenthesis are required
anyway, no additional parenthesis have to be inserted.

Examples:

    // Statements don't need parenthesis
    yield $foo;
    yield $foo => $bar;

    // Yield without value doesn't need parenthesis either
    $data = yield;

    // Parentheses don't have to be duplicated
    foo(yield $bar);
    if (yield $bar) { ... }

    // But we have to use parentheses here
    $foo = (yield $bar);

This commit also fixes an issue with by-ref passing of $foo[0] like
variables. They previously weren't properly fetched for write.

Additionally this fixes valgrind warnings which were caused by access to
uninitialized memory in zend_is_function_or_method_call().
This commit is contained in:
Nikita Popov 2012-07-21 21:05:46 +02:00
parent 1f70a4c5fe
commit 80748631aa
6 changed files with 109 additions and 47 deletions

View File

@ -6,7 +6,7 @@ $generator->send() returns the yielded value
function reverseEchoGenerator() {
$data = yield;
while (true) {
$data = yield strrev($data);
$data = (yield strrev($data));
}
}

View File

@ -0,0 +1,26 @@
--TEST--
Array offsets can be yielded by reference
--FILE--
<?php
function &gen(array &$array) {
yield $array[0];
}
$array = [1, 2, 3];
$gen = gen($array);
foreach ($gen as &$val) {
$val *= -1;
}
var_dump($array);
?>
--EXPECT--
array(3) {
[0]=>
&int(-1)
[1]=>
int(2)
[2]=>
int(3)
}

View File

@ -0,0 +1,23 @@
--TEST--
No additional parenthesis are required around yield if they are already present
--FILE--
<?php
function gen() {
if (yield $foo); elseif (yield $foo);
if (yield $foo): elseif (yield $foo): endif;
while (yield $foo);
do {} while (yield $foo);
switch (yield $foo) {}
(yield $foo);
die(yield $foo);
func(yield $foo);
$foo->func(yield $foo);
new Foo(yield $foo);
}
echo "Done";
?>
--EXPECT--
Done

View File

@ -2663,7 +2663,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */
}
/* }}} */
void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC) /* {{{ */
void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC) /* {{{ */
{
zend_op *opline;
@ -2673,6 +2673,14 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC
CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR;
if (is_variable) {
if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(value)) {
zend_do_end_variable_parse(value, BP_VAR_W, 0 TSRMLS_CC);
} else {
zend_do_end_variable_parse(value, BP_VAR_R, 0 TSRMLS_CC);
}
}
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_YIELD;
@ -2680,7 +2688,7 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC
if (value) {
SET_NODE(opline->op1, value);
if (zend_is_function_or_method_call(value)) {
if (is_variable && zend_is_function_or_method_call(value)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
}
} else {

View File

@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c
int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC);
void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC);
void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC);
void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC);
void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC);
void zend_do_handle_exception(TSRMLS_D);

View File

@ -278,10 +278,10 @@ statement:
unticked_statement:
'{' inner_statement_list '}'
| T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); }
| T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); }
| T_WHILE '(' { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); }
| T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE '(' { $5.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' ';' { zend_do_do_while_end(&$1, &$5, &$7 TSRMLS_CC); }
| T_IF parenthesis_expr { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } statement { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); }
| T_IF parenthesis_expr ':' { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); }
| T_WHILE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr { zend_do_while_cond(&$3, &$$ TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$4 TSRMLS_CC); }
| T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE { $4.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr ';' { zend_do_do_while_end(&$1, &$4, &$6 TSRMLS_CC); }
| T_FOR
'('
for_expr
@ -291,7 +291,7 @@ unticked_statement:
for_expr
')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); }
for_statement { zend_do_for_end(&$7 TSRMLS_CC); }
| T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); }
| T_SWITCH parenthesis_expr { zend_do_switch_cond(&$2 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$4 TSRMLS_CC); }
| T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); }
| T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); }
| T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); }
@ -299,7 +299,7 @@ unticked_statement:
| T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); }
| T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); }
| T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); }
| T_YIELD expr T_DOUBLE_ARROW expr ';' { zend_do_yield(&$$, &$4, &$2 TSRMLS_CC); }
| yield_expr ';' { $$ = $1; }
| T_GLOBAL global_var_list ';'
| T_STATIC static_var_list ';'
| T_ECHO echo_expr_list ';'
@ -484,13 +484,13 @@ while_statement:
elseif_list:
/* empty */
| elseif_list T_ELSEIF '(' expr ')' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } statement { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); }
| elseif_list T_ELSEIF parenthesis_expr { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } statement { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); }
;
new_elseif_list:
/* empty */
| new_elseif_list T_ELSEIF '(' expr ')' ':' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); }
| new_elseif_list T_ELSEIF parenthesis_expr ':' { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); }
;
@ -533,8 +533,9 @@ optional_class_type:
function_call_parameter_list:
non_empty_function_call_parameter_list { $$ = $1; }
| /* empty */ { Z_LVAL($$.u.constant) = 0; }
'(' ')' { Z_LVAL($$.u.constant) = 0; }
| '(' non_empty_function_call_parameter_list ')' { $$ = $2; }
| '(' yield_expr ')' { Z_LVAL($$.u.constant) = 1; zend_do_pass_param(&$2, ZEND_SEND_VAL, Z_LVAL($$.u.constant) TSRMLS_CC); }
;
@ -774,7 +775,7 @@ expr_without_variable:
| expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
| expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); }
| expr T_INSTANCEOF class_name_reference { zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); }
| '(' expr ')' { $$ = $2; }
| parenthesis_expr { $$ = $1; }
| new_expr { $$ = $1; }
| '(' new_expr ')' { $$ = $2; } instance_call { $$ = $5; }
| expr '?' { zend_do_begin_qm_op(&$1, &$2 TSRMLS_CC); }
@ -797,8 +798,9 @@ expr_without_variable:
| combined_scalar { $$ = $1; }
| '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); }
| T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); }
| T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); }
| T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); }
| T_YIELD { zend_do_yield(&$$, NULL, NULL, 0 TSRMLS_CC); }
/*| T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); }
| T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); }*/
| T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); }
| function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); }
'(' parameter_list ')' lexical_vars
@ -808,6 +810,13 @@ expr_without_variable:
'{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $4; }
;
yield_expr:
T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); }
| T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); }
| T_YIELD expr T_DOUBLE_ARROW expr_without_variable { zend_do_yield(&$$, &$4, &$2, 0 TSRMLS_CC); }
| T_YIELD expr T_DOUBLE_ARROW variable { zend_do_yield(&$$, &$4, &$2, 1 TSRMLS_CC); }
;
combined_scalar_offset:
combined_scalar '[' dim_offset ']' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
| combined_scalar_offset '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
@ -834,30 +843,22 @@ lexical_var_list:
;
function_call:
namespace_name '(' { $2.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name '(' { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $4.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$1, &$$, &$6, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NS_SEPARATOR namespace_name '(' { $3.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(&$2, &$$, &$5, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { $4.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$6, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list
')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_without_objects '(' { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list ')'
{ zend_do_end_function_call(&$1, &$$, &$4, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $$.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$5, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NS_SEPARATOR namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$2, &$$, &$4, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { $$.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$5, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_without_objects { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
;
class_name:
@ -902,7 +903,7 @@ dynamic_class_name_variable_property:
exit_expr:
/* empty */ { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; }
| '(' ')' { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; }
| '(' expr ')' { $$ = $2; }
| parenthesis_expr { $$ = $1; }
;
backticks_expr:
@ -913,8 +914,8 @@ backticks_expr:
ctor_arguments:
/* empty */ { Z_LVAL($$.u.constant)=0; }
| '(' function_call_parameter_list ')' { $$ = $2; }
/* empty */ { Z_LVAL($$.u.constant) = 0; }
| function_call_parameter_list { $$ = $1; }
;
@ -986,6 +987,11 @@ expr:
| expr_without_variable { $$ = $1; }
;
parenthesis_expr:
'(' expr ')' { $$ = $2; }
| '(' yield_expr ')' { $$ = $2; }
;
r_variable:
variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; }
@ -1025,9 +1031,8 @@ array_method_dereference:
;
method:
'(' { zend_do_pop_object(&$1 TSRMLS_CC); zend_do_begin_method_call(&$1 TSRMLS_CC); }
function_call_parameter_list ')'
{ zend_do_end_function_call(&$1, &$$, &$3, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
{ zend_do_pop_object(&$$ TSRMLS_CC); zend_do_begin_method_call(&$$ TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$2, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
;
method_or_not: