From 9662259cb93ff04be80766bdade39d2e827e0e16 Mon Sep 17 00:00:00 2001 From: Levi Morrison Date: Thu, 28 Apr 2016 15:26:57 -0600 Subject: [PATCH] Add nullable parameter types This works off of Dmitry's commit for nullable return types --- Zend/tests/bug71428.1.phpt | 2 +- Zend/tests/bug71428.3.phpt | 2 +- Zend/tests/bug72119.phpt | 1 + Zend/tests/nullable_types/array.phpt | 17 +++++++++++++ ...contravariant_nullable_param_succeeds.phpt | 19 +++++++++++++++ .../contravariant_nullable_return_fails.phpt | 17 +++++++++++++ .../covariant_nullable_param_fails.phpt | 17 +++++++++++++ .../covariant_nullable_return_succeds.phpt | 16 +++++++++++++ Zend/tests/nullable_types/float.phpt | 16 +++++++++++++ Zend/tests/nullable_types/int.phpt | 16 +++++++++++++ .../invariant_param_and_return_succeeds.phpt | 24 +++++++++++++++++++ ..._parameters_do_not_have_default_value.phpt | 17 +++++++++++++ Zend/tests/nullable_types/string.phpt | 16 +++++++++++++ ...g_additional_optional_parameter_error.phpt | 2 +- Zend/zend_compile.c | 5 ++-- Zend/zend_inheritance.c | 10 ++++---- Zend/zend_language_parser.y | 12 ++++++---- 17 files changed, 195 insertions(+), 14 deletions(-) create mode 100644 Zend/tests/nullable_types/array.phpt create mode 100644 Zend/tests/nullable_types/contravariant_nullable_param_succeeds.phpt create mode 100644 Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt create mode 100644 Zend/tests/nullable_types/covariant_nullable_param_fails.phpt create mode 100644 Zend/tests/nullable_types/covariant_nullable_return_succeds.phpt create mode 100644 Zend/tests/nullable_types/float.phpt create mode 100644 Zend/tests/nullable_types/int.phpt create mode 100644 Zend/tests/nullable_types/invariant_param_and_return_succeeds.phpt create mode 100644 Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt create mode 100644 Zend/tests/nullable_types/string.phpt diff --git a/Zend/tests/bug71428.1.phpt b/Zend/tests/bug71428.1.phpt index fbf342380f..064e8cfa51 100644 --- a/Zend/tests/bug71428.1.phpt +++ b/Zend/tests/bug71428.1.phpt @@ -11,5 +11,5 @@ class B extends A { public function m(array $a = []) {} } --EXPECTF-- -Warning: Declaration of B::m(array $a = Array) should be compatible with A::m(array $a = NULL) in %sbug71428.1.php on line 7 +Warning: Declaration of B::m(array $a = Array) should be compatible with A::m(?array $a = NULL) in %sbug71428.1.php on line 7 diff --git a/Zend/tests/bug71428.3.phpt b/Zend/tests/bug71428.3.phpt index 65d397052e..53e5129d89 100644 --- a/Zend/tests/bug71428.3.phpt +++ b/Zend/tests/bug71428.3.phpt @@ -9,5 +9,5 @@ class B { public function m(A $a = NULL, $n) { echo "B.m";} }; class C extends B { public function m(A $a , $n) { echo "C.m";} }; ?> --EXPECTF-- -Warning: Declaration of C::m(A $a, $n) should be compatible with B::m(A $a = NULL, $n) in %sbug71428.3.php on line 4 +Warning: Declaration of C::m(A $a, $n) should be compatible with B::m(?A $a, $n) in %sbug71428.3.php on line 4 diff --git a/Zend/tests/bug72119.phpt b/Zend/tests/bug72119.phpt index b8f070a25a..eb9b4a73a7 100644 --- a/Zend/tests/bug72119.phpt +++ b/Zend/tests/bug72119.phpt @@ -16,3 +16,4 @@ echo "OK\n"; ?> --EXPECT-- OK + diff --git a/Zend/tests/nullable_types/array.phpt b/Zend/tests/nullable_types/array.phpt new file mode 100644 index 0000000000..7b0a2ed4d5 --- /dev/null +++ b/Zend/tests/nullable_types/array.phpt @@ -0,0 +1,17 @@ +--TEST-- +Explicitly nullable array type +--FILE-- +method(null); + +--EXPECT-- + diff --git a/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt b/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt new file mode 100644 index 0000000000..c9be479ead --- /dev/null +++ b/Zend/tests/nullable_types/contravariant_nullable_return_fails.phpt @@ -0,0 +1,17 @@ +--TEST-- +Return type cannot add nullability (contravariance) + +--FILE-- +method(null)); +var_dump($b->method(1)); + +--EXPECT-- +NULL +int(1) + diff --git a/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt b/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt new file mode 100644 index 0000000000..13b25e0dac --- /dev/null +++ b/Zend/tests/nullable_types/nullable_type_parameters_do_not_have_default_value.phpt @@ -0,0 +1,17 @@ +--TEST-- +Explicit nullable types do not imply a default value + +--FILE-- + --EXPECTF-- -Fatal error: Declaration of MySQL::query($query, int $extraParam = NULL, string ...$params) must be compatible with DB::query($query, string ...$params) in %s on line %d +Fatal error: Declaration of MySQL::query($query, ?int $extraParam = NULL, string ...$params) must be compatible with DB::query($query, string ...$params) in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index fe81aadcf8..b5f91c4495 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4866,7 +4866,7 @@ static void zend_compile_typename(zend_ast *ast, zend_arg_info *arg_info) /* {{{ zend_uchar type = zend_lookup_builtin_type_by_name(class_name); if (type != 0) { - if (ast->attr != ZEND_NAME_NOT_FQ) { + if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { zend_error_noreturn(E_COMPILE_ERROR, "Scalar type declaration '%s' must be unqualified", ZSTR_VAL(zend_string_tolower(class_name))); @@ -4999,9 +4999,10 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ && (Z_TYPE(default_node.u.constant) == IS_NULL || (Z_TYPE(default_node.u.constant) == IS_CONSTANT && strcasecmp(Z_STRVAL(default_node.u.constant), "NULL") == 0)); + zend_bool is_explicitly_nullable = (type_ast->attr & ZEND_TYPE_NULLABLE) == ZEND_TYPE_NULLABLE; op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; - arg_info->allow_null = has_null_default; + arg_info->allow_null = has_null_default || is_explicitly_nullable; zend_compile_typename(type_ast, arg_info); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index df7dbfd63e..de9b63e342 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -355,6 +355,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */ { + + if (arg_info->type_hint != IS_UNDEF && arg_info->allow_null) { + smart_str_appendc(str, '?'); + } + if (arg_info->class_name) { const char *class_name; size_t class_name_len; @@ -495,8 +500,6 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function } else { smart_str_appends(&str, "NULL"); } - } else if (arg_info->type_hint && arg_info->allow_null) { - smart_str_appends(&str, " = NULL"); } if (++i < num_args) { @@ -510,9 +513,6 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { smart_str_appends(&str, ": "); - if (fptr->common.arg_info[-1].allow_null) { - smart_str_appendc(&str, '?'); - } zend_append_type_hint(&str, fptr, fptr->common.arg_info - 1, 1); } smart_str_0(&str); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index c995e5fd4d..7682d69b68 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -252,7 +252,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %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 unkeyed_assignment_list keyed_assignment_list -%type isset_variable type return_type +%type isset_variable type return_type type_expr %type identifier %type returns_ref function is_reference is_variadic variable_modifiers @@ -644,7 +644,12 @@ parameter: optional_type: /* empty */ { $$ = NULL; } - | type { $$ = $1; } + | type_expr { $$ = $1; } +; + +type_expr: + type { $$ = $1; } + | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } ; type: @@ -655,8 +660,7 @@ type: return_type: /* empty */ { $$ = NULL; } - | ':' type { $$ = $2; } - | ':' '?' type { $$ = $3; $$->attr |= ZEND_TYPE_NULLABLE; } + | ':' type_expr { $$ = $2; } ; argument_list: -- 2.50.1