diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9d6ab3f954c70..6b77ab857d1d2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2462,11 +2462,12 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type); void zend_compile_assign(znode *result, zend_ast *ast); static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node); +static void zend_compile_assign_ref(znode *result, zend_ast *ast); static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */ { znode dummy_node; - if (var_ast->kind == ZEND_AST_LIST) { + if (var_ast->kind == ZEND_AST_ARRAY) { zend_compile_list_assign(&dummy_node, var_ast, value_node); } else { zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast, @@ -2477,6 +2478,14 @@ static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) } /* }}} */ +static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node) /* {{{ */ +{ + zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN_REF, var_ast, + zend_ast_create_znode(value_node)); + zend_compile_assign_ref(NULL, assign_ast); +} +/* }}} */ + static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ { zend_ast *var_ast = ast->child[0]; @@ -2618,9 +2627,13 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n zend_bool has_elems = 0; for (i = 0; i < list->children; ++i) { - zend_ast *var_ast = list->child[i]; + zend_ast *pair_ast = list->child[i]; + zend_ast *var_ast = pair_ast->child[0]; znode fetch_result, dim_node; + if (pair_ast->child[1] != NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Keys are not allowed in array decomposition"); + } if (var_ast == NULL) { continue; } @@ -2633,8 +2646,22 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n Z_TRY_ADDREF(expr_node->u.constant); } - zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node); - zend_emit_assign_znode(var_ast, &fetch_result); + if (pair_ast->attr) { + zend_error_noreturn(E_COMPILE_ERROR, "References are not allowed in array decomposition"); + } else { + switch (var_ast->kind) { + case ZEND_AST_VAR: + case ZEND_AST_STATIC_PROP: + case ZEND_AST_DIM: + case ZEND_AST_PROP: + case ZEND_AST_ARRAY: + zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node); + zend_emit_assign_znode(var_ast, &fetch_result); + break; + default: + zend_error_noreturn(E_COMPILE_ERROR, "Cannot assign to an expression"); + } + } } if (!has_elems) { @@ -2694,7 +2721,7 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{ } /* Recursively check nested list()s */ - if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) { + if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) { return 1; } @@ -2774,7 +2801,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ zend_emit_op_data(&expr_node); return; - case ZEND_AST_LIST: + case ZEND_AST_ARRAY: if (zend_list_has_assign_to_self(var_ast, expr_ast)) { /* list($a, $b) = $a should evaluate the right $a first */ zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0); @@ -2802,29 +2829,26 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ } zend_ensure_writable_variable(target_ast); - zend_compile_var(&target_node, target_ast, BP_VAR_W); zend_compile_var(&source_node, source_ast, BP_VAR_REF); - if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use result of built-in function in write context"); } - opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node); - if (!result) { - opline->result_type |= EXT_TYPE_UNUSED; + if (target_ast->kind != ZEND_AST_ARRAY) { + zend_compile_var(&target_node, target_ast, BP_VAR_W); + + opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node); + if (!result) { + opline->result_type |= EXT_TYPE_UNUSED; + } + } else { + zend_compile_list_assign(&target_node, target_ast, &source_node); } if (zend_is_call(source_ast)) { opline->extended_value = ZEND_RETURNS_FUNCTION; } -} -/* }}} */ -static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node) /* {{{ */ -{ - zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN_REF, var_ast, - zend_ast_create_znode(value_node)); - zend_compile_assign_ref(NULL, assign_ast); } /* }}} */ @@ -4089,7 +4113,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ if (key_ast->kind == ZEND_AST_REF) { zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference"); } - if (key_ast->kind == ZEND_AST_LIST) { + if (key_ast->kind == ZEND_AST_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element"); } } @@ -6034,7 +6058,13 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */ for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; zend_bool by_ref = elem_ast->attr; - zend_eval_const_expr(&elem_ast->child[0]); + if (!elem_ast->child[0]) { + zval null_value; + ZVAL_NULL(&null_value); + elem_ast->child[0] = zend_ast_create_zval(&null_value); + } else { + zend_eval_const_expr(&elem_ast->child[0]); + } zend_eval_const_expr(&elem_ast->child[1]); if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL @@ -6661,11 +6691,16 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */ key_node_ptr = &key_node; } - if (by_ref) { - zend_ensure_writable_variable(value_ast); - zend_compile_var(&value_node, value_ast, BP_VAR_W); + if (value_ast) { + if (by_ref) { + zend_ensure_writable_variable(value_ast); + zend_compile_var(&value_node, value_ast, BP_VAR_W); + } else { + zend_compile_expr(&value_node, value_ast); + } } else { - zend_compile_expr(&value_node, value_ast); + value_node.op_type = IS_CONST; + ZVAL_NULL(&value_node.u.constant); } if (i == 0) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 3fc64947020f2..18c6547f2e3b4 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -233,7 +233,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type unprefixed_use_declarations const_decl inner_statement %type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable -%type extends_from parameter optional_type argument expr_without_variable global_var +%type extends_from parameter optional_type argument expr_without_variable expr_assignment global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_expr anonymous_class class_name class_name_reference simple_variable @@ -241,7 +241,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type exit_expr scalar backticks_expr lexical_var function_call member_name property_name %type variable_class_name dereferencable_scalar constant dereferencable %type callable_expr callable_variable static_member new_variable -%type assignment_list_element array_pair encaps_var encaps_var_offset isset_variables +%type array_pair omittable_array_pair encaps_var encaps_var_offset isset_variables %type top_statement_list use_declarations const_list inner_statement_list if_stmt %type alt_if_stmt for_exprs switch_case_list global_var_list static_var_list %type echo_expr_list unset_variables catch_list parameter_list class_statement_list @@ -249,8 +249,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type non_empty_parameter_list argument_list non_empty_argument_list property_list %type class_const_list class_const_decl name_list trait_adaptations method_body non_empty_for_exprs %type ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars -%type lexical_var_list encaps_list array_pair_list non_empty_array_pair_list -%type assignment_list isset_variable type return_type +%type lexical_var_list encaps_list array array_pair_list non_empty_array_pair_list omittable_array_pair_list non_empty_omittable_array_pair_list +%type isset_variable type return_type %type identifier %type returns_ref function is_reference is_variadic variable_modifiers @@ -538,7 +538,7 @@ implements_list: foreach_variable: variable { $$ = $1; } | '&' variable { $$ = zend_ast_create(ZEND_AST_REF, $2); } - | T_LIST '(' assignment_list ')' { $$ = $3; } + | array { $$ = $1; } ; for_statement: @@ -852,14 +852,17 @@ new_expr: { $$ = $2; } ; -expr_without_variable: - T_LIST '(' assignment_list ')' '=' expr - { $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); } +expr_assignment: + array '=' expr + { $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); } | variable '=' expr { $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); } | variable '=' '&' variable { $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1, $4); } - | T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); } +; + +expr_without_variable: + T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); } | variable T_PLUS_EQUAL expr { $$ = zend_ast_create_assign_op(ZEND_ASSIGN_ADD, $1, $3); } | variable T_MINUS_EQUAL expr @@ -969,6 +972,7 @@ expr_without_variable: { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $3 | ZEND_ACC_STATIC, $2, $9, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $5, $7, $11, $8); } + | expr_assignment ; function: @@ -1041,9 +1045,14 @@ ctor_arguments: ; -dereferencable_scalar: +array: T_ARRAY '(' array_pair_list ')' { $$ = $3; } + | T_LIST '(' omittable_array_pair_list ')' { $$ = $3; } | '[' array_pair_list ']' { $$ = $2; } +; + +dereferencable_scalar: + array { $$ = $1; } | T_CONSTANT_ENCAPSED_STRING { $$ = $1; } ; @@ -1169,20 +1178,6 @@ property_name: | simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); } ; -assignment_list: - assignment_list ',' assignment_list_element - { $$ = zend_ast_list_add($1, $3); } - | assignment_list_element - { $$ = zend_ast_create_list(1, ZEND_AST_LIST, $1); } -; - -assignment_list_element: - variable { $$ = $1; } - | T_LIST '(' assignment_list ')' { $$ = $3; } - | /* empty */ { $$ = NULL; } -; - - array_pair_list: /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); } | non_empty_array_pair_list possible_comma { $$ = $1; } @@ -1195,6 +1190,23 @@ non_empty_array_pair_list: { $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); } ; +omittable_array_pair_list: + /* empty */ + { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); } + | non_empty_omittable_array_pair_list + { $$ = $1; } + | ',' omittable_array_pair + { $$ = zend_ast_create_list(2, ZEND_AST_ARRAY, + zend_ast_create(ZEND_AST_ARRAY_ELEM, NULL, NULL), $2); } +; + +non_empty_omittable_array_pair_list: + non_empty_omittable_array_pair_list ',' omittable_array_pair + { $$ = zend_ast_list_add($1, $3); } + | array_pair + { $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); } +; + array_pair: expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); } @@ -1205,6 +1217,13 @@ array_pair: { $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $2, NULL); } ; +omittable_array_pair: + array_pair + { $$ = $1; } + | /* empty */ + { $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, NULL, NULL); } +; + encaps_list: encaps_list encaps_var { $$ = zend_ast_list_add($1, $2); }