From 2222804028b37e38ed6ffc4c91919be141701320 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Thu, 6 Aug 2020 22:34:28 -0400 Subject: [PATCH 1/2] Proposal: Make match a constant expression I'd forgotten to check if this was part of the original RFC. It seems like constant expressions using match can be thought of as a much more concise form than chained ternary operators. It's already possible for constant expressions to throw, e.g. - Undeclared constants. - Wrong operands for unary or binary operators. --- Zend/tests/match/043.phpt | 47 +++++++++++++++++++++++++++++++++++++++ Zend/tests/match/044.phpt | 14 ++++++++++++ Zend/zend_ast.c | 45 +++++++++++++++++++++++++++++++++++++ Zend/zend_compile.c | 4 +++- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/match/043.phpt create mode 100644 Zend/tests/match/044.phpt diff --git a/Zend/tests/match/043.phpt b/Zend/tests/match/043.phpt new file mode 100644 index 0000000000000..02d362d44f82a --- /dev/null +++ b/Zend/tests/match/043.phpt @@ -0,0 +1,47 @@ +--TEST-- +Match expression can be used as a constant expression +--FILE-- + 123, +}; +echo "X=",X,"\n"; +const Y = match (dynamic_value) { + 'a', 'b' => 'default', + default => dynamic_value . 'd', +}; +echo "Y=",Y,"\n"; +const Z = match (dynamic_value) { + 'a', 'foo', 'b' => [dynamic_value], + default => dynamic_value, +}; +echo "Z=",json_encode(Z),"\n"; + +echo json_encode(my_test([dynamic_value])), "\n"; +for ($i = 0; $i < 2; $i++) { + try { + my_test(); + } catch (UnhandledMatchError $e) { + echo "Caught {$e->getMessage()} at line {$e->getLine()}\n"; + } +} + +?> +--EXPECT-- +X=123 +Y=food +Z=["foo"] +["foo"] +Caught Unhandled match value of type string at line 9 +Caught Unhandled match value of type string at line 9 diff --git a/Zend/tests/match/044.phpt b/Zend/tests/match/044.phpt new file mode 100644 index 0000000000000..1283063ac789d --- /dev/null +++ b/Zend/tests/match/044.phpt @@ -0,0 +1,14 @@ +--TEST-- +Match expression can be used as a constant expression unless parts aren't constant +--FILE-- + 123, +}; +?> +--EXPECTF-- +Fatal error: Constant expression contains invalid operations in %s044.php on line 6 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index e2a2aca698d48..6d5fc008e6e6d 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -654,6 +654,51 @@ ZEND_API int ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_c zval_ptr_dtor_nogc(&op1); } break; + case ZEND_AST_MATCH: + { + zend_ast_list *list; + uint32_t i; + if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) { + ret = FAILURE; + break; + } + list = zend_ast_get_list(ast->child[1]); + for (i = 0; i < list->children; i++) { + zend_ast *arm = list->child[i]; + zend_ast_list *arm_expr_list; + uint32_t j; + if (arm->child[0] == NULL) { +return_arm_expr: + if (UNEXPECTED(zend_ast_evaluate(result, arm->child[1], scope) != SUCCESS)) { + zval_ptr_dtor_nogc(&op1); + return FAILURE; + } + zval_ptr_dtor_nogc(&op1); + return SUCCESS; + } + arm_expr_list = zend_ast_get_list(arm->child[0]); + for (j = 0; j < arm_expr_list->children; j++) { + zval is_identical_zv; + if (UNEXPECTED(zend_ast_evaluate(&op2, arm_expr_list->child[j], scope) != SUCCESS)) { + return FAILURE; + } + ret = is_identical_function(&is_identical_zv, &op1, &op2); + zval_ptr_dtor_nogc(&op2); + if (ret != SUCCESS) { + zval_ptr_dtor_nogc(&op1); + return ret; + } + if (Z_TYPE(is_identical_zv) == IS_TRUE) { + goto return_arm_expr; + } + } + } + + zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(&op1)); + zval_ptr_dtor_nogc(&op1); + ret = FAILURE; + break; + } case ZEND_AST_COALESCE: if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) { ret = FAILURE; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5e61760e9029e..6ae6b2cfa7b30 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9144,7 +9144,9 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_UNPACK || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST || kind == ZEND_AST_CLASS_NAME - || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE; + || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE + || kind == ZEND_AST_MATCH || kind == ZEND_AST_MATCH_ARM + || kind == ZEND_AST_MATCH_ARM_LIST || kind == ZEND_AST_EXPR_LIST; } /* }}} */ From be9c052dded84faf221df9ada1be91dae3f27355 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Fri, 7 Aug 2020 09:44:55 -0400 Subject: [PATCH 2/2] Fix test message --- Zend/tests/match/044.phpt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Zend/tests/match/044.phpt b/Zend/tests/match/044.phpt index 1283063ac789d..7dc89eaa79b72 100644 --- a/Zend/tests/match/044.phpt +++ b/Zend/tests/match/044.phpt @@ -3,12 +3,10 @@ Match expression can be used as a constant expression unless parts aren't consta --FILE-- 123, }; ?> --EXPECTF-- -Fatal error: Constant expression contains invalid operations in %s044.php on line 6 +Fatal error: Constant expression contains invalid operations in %s044.php on line 4