diff --git a/NEWS b/NEWS index 4ace7d3dc462..7467c7983419 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,14 @@ PHP NEWS ?? ??? ????, PHP 8.6.0alpha1 - Core: + . Added generics: type parameters on classes, interfaces, traits, functions, + methods, closures, and arrow functions, with optional bounds, defaults, + and variance markers; turbofish syntax (`f::()`) at call sites; and + type arguments on named types (`Box`, `array`, `iterable`, + `self`, `static`, `parent`). Type parameters erase to their bound + at runtime; type arguments are discarded. Pre-erasure metadata is preserved + for Reflection so static-analysis tools can consume generics without + re-parsing source. (azjezz) . Added first-class callable cache to share instances for the duration of the request. (ilutov) . It is now possible to use reference assign on WeakMap without the key diff --git a/UPGRADING b/UPGRADING index d271eb47f7d1..ed36bc6f61c8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -127,6 +127,31 @@ PHP 8.6 UPGRADE NOTES ======================================== - Core: + . Added support for runtime-bound-checked generics. Classes, interfaces, + traits, functions, methods, closures, and arrow functions can now declare + type parameters with optional bounds (`T : Foo`), defaults (`T = int`), + and variance markers (`+T`, `-T`): + + class Box { + public T $value; + public function get(): T { return $this->value; } + } + + function id(T $x): T { return $x; } + + Call sites accept turbofish type arguments (`Box::::new()`, + `id::(7)`); use sites accept type arguments on named types + (`Box`, `array`, `iterable`, `self`, + `static`, `parent`). Recursive bounds (`T : Comparable`) + are supported. Anonymous classes cannot declare type parameters. + + At runtime each type parameter is replaced by its declared bound + (or `mixed` when unbounded, or when the bound is invalid in the + target position, e.g. `callable` on a property), and type + arguments are discarded. Pre-erasure metadata is preserved on + functions, methods, and class entries and is exposed through + Reflection so that PHP-based static-analysis tools can consume + generics without re-parsing source. . It is now possible to use reference assign on WeakMap without the key needing to be present beforehand. @@ -232,6 +257,23 @@ PHP 8.6 UPGRADE NOTES RFC: https://wiki.php.net/rfc/isreadable-iswriteable . Added ReflectionParameter::getDocComment(). RFC: https://wiki.php.net/rfc/parameter-doccomments + . Added ReflectionFunctionAbstract::isGeneric() and + ReflectionFunctionAbstract::getGenericParameters() (covers + ReflectionFunction, ReflectionMethod, closures, and arrow functions). + . Added ReflectionClass::isGeneric() and + ReflectionClass::getGenericParameters(). + . Added ReflectionClass::getGenericArgumentsForParentClass(), + ReflectionClass::getGenericArgumentsForParentInterface(string $name), + and ReflectionClass::getGenericArgumentsForUsedTrait(string $name) for + inspecting the type arguments supplied at a class's own extends / + implements / use sites. Returns null when no type arguments were + specified for that ancestor at this class's clause site (consumers + enumerate ancestors via the existing getParentClass() / getInterfaces() + / getTraits() APIs). + . Added ReflectionNamedType::hasGenericArguments() and + ReflectionNamedType::getGenericArguments(). The arguments are returned + as ReflectionType instances in source order (pre-erasure form); + ReflectionNamedType::getName() continues to return the erased name. - Intl: . `grapheme_strrev()` returns strrev for grapheme cluster unit. @@ -258,6 +300,15 @@ PHP 8.6 UPGRADE NOTES . Openssl\Session RFC: https://wiki.php.net/rfc/tls_session_resumption +- Reflection: + . ReflectionGenericTypeParameter (final, instances obtained via + ReflectionClass::getGenericParameters() and + ReflectionFunctionAbstract::getGenericParameters()). + . ReflectionTypeParameterReference (extends ReflectionType, appears only + inside pre-erasure type expressions: bounds, defaults, and the elements + of ReflectionNamedType::getGenericArguments()). + . enum ReflectionGenericVariance { Invariant; Covariant; Contravariant }. + - Standard: . enum SortDirection RFC: https://wiki.php.net/rfc/sort_direction_enum diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index a4297eec5870..314fe51ec7e5 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -103,6 +103,54 @@ PHP 8.6 INTERNALS UPGRADE NOTES . The deprecated Z_IMMUTABLE(), Z_IMMUTABLE_P(), Z_OPT_IMMUTABLE(), and Z_OPT_IMMUTABLE_P() macros have been removed. Check for IS_ARRAY && !REFCOUNTED directly. + . Added support for runtime-bound-checked generic type parameters. The + main additions: + . New types in zend_compile.h: zend_generic_parameter, + zend_generic_parameter_list, zend_generic_type_table, and + zend_generic_scope_entry. Allocate / destroy via + zend_generic_parameter_list_alloc(), + zend_generic_parameter_list_destroy(), + zend_generic_type_table_alloc(), and + zend_generic_type_table_destroy(). + . zend_op_array and zend_class_entry both gained an optional + `generic_parameters` (declared parameter list) and an optional + `generic_types` side table holding the pre-erasure forms of + return types, parameter types, property types, class-constant + types, the extends type, implements list, and trait-use list. + The runtime arg_info / property / class-constant slots continue + to hold only the erased form. + . New AST kinds: ZEND_AST_GENERIC_TYPE_PARAMETER_LIST, + ZEND_AST_GENERIC_TYPE_PARAMETER, ZEND_AST_GENERIC_NAMED_TYPE, + ZEND_AST_GENERIC_TYPE_ARGUMENT_LIST, ZEND_AST_TURBOFISH. + . zend_ast_decl::child[] grew from 5 to 6 entries; the new slot + carries an optional generic-parameter-list AST. + . The child-count groups of ZEND_AST_CALL, ZEND_AST_NEW, + ZEND_AST_METHOD_CALL, ZEND_AST_NULLSAFE_METHOD_CALL, and + ZEND_AST_STATIC_CALL each gained one optional child holding the + call-site turbofish type-argument list. Code that walks these + nodes by hard-coded child count must be updated. + . zend_ast_export handles the new generic AST kinds. + . Two new bits on zend_type's type_mask: + _ZEND_TYPE_TYPE_PARAMETER_BIT (1u << 25) and + _ZEND_TYPE_NAMED_WITH_ARGS_BIT (1u << 31), with payload structs + zend_type_parameter_ref { zend_string *name; uint32_t index; + uint8_t origin; } and zend_type_named_with_args { zend_string + *name; uint32_t name_attr; uint32_t count; zend_type args[]; }. + These bits only ever appear in pre-erasure forms held by the + side table; runtime arg_info / property / class-constant types + never carry them. Helpers: ZEND_TYPE_HAS_TYPE_PARAMETER(), + ZEND_TYPE_TYPE_PARAMETER(), ZEND_TYPE_HAS_NAMED_WITH_ARGS(), + ZEND_TYPE_NAMED_WITH_ARGS(). + . New compiler-globals fields: CG(type_arg_depth) (right-angle + split state used by the zendlex wrapper), CG(token_residual) + (single-token pushback slot), and CG(generic_scope) (linked + stack of in-scope type parameters). + . New T_TURBOFISH lexer token (literal `::<`). The zendlex wrapper + splits T_SR (`>>`), T_IS_GREATER_OR_EQUAL (`>=`), and T_SR_EQUAL + (`>>=`) into separate `>` tokens whenever CG(type_arg_depth) is + non-zero, with a single-token pushback slot. + . Module API bumped to 20260506; extension API bumped to + 420260506. All extensions must be recompiled. ======================== 2. Build system changes diff --git a/Zend/tests/generics/erasure/arrow_fn_erasure.phpt b/Zend/tests/generics/erasure/arrow_fn_erasure.phpt new file mode 100644 index 000000000000..d14f301d4cd4 --- /dev/null +++ b/Zend/tests/generics/erasure/arrow_fn_erasure.phpt @@ -0,0 +1,10 @@ +--TEST-- +Erasure: arrow function with type parameters erases to bound +--FILE-- +(T $x): T => $x; +$r = new ReflectionFunction($f); +echo $r->getParameters()[0]->getType()->__toString(), "\n"; +?> +--EXPECT-- +int diff --git a/Zend/tests/generics/erasure/bound_object.phpt b/Zend/tests/generics/erasure/bound_object.phpt new file mode 100644 index 000000000000..815dd1a5e92c --- /dev/null +++ b/Zend/tests/generics/erasure/bound_object.phpt @@ -0,0 +1,12 @@ +--TEST-- +Erasure: T : object erases to object +--FILE-- +(T $x): T { return $x; } +$r = new ReflectionFunction('id'); +echo $r->getParameters()[0]->getType()->__toString(), "\n"; +echo $r->getReturnType()->__toString(), "\n"; +?> +--EXPECT-- +object +object diff --git a/Zend/tests/generics/erasure/bound_to_class.phpt b/Zend/tests/generics/erasure/bound_to_class.phpt new file mode 100644 index 000000000000..7b1cfa50414e --- /dev/null +++ b/Zend/tests/generics/erasure/bound_to_class.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: T : Foo erases to Foo +--FILE-- +(T $x): T { return $x; } +$r = new ReflectionFunction('id'); +echo $r->getParameters()[0]->getType()->getName(), "\n"; +echo $r->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +Foo +Foo diff --git a/Zend/tests/generics/erasure/builtin_array_erasure.phpt b/Zend/tests/generics/erasure/builtin_array_erasure.phpt new file mode 100644 index 000000000000..8db0cfa876b2 --- /dev/null +++ b/Zend/tests/generics/erasure/builtin_array_erasure.phpt @@ -0,0 +1,18 @@ +--TEST-- +Erasure: array<...> erases to plain array at runtime +--FILE-- + $x): array { return $x; } +$r = new ReflectionFunction('f'); +$pt = $r->getParameters()[0]->getType(); +echo $pt->getName(), "\n"; +$rt = $r->getReturnType(); +echo $rt->getName(), "\n"; + +f([1, 2, 3]); +echo "ok\n"; +?> +--EXPECT-- +array +array +ok diff --git a/Zend/tests/generics/erasure/closure_erasure.phpt b/Zend/tests/generics/erasure/closure_erasure.phpt new file mode 100644 index 000000000000..6a533558b3aa --- /dev/null +++ b/Zend/tests/generics/erasure/closure_erasure.phpt @@ -0,0 +1,10 @@ +--TEST-- +Erasure: closure with type parameters erases to bound +--FILE-- +(T $x): T { return $x; }; +$r = new ReflectionFunction($f); +echo $r->getParameters()[0]->getType()->__toString(), "\n"; +?> +--EXPECT-- +object diff --git a/Zend/tests/generics/erasure/composite_bound.phpt b/Zend/tests/generics/erasure/composite_bound.phpt new file mode 100644 index 000000000000..5e38afba9a81 --- /dev/null +++ b/Zend/tests/generics/erasure/composite_bound.phpt @@ -0,0 +1,11 @@ +--TEST-- +Erasure: composite bound erased +--FILE-- +(T $x): T { return $x; } +$rt = (new ReflectionFunction('f'))->getReturnType(); +echo get_class($rt), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/Zend/tests/generics/erasure/erased_signature_match.phpt b/Zend/tests/generics/erasure/erased_signature_match.phpt new file mode 100644 index 000000000000..64406feb72a6 --- /dev/null +++ b/Zend/tests/generics/erasure/erased_signature_match.phpt @@ -0,0 +1,20 @@ +--TEST-- +Erasure: generic signature matches hand-erased equivalent +--FILE-- + { + public function get(): T { return new Foo; } +} + +class Erased { + public function get(): Foo { return new Foo; } +} + +$rg = (new ReflectionClass('Generic'))->getMethod('get')->getReturnType()->__toString(); +$re = (new ReflectionClass('Erased'))->getMethod('get')->getReturnType()->__toString(); +var_dump($rg === $re); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/generics/erasure/extends_args_discarded.phpt b/Zend/tests/generics/erasure/extends_args_discarded.phpt new file mode 100644 index 000000000000..9fb1c7f59c72 --- /dev/null +++ b/Zend/tests/generics/erasure/extends_args_discarded.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: extends type arguments don't change runtime parent +--FILE-- + {} +class Derived extends Base {} +echo (new ReflectionClass('Derived'))->getParentClass()->getName(), "\n"; +$d = new Derived; +var_dump($d instanceof Base); +?> +--EXPECT-- +Base +bool(true) diff --git a/Zend/tests/generics/erasure/generic_class_param.phpt b/Zend/tests/generics/erasure/generic_class_param.phpt new file mode 100644 index 000000000000..bde3ae6281c5 --- /dev/null +++ b/Zend/tests/generics/erasure/generic_class_param.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: generic class parameter type erased to bound +--FILE-- + { + public function set(T $x): void {} +} +$rm = (new ReflectionClass('Box'))->getMethod('set'); +echo $rm->getParameters()[0]->getType()->getName(), "\n"; +?> +--EXPECT-- +Iface diff --git a/Zend/tests/generics/erasure/get_class_returns_erased.phpt b/Zend/tests/generics/erasure/get_class_returns_erased.phpt new file mode 100644 index 000000000000..51e27f3d54a6 --- /dev/null +++ b/Zend/tests/generics/erasure/get_class_returns_erased.phpt @@ -0,0 +1,10 @@ +--TEST-- +Erasure: get_class returns the erased class name +--FILE-- + {} +$b = new Box::; +echo get_class($b), "\n"; +?> +--EXPECT-- +Box diff --git a/Zend/tests/generics/erasure/instanceof_args_discarded.phpt b/Zend/tests/generics/erasure/instanceof_args_discarded.phpt new file mode 100644 index 000000000000..459815492d66 --- /dev/null +++ b/Zend/tests/generics/erasure/instanceof_args_discarded.phpt @@ -0,0 +1,14 @@ +--TEST-- +Erasure: instanceof type arguments discarded at runtime +--FILE-- +); +var_dump($c instanceof C); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/Zend/tests/generics/erasure/method_bound.phpt b/Zend/tests/generics/erasure/method_bound.phpt new file mode 100644 index 000000000000..4562b258efad --- /dev/null +++ b/Zend/tests/generics/erasure/method_bound.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: method-level type parameter erased +--FILE-- +(U $x): U { return $x; } +} +$rt = (new ReflectionClass('C'))->getMethod('m')->getReturnType(); +echo $rt->getName(), "\n"; +?> +--EXPECT-- +Stringable2 diff --git a/Zend/tests/generics/erasure/method_inheritance_erased.phpt b/Zend/tests/generics/erasure/method_inheritance_erased.phpt new file mode 100644 index 000000000000..a94f25177bba --- /dev/null +++ b/Zend/tests/generics/erasure/method_inheritance_erased.phpt @@ -0,0 +1,19 @@ +--TEST-- +Erasure: child overriding generic parent method works on erased signature +--FILE-- +(T $x): T { return $x; } +} +class Child extends Parent2 { + public function f(U $x): U { return $x; } +} +$pm = (new ReflectionClass('Parent2'))->getMethod('f'); +$cm = (new ReflectionClass('Child'))->getMethod('f'); +echo $pm->getParameters()[0]->getType()->getName(), "\n"; +echo $cm->getParameters()[0]->getType()->getName(), "\n"; +?> +--EXPECT-- +Animal +Animal diff --git a/Zend/tests/generics/erasure/named_args_stripped.phpt b/Zend/tests/generics/erasure/named_args_stripped.phpt new file mode 100644 index 000000000000..437580417c71 --- /dev/null +++ b/Zend/tests/generics/erasure/named_args_stripped.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: type arguments stripped from named types in runtime view +--FILE-- + $x): Container { return $x; } +$r = new ReflectionFunction('f'); +echo $r->getParameters()[0]->getType()->getName(), "\n"; +echo $r->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +Container +Container diff --git a/Zend/tests/generics/erasure/property_type_erased.phpt b/Zend/tests/generics/erasure/property_type_erased.phpt new file mode 100644 index 000000000000..9b1e93e3d5a5 --- /dev/null +++ b/Zend/tests/generics/erasure/property_type_erased.phpt @@ -0,0 +1,16 @@ +--TEST-- +Erasure: generic property type erased to bound +--FILE-- + { + public T $value; +} +$rp = (new ReflectionClass('Box'))->getProperty('value'); +$rt = $rp->getType(); +echo get_class($rt), "\n"; +echo $rt->getName(), "\n"; +?> +--EXPECT-- +ReflectionNamedType +Foo diff --git a/Zend/tests/generics/erasure/runtime_bound_check.phpt b/Zend/tests/generics/erasure/runtime_bound_check.phpt new file mode 100644 index 000000000000..dc2b563701ec --- /dev/null +++ b/Zend/tests/generics/erasure/runtime_bound_check.phpt @@ -0,0 +1,21 @@ +--TEST-- +Erasure: bound is enforced at runtime +--FILE-- +(T $x): T { return $x; } + +echo get_class(f(new Dog)), "\n"; + +try { + f("not an animal"); + echo "FAIL\n"; +} catch (TypeError $e) { + echo "caught\n"; +} +?> +--EXPECT-- +Dog +caught diff --git a/Zend/tests/generics/erasure/runtime_unbounded.phpt b/Zend/tests/generics/erasure/runtime_unbounded.phpt new file mode 100644 index 000000000000..e86b52e60926 --- /dev/null +++ b/Zend/tests/generics/erasure/runtime_unbounded.phpt @@ -0,0 +1,20 @@ +--TEST-- +Erasure: unbounded T accepts anything (mixed) +--FILE-- +(T $x): T { return $x; } +var_dump(id(42)); +var_dump(id("hello")); +var_dump(id(null)); +var_dump(id([1, 2])); +?> +--EXPECT-- +int(42) +string(5) "hello" +NULL +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/generics/erasure/turbofish_no_runtime_effect.phpt b/Zend/tests/generics/erasure/turbofish_no_runtime_effect.phpt new file mode 100644 index 000000000000..a41ecae4ace3 --- /dev/null +++ b/Zend/tests/generics/erasure/turbofish_no_runtime_effect.phpt @@ -0,0 +1,13 @@ +--TEST-- +Erasure: turbofish (with matching arity) has zero runtime effect +--FILE-- +($x) { return $x * 2; } +var_dump(f(5)); +var_dump(f::(5)); +var_dump(f::(5)); +?> +--EXPECT-- +int(10) +int(10) +int(10) diff --git a/Zend/tests/generics/erasure/unbounded_to_mixed.phpt b/Zend/tests/generics/erasure/unbounded_to_mixed.phpt new file mode 100644 index 000000000000..15ff253c3173 --- /dev/null +++ b/Zend/tests/generics/erasure/unbounded_to_mixed.phpt @@ -0,0 +1,12 @@ +--TEST-- +Erasure: unbounded T erases to mixed +--FILE-- +(T $x): T { return $x; } +$r = new ReflectionFunction('id'); +echo $r->getParameters()[0]->getType()->__toString(), "\n"; +echo $r->getReturnType()->__toString(), "\n"; +?> +--EXPECT-- +mixed +mixed diff --git a/Zend/tests/generics/erasure/var_dump_class_name.phpt b/Zend/tests/generics/erasure/var_dump_class_name.phpt new file mode 100644 index 000000000000..c852fe9d88ee --- /dev/null +++ b/Zend/tests/generics/erasure/var_dump_class_name.phpt @@ -0,0 +1,14 @@ +--TEST-- +Erasure: var_dump shows erased class name +--FILE-- + { + public int $x = 1; +} +var_dump(new Box::); +?> +--EXPECT-- +object(Box)#1 (1) { + ["x"]=> + int(1) +} diff --git a/Zend/tests/generics/errors/anonymous_class_no_params.phpt b/Zend/tests/generics/errors/anonymous_class_no_params.phpt new file mode 100644 index 000000000000..6aca672cb9b2 --- /dev/null +++ b/Zend/tests/generics/errors/anonymous_class_no_params.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: anonymous classes cannot declare type parameters +--FILE-- + {};'); +} catch (ParseError $e) { + echo "parse error: ", $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +parse error: %s diff --git a/Zend/tests/generics/errors/closure_shadows_function.phpt b/Zend/tests/generics/errors/closure_shadows_function.phpt new file mode 100644 index 000000000000..29c6c8397605 --- /dev/null +++ b/Zend/tests/generics/errors/closure_shadows_function.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: closure type parameter shadows enclosing function type parameter +--FILE-- +(): void { + $cl = function (): void {}; +} +?> +--EXPECTF-- +Fatal error: Type parameter T shadows enclosing type parameter in %s on line %d diff --git a/Zend/tests/generics/errors/duplicate_in_method.phpt b/Zend/tests/generics/errors/duplicate_in_method.phpt new file mode 100644 index 000000000000..78fd038e233c --- /dev/null +++ b/Zend/tests/generics/errors/duplicate_in_method.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: duplicate type parameter inside a method's own list +--FILE-- +(): void {} } +?> +--EXPECTF-- +Fatal error: Cannot redeclare type parameter T in %s on line %d diff --git a/Zend/tests/generics/errors/duplicate_type_param.phpt b/Zend/tests/generics/errors/duplicate_type_param.phpt new file mode 100644 index 000000000000..77c148794173 --- /dev/null +++ b/Zend/tests/generics/errors/duplicate_type_param.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: duplicate type parameter name +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Cannot redeclare type parameter T in %s on line %d diff --git a/Zend/tests/generics/errors/duplicate_type_param_function.phpt b/Zend/tests/generics/errors/duplicate_type_param_function.phpt new file mode 100644 index 000000000000..f9ad4e8b270b --- /dev/null +++ b/Zend/tests/generics/errors/duplicate_type_param_function.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: duplicate type parameter name on function +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Cannot redeclare type parameter U in %s on line %d diff --git a/Zend/tests/generics/errors/empty_arg_list.phpt b/Zend/tests/generics/errors/empty_arg_list.phpt new file mode 100644 index 000000000000..6ac10dcff2db --- /dev/null +++ b/Zend/tests/generics/errors/empty_arg_list.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: empty type argument list +--FILE-- + $x): void {}'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/empty_param_list.phpt b/Zend/tests/generics/errors/empty_param_list.phpt new file mode 100644 index 000000000000..918533b25f26 --- /dev/null +++ b/Zend/tests/generics/errors/empty_param_list.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: empty type parameter list +--FILE-- + {}'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/forward_ref_at_self.phpt b/Zend/tests/generics/errors/forward_ref_at_self.phpt new file mode 100644 index 000000000000..b4dd1601bd0a --- /dev/null +++ b/Zend/tests/generics/errors/forward_ref_at_self.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: bound `T : T` (top-level self-reference) is rejected +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Type parameter T cannot reference itself in its own bound or default outside of a generic type argument in %s on line %d diff --git a/Zend/tests/generics/errors/forward_reference.phpt b/Zend/tests/generics/errors/forward_reference.phpt new file mode 100644 index 000000000000..e6bcbc320a13 --- /dev/null +++ b/Zend/tests/generics/errors/forward_reference.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: forward reference inside type parameter list +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Type parameter T referenced before declaration in %s on line %d diff --git a/Zend/tests/generics/errors/forward_reference_in_default.phpt b/Zend/tests/generics/errors/forward_reference_in_default.phpt new file mode 100644 index 000000000000..915c0b4d09dd --- /dev/null +++ b/Zend/tests/generics/errors/forward_reference_in_default.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: forward reference in default of earlier parameter +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Type parameter T referenced before declaration in %s on line %d diff --git a/Zend/tests/generics/errors/forward_reference_nested.phpt b/Zend/tests/generics/errors/forward_reference_nested.phpt new file mode 100644 index 000000000000..7d50c72349e8 --- /dev/null +++ b/Zend/tests/generics/errors/forward_reference_nested.phpt @@ -0,0 +1,9 @@ +--TEST-- +Errors: forward reference detected through nested type argument +--FILE-- + {} +function g, T>(): void {} +?> +--EXPECTF-- +Fatal error: Type parameter T referenced before declaration in %s on line %d diff --git a/Zend/tests/generics/errors/method_shadows_class_param.phpt b/Zend/tests/generics/errors/method_shadows_class_param.phpt new file mode 100644 index 000000000000..6c8b3cfd7935 --- /dev/null +++ b/Zend/tests/generics/errors/method_shadows_class_param.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: method type parameter shadows class type parameter +--FILE-- + { + public function f(): void {} +} +?> +--EXPECTF-- +Fatal error: Type parameter T shadows enclosing type parameter in %s on line %d diff --git a/Zend/tests/generics/errors/missing_close_angle.phpt b/Zend/tests/generics/errors/missing_close_angle.phpt new file mode 100644 index 000000000000..0ee4bae3581f --- /dev/null +++ b/Zend/tests/generics/errors/missing_close_angle.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: missing closing angle in type parameter list +--FILE-- + +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/missing_param_after_comma.phpt b/Zend/tests/generics/errors/missing_param_after_comma.phpt new file mode 100644 index 000000000000..df223a838c29 --- /dev/null +++ b/Zend/tests/generics/errors/missing_param_after_comma.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: missing parameter after trailing comma plus comma +--FILE-- + {}'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/optional_only_at_tail_ok.phpt b/Zend/tests/generics/errors/optional_only_at_tail_ok.phpt new file mode 100644 index 000000000000..101a3f0c6f85 --- /dev/null +++ b/Zend/tests/generics/errors/optional_only_at_tail_ok.phpt @@ -0,0 +1,15 @@ +--TEST-- +Errors: optional type parameters at the tail are accepted +--FILE-- + {} +class B {} +class C {} +class D {} +class E {} +function f(): void {} +function g(): void {} +echo "ok\n"; +?> +--EXPECT-- +ok diff --git a/Zend/tests/generics/errors/recursive_bound_ok.phpt b/Zend/tests/generics/errors/recursive_bound_ok.phpt new file mode 100644 index 000000000000..2b05466e6096 --- /dev/null +++ b/Zend/tests/generics/errors/recursive_bound_ok.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: recursive bound (T : Comparable) is allowed (positive test) +--FILE-- + {} +function f>(T $x): T { return $x; } +echo "ok\n"; +?> +--EXPECT-- +ok diff --git a/Zend/tests/generics/errors/required_after_optional.phpt b/Zend/tests/generics/errors/required_after_optional.phpt new file mode 100644 index 000000000000..65a48dad1157 --- /dev/null +++ b/Zend/tests/generics/errors/required_after_optional.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: required type parameter cannot follow an optional one +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Optional type parameter T cannot be declared before required type parameter U in %s on line %d diff --git a/Zend/tests/generics/errors/required_after_optional_function.phpt b/Zend/tests/generics/errors/required_after_optional_function.phpt new file mode 100644 index 000000000000..3bfefa731931 --- /dev/null +++ b/Zend/tests/generics/errors/required_after_optional_function.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: required type parameter cannot follow an optional one +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Optional type parameter T cannot be declared before required type parameter U in %s on line %d diff --git a/Zend/tests/generics/errors/required_after_optional_three_params.phpt b/Zend/tests/generics/errors/required_after_optional_three_params.phpt new file mode 100644 index 000000000000..a4a5ad579ace --- /dev/null +++ b/Zend/tests/generics/errors/required_after_optional_three_params.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: required type parameter following two optional ones names the first optional +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Optional type parameter T cannot be declared before required type parameter V in %s on line %d diff --git a/Zend/tests/generics/errors/runtime_bound_violation.phpt b/Zend/tests/generics/errors/runtime_bound_violation.phpt new file mode 100644 index 000000000000..c6589429f414 --- /dev/null +++ b/Zend/tests/generics/errors/runtime_bound_violation.phpt @@ -0,0 +1,16 @@ +--TEST-- +Errors: runtime type error when bound is violated +--FILE-- +(T $x): T { return $x; } + +try { + f(42); + echo "no error\n"; +} catch (TypeError $e) { + echo "type error\n"; +} +?> +--EXPECT-- +type error diff --git a/Zend/tests/generics/errors/runtime_unbound_accepts_anything.phpt b/Zend/tests/generics/errors/runtime_unbound_accepts_anything.phpt new file mode 100644 index 000000000000..23084a77f8dd --- /dev/null +++ b/Zend/tests/generics/errors/runtime_unbound_accepts_anything.phpt @@ -0,0 +1,19 @@ +--TEST-- +Errors: runtime accepts anything for unbounded T (mixed) +--FILE-- +(T $x): T { return $x; } + +var_dump(f(42)); +var_dump(f("foo")); +var_dump(f(null)); +var_dump(f(new stdClass)); +echo "no errors\n"; +?> +--EXPECT-- +int(42) +string(3) "foo" +NULL +object(stdClass)#1 (0) { +} +no errors diff --git a/Zend/tests/generics/errors/self_ref_in_default.phpt b/Zend/tests/generics/errors/self_ref_in_default.phpt new file mode 100644 index 000000000000..d852a85d83a5 --- /dev/null +++ b/Zend/tests/generics/errors/self_ref_in_default.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: top-level self-reference in default is rejected +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Type parameter T cannot reference itself in its own bound or default outside of a generic type argument in %s on line %d diff --git a/Zend/tests/generics/errors/self_ref_in_intersection.phpt b/Zend/tests/generics/errors/self_ref_in_intersection.phpt new file mode 100644 index 000000000000..3a6970bf3a4a --- /dev/null +++ b/Zend/tests/generics/errors/self_ref_in_intersection.phpt @@ -0,0 +1,9 @@ +--TEST-- +Errors: top-level self-reference inside an intersection bound is rejected +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Type parameter T cannot reference itself in its own bound or default outside of a generic type argument in %s on line %d diff --git a/Zend/tests/generics/errors/self_ref_in_union.phpt b/Zend/tests/generics/errors/self_ref_in_union.phpt new file mode 100644 index 000000000000..e40264a7b61d --- /dev/null +++ b/Zend/tests/generics/errors/self_ref_in_union.phpt @@ -0,0 +1,8 @@ +--TEST-- +Errors: top-level self-reference inside a union bound is rejected +--FILE-- +(): void {} +?> +--EXPECTF-- +Fatal error: Type parameter T cannot reference itself in its own bound or default outside of a generic type argument in %s on line %d diff --git a/Zend/tests/generics/errors/turbofish_no_call_target.phpt b/Zend/tests/generics/errors/turbofish_no_call_target.phpt new file mode 100644 index 000000000000..fd3c7fb8be6c --- /dev/null +++ b/Zend/tests/generics/errors/turbofish_no_call_target.phpt @@ -0,0 +1,12 @@ +--TEST-- +Errors: bare turbofish without function call +--FILE-- +;'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/turbofish_with_space.phpt b/Zend/tests/generics/errors/turbofish_with_space.phpt new file mode 100644 index 000000000000..a7460772979a --- /dev/null +++ b/Zend/tests/generics/errors/turbofish_with_space.phpt @@ -0,0 +1,13 @@ +--TEST-- +Errors: whitespace between :: and < is not turbofish +--FILE-- +::f();'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/type_args_in_class_const.phpt b/Zend/tests/generics/errors/type_args_in_class_const.phpt new file mode 100644 index 000000000000..8077a89c3e38 --- /dev/null +++ b/Zend/tests/generics/errors/type_args_in_class_const.phpt @@ -0,0 +1,13 @@ +--TEST-- +Errors: type arguments not allowed in expression position (Foo::class) +--FILE-- +::class;'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/type_args_in_static_call.phpt b/Zend/tests/generics/errors/type_args_in_static_call.phpt new file mode 100644 index 000000000000..509cd35991a2 --- /dev/null +++ b/Zend/tests/generics/errors/type_args_in_static_call.phpt @@ -0,0 +1,13 @@ +--TEST-- +Errors: type arguments not allowed before :: (use ::<...> turbofish instead) +--FILE-- +::bar();'); +} catch (ParseError $e) { + echo "parse error\n"; +} +?> +--EXPECT-- +parse error diff --git a/Zend/tests/generics/errors/type_param_in_catch.phpt b/Zend/tests/generics/errors/type_param_in_catch.phpt new file mode 100644 index 000000000000..9141899cb0f9 --- /dev/null +++ b/Zend/tests/generics/errors/type_param_in_catch.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: type parameter cannot be used at runtime in `catch` +--FILE-- +(): void { + try { /* ... */ } catch (T $e) {} +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/errors/type_param_in_class_const.phpt b/Zend/tests/generics/errors/type_param_in_class_const.phpt new file mode 100644 index 000000000000..7db77b73d862 --- /dev/null +++ b/Zend/tests/generics/errors/type_param_in_class_const.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: type parameter cannot be used at runtime in `T::class` +--FILE-- +(): string { + return T::class; +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/errors/type_param_in_instanceof.phpt b/Zend/tests/generics/errors/type_param_in_instanceof.phpt new file mode 100644 index 000000000000..34b51546cda1 --- /dev/null +++ b/Zend/tests/generics/errors/type_param_in_instanceof.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: type parameter cannot be used at runtime in `instanceof` +--FILE-- +($x): bool { + return $x instanceof T; +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/errors/type_param_in_new_expression.phpt b/Zend/tests/generics/errors/type_param_in_new_expression.phpt new file mode 100644 index 000000000000..f10f9ace66e0 --- /dev/null +++ b/Zend/tests/generics/errors/type_param_in_new_expression.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: type parameter cannot be used at runtime as a class in `new T()` +--FILE-- +(): void { + $x = new T(); +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/errors/type_param_in_static_call.phpt b/Zend/tests/generics/errors/type_param_in_static_call.phpt new file mode 100644 index 000000000000..ed23a33db626 --- /dev/null +++ b/Zend/tests/generics/errors/type_param_in_static_call.phpt @@ -0,0 +1,10 @@ +--TEST-- +Errors: type parameter cannot be used at runtime as a static-call target +--FILE-- +(): void { + T::foo(); +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/extends_non_generic.phpt b/Zend/tests/generics/inheritance/arity/extends_non_generic.phpt new file mode 100644 index 000000000000..c4d213c2c4d4 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/extends_non_generic.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: extends a non-generic class with type arguments is a compile error +--FILE-- + {} +?> +--EXPECTF-- +Fatal error: Too many generic type arguments to extends Plain in Bad, 1 passed and exactly 0 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/extends_over_default_max.phpt b/Zend/tests/generics/inheritance/arity/extends_over_default_max.phpt new file mode 100644 index 000000000000..ed07eb160fbf --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/extends_over_default_max.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: extends Pair with 3 args is a compile error +--FILE-- + {} +class Bad extends Pair {} +?> +--EXPECTF-- +Fatal error: Too many generic type arguments to extends Pair in Bad, 3 passed and at most 2 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/extends_too_few.phpt b/Zend/tests/generics/inheritance/arity/extends_too_few.phpt new file mode 100644 index 000000000000..ca15febd2da7 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/extends_too_few.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: extends with too few type arguments is a compile error +--FILE-- + {} +class Bad extends Pair {} +?> +--EXPECTF-- +Fatal error: Too few generic type arguments to extends Pair in Bad, 1 passed and exactly 2 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/extends_too_many.phpt b/Zend/tests/generics/inheritance/arity/extends_too_many.phpt new file mode 100644 index 000000000000..d8e7cdc124ba --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/extends_too_many.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: extends with too many type arguments is a compile error +--FILE-- + {} +class Bad extends Box {} +?> +--EXPECTF-- +Fatal error: Too many generic type arguments to extends Box in Bad, 2 passed and exactly 1 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/implements_too_few.phpt b/Zend/tests/generics/inheritance/arity/implements_too_few.phpt new file mode 100644 index 000000000000..1902c7df1a99 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/implements_too_few.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: implements with too few type arguments is a compile error +--FILE-- + {} +class Bad implements Map {} +?> +--EXPECTF-- +Fatal error: Too few generic type arguments to implements Map in Bad, 1 passed and exactly 2 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/interface_extends_arity.phpt b/Zend/tests/generics/inheritance/arity/interface_extends_arity.phpt new file mode 100644 index 000000000000..1ebd7e7586e1 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/interface_extends_arity.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: interface extends another interface with wrong arity is a compile error +--FILE-- + {} +interface IExt extends J {} +?> +--EXPECTF-- +Fatal error: Too many generic type arguments to extends J in IExt, 2 passed and exactly 1 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/use_trait_too_many.phpt b/Zend/tests/generics/inheritance/arity/use_trait_too_many.phpt new file mode 100644 index 000000000000..1c9f085a4074 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/use_trait_too_many.phpt @@ -0,0 +1,9 @@ +--TEST-- +Inheritance arity: use trait with too many type arguments is a compile error +--FILE-- + { public T $v; } +class Bad { use Holder; } +?> +--EXPECTF-- +Fatal error: Too many generic type arguments to use Holder in Bad, 2 passed and exactly 1 expected in %s on line %d diff --git a/Zend/tests/generics/inheritance/arity/with_defaults.phpt b/Zend/tests/generics/inheritance/arity/with_defaults.phpt new file mode 100644 index 000000000000..7420fbe70c36 --- /dev/null +++ b/Zend/tests/generics/inheritance/arity/with_defaults.phpt @@ -0,0 +1,19 @@ +--TEST-- +Inheritance arity: defaults extend the accepted range +--FILE-- + {} +interface Maybe {} +trait Slot {} + +// Required-only arity +class P1 extends Pair {} +// Including the optional +class P2 extends Pair {} +// Trait with default — works without args +class T1 { use Slot; } + +echo "OK\n"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/generics/limit/arg_list_return_type_256_rejected.phpt b/Zend/tests/generics/limit/arg_list_return_type_256_rejected.phpt new file mode 100644 index 000000000000..27c61cba4cf1 --- /dev/null +++ b/Zend/tests/generics/limit/arg_list_return_type_256_rejected.phpt @@ -0,0 +1,16 @@ +--TEST-- +Generics: 255 type arguments on a function return type is OK; 256 is a compile error +--FILE-- + {}"); +echo "OK\n"; + +$N = implode(",", array_fill(0, 256, "int")); +eval("function f256(): Box<{$N}> {}"); +?> +--EXPECTF-- +OK + +Fatal error: Cannot specify more than 255 generic type arguments (got 256) in %s : eval()'d code on line %d diff --git a/Zend/tests/generics/limit/param_list_class_256_rejected.phpt b/Zend/tests/generics/limit/param_list_class_256_rejected.phpt new file mode 100644 index 000000000000..527c5d00e57c --- /dev/null +++ b/Zend/tests/generics/limit/param_list_class_256_rejected.phpt @@ -0,0 +1,15 @@ +--TEST-- +Generics: 255 type parameters on a class is OK; 256 is a compile error +--FILE-- + "T{$i}", range(0, 254))); +eval("class C255<{$names255}> {}"); +echo "OK\n"; + +$names256 = implode(",", array_map(fn($i) => "T{$i}", range(0, 255))); +eval("class C256<{$names256}> {}"); +?> +--EXPECTF-- +OK + +Fatal error: Cannot declare more than 255 generic type parameters (got 256) in %s : eval()'d code on line %d diff --git a/Zend/tests/generics/limit/turbofish_function_call_256_rejected.phpt b/Zend/tests/generics/limit/turbofish_function_call_256_rejected.phpt new file mode 100644 index 000000000000..e1683bc366bf --- /dev/null +++ b/Zend/tests/generics/limit/turbofish_function_call_256_rejected.phpt @@ -0,0 +1,16 @@ +--TEST-- +Generics: 255 type arguments on a function call turbofish is OK; 256 is a compile error +--FILE-- +(); }"); +echo "OK\n"; + +$N = implode(",", array_fill(0, 256, "int")); +eval("function g256() { f::<{$N}>(); }"); +?> +--EXPECTF-- +OK + +Fatal error: Cannot specify more than 255 generic type arguments (got 256) in %s : eval()'d code on line %d diff --git a/Zend/tests/generics/scoping/closure_returned_visible_in_reflection.phpt b/Zend/tests/generics/scoping/closure_returned_visible_in_reflection.phpt new file mode 100644 index 000000000000..8ceeac6f49a9 --- /dev/null +++ b/Zend/tests/generics/scoping/closure_returned_visible_in_reflection.phpt @@ -0,0 +1,13 @@ +--TEST-- +Scoping: nested closure has its own ReflectionFunction view +--FILE-- +(): Closure { + return function (T $y): T { return $y; }; +} +$cl = f(); +$r = new ReflectionFunction($cl); +echo $r->getReturnType()->__toString(), "\n"; +?> +--EXPECT-- +object diff --git a/Zend/tests/generics/scoping/extends_clause.phpt b/Zend/tests/generics/scoping/extends_clause.phpt new file mode 100644 index 000000000000..d9839bf3d48f --- /dev/null +++ b/Zend/tests/generics/scoping/extends_clause.phpt @@ -0,0 +1,11 @@ +--TEST-- +Scoping: T is visible in extends clause +--FILE-- + {} +class Derived extends Base {} +$rc = new ReflectionClass('Derived'); +echo $rc->getParentClass()->getName(), "\n"; +?> +--EXPECT-- +Base diff --git a/Zend/tests/generics/scoping/file_scope_class_does_not_shadow.phpt b/Zend/tests/generics/scoping/file_scope_class_does_not_shadow.phpt new file mode 100644 index 000000000000..09c19fc79aea --- /dev/null +++ b/Zend/tests/generics/scoping/file_scope_class_does_not_shadow.phpt @@ -0,0 +1,11 @@ +--TEST-- +Scoping: a file-scope class does NOT shadow a generic type parameter +--FILE-- +(): void { + new T(); +} +?> +--EXPECTF-- +Fatal error: Cannot use generic type parameter T as a class reference at runtime; bound-erased generic types have no runtime representation in %s on line %d diff --git a/Zend/tests/generics/scoping/fq_name_bypasses_type_param.phpt b/Zend/tests/generics/scoping/fq_name_bypasses_type_param.phpt new file mode 100644 index 000000000000..032fad8b69b4 --- /dev/null +++ b/Zend/tests/generics/scoping/fq_name_bypasses_type_param.phpt @@ -0,0 +1,15 @@ +--TEST-- +Scoping: a fully qualified name `\T` bypasses the type-parameter rule +--FILE-- +(): int { + $x = new \T(); + return $x->v; +} +echo f(), "\n"; +?> +--EXPECT-- +5 diff --git a/Zend/tests/generics/scoping/implements_clause.phpt b/Zend/tests/generics/scoping/implements_clause.phpt new file mode 100644 index 000000000000..44de9db4b292 --- /dev/null +++ b/Zend/tests/generics/scoping/implements_clause.phpt @@ -0,0 +1,11 @@ +--TEST-- +Scoping: T is visible in implements clause +--FILE-- + {} +class Sink implements IConsumer {} +$rc = new ReflectionClass('Sink'); +echo $rc->getInterfaceNames()[0], "\n"; +?> +--EXPECT-- +IConsumer diff --git a/Zend/tests/generics/scoping/inner_class_shadowing_is_case_insensitive.phpt b/Zend/tests/generics/scoping/inner_class_shadowing_is_case_insensitive.phpt new file mode 100644 index 000000000000..0261f6a21b33 --- /dev/null +++ b/Zend/tests/generics/scoping/inner_class_shadowing_is_case_insensitive.phpt @@ -0,0 +1,15 @@ +--TEST-- +Scoping: inner-class shadowing of a type parameter is case-insensitive +--FILE-- +(): void { + class t { + public int $v = 9; + } + $x = new T(); + echo $x->v, "\n"; +} +f(); +?> +--EXPECT-- +9 diff --git a/Zend/tests/generics/scoping/inner_class_shadows_in_instanceof.phpt b/Zend/tests/generics/scoping/inner_class_shadows_in_instanceof.phpt new file mode 100644 index 000000000000..a4637ae93909 --- /dev/null +++ b/Zend/tests/generics/scoping/inner_class_shadows_in_instanceof.phpt @@ -0,0 +1,14 @@ +--TEST-- +Scoping: inner class shadows the type parameter for `instanceof` +--FILE-- +($x): bool { + class T {} + return $x instanceof T; +} +$called = false; +$result = f(new stdClass); +var_dump($result); +?> +--EXPECT-- +bool(false) diff --git a/Zend/tests/generics/scoping/inner_class_shadows_type_param.phpt b/Zend/tests/generics/scoping/inner_class_shadows_type_param.phpt new file mode 100644 index 000000000000..813387557be2 --- /dev/null +++ b/Zend/tests/generics/scoping/inner_class_shadows_type_param.phpt @@ -0,0 +1,15 @@ +--TEST-- +Scoping: a class declared inside a generic body shadows the type parameter +--FILE-- +(): void { + class T { + public int $v = 7; + } + $x = new T(); + echo $x->v, "\n"; +} +f(); +?> +--EXPECT-- +7 diff --git a/Zend/tests/generics/scoping/left_to_right_param_bounds.phpt b/Zend/tests/generics/scoping/left_to_right_param_bounds.phpt new file mode 100644 index 000000000000..479039281346 --- /dev/null +++ b/Zend/tests/generics/scoping/left_to_right_param_bounds.phpt @@ -0,0 +1,12 @@ +--TEST-- +Scoping: type parameters resolve in subsequent bounds (left to right) +--FILE-- + { + public U $val; +} +$rt = (new ReflectionClass('Box'))->getProperty('val')->getType(); +echo $rt->getName(), "\n"; +?> +--EXPECT-- +object diff --git a/Zend/tests/generics/scoping/method_introduces_own.phpt b/Zend/tests/generics/scoping/method_introduces_own.phpt new file mode 100644 index 000000000000..5c3ff1c900f2 --- /dev/null +++ b/Zend/tests/generics/scoping/method_introduces_own.phpt @@ -0,0 +1,18 @@ +--TEST-- +Scoping: method introduces its own type parameter +--FILE-- + { + public function morph(T $x, U $y): U { return $y; } +} +$rm = (new ReflectionClass('Box'))->getMethod('morph'); +echo $rm->getParameters()[0]->getType()->getName(), "\n"; +echo $rm->getParameters()[1]->getType()->getName(), "\n"; +echo $rm->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +A +B +B diff --git a/Zend/tests/generics/scoping/method_param_does_not_leak.phpt b/Zend/tests/generics/scoping/method_param_does_not_leak.phpt new file mode 100644 index 000000000000..ec5bd29b0eb5 --- /dev/null +++ b/Zend/tests/generics/scoping/method_param_does_not_leak.phpt @@ -0,0 +1,20 @@ +--TEST-- +Scoping: method type parameter does not leak to other methods +--FILE-- +(): int { + return 1; + } + public function m2(): T { + return new T; + } +} +$b = new Box; +echo $b->m2()->val, "\n"; +?> +--EXPECT-- +7 diff --git a/Zend/tests/generics/scoping/method_param_type.phpt b/Zend/tests/generics/scoping/method_param_type.phpt new file mode 100644 index 000000000000..3be048c7b36e --- /dev/null +++ b/Zend/tests/generics/scoping/method_param_type.phpt @@ -0,0 +1,13 @@ +--TEST-- +Scoping: T from class scope is visible in method parameter types +--FILE-- + { + public function set(T $x): void {} +} +$rm = (new ReflectionClass('Box'))->getMethod('set'); +echo $rm->getParameters()[0]->getType()->getName(), "\n"; +?> +--EXPECT-- +Iface diff --git a/Zend/tests/generics/scoping/method_return_type.phpt b/Zend/tests/generics/scoping/method_return_type.phpt new file mode 100644 index 000000000000..6951dcc9e65d --- /dev/null +++ b/Zend/tests/generics/scoping/method_return_type.phpt @@ -0,0 +1,13 @@ +--TEST-- +Scoping: T from class scope is visible in method return types +--FILE-- + { + public function get(): T { return new Animal; } +} +$rm = (new ReflectionClass('Container'))->getMethod('get'); +echo $rm->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +Animal diff --git a/Zend/tests/generics/scoping/nested_anon_class_captures.phpt b/Zend/tests/generics/scoping/nested_anon_class_captures.phpt new file mode 100644 index 000000000000..b7dd6bd3769f --- /dev/null +++ b/Zend/tests/generics/scoping/nested_anon_class_captures.phpt @@ -0,0 +1,21 @@ +--TEST-- +Scoping: nested anonymous class captures enclosing type parameter +--FILE-- +(T $x): object { + return new class($x) { + public T $val; + public function __construct(T $x) { $this->val = $x; } + public function get(): T { return $this->val; } + }; +} +class Dog implements Animal {} +$obj = makeAnimalBox(new Dog); +$r = new ReflectionClass($obj); +echo $r->getProperty('val')->getType()->getName(), "\n"; +echo $r->getMethod('get')->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +Animal +Animal diff --git a/Zend/tests/generics/scoping/nested_arrow_captures.phpt b/Zend/tests/generics/scoping/nested_arrow_captures.phpt new file mode 100644 index 000000000000..1dce854b025d --- /dev/null +++ b/Zend/tests/generics/scoping/nested_arrow_captures.phpt @@ -0,0 +1,15 @@ +--TEST-- +Scoping: nested arrow function captures enclosing type parameter +--FILE-- +(): Closure { + return fn(T $y): T => $y; +} +$cl = f(); +$r = new ReflectionFunction($cl); +echo $r->getParameters()[0]->getType()->getName(), "\n"; +echo $r->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +int +int diff --git a/Zend/tests/generics/scoping/nested_closure_captures.phpt b/Zend/tests/generics/scoping/nested_closure_captures.phpt new file mode 100644 index 000000000000..d0fcf8ec4118 --- /dev/null +++ b/Zend/tests/generics/scoping/nested_closure_captures.phpt @@ -0,0 +1,16 @@ +--TEST-- +Scoping: nested closure captures enclosing function's type parameter +--FILE-- +(T $x): Closure { + return function (T $y): T { return $y; }; +} +class Foo {} +$cl = f(new Foo); +$r = new ReflectionFunction($cl); +echo $r->getParameters()[0]->getType()->getName(), "\n"; +echo $r->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +object +object diff --git a/Zend/tests/generics/scoping/property_type.phpt b/Zend/tests/generics/scoping/property_type.phpt new file mode 100644 index 000000000000..898ecd1d96b9 --- /dev/null +++ b/Zend/tests/generics/scoping/property_type.phpt @@ -0,0 +1,12 @@ +--TEST-- +Scoping: T from class scope is visible in property types +--FILE-- + { + public T $value; +} +$rt = (new ReflectionClass('Box'))->getProperty('value')->getType(); +echo $rt->getName(), "\n"; +?> +--EXPECT-- +object diff --git a/Zend/tests/generics/scoping/trait_use_clause.phpt b/Zend/tests/generics/scoping/trait_use_clause.phpt new file mode 100644 index 000000000000..7f3ff0b30690 --- /dev/null +++ b/Zend/tests/generics/scoping/trait_use_clause.phpt @@ -0,0 +1,17 @@ +--TEST-- +Scoping: T is visible in trait use clause (no compile error) +--FILE-- + { + public function tag(X $x): X { return $x; } +} +class Box { + use Holder; +} +echo (new ReflectionClass('Box'))->getMethod('tag')->getReturnType()->getName(), "\n"; +$b = new Box; +echo get_class($b), "\n"; +?> +--EXPECT-- +mixed +Box diff --git a/Zend/tests/generics/syntax/args_dnf.phpt b/Zend/tests/generics/syntax/args_dnf.phpt new file mode 100644 index 000000000000..61829c694c59 --- /dev/null +++ b/Zend/tests/generics/syntax/args_dnf.phpt @@ -0,0 +1,15 @@ +--TEST-- +Generic syntax: type argument is DNF +--FILE-- + $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +$arg = $pt->getGenericArguments()[0]; +echo get_class($arg), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/Zend/tests/generics/syntax/args_intersection.phpt b/Zend/tests/generics/syntax/args_intersection.phpt new file mode 100644 index 000000000000..5cd01bda2a66 --- /dev/null +++ b/Zend/tests/generics/syntax/args_intersection.phpt @@ -0,0 +1,14 @@ +--TEST-- +Generic syntax: type argument is an intersection +--FILE-- + $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +$arg = $pt->getGenericArguments()[0]; +echo get_class($arg), "\n"; +?> +--EXPECT-- +ReflectionIntersectionType diff --git a/Zend/tests/generics/syntax/args_union.phpt b/Zend/tests/generics/syntax/args_union.phpt new file mode 100644 index 000000000000..206b88266774 --- /dev/null +++ b/Zend/tests/generics/syntax/args_union.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: type argument is a union +--FILE-- + $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +$arg = $pt->getGenericArguments()[0]; +echo get_class($arg), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/Zend/tests/generics/syntax/arrow_fn_decl.phpt b/Zend/tests/generics/syntax/arrow_fn_decl.phpt new file mode 100644 index 000000000000..22790e280438 --- /dev/null +++ b/Zend/tests/generics/syntax/arrow_fn_decl.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: arrow function with type parameters +--FILE-- +(T $x): T => $x; +$r = new ReflectionFunction($f); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +T diff --git a/Zend/tests/generics/syntax/bound_and_default_with_args.phpt b/Zend/tests/generics/syntax/bound_and_default_with_args.phpt new file mode 100644 index 000000000000..620484d0bf05 --- /dev/null +++ b/Zend/tests/generics/syntax/bound_and_default_with_args.phpt @@ -0,0 +1,20 @@ +--TEST-- +Generic syntax: bound and default both carry type arguments, closing >> +--FILE-- + {} + +// class A has a generic parameter T with bound B and default B +class A=B> {} + +$p = (new ReflectionClass('A'))->getGenericParameters()[0]; + +$b = $p->getBound(); +echo "bound: ", $b->getName(), "<", $b->getGenericArguments()[0]->getName(), ">\n"; + +$d = $p->getDefault(); +echo "default: ", $d->getName(), "<", $d->getGenericArguments()[0]->getName(), ">\n"; +?> +--EXPECT-- +bound: B +default: B diff --git a/Zend/tests/generics/syntax/bound_dnf.phpt b/Zend/tests/generics/syntax/bound_dnf.phpt new file mode 100644 index 000000000000..7d65ae2dcbd7 --- /dev/null +++ b/Zend/tests/generics/syntax/bound_dnf.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generic syntax: bound is a DNF type +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +echo get_class($p->getBound()), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/Zend/tests/generics/syntax/bound_intersection.phpt b/Zend/tests/generics/syntax/bound_intersection.phpt new file mode 100644 index 000000000000..f73eeb2e4357 --- /dev/null +++ b/Zend/tests/generics/syntax/bound_intersection.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: bound is an intersection type +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +echo get_class($p->getBound()), "\n"; +?> +--EXPECT-- +ReflectionIntersectionType diff --git a/Zend/tests/generics/syntax/bound_single_class.phpt b/Zend/tests/generics/syntax/bound_single_class.phpt new file mode 100644 index 000000000000..d6000909b072 --- /dev/null +++ b/Zend/tests/generics/syntax/bound_single_class.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generic syntax: bound on a single class type +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +var_dump($p->hasBound()); +echo $p->getBound()->getName(), "\n"; +?> +--EXPECT-- +bool(true) +Animal diff --git a/Zend/tests/generics/syntax/bound_union.phpt b/Zend/tests/generics/syntax/bound_union.phpt new file mode 100644 index 000000000000..2c9c71f9d4b1 --- /dev/null +++ b/Zend/tests/generics/syntax/bound_union.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: bound is a union type +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +$b = $p->getBound(); +echo get_class($b), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/Zend/tests/generics/syntax/builtin_array.phpt b/Zend/tests/generics/syntax/builtin_array.phpt new file mode 100644 index 000000000000..83284800f6fc --- /dev/null +++ b/Zend/tests/generics/syntax/builtin_array.phpt @@ -0,0 +1,18 @@ +--TEST-- +Generic syntax: array<...> with various arities +--FILE-- + $x): array { return []; } +function f2(array $x): array { return []; } +function f3(array $x): void {} + +foreach (['f1', 'f2', 'f3'] as $name) { + $r = new ReflectionFunction($name); + $pt = $r->getParameters()[0]->getType(); + echo "$name: ", $pt->getName(), " args=", count($pt->getGenericArguments()), "\n"; +} +?> +--EXPECT-- +f1: array args=1 +f2: array args=2 +f3: array args=3 diff --git a/Zend/tests/generics/syntax/builtin_iterable.phpt b/Zend/tests/generics/syntax/builtin_iterable.phpt new file mode 100644 index 000000000000..89e157ee0af7 --- /dev/null +++ b/Zend/tests/generics/syntax/builtin_iterable.phpt @@ -0,0 +1,14 @@ +--TEST-- +Generic syntax: iterable<...> +--FILE-- + $x): iterable { return []; } +$r = new ReflectionFunction('f'); +$pt = $r->getParameters()[0]->getType(); +echo $pt->getName(), " args=", count($pt->getGenericArguments()), "\n"; +$rt = $r->getReturnType(); +echo $rt->getName(), " args=", count($rt->getGenericArguments()), "\n"; +?> +--EXPECT-- +iterable args=1 +iterable args=2 diff --git a/Zend/tests/generics/syntax/builtin_self_parent_static.phpt b/Zend/tests/generics/syntax/builtin_self_parent_static.phpt new file mode 100644 index 000000000000..35934d41fa6e --- /dev/null +++ b/Zend/tests/generics/syntax/builtin_self_parent_static.phpt @@ -0,0 +1,20 @@ +--TEST-- +Generic syntax: self, parent, static +--FILE-- + {} + public function p(): parent {} + public function st(): static {} +} +$rc = new ReflectionClass('Foo'); +foreach (['s' => 'self', 'p' => 'parent', 'st' => 'static'] as $m => $expected) { + $rt = $rc->getMethod($m)->getReturnType(); + echo "$m: ", $rt->getName(), " hasArgs=", var_export($rt->hasGenericArguments(), true), "\n"; +} +?> +--EXPECT-- +s: Foo hasArgs=true +p: Base hasArgs=true +st: static hasArgs=true diff --git a/Zend/tests/generics/syntax/catch_with_args.phpt b/Zend/tests/generics/syntax/catch_with_args.phpt new file mode 100644 index 000000000000..bf82150c2660 --- /dev/null +++ b/Zend/tests/generics/syntax/catch_with_args.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generic syntax: catch with type arguments (args discarded at runtime) +--FILE-- + $e) { + echo "caught: ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +caught: boom diff --git a/Zend/tests/generics/syntax/class_multiple_params.phpt b/Zend/tests/generics/syntax/class_multiple_params.phpt new file mode 100644 index 000000000000..4f66e3173fe2 --- /dev/null +++ b/Zend/tests/generics/syntax/class_multiple_params.phpt @@ -0,0 +1,14 @@ +--TEST-- +Generic syntax: class with multiple type parameters +--FILE-- + {} +$r = new ReflectionClass('Map'); +$ps = $r->getGenericParameters(); +echo count($ps), "\n"; +foreach ($ps as $p) echo $p->getName(), "\n"; +?> +--EXPECT-- +2 +K +V diff --git a/Zend/tests/generics/syntax/class_single_param.phpt b/Zend/tests/generics/syntax/class_single_param.phpt new file mode 100644 index 000000000000..6a5a8bed0cef --- /dev/null +++ b/Zend/tests/generics/syntax/class_single_param.phpt @@ -0,0 +1,14 @@ +--TEST-- +Generic syntax: class with single type parameter +--FILE-- + {} +$r = new ReflectionClass('Box'); +var_dump($r->isGeneric()); +echo count($r->getGenericParameters()), "\n"; +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +1 +T diff --git a/Zend/tests/generics/syntax/closure_decl.phpt b/Zend/tests/generics/syntax/closure_decl.phpt new file mode 100644 index 000000000000..35620b3ae631 --- /dev/null +++ b/Zend/tests/generics/syntax/closure_decl.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: closure with type parameters +--FILE-- +(T $x): T { return $x; }; +$r = new ReflectionFunction($f); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +T diff --git a/Zend/tests/generics/syntax/default_value.phpt b/Zend/tests/generics/syntax/default_value.phpt new file mode 100644 index 000000000000..03f6a867fe3b --- /dev/null +++ b/Zend/tests/generics/syntax/default_value.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: type parameter with default +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +var_dump($p->hasDefault()); +echo $p->getDefault(), "\n"; +?> +--EXPECT-- +bool(true) +mixed diff --git a/Zend/tests/generics/syntax/default_with_nested_args.phpt b/Zend/tests/generics/syntax/default_with_nested_args.phpt new file mode 100644 index 000000000000..8a2af325ff86 --- /dev/null +++ b/Zend/tests/generics/syntax/default_with_nested_args.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: >>= splitting in default position +--FILE-- +> {} +$p = (new ReflectionClass('C'))->getGenericParameters()[0]; +echo $p, "\n"; +?> +--EXPECT-- +T = MapT diff --git a/Zend/tests/generics/syntax/extends_with_args.phpt b/Zend/tests/generics/syntax/extends_with_args.phpt new file mode 100644 index 000000000000..9a475577183d --- /dev/null +++ b/Zend/tests/generics/syntax/extends_with_args.phpt @@ -0,0 +1,10 @@ +--TEST-- +Generic syntax: extends with type arguments +--FILE-- + {} +class Derived extends Base {} +echo (new ReflectionClass('Derived'))->getParentClass()->getName(), "\n"; +?> +--EXPECT-- +Base diff --git a/Zend/tests/generics/syntax/function_decl.phpt b/Zend/tests/generics/syntax/function_decl.phpt new file mode 100644 index 000000000000..8ba5cde4314a --- /dev/null +++ b/Zend/tests/generics/syntax/function_decl.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: function declaration with type parameters +--FILE-- +(T $x): T { return $x; } +$r = new ReflectionFunction('id'); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +T diff --git a/Zend/tests/generics/syntax/implements_with_args.phpt b/Zend/tests/generics/syntax/implements_with_args.phpt new file mode 100644 index 000000000000..5221dffc4f13 --- /dev/null +++ b/Zend/tests/generics/syntax/implements_with_args.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: implements with type arguments +--FILE-- + {} +class C implements Iface {} +$r = new ReflectionClass('C'); +foreach ($r->getInterfaceNames() as $n) echo $n, "\n"; +?> +--EXPECT-- +Iface diff --git a/Zend/tests/generics/syntax/instanceof_with_args.phpt b/Zend/tests/generics/syntax/instanceof_with_args.phpt new file mode 100644 index 000000000000..5e303b14bb6a --- /dev/null +++ b/Zend/tests/generics/syntax/instanceof_with_args.phpt @@ -0,0 +1,15 @@ +--TEST-- +Generic syntax: instanceof with type arguments (args discarded at runtime) +--FILE-- +); +var_dump($c instanceof C); +$x = new stdClass; +var_dump($x instanceof C); +?> +--EXPECT-- +bool(true) +bool(true) +bool(false) diff --git a/Zend/tests/generics/syntax/interface_decl.phpt b/Zend/tests/generics/syntax/interface_decl.phpt new file mode 100644 index 000000000000..e0dbb24a6457 --- /dev/null +++ b/Zend/tests/generics/syntax/interface_decl.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: interface with type parameters +--FILE-- + {} +$r = new ReflectionClass('Iter'); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +T diff --git a/Zend/tests/generics/syntax/method_decl.phpt b/Zend/tests/generics/syntax/method_decl.phpt new file mode 100644 index 000000000000..8a76b03588db --- /dev/null +++ b/Zend/tests/generics/syntax/method_decl.phpt @@ -0,0 +1,14 @@ +--TEST-- +Generic syntax: method declaration with type parameters +--FILE-- +(callable $f): U { return $f(); } +} +$r = (new ReflectionClass('C'))->getMethod('map'); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +U diff --git a/Zend/tests/generics/syntax/named_type_args.phpt b/Zend/tests/generics/syntax/named_type_args.phpt new file mode 100644 index 000000000000..91e98d4c6273 --- /dev/null +++ b/Zend/tests/generics/syntax/named_type_args.phpt @@ -0,0 +1,16 @@ +--TEST-- +Generic syntax: type arguments on a named type at use site +--FILE-- + $x): Container { return $x; } +$r = new ReflectionFunction('f'); +$pt = $r->getParameters()[0]->getType(); +echo $pt->getName(), "\n"; +var_dump($pt->hasGenericArguments()); +foreach ($pt->getGenericArguments() as $a) echo $a->getName(), "\n"; +?> +--EXPECT-- +Container +bool(true) +int diff --git a/Zend/tests/generics/syntax/nested_three_levels.phpt b/Zend/tests/generics/syntax/nested_three_levels.phpt new file mode 100644 index 000000000000..9095735c9ae0 --- /dev/null +++ b/Zend/tests/generics/syntax/nested_three_levels.phpt @@ -0,0 +1,21 @@ +--TEST-- +Generic syntax: three levels of nesting (>>> splitting) +--FILE-- +>> { return new A; } +$rt = (new ReflectionFunction('f'))->getReturnType(); +echo $rt->getName(), "\n"; +$arg1 = $rt->getGenericArguments()[0]; +echo $arg1->getName(), "\n"; +$arg2 = $arg1->getGenericArguments()[0]; +echo $arg2->getName(), "\n"; +echo $arg2->getGenericArguments()[0]->getName(), "\n"; +?> +--EXPECT-- +A +B +C +int diff --git a/Zend/tests/generics/syntax/nested_two_levels.phpt b/Zend/tests/generics/syntax/nested_two_levels.phpt new file mode 100644 index 000000000000..f5d3dd38e86f --- /dev/null +++ b/Zend/tests/generics/syntax/nested_two_levels.phpt @@ -0,0 +1,17 @@ +--TEST-- +Generic syntax: two levels of nesting (>> splitting) +--FILE-- +> { return new Outer; } +$rt = (new ReflectionFunction('f'))->getReturnType(); +echo $rt->getName(), "\n"; +$arg = $rt->getGenericArguments()[0]; +echo $arg->getName(), "\n"; +echo $arg->getGenericArguments()[0]->getName(), "\n"; +?> +--EXPECT-- +Outer +Inner +int diff --git a/Zend/tests/generics/syntax/trailing_comma_args.phpt b/Zend/tests/generics/syntax/trailing_comma_args.phpt new file mode 100644 index 000000000000..d7de432dea15 --- /dev/null +++ b/Zend/tests/generics/syntax/trailing_comma_args.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: trailing comma in type argument list +--FILE-- + $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +echo count($pt->getGenericArguments()), "\n"; +?> +--EXPECT-- +2 diff --git a/Zend/tests/generics/syntax/trailing_comma_params.phpt b/Zend/tests/generics/syntax/trailing_comma_params.phpt new file mode 100644 index 000000000000..3a386ffdeeae --- /dev/null +++ b/Zend/tests/generics/syntax/trailing_comma_params.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: trailing comma in type parameter list +--FILE-- + {} +class Pair {} +echo (new ReflectionClass('Box'))->isGeneric() ? 'ok' : 'fail', "\n"; +echo (new ReflectionClass('Pair'))->isGeneric() ? 'ok' : 'fail', "\n"; +?> +--EXPECT-- +ok +ok diff --git a/Zend/tests/generics/syntax/trait_decl.phpt b/Zend/tests/generics/syntax/trait_decl.phpt new file mode 100644 index 000000000000..32d46440ea81 --- /dev/null +++ b/Zend/tests/generics/syntax/trait_decl.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: trait with type parameters +--FILE-- + {} +$r = new ReflectionClass('Container'); +var_dump($r->isGeneric()); +echo $r->getGenericParameters()[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +T diff --git a/Zend/tests/generics/syntax/trait_use_args.phpt b/Zend/tests/generics/syntax/trait_use_args.phpt new file mode 100644 index 000000000000..acf319fb65ba --- /dev/null +++ b/Zend/tests/generics/syntax/trait_use_args.phpt @@ -0,0 +1,15 @@ +--TEST-- +Generic syntax: trait use with type arguments +--FILE-- + { + public T $tval; +} +class Box { + use Container; +} +$r = new ReflectionClass('Box'); +echo $r->isGeneric() ? 'ok' : 'fail', "\n"; +?> +--EXPECT-- +ok diff --git a/Zend/tests/generics/syntax/turbofish_complex_args.phpt b/Zend/tests/generics/syntax/turbofish_complex_args.phpt new file mode 100644 index 000000000000..460f6d65fa1b --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_complex_args.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: turbofish with composite type arguments +--FILE-- +($x) { return $x; } +echo f::(42), "\n"; +echo f::('hi'), "\n"; +?> +--EXPECT-- +42 +hi diff --git a/Zend/tests/generics/syntax/turbofish_fcc.phpt b/Zend/tests/generics/syntax/turbofish_fcc.phpt new file mode 100644 index 000000000000..31080672c5b4 --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_fcc.phpt @@ -0,0 +1,24 @@ +--TEST-- +Generic syntax: turbofish on first-class callable creation +--FILE-- +(int $x): int { return $x * 2; } +class C { + public static function s(int $x): int { return $x + 100; } + public function m(int $x): int { return $x - 1; } +} + +$f = f::(...); +echo $f(5), "\n"; + +$g = C::s::(...); +echo $g(5), "\n"; + +$c = new C; +$h = $c->m::(...); +echo $h(5), "\n"; +?> +--EXPECT-- +10 +105 +4 diff --git a/Zend/tests/generics/syntax/turbofish_function_call.phpt b/Zend/tests/generics/syntax/turbofish_function_call.phpt new file mode 100644 index 000000000000..e397c8344643 --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_function_call.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generic syntax: turbofish on function call +--FILE-- +($a, $b) { return $a + $b; } +echo add::(1, 2), "\n"; +echo add::(3, 4), "\n"; +echo add::(5, 6), "\n"; +?> +--EXPECT-- +3 +7 +11 diff --git a/Zend/tests/generics/syntax/turbofish_method_call.phpt b/Zend/tests/generics/syntax/turbofish_method_call.phpt new file mode 100644 index 000000000000..0bfe9f7a2230 --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_method_call.phpt @@ -0,0 +1,17 @@ +--TEST-- +Generic syntax: turbofish on instance method call (including nullsafe) +--FILE-- +($x) { return $x * 3; } +} +$c = new C; +echo $c->m::(5), "\n"; +echo $c?->m::(7), "\n"; +$null = null; +var_dump($null?->m::(9)); +?> +--EXPECT-- +15 +21 +NULL diff --git a/Zend/tests/generics/syntax/turbofish_new.phpt b/Zend/tests/generics/syntax/turbofish_new.phpt new file mode 100644 index 000000000000..bc71fbbe37f0 --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_new.phpt @@ -0,0 +1,32 @@ +--TEST-- +Generic syntax: turbofish on new with various class-ref forms +--FILE-- + { + public function __construct(public int $v) {} +} + +$b = new Box::(1); +echo $b->v, "\n"; + +$cls = 'Box'; +$b = new $cls::(2); +echo $b->v, "\n"; + +$expr = fn() => 'Box'; +$b = new ($expr())::(3); +echo $b->v, "\n"; + +class Container { + public static function make(): self { + return new self::; + } +} +$c = Container::make(); +echo get_class($c), "\n"; +?> +--EXPECT-- +1 +2 +3 +Container diff --git a/Zend/tests/generics/syntax/turbofish_static_call.phpt b/Zend/tests/generics/syntax/turbofish_static_call.phpt new file mode 100644 index 000000000000..b2a1d74d4b61 --- /dev/null +++ b/Zend/tests/generics/syntax/turbofish_static_call.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generic syntax: turbofish on static method call +--FILE-- +($x) { return $x * 2; } +} +echo C::s::(5), "\n"; +echo C::s::(7), "\n"; +?> +--EXPECT-- +10 +14 diff --git a/Zend/tests/generics/syntax/variance_bound_default.phpt b/Zend/tests/generics/syntax/variance_bound_default.phpt new file mode 100644 index 000000000000..55c6460fef55 --- /dev/null +++ b/Zend/tests/generics/syntax/variance_bound_default.phpt @@ -0,0 +1,11 @@ +--TEST-- +Generic syntax: variance + bound + default combined +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +echo $p, "\n"; +?> +--EXPECT-- ++T : Bar = Bar diff --git a/Zend/tests/generics/syntax/variance_contravariant.phpt b/Zend/tests/generics/syntax/variance_contravariant.phpt new file mode 100644 index 000000000000..97d1b4d07e91 --- /dev/null +++ b/Zend/tests/generics/syntax/variance_contravariant.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: contravariant type parameter (-T) +--FILE-- + {} +$p = (new ReflectionClass('Consumer'))->getGenericParameters()[0]; +echo $p->getName(), "\n"; +echo $p->getVariance()->name, "\n"; +?> +--EXPECT-- +T +Contravariant diff --git a/Zend/tests/generics/syntax/variance_covariant.phpt b/Zend/tests/generics/syntax/variance_covariant.phpt new file mode 100644 index 000000000000..ea3d76f8d2d4 --- /dev/null +++ b/Zend/tests/generics/syntax/variance_covariant.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: covariant type parameter (+T) +--FILE-- + {} +$p = (new ReflectionClass('Producer'))->getGenericParameters()[0]; +echo $p->getName(), "\n"; +echo $p->getVariance()->name, "\n"; +?> +--EXPECT-- +T +Covariant diff --git a/Zend/tests/generics/syntax/variance_invariant_default.phpt b/Zend/tests/generics/syntax/variance_invariant_default.phpt new file mode 100644 index 000000000000..34ba224d09ff --- /dev/null +++ b/Zend/tests/generics/syntax/variance_invariant_default.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generic syntax: type parameter without variance is invariant +--FILE-- + {} +$p = (new ReflectionClass('Box'))->getGenericParameters()[0]; +echo $p->getVariance()->name, "\n"; +echo $p->getVariance()->value, "\n"; +?> +--EXPECT-- +Invariant +0 diff --git a/Zend/tests/generics/traits/basic_trait.phpt b/Zend/tests/generics/traits/basic_trait.phpt new file mode 100644 index 000000000000..9a570aa5578a --- /dev/null +++ b/Zend/tests/generics/traits/basic_trait.phpt @@ -0,0 +1,18 @@ +--TEST-- +Traits: trait declares its own type parameter +--FILE-- + { + public T $value; + public function get(): T { return $this->value; } +} +class Box { + use Holder; +} +$rc = new ReflectionClass('Box'); +echo $rc->getProperty('value')->getType()->getName(), "\n"; +echo $rc->getMethod('get')->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +object +object diff --git a/Zend/tests/generics/traits/multiple_traits.phpt b/Zend/tests/generics/traits/multiple_traits.phpt new file mode 100644 index 000000000000..ffc3361d80b2 --- /dev/null +++ b/Zend/tests/generics/traits/multiple_traits.phpt @@ -0,0 +1,22 @@ +--TEST-- +Traits: class uses multiple generic traits +--FILE-- + { + public function a(): X { return $this->ax; } + public X $ax; +} +trait B { + public function b(): Y { return $this->by; } + public Y $by; +} +class C { + use A, B; +} +$rc = new ReflectionClass('C'); +echo $rc->getMethod('a')->getReturnType()->getName(), "\n"; +echo $rc->getMethod('b')->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +object +int diff --git a/Zend/tests/generics/traits/trait_constant_type.phpt b/Zend/tests/generics/traits/trait_constant_type.phpt new file mode 100644 index 000000000000..ec11ea49297c --- /dev/null +++ b/Zend/tests/generics/traits/trait_constant_type.phpt @@ -0,0 +1,18 @@ +--TEST-- +Traits: trait class constant with type parameter +--FILE-- + { + const T MIN = 0; +} +class Box { + use Holder; +} +$rc = new ReflectionClass('Box'); +$consts = $rc->getReflectionConstants(); +foreach ($consts as $c) { + echo $c->getName(), ": ", $c->getValue(), "\n"; +} +?> +--EXPECT-- +MIN: 0 diff --git a/Zend/tests/generics/traits/trait_get_generic_params.phpt b/Zend/tests/generics/traits/trait_get_generic_params.phpt new file mode 100644 index 000000000000..0a437a414821 --- /dev/null +++ b/Zend/tests/generics/traits/trait_get_generic_params.phpt @@ -0,0 +1,14 @@ +--TEST-- +Traits: ReflectionClass::getGenericParameters on trait +--FILE-- + {} +$rc = new ReflectionClass('T'); +foreach ($rc->getGenericParameters() as $p) { + echo $p->getName(), "\n"; +} +?> +--EXPECT-- +A +B +C diff --git a/Zend/tests/generics/traits/trait_isgeneric.phpt b/Zend/tests/generics/traits/trait_isgeneric.phpt new file mode 100644 index 000000000000..b142dfe56deb --- /dev/null +++ b/Zend/tests/generics/traits/trait_isgeneric.phpt @@ -0,0 +1,12 @@ +--TEST-- +Traits: ReflectionClass::isGeneric on trait +--FILE-- + {} +trait U {} +echo (new ReflectionClass('T'))->isGeneric() ? "T:gen\n" : "T:not\n"; +echo (new ReflectionClass('U'))->isGeneric() ? "U:gen\n" : "U:not\n"; +?> +--EXPECT-- +T:gen +U:not diff --git a/Zend/tests/generics/traits/trait_method_isgeneric.phpt b/Zend/tests/generics/traits/trait_method_isgeneric.phpt new file mode 100644 index 000000000000..959155d68cfc --- /dev/null +++ b/Zend/tests/generics/traits/trait_method_isgeneric.phpt @@ -0,0 +1,17 @@ +--TEST-- +Traits: trait method's isGeneric and getGenericParameters +--FILE-- +(U $x): U { return $x; } +} +class C { use T; } +$rm = (new ReflectionClass('C'))->getMethod('f'); +echo $rm->isGeneric() ? "gen\n" : "not\n"; +foreach ($rm->getGenericParameters() as $p) { + echo $p->getName(), "\n"; +} +?> +--EXPECT-- +gen +U diff --git a/Zend/tests/generics/traits/trait_method_runtime.phpt b/Zend/tests/generics/traits/trait_method_runtime.phpt new file mode 100644 index 000000000000..92d4133ec3de --- /dev/null +++ b/Zend/tests/generics/traits/trait_method_runtime.phpt @@ -0,0 +1,26 @@ +--TEST-- +Traits: methods from trait work at runtime +--FILE-- + { + public function get(T $x): T { return $x; } +} +class Box { + use Holder; +} + +$a = new A; +$b = new Box; +echo get_class($b->get($a)), "\n"; + +try { + $b->get("string"); + echo "no error\n"; +} catch (TypeError $e) { + echo "type error\n"; +} +?> +--EXPECT-- +A +type error diff --git a/Zend/tests/generics/traits/trait_property_type.phpt b/Zend/tests/generics/traits/trait_property_type.phpt new file mode 100644 index 000000000000..2855a6824188 --- /dev/null +++ b/Zend/tests/generics/traits/trait_property_type.phpt @@ -0,0 +1,16 @@ +--TEST-- +Traits: trait property type +--FILE-- + { + public T $val; +} +class Box { + use Holder; +} +$rp = (new ReflectionClass('Box'))->getProperty('val'); +echo $rp->getType()->getName(), "\n"; +?> +--EXPECT-- +Foo diff --git a/Zend/tests/generics/traits/trait_use_args_compile.phpt b/Zend/tests/generics/traits/trait_use_args_compile.phpt new file mode 100644 index 000000000000..84ae43ab0ff0 --- /dev/null +++ b/Zend/tests/generics/traits/trait_use_args_compile.phpt @@ -0,0 +1,17 @@ +--TEST-- +Traits: trait use clause with type arguments compiles +--FILE-- + { + public function tag(X $x): X { return $x; } +} +class Box { + use Holder; +} +$b = new Box; +echo get_class($b), "\n"; +echo "ok\n"; +?> +--EXPECT-- +Box +ok diff --git a/Zend/tests/generics/traits/trait_with_method_param.phpt b/Zend/tests/generics/traits/trait_with_method_param.phpt new file mode 100644 index 000000000000..2203f9b20431 --- /dev/null +++ b/Zend/tests/generics/traits/trait_with_method_param.phpt @@ -0,0 +1,17 @@ +--TEST-- +Traits: trait method declares its own type parameter +--FILE-- +(U $x): U { return $x; } +} +class C { + use T; +} +$rm = (new ReflectionClass('C'))->getMethod('f'); +echo $rm->getParameters()[0]->getType()->getName(), "\n"; +echo $rm->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +int +int diff --git a/Zend/tests/generics/turbofish/arity/attribute_arity.phpt b/Zend/tests/generics/turbofish/arity/attribute_arity.phpt new file mode 100644 index 000000000000..9f6fa044538e --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/attribute_arity.phpt @@ -0,0 +1,38 @@ +--TEST-- +Turbofish arity: ReflectionAttribute::newInstance() enforces arity against the attribute class +--FILE-- + { + public function __construct(public string $tag) {} +} + +#[TaggedAttr::(tag: "ok")] +class A {} + +#[TaggedAttr::(tag: "bad")] +class B {} + +#[TaggedAttr(tag: "no-turbo")] +class D {} + +$ra = (new ReflectionClass("A"))->getAttributes()[0]; +echo "A: ", $ra->newInstance()->tag, "\n"; + +$rb = (new ReflectionClass("B"))->getAttributes()[0]; +try { + $rb->newInstance(); +} catch (ArgumentCountError $e) { + echo "B: ", $e->getMessage(), "\n"; +} + +// no turbofish at all -> no arity check, defaults apply (here T has no default) +// but newInstance() doesn't supply type args, which is arity 0; required is 1 -> still fails? No. +// arity 0 is treated as "no turbofish" and skips the check entirely. +$rd = (new ReflectionClass("D"))->getAttributes()[0]; +echo "D: ", $rd->newInstance()->tag, "\n"; +?> +--EXPECT-- +A: ok +B: Too many generic type arguments to new TaggedAttr, 2 passed and exactly 1 expected +D: no-turbo diff --git a/Zend/tests/generics/turbofish/arity/below_required.phpt b/Zend/tests/generics/turbofish/arity/below_required.phpt new file mode 100644 index 000000000000..09514d55a338 --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/below_required.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish arity: providing fewer than required even with some defaults still throws +--FILE-- +($x) { return $x; } +try { + f::(1); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Too few generic type arguments to f(), 1 passed and at least 2 expected diff --git a/Zend/tests/generics/turbofish/arity/function_too_few.phpt b/Zend/tests/generics/turbofish/arity/function_too_few.phpt new file mode 100644 index 000000000000..d5469444288d --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/function_too_few.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish arity: too few type arguments to a function throws ArgumentCountError +--FILE-- +($x, $y) { return [$x, $y]; } +try { + f::(1, 2); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Too few generic type arguments to f(), 1 passed and exactly 2 expected diff --git a/Zend/tests/generics/turbofish/arity/function_too_many.phpt b/Zend/tests/generics/turbofish/arity/function_too_many.phpt new file mode 100644 index 000000000000..15747c2384fd --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/function_too_many.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish arity: too many type arguments to a function throws ArgumentCountError +--FILE-- +($x) { return $x; } +try { + f::(1); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Too many generic type arguments to f(), 2 passed and exactly 1 expected diff --git a/Zend/tests/generics/turbofish/arity/method_arity.phpt b/Zend/tests/generics/turbofish/arity/method_arity.phpt new file mode 100644 index 000000000000..81eb1cb37219 --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/method_arity.phpt @@ -0,0 +1,24 @@ +--TEST-- +Turbofish arity: instance method, static method, nullsafe call all enforce arity +--FILE-- +($x) { return $x; } + public static function s($x) { return $x; } +} + +$c = new C; + +try { $c->m::(1); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } + +try { $c?->m::(1); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } + +try { C::s::(1); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } +?> +--EXPECT-- +Too many generic type arguments to C::m(), 2 passed and exactly 1 expected +Too many generic type arguments to C::m(), 2 passed and exactly 1 expected +Too few generic type arguments to C::s(), 1 passed and exactly 2 expected diff --git a/Zend/tests/generics/turbofish/arity/new_arity.phpt b/Zend/tests/generics/turbofish/arity/new_arity.phpt new file mode 100644 index 000000000000..de36227a7502 --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/new_arity.phpt @@ -0,0 +1,28 @@ +--TEST-- +Turbofish arity: new ::<...> enforces arity against class generic parameters +--FILE-- + { + public function __construct(public int $v) {} +} +class Pair {} +class Plain {} + +try { new Box::(1); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } + +try { new Plain::(); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } + +// Pair: K required, V has default - so 1 or 2 args is OK, 0 or 3 is not +$p = new Pair::(); +echo get_class($p), "\n"; + +try { new Pair::(); } +catch (ArgumentCountError $e) { echo $e->getMessage(), "\n"; } +?> +--EXPECT-- +Too many generic type arguments to new Box, 2 passed and exactly 1 expected +Too many generic type arguments to new Plain, 1 passed and exactly 0 expected +Pair +Too many generic type arguments to new Pair, 3 passed and at most 2 expected diff --git a/Zend/tests/generics/turbofish/arity/non_generic_callee.phpt b/Zend/tests/generics/turbofish/arity/non_generic_callee.phpt new file mode 100644 index 000000000000..5e7a6a622205 --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/non_generic_callee.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish arity: turbofish on a non-generic callee throws ArgumentCountError +--FILE-- +(42); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Too many generic type arguments to f(), 1 passed and exactly 0 expected diff --git a/Zend/tests/generics/turbofish/arity/with_defaults.phpt b/Zend/tests/generics/turbofish/arity/with_defaults.phpt new file mode 100644 index 000000000000..24509e622cb2 --- /dev/null +++ b/Zend/tests/generics/turbofish/arity/with_defaults.phpt @@ -0,0 +1,27 @@ +--TEST-- +Turbofish arity: defaults extend the accepted arity range +--FILE-- +($x) { return $x; } + +// arity 1, 2, 3 all valid +var_dump(f::(1)); +var_dump(f::(2)); +var_dump(f::(3)); + +// arity 0 (i.e., no turbofish) is also valid +var_dump(f(4)); + +// arity 4 exceeds total +try { + f::(5); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +int(1) +int(2) +int(3) +int(4) +Too many generic type arguments to f(), 4 passed and at most 3 expected diff --git a/Zend/tests/generics/turbofish/composite_args.phpt b/Zend/tests/generics/turbofish/composite_args.phpt new file mode 100644 index 000000000000..8c69b6555b8b --- /dev/null +++ b/Zend/tests/generics/turbofish/composite_args.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish: union and intersection type arguments +--FILE-- +($x) { return $x; } +var_dump(f::(42)); +var_dump(f::(42)); +var_dump(f::<(A&B)|C>(42)); +?> +--EXPECT-- +int(42) +int(42) +int(42) diff --git a/Zend/tests/generics/turbofish/fcc_function.phpt b/Zend/tests/generics/turbofish/fcc_function.phpt new file mode 100644 index 000000000000..c304fb7ceed2 --- /dev/null +++ b/Zend/tests/generics/turbofish/fcc_function.phpt @@ -0,0 +1,10 @@ +--TEST-- +Turbofish: first-class callable on function +--FILE-- +($x) { return $x; } +$cl = id::(...); +var_dump($cl(7)); +?> +--EXPECT-- +int(7) diff --git a/Zend/tests/generics/turbofish/fcc_method.phpt b/Zend/tests/generics/turbofish/fcc_method.phpt new file mode 100644 index 000000000000..48436c4faee5 --- /dev/null +++ b/Zend/tests/generics/turbofish/fcc_method.phpt @@ -0,0 +1,10 @@ +--TEST-- +Turbofish: first-class callable on instance method +--FILE-- +($x) { return $x * 2; } } +$cl = (new C)->m::(...); +var_dump($cl(5)); +?> +--EXPECT-- +int(10) diff --git a/Zend/tests/generics/turbofish/fcc_static_method.phpt b/Zend/tests/generics/turbofish/fcc_static_method.phpt new file mode 100644 index 000000000000..56eaf1d671cd --- /dev/null +++ b/Zend/tests/generics/turbofish/fcc_static_method.phpt @@ -0,0 +1,10 @@ +--TEST-- +Turbofish: first-class callable on static method +--FILE-- +($x) { return $x; } } +$cl = C::m::(...); +var_dump($cl(11)); +?> +--EXPECT-- +int(11) diff --git a/Zend/tests/generics/turbofish/function_call.phpt b/Zend/tests/generics/turbofish/function_call.phpt new file mode 100644 index 000000000000..8de497d7136e --- /dev/null +++ b/Zend/tests/generics/turbofish/function_call.phpt @@ -0,0 +1,9 @@ +--TEST-- +Turbofish: function call with single type argument +--FILE-- +($x) { return $x; } +var_dump(f::(42)); +?> +--EXPECT-- +int(42) diff --git a/Zend/tests/generics/turbofish/function_call_multi.phpt b/Zend/tests/generics/turbofish/function_call_multi.phpt new file mode 100644 index 000000000000..f9f921f6751f --- /dev/null +++ b/Zend/tests/generics/turbofish/function_call_multi.phpt @@ -0,0 +1,14 @@ +--TEST-- +Turbofish: function call with multiple type arguments +--FILE-- +($a, $b) { return [$a, $b]; } +var_dump(combine::(1, "x")); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + string(1) "x" +} diff --git a/Zend/tests/generics/turbofish/in_chain.phpt b/Zend/tests/generics/turbofish/in_chain.phpt new file mode 100644 index 000000000000..f11b82a5c847 --- /dev/null +++ b/Zend/tests/generics/turbofish/in_chain.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish: chained calls +--FILE-- +() { return $this; } + public function n($x) { return $x; } +} +$c = new C; +var_dump($c->m::()->n::("hello")); +?> +--EXPECT-- +string(5) "hello" diff --git a/Zend/tests/generics/turbofish/instance_method_call.phpt b/Zend/tests/generics/turbofish/instance_method_call.phpt new file mode 100644 index 000000000000..a93855b4fb14 --- /dev/null +++ b/Zend/tests/generics/turbofish/instance_method_call.phpt @@ -0,0 +1,12 @@ +--TEST-- +Turbofish: instance method call +--FILE-- +($x) { return $x; } +} +$c = new C; +var_dump($c->m::(99)); +?> +--EXPECT-- +int(99) diff --git a/Zend/tests/generics/turbofish/instanceof_with_args.phpt b/Zend/tests/generics/turbofish/instanceof_with_args.phpt new file mode 100644 index 000000000000..a7672b7f3a84 --- /dev/null +++ b/Zend/tests/generics/turbofish/instanceof_with_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Turbofish: instanceof discards type arguments +--FILE-- +); +var_dump($c instanceof C>); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/Zend/tests/generics/turbofish/many_type_args.phpt b/Zend/tests/generics/turbofish/many_type_args.phpt new file mode 100644 index 000000000000..59d5193f5e1a --- /dev/null +++ b/Zend/tests/generics/turbofish/many_type_args.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish: arity must match callee's declared generic parameters +--FILE-- +($x) { return $x; } +try { + f::(7); +} catch (ArgumentCountError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Too many generic type arguments to f(), 3 passed and exactly 1 expected diff --git a/Zend/tests/generics/turbofish/nested_args.phpt b/Zend/tests/generics/turbofish/nested_args.phpt new file mode 100644 index 000000000000..c3c77794e157 --- /dev/null +++ b/Zend/tests/generics/turbofish/nested_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +Turbofish: nested type arguments +--FILE-- +($x) { return $x; } +var_dump(f::>(["a", "b"])); +?> +--EXPECT-- +array(2) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" +} diff --git a/Zend/tests/generics/turbofish/new_with_args.phpt b/Zend/tests/generics/turbofish/new_with_args.phpt new file mode 100644 index 000000000000..494aeb67dbe1 --- /dev/null +++ b/Zend/tests/generics/turbofish/new_with_args.phpt @@ -0,0 +1,13 @@ +--TEST-- +Turbofish: new with type arguments +--FILE-- + { + public int $x; + public function __construct(int $x) { $this->x = $x; } +} +$b = new Box::(42); +echo $b->x, "\n"; +?> +--EXPECT-- +42 diff --git a/Zend/tests/generics/turbofish/no_runtime_effect.phpt b/Zend/tests/generics/turbofish/no_runtime_effect.phpt new file mode 100644 index 000000000000..632fee88d045 --- /dev/null +++ b/Zend/tests/generics/turbofish/no_runtime_effect.phpt @@ -0,0 +1,15 @@ +--TEST-- +Turbofish: with matching arity has zero runtime effect on values +--FILE-- +(int $x): int { return $x * 2; } +var_dump(f(5)); +var_dump(f::(5)); +var_dump(f::(5)); +var_dump(f::(5)); +?> +--EXPECT-- +int(10) +int(10) +int(10) +int(10) diff --git a/Zend/tests/generics/turbofish/nullsafe_method_call.phpt b/Zend/tests/generics/turbofish/nullsafe_method_call.phpt new file mode 100644 index 000000000000..fe4d04782ecc --- /dev/null +++ b/Zend/tests/generics/turbofish/nullsafe_method_call.phpt @@ -0,0 +1,15 @@ +--TEST-- +Turbofish: nullsafe method call +--FILE-- +($x) { return $x; } +} +$c = new C; +var_dump($c?->m::(5)); +$null = null; +var_dump($null?->m::(5)); +?> +--EXPECT-- +int(5) +NULL diff --git a/Zend/tests/generics/turbofish/static_method_call.phpt b/Zend/tests/generics/turbofish/static_method_call.phpt new file mode 100644 index 000000000000..681b35adac81 --- /dev/null +++ b/Zend/tests/generics/turbofish/static_method_call.phpt @@ -0,0 +1,11 @@ +--TEST-- +Turbofish: static method call +--FILE-- +($x) { return $x; } +} +var_dump(C::m::(7)); +?> +--EXPECT-- +int(7) diff --git a/Zend/tests/generics/turbofish/turbofish_in_attribute.phpt b/Zend/tests/generics/turbofish/turbofish_in_attribute.phpt new file mode 100644 index 000000000000..c8822c08ccf7 --- /dev/null +++ b/Zend/tests/generics/turbofish/turbofish_in_attribute.phpt @@ -0,0 +1,39 @@ +--TEST-- +Turbofish: type arguments are accepted on attribute declarations +--FILE-- + { + public function __construct(public int $priority = 0) {} +} + +class UserCreated {} +class UserDeleted {} +class OrderPlaced {} + +class EventListener { + #[Listens::] + public function onUserCreated(object $event): void {} + + #[Listens::()] + public function onUserDeleted(object $event): void {} + + #[Listens::(priority: -1)] + public function onOrderPlaced(object $event): void {} +} + +$rc = new ReflectionClass(EventListener::class); +foreach ($rc->getMethods() as $method) { + foreach ($method->getAttributes() as $attr) { + $instance = $attr->newInstance(); + echo $method->getName(), ": #[", $attr->getName(), + "] priority=", $instance->priority, "\n"; + } +} +?> +--EXPECT-- +onUserCreated: #[Listens] priority=0 +onUserDeleted: #[Listens] priority=0 +onOrderPlaced: #[Listens] priority=-1 diff --git a/Zend/tests/generics/turbofish/with_named_args.phpt b/Zend/tests/generics/turbofish/with_named_args.phpt new file mode 100644 index 000000000000..3514c99fb2ed --- /dev/null +++ b/Zend/tests/generics/turbofish/with_named_args.phpt @@ -0,0 +1,11 @@ +--TEST-- +Turbofish: combined with named arguments +--FILE-- +($a, $b) { return "$a:$b"; } +echo f::(a: 1, b: 2), "\n"; +echo f::(b: "x", a: 5), "\n"; +?> +--EXPECT-- +1:2 +5:x diff --git a/Zend/tests/generics/turbofish/with_spread.phpt b/Zend/tests/generics/turbofish/with_spread.phpt new file mode 100644 index 000000000000..c35332c212a6 --- /dev/null +++ b/Zend/tests/generics/turbofish/with_spread.phpt @@ -0,0 +1,10 @@ +--TEST-- +Turbofish: combined with spread/unpacking +--FILE-- +($a, $b, $c) { return $a + $b + $c; } +$args = [1, 2, 3]; +echo f::(...$args), "\n"; +?> +--EXPECT-- +6 diff --git a/Zend/zend.h b/Zend/zend.h index 0d5303192b57..dcaf46ad462f 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -228,6 +228,9 @@ struct _zend_class_entry { zend_string *doc_comment; + struct _zend_generic_parameter_list *generic_parameters; + struct _zend_generic_type_table *generic_types; + union { struct { zend_string *filename; diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a7e26711cd17..8282d33ecbe6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -136,7 +136,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast * ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, + zend_ast *child4, zend_ast *child5 ) { zend_ast_decl *ast; @@ -153,6 +154,7 @@ ZEND_API zend_ast *zend_ast_create_decl( ast->child[2] = child2; ast->child[3] = child3; ast->child[4] = child4; + ast->child[5] = child5; return (zend_ast *) ast; } @@ -1504,7 +1506,8 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) zend_ast_destroy(decl->child[1]); zend_ast_destroy(decl->child[2]); zend_ast_destroy(decl->child[3]); - ast = decl->child[4]; + zend_ast_destroy(decl->child[4]); + ast = decl->child[5]; goto tail_call; } else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) { zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast; @@ -2035,6 +2038,8 @@ static ZEND_COLD void zend_ast_export_visibility(smart_str *str, uint32_t flags, } } +static ZEND_COLD void zend_ast_export_generic_type_argument_list(smart_str *str, zend_ast *list_ast, int indent); + static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) { if (ast->kind == ZEND_AST_TYPE_UNION) { const zend_ast_list *list = zend_ast_get_list(ast); @@ -2056,12 +2061,88 @@ static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int in } return; } + if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + if (ast->attr & ZEND_TYPE_NULLABLE) { + smart_str_appendc(str, '?'); + } + + zend_ast *name_ast = ast->child[0]; + if (name_ast->kind == ZEND_AST_TYPE) { + zend_ast_export_type(str, name_ast, indent); + } else { + zend_ast_export_ns_name(str, name_ast, 0, indent); + } + zend_ast_export_generic_type_argument_list(str, ast->child[1], indent); + return; + } if (ast->attr & ZEND_TYPE_NULLABLE) { smart_str_appendc(str, '?'); } zend_ast_export_ns_name(str, ast, 0, indent); } +static ZEND_COLD void zend_ast_export_generic_type_argument_list(smart_str *str, zend_ast *list_ast, int indent) +{ + const zend_ast_list *list = zend_ast_get_list(list_ast); + smart_str_appendc(str, '<'); + for (uint32_t i = 0; i < list->children; i++) { + if (i != 0) { + smart_str_appends(str, ", "); + } + zend_ast_export_type(str, list->child[i], indent); + } + smart_str_appendc(str, '>'); +} + +static ZEND_COLD void zend_ast_export_generic_call_type_argument_list(smart_str *str, zend_ast *list_ast, int indent) +{ + const zend_ast_list *list = zend_ast_get_list(list_ast); + smart_str_appends(str, "::<"); + for (uint32_t i = 0; i < list->children; i++) { + if (i != 0) { + smart_str_appends(str, ", "); + } + zend_ast_export_type(str, list->child[i], indent); + } + smart_str_appendc(str, '>'); +} + +static ZEND_COLD void zend_ast_export_generic_type_parameter(smart_str *str, zend_ast *ast, int indent) +{ + switch ((zend_generic_variance) ast->attr) { + case ZEND_GENERIC_VARIANCE_COVARIANT: + smart_str_appendc(str, '+'); + break; + case ZEND_GENERIC_VARIANCE_CONTRAVARIANT: + smart_str_appendc(str, '-'); + break; + case ZEND_GENERIC_VARIANCE_INVARIANT: + break; + } + smart_str_append(str, zend_ast_get_str(ast->child[0])); + if (ast->child[1]) { + smart_str_appends(str, " : "); + zend_ast_export_type(str, ast->child[1], indent); + } + if (ast->child[2]) { + smart_str_appends(str, " = "); + zend_ast_export_type(str, ast->child[2], indent); + } +} + +static ZEND_COLD void zend_ast_export_generic_type_parameter_list(smart_str *str, zend_ast *list_ast, int indent) +{ + const zend_ast_list *list = zend_ast_get_list(list_ast); + smart_str_appendc(str, '<'); + for (uint32_t i = 0; i < list->children; i++) { + if (i != 0) { + smart_str_appends(str, ", "); + } + zend_ast_export_generic_type_parameter(str, list->child[i], indent); + } + smart_str_appendc(str, '>'); +} + static ZEND_COLD void zend_ast_export_hook_list(smart_str *str, const zend_ast_list *hook_list, int indent) { smart_str_appends(str, " {"); @@ -2211,6 +2292,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio if (ast->kind != ZEND_AST_CLOSURE && ast->kind != ZEND_AST_ARROW_FUNC) { smart_str_append(str, decl->name); } + if (decl->child[5]) { + zend_ast_export_generic_type_parameter_list(str, decl->child[5], indent); + } smart_str_appendc(str, '('); zend_ast_export_ex(str, decl->child[0], 0, indent); smart_str_appendc(str, ')'); @@ -2268,6 +2352,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "class "); } smart_str_append(str, decl->name); + if (decl->child[5]) { + zend_ast_export_generic_type_parameter_list(str, decl->child[5], indent); + } if (decl->flags & ZEND_ACC_ENUM && decl->child[4]) { smart_str_appends(str, ": "); zend_ast_export_type(str, decl->child[4], indent); @@ -2374,6 +2461,15 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_NAME_LIST: zend_ast_export_name_list(str, zend_ast_get_list(ast), indent); break; + case ZEND_AST_GENERIC_TYPE_PARAMETER_LIST: + zend_ast_export_generic_type_parameter_list(str, ast, indent); + break; + case ZEND_AST_GENERIC_TYPE_ARGUMENT_LIST: + zend_ast_export_generic_type_argument_list(str, ast, indent); + break; + case ZEND_AST_GENERIC_CALL_TYPE_ARGUMENT_LIST: + zend_ast_export_generic_call_type_argument_list(str, ast, indent); + break; case ZEND_AST_USE: smart_str_appends(str, "use "); if (ast->attr == T_FUNCTION) { @@ -2549,6 +2645,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } else { zend_ast_export_ns_name(str, left, 0, indent); } + if (ast->child[2]) { + zend_ast_export_generic_call_type_argument_list(str, ast->child[2], indent); + } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2657,6 +2756,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_class_no_header(str, decl, indent); } else { zend_ast_export_ns_name(str, ast->child[0], 0, indent); + if (ast->child[2]) { + zend_ast_export_generic_call_type_argument_list(str, ast->child[2], indent); + } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2667,6 +2769,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, " instanceof "); zend_ast_export_ns_name(str, ast->child[1], 0, indent); break; + case ZEND_AST_GENERIC_NAMED_TYPE: + zend_ast_export_type(str, ast, indent); + break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); smart_str_appends(str, "yield "); @@ -2847,12 +2952,15 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio ast = ast->child[1]; goto tail_call; - /* 3 child nodes */ + /* 4 child nodes */ case ZEND_AST_METHOD_CALL: case ZEND_AST_NULLSAFE_METHOD_CALL: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, ast->kind == ZEND_AST_NULLSAFE_METHOD_CALL ? "?->" : "->"); zend_ast_export_var(str, ast->child[1], indent); + if (ast->child[3]) { + zend_ast_export_generic_call_type_argument_list(str, ast->child[3], indent); + } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); @@ -2861,6 +2969,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], indent); + if (ast->child[3]) { + zend_ast_export_generic_call_type_argument_list(str, ast->child[3], indent); + } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); @@ -2941,6 +3052,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_ex(str, ast->child[1], 0, indent); } break; + case ZEND_AST_GENERIC_TYPE_PARAMETER: + zend_ast_export_generic_type_parameter(str, ast, indent); + break; /* 4 child nodes */ case ZEND_AST_FOR: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 24b77d7d3493..0bb2c31d2c96 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -69,6 +69,9 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, ZEND_AST_MODIFIER_LIST, + ZEND_AST_GENERIC_TYPE_PARAMETER_LIST, + ZEND_AST_GENERIC_TYPE_ARGUMENT_LIST, + ZEND_AST_GENERIC_CALL_TYPE_ARGUMENT_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -117,7 +120,6 @@ enum _zend_ast_kind { ZEND_AST_PROP, ZEND_AST_NULLSAFE_PROP, ZEND_AST_STATIC_PROP, - ZEND_AST_CALL, ZEND_AST_CLASS_CONST, ZEND_AST_ASSIGN, ZEND_AST_ASSIGN_REF, @@ -128,7 +130,6 @@ enum _zend_ast_kind { ZEND_AST_AND, ZEND_AST_OR, ZEND_AST_ARRAY_ELEM, - ZEND_AST_NEW, ZEND_AST_INSTANCEOF, ZEND_AST_YIELD, ZEND_AST_COALESCE, @@ -153,18 +154,18 @@ enum _zend_ast_kind { ZEND_AST_MATCH_ARM, ZEND_AST_NAMED_ARG, ZEND_AST_PIPE, + ZEND_AST_GENERIC_NAMED_TYPE, /* 3 child nodes */ - ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, - ZEND_AST_NULLSAFE_METHOD_CALL, - ZEND_AST_STATIC_CALL, - ZEND_AST_CONDITIONAL, - + ZEND_AST_CONDITIONAL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_TRY, ZEND_AST_CATCH, ZEND_AST_PROP_GROUP, ZEND_AST_CONST_ELEM, ZEND_AST_CLASS_CONST_GROUP, + ZEND_AST_CALL, + ZEND_AST_NEW, + ZEND_AST_GENERIC_TYPE_PARAMETER, /* 4 child nodes */ ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -175,6 +176,11 @@ enum _zend_ast_kind { // Pseudo node for initializing enums ZEND_AST_CONST_ENUM_INIT, + /* Method/static call forms with optional call_type_arguments slot. */ + ZEND_AST_METHOD_CALL, + ZEND_AST_NULLSAFE_METHOD_CALL, + ZEND_AST_STATIC_CALL, + /* 5 child nodes */ /* 6 child nodes */ @@ -225,7 +231,7 @@ typedef struct _zend_ast_decl { uint32_t flags; zend_string *doc_comment; zend_string *name; - zend_ast *child[5]; + zend_ast *child[6]; } zend_ast_decl; // TODO: rename @@ -337,7 +343,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, + zend_ast *child4, zend_ast *child5 ); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 71f0d788e921..1bc382248fd0 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -508,6 +508,7 @@ ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_string attr->lineno = lineno; attr->offset = offset; attr->argc = argc; + attr->generic_arity = 0; /* Initialize arguments to avoid partial initialization in case of fatal errors. */ for (uint32_t i = 0; i < argc; i++) { diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index c044ef073cc8..b258db6d31dc 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -67,6 +67,8 @@ typedef struct _zend_attribute { /* Parameter offsets start at 1, everything else uses 0. */ uint32_t offset; uint32_t argc; + /* Number of generic type arguments at the attribute use-site. */ + uint8_t generic_arity; zend_attribute_arg args[1]; } zend_attribute; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a96af71aa900..1ad7600115db 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -101,6 +101,7 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t static void zend_compile_expr(znode *result, zend_ast *ast); static void zend_compile_stmt(zend_ast *ast); static void zend_compile_assign(znode *result, zend_ast *ast, bool stmt, uint32_t type); +static zend_type zend_compile_typename(zend_ast *ast); #ifdef ZEND_CHECK_STACK_LIMIT zend_never_inline static void zend_stack_limit_error(void) @@ -431,6 +432,482 @@ void zend_init_compiler_data_structures(void) /* {{{ */ CG(encoding_declared) = 0; CG(memoized_exprs) = NULL; CG(memoize_mode) = ZEND_MEMOIZE_NONE; + CG(type_arg_depth) = 0; + CG(type_arg_residual_token) = 0; + CG(generic_scope) = NULL; +} +/* }}} */ + +static void zend_generic_scope_push(zend_generic_parameter_list *params, zend_generic_origin origin) /* {{{ */ +{ + zend_generic_scope_entry *entry = emalloc(sizeof(zend_generic_scope_entry)); + entry->params = params; + entry->visible_count = params ? params->count : 0; + entry->self_compiling = NULL; + entry->shadowing_classes = NULL; + entry->origin = origin; + entry->outer = CG(generic_scope); + CG(generic_scope) = entry; +} +/* }}} */ + +static void zend_generic_scope_pop(void) /* {{{ */ +{ + zend_generic_scope_entry *top = CG(generic_scope); + ZEND_ASSERT(top != NULL); + if (top->shadowing_classes) { + zend_hash_destroy(top->shadowing_classes); + FREE_HASHTABLE(top->shadowing_classes); + } + + CG(generic_scope) = top->outer; + efree(top); +} +/* }}} */ + +static zend_generic_parameter *zend_generic_lookup_full( + zend_string *name, zend_generic_origin *origin_out, uint32_t *index_out) /* {{{ */ +{ + zend_string *lc_name = zend_string_tolower(name); + for (zend_generic_scope_entry *e = CG(generic_scope); e; e = e->outer) { + if (e->shadowing_classes && zend_hash_exists(e->shadowing_classes, lc_name)) { + zend_string_release(lc_name); + return NULL; + } + + zend_generic_parameter_list *params = e->params; + for (uint32_t i = 0; i < e->visible_count; i++) { + if (zend_string_equals(params->parameters[i].name, name)) { + if (origin_out) *origin_out = e->origin; + if (index_out) *index_out = i; + zend_string_release(lc_name); + return ¶ms->parameters[i]; + } + } + } + + zend_string_release(lc_name); + return NULL; +} +/* }}} */ + +/* Detects forward reference: a name that matches a parameter declared later in + * the innermost scope's parameter list. Returns the index of the not-yet-visible + * parameter, or -1 if no match. */ +static int zend_generic_lookup_forward(const zend_string *name) /* {{{ */ +{ + zend_generic_scope_entry *e = CG(generic_scope); + if (!e) { + return -1; + } + zend_generic_parameter_list *params = e->params; + for (uint32_t i = e->visible_count; i < params->count; i++) { + if (zend_string_equals(params->parameters[i].name, name)) { + return (int) i; + } + } + return -1; +} +/* }}} */ + +static bool zend_type_ast_has_generic_content(zend_ast *ast); +static zend_type zend_compile_pre_erasure_typename(zend_ast *ast); + +static zend_generic_parameter *zend_generic_lookup(zend_string *name) /* {{{ */ +{ + return zend_generic_lookup_full(name, NULL, NULL); +} +/* }}} */ + +ZEND_API void zend_check_generic_param_list_size(zend_ast *list_ast) /* {{{ */ +{ + if (list_ast == NULL) { + return; + } + + uint32_t children = zend_ast_get_list(list_ast)->children; + if (children > ZEND_GENERIC_MAX_PARAMS) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot declare more than %u generic type parameters (got %u)", + ZEND_GENERIC_MAX_PARAMS, children); + } +} +/* }}} */ + +ZEND_API void zend_check_generic_arg_list_size(zend_ast *list_ast) /* {{{ */ +{ + if (list_ast == NULL) { + return; + } + + uint32_t children = zend_ast_get_list(list_ast)->children; + if (children > ZEND_GENERIC_MAX_PARAMS) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot specify more than %u generic type arguments (got %u)", + ZEND_GENERIC_MAX_PARAMS, children); + } +} +/* }}} */ + +static zend_always_inline void zend_compute_generic_required_total( + const zend_generic_parameter_list *params, uint32_t *required, uint32_t *total) +{ + uint32_t t = params ? params->count : 0; + uint32_t r = 0; + if (params) { + while (r < t && !ZEND_TYPE_IS_SET(params->parameters[r].default_type)) { + r++; + } + } + + *required = r; + *total = t; +} + +ZEND_API void zend_check_generic_call_arity(const zend_function *fbc, uint32_t arity) +{ + const zend_generic_parameter_list *params = NULL; + if (ZEND_USER_CODE(fbc->common.type)) { + params = fbc->op_array.generic_parameters; + } + + uint32_t required, total; + zend_compute_generic_required_total(params, &required, &total); + + if (arity > total) { + zend_throw_error(zend_ce_argument_count_error, + "Too many generic type arguments to %s%s%s(), %u passed and %s %u expected", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", + fbc->common.function_name ? ZSTR_VAL(fbc->common.function_name) : "{closure}", + arity, + required == total ? "exactly" : "at most", + total); + return; + } + if (arity < required) { + zend_throw_error(zend_ce_argument_count_error, + "Too few generic type arguments to %s%s%s(), %u passed and %s %u expected", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", + fbc->common.function_name ? ZSTR_VAL(fbc->common.function_name) : "{closure}", + arity, + required == total ? "exactly" : "at least", + required); + } +} + +ZEND_API void zend_check_generic_new_arity(const zend_class_entry *ce, uint32_t arity) +{ + uint32_t required, total; + zend_compute_generic_required_total(ce->generic_parameters, &required, &total); + + if (arity > total) { + zend_throw_error(zend_ce_argument_count_error, + "Too many generic type arguments to new %s, %u passed and %s %u expected", + ZSTR_VAL(ce->name), arity, + required == total ? "exactly" : "at most", total); + return; + } + if (arity < required) { + zend_throw_error(zend_ce_argument_count_error, + "Too few generic type arguments to new %s, %u passed and %s %u expected", + ZSTR_VAL(ce->name), arity, + required == total ? "exactly" : "at least", required); + } +} + +#define ZEND_VERIFY_ARITY_KIND_CALL 0 +#define ZEND_VERIFY_ARITY_KIND_NEW 1 + +static void zend_emit_verify_generic_arity(zend_ast *turbofish_ast, uint8_t kind, const znode *new_result) +{ + if (turbofish_ast == NULL) { + return; + } + uint32_t arity = zend_ast_get_list(turbofish_ast)->children; + ZEND_ASSERT(arity > 0 && arity <= ZEND_GENERIC_MAX_PARAMS); + + zend_op *opline = get_next_op(); + opline->opcode = ZEND_VERIFY_GENERIC_ARITY; + opline->op2_type = IS_UNUSED; + opline->op2.num = arity; + opline->extended_value = 0; + if (kind == ZEND_VERIFY_ARITY_KIND_NEW) { + ZEND_ASSERT(new_result != NULL && new_result->op_type == IS_TMP_VAR); + opline->op1_type = new_result->op_type; + opline->op1.var = new_result->u.op.var; + } else { + opline->op1_type = IS_UNUSED; + opline->op1.num = 0; + } + opline->result_type = IS_UNUSED; + opline->result.num = 0; +} + +static zend_generic_parameter_list *zend_compile_generic_type_parameter_list(zend_ast *list_ast) /* {{{ */ +{ + if (!list_ast) { + return NULL; + } + zend_ast_list *list = zend_ast_get_list(list_ast); + ZEND_ASSERT(list->children > 0); + + zend_generic_parameter_list *params = + zend_generic_parameter_list_alloc(list->children, /* persistent */ false); + + zend_string *prev_optional_name = NULL; + + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *param_ast = list->child[i]; + ZEND_ASSERT(param_ast->kind == ZEND_AST_GENERIC_TYPE_PARAMETER); + zend_string *name = zval_make_interned_string(zend_ast_get_zval(param_ast->child[0])); + bool has_default = param_ast->child[2] != NULL; + + for (uint32_t j = 0; j < i; j++) { + if (zend_string_equals(params->parameters[j].name, name)) { + zend_string *dup = zend_string_copy(name); + zend_generic_parameter_list_destroy(params); + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot redeclare type parameter %s", ZSTR_VAL(dup)); + } + } + + if (zend_generic_lookup(name)) { + zend_string *dup = zend_string_copy(name); + zend_generic_parameter_list_destroy(params); + zend_error_noreturn(E_COMPILE_ERROR, + "Type parameter %s shadows enclosing type parameter", ZSTR_VAL(dup)); + } + + if (!has_default && prev_optional_name != NULL) { + zend_string *cur = zend_string_copy(name); + zend_string *prev = zend_string_copy(prev_optional_name); + zend_generic_parameter_list_destroy(params); + zend_error_noreturn(E_COMPILE_ERROR, + "Optional type parameter %s cannot be declared before required type parameter %s", + ZSTR_VAL(prev), ZSTR_VAL(cur)); + } + + if (has_default && prev_optional_name == NULL) { + prev_optional_name = name; + } + + params->parameters[i].name = zend_string_copy(name); + params->parameters[i].variance = (zend_generic_variance) param_ast->attr; + } + + params->count = list->children; + + zend_generic_scope_push(params, ZEND_GENERIC_ORIGIN_CLASS_LIKE); + CG(generic_scope)->visible_count = 0; + + for (uint32_t i = 0; i < list->children; i++) { + zend_ast *param_ast = list->child[i]; + CG(generic_scope)->visible_count = i + 1; + CG(generic_scope)->self_compiling = ¶ms->parameters[i]; + if (param_ast->child[1]) { + params->parameters[i].bound = zend_compile_typename(param_ast->child[1]); + if (zend_type_ast_has_generic_content(param_ast->child[1])) { + params->parameters[i].bound_pre_erasure = + zend_compile_pre_erasure_typename(param_ast->child[1]); + } + } + + if (param_ast->child[2]) { + params->parameters[i].default_type = zend_compile_typename(param_ast->child[2]); + if (zend_type_ast_has_generic_content(param_ast->child[2])) { + params->parameters[i].default_pre_erasure = + zend_compile_pre_erasure_typename(param_ast->child[2]); + } + } + + CG(generic_scope)->self_compiling = NULL; + } + + zend_generic_scope_pop(); + + return params; +} +/* }}} */ + +/* Returns true if the AST contains any generic-aware constructs (a type-parameter + * reference or a named type with type arguments) that need pre-erasure capture. */ +static bool zend_type_ast_has_generic_content(zend_ast *ast) +{ + if (!ast) { + return false; + } + zend_ast_attr orig = ast->attr; + ast->attr &= ~ZEND_TYPE_NULLABLE; + bool result = false; + + if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + result = true; + } else if (ast->kind == ZEND_AST_TYPE_UNION || ast->kind == ZEND_AST_TYPE_INTERSECTION) { + zend_ast_list *list = zend_ast_get_list(ast); + for (uint32_t i = 0; i < list->children; i++) { + if (zend_type_ast_has_generic_content(list->child[i])) { + result = true; + break; + } + } + } else if (ast->kind == ZEND_AST_ZVAL) { + if ((ast->attr & ZEND_NAME_NOT_FQ) == ZEND_NAME_NOT_FQ) { + const zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING && zend_generic_lookup(Z_STR_P(zv))) { + result = true; + } + } + } + + ast->attr = orig; + return result; +} + +/* Build a pre-erasure zend_type from a type-expression AST. The returned type + * may carry type-parameter references (TYPE_PARAMETER bit) or named-with-args + * payloads (NAMED_WITH_ARGS bit). Used only for the side table; the runtime + * never sees this form. */ +static zend_type zend_compile_pre_erasure_typename(zend_ast *ast) +{ + bool is_marked_nullable = ast->attr & ZEND_TYPE_NULLABLE; + zend_ast_attr orig_attr = ast->attr; + if (is_marked_nullable) { + ast->attr &= ~ZEND_TYPE_NULLABLE; + } + + zend_type result = ZEND_TYPE_INIT_NONE(0); + + if (ast->kind == ZEND_AST_TYPE_UNION || ast->kind == ZEND_AST_TYPE_INTERSECTION) { + zend_ast_list *list = zend_ast_get_list(ast); + zend_type_list *type_list = emalloc(ZEND_TYPE_LIST_SIZE(list->children)); + type_list->num_types = list->children; + for (uint32_t i = 0; i < list->children; i++) { + type_list->types[i] = zend_compile_pre_erasure_typename(list->child[i]); + } + ZEND_TYPE_SET_PTR(result, type_list); + ZEND_TYPE_FULL_MASK(result) |= _ZEND_TYPE_LIST_BIT | + (ast->kind == ZEND_AST_TYPE_UNION ? _ZEND_TYPE_UNION_BIT : _ZEND_TYPE_INTERSECTION_BIT); + } else if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + zend_ast *name_ast = ast->child[0]; + zend_ast_list *args_list = zend_ast_get_list(ast->child[1]); + zend_type_named_with_args *payload = emalloc(ZEND_TYPE_NAMED_WITH_ARGS_SIZE(args_list->children)); + if (name_ast->kind == ZEND_AST_TYPE) { + const char *cname; + switch (name_ast->attr) { + case IS_ARRAY: + cname = "array"; + break; + case IS_STATIC: + cname = "static"; + break; + default: + ZEND_UNREACHABLE(); + } + + payload->name = zend_string_init_interned(cname, strlen(cname), 0); + payload->name_attr = 0; + } else { + payload->name = zend_string_copy(zval_make_interned_string(zend_ast_get_zval(name_ast))); + payload->name_attr = name_ast->attr; + } + + payload->count = args_list->children; + for (uint32_t i = 0; i < args_list->children; i++) { + payload->args[i] = zend_compile_pre_erasure_typename(args_list->child[i]); + } + + ZEND_TYPE_SET_PTR(result, payload); + ZEND_TYPE_FULL_MASK(result) |= _ZEND_TYPE_NAMED_WITH_ARGS_BIT; + } else if (ast->kind == ZEND_AST_TYPE) { + /* Builtin pseudo-type: same as erased. */ + result = (zend_type) ZEND_TYPE_INIT_CODE(ast->attr, 0, 0); + } else if (ast->kind == ZEND_AST_ZVAL) { + zend_generic_origin origin; + uint32_t index; + zend_generic_parameter *param = NULL; + if ((ast->attr & ZEND_NAME_NOT_FQ) == ZEND_NAME_NOT_FQ) { + const zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING) { + param = zend_generic_lookup_full(Z_STR_P(zv), &origin, &index); + } + } + if (param) { + zend_type_parameter_ref *ref = emalloc(sizeof(*ref)); + ref->name = zend_string_copy(param->name); + ref->index = index; + ref->origin = origin; + ZEND_TYPE_SET_PTR(result, ref); + ZEND_TYPE_FULL_MASK(result) |= _ZEND_TYPE_TYPE_PARAMETER_BIT; + } else { + if ((ast->attr & ZEND_NAME_NOT_FQ) == ZEND_NAME_NOT_FQ) { + const zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING + && zend_generic_lookup_forward(Z_STR_P(zv)) >= 0) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type parameter %s referenced before declaration", + Z_STRVAL_P(zv)); + } + } + zend_string *name = zval_make_interned_string(zend_ast_get_zval(ast)); + uint8_t code = zend_lookup_builtin_type_by_name(name); + if (code != 0) { + if (code == IS_ITERABLE) { + zend_type iterable = (zend_type) ZEND_TYPE_INIT_CLASS_MASK( + ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), + (MAY_BE_ARRAY|_ZEND_TYPE_ITERABLE_BIT)); + result = iterable; + } else { + result = (zend_type) ZEND_TYPE_INIT_CODE(code, 0, 0); + } + } else { + zend_string_addref(name); + result = (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0); + } + } + } else { + ZEND_UNREACHABLE(); + } + + if (is_marked_nullable) { + ZEND_TYPE_FULL_MASK(result) |= _ZEND_TYPE_NULLABLE_BIT; + } + ast->attr = orig_attr; + return result; +} + +/* Ensure op_array->generic_types is allocated, then return it. */ +static zend_generic_type_table *zend_generic_get_or_create_op_array_table(zend_op_array *op_array) +{ + if (!op_array->generic_types) { + op_array->generic_types = zend_generic_type_table_alloc(); + } + return op_array->generic_types; +} + +/* Ensure ce->generic_types is allocated, then return it. */ +static zend_generic_type_table *zend_generic_get_or_create_class_table(zend_class_entry *ce) +{ + if (!ce->generic_types) { + ce->generic_types = zend_generic_type_table_alloc(); + } + return ce->generic_types; +} + +static zend_generic_parameter *zend_generic_lookup_name(zend_ast *ast) /* {{{ */ +{ + if (!CG(generic_scope) || ast->kind != ZEND_AST_ZVAL) { + return NULL; + } + if ((ast->attr & ZEND_NAME_NOT_FQ) != ZEND_NAME_NOT_FQ) { + return NULL; + } + zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) != IS_STRING) { + return NULL; + } + return zend_generic_lookup(Z_STR_P(zv)); } /* }}} */ @@ -1223,10 +1700,23 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* static zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ { + if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + ast = ast->child[0]; + } + const zval *class_name = zend_ast_get_zval(ast); if (Z_TYPE_P(class_name) != IS_STRING) { zend_error_noreturn(E_COMPILE_ERROR, "Illegal class name"); } + + if ((ast->attr & ZEND_NAME_NOT_FQ) == ZEND_NAME_NOT_FQ + && zend_generic_lookup(Z_STR_P(class_name))) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use generic type parameter %s as a class reference at runtime; " + "bound-erased generic types have no runtime representation", + Z_STRVAL_P(class_name)); + } + return zend_resolve_class_name(Z_STR_P(class_name), ast->attr); } /* }}} */ @@ -1771,6 +2261,9 @@ static uint32_t zend_get_class_fetch_type_ast(zend_ast *name_ast) /* {{{ */ static zend_string *zend_resolve_const_class_name_reference(zend_ast *ast, const char *type) { + if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + ast = ast->child[0]; + } zend_string *class_name = zend_ast_get_str(ast); if (ZEND_FETCH_CLASS_DEFAULT != zend_get_class_fetch_type_ast(ast)) { zend_error_noreturn(E_COMPILE_ERROR, @@ -2048,6 +2541,12 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem) /* {{{ */ zval zv; int ret; + if (CG(type_arg_residual_token)) { + ret = CG(type_arg_residual_token); + CG(type_arg_residual_token) = 0; + return ret; + } + if (CG(increment_lineno)) { CG(zend_lineno)++; CG(increment_lineno) = 0; @@ -2055,8 +2554,22 @@ int ZEND_FASTCALL zendlex(zend_parser_stack_elem *elem) /* {{{ */ ret = lex_scan(&zv, elem); ZEND_ASSERT(!EG(exception) || ret == T_ERROR); - return ret; + if (CG(type_arg_depth) > 0) { + switch (ret) { + case T_SR: + CG(type_arg_residual_token) = '>'; + return '>'; + case T_IS_GREATER_OR_EQUAL: + CG(type_arg_residual_token) = '='; + return '>'; + case T_SR_EQUAL: + CG(type_arg_residual_token) = T_IS_GREATER_OR_EQUAL; + return '>'; + } + } + + return ret; } /* }}} */ @@ -2090,6 +2603,8 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand ce->attributes = NULL; ce->enum_backing_type = IS_UNDEF; ce->backed_enum_table = NULL; + ce->generic_parameters = NULL; + ce->generic_types = NULL; if (nullify_handlers) { ce->constructor = NULL; @@ -2800,6 +3315,9 @@ static inline bool zend_can_write_to_variable(const zend_ast *ast) /* {{{ */ static inline bool zend_is_const_default_class_ref(zend_ast *name_ast) /* {{{ */ { + if (name_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + name_ast = name_ast->child[0]; + } if (name_ast->kind != ZEND_AST_ZVAL) { return false; } @@ -2856,6 +3374,11 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f { uint32_t fetch_type; + /* Generic named type: discard type arguments and resolve the bare name. */ + if (name_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + name_ast = name_ast->child[0]; + } + if (name_ast->kind != ZEND_AST_ZVAL) { znode name_node; @@ -4004,11 +4527,13 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f } /* }}} */ -static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno, uint32_t type) /* {{{ */ +static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno, uint32_t type, zend_ast *turbofish_ast, uint8_t verify_kind, const znode *new_result) /* {{{ */ { zend_op *opline; uint32_t opnum_init = get_next_op_number() - 1; + zend_emit_verify_generic_arity(turbofish_ast, verify_kind, new_result); + if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) { opline = &CG(active_op_array)->opcodes[opnum_init]; opline->extended_value = 0; @@ -4090,7 +4615,7 @@ static bool zend_compile_function_name(znode *name_node, zend_ast *name_ast) /* } /* }}} */ -static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */ +static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_ast, zend_ast *turbofish_ast, uint32_t lineno, uint32_t type) /* {{{ */ { if (name_node->op_type == IS_CONST && Z_TYPE(name_node->u.constant) == IS_STRING) { const char *colon; @@ -4120,7 +4645,7 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast zend_emit_op(NULL, ZEND_INIT_DYNAMIC_CALL, NULL, name_node); } - zend_compile_call_common(result, args_ast, NULL, lineno, type); + zend_compile_call_common(result, args_ast, NULL, lineno, type, turbofish_ast, ZEND_VERIFY_ARITY_KIND_CALL, NULL); } /* }}} */ @@ -4488,7 +5013,7 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string args = (zend_ast_list *)zend_ast_list_add((zend_ast *) args, arg); } - zend_compile_call_common(result, (zend_ast*)args, fbc, lineno, type); + zend_compile_call_common(result, (zend_ast*)args, fbc, lineno, type, NULL, ZEND_VERIFY_ARITY_KIND_CALL, NULL); opline = &CG(active_op_array)->opcodes[check_op_number]; opline->op2.opline_num = get_next_op_number(); @@ -4814,7 +5339,7 @@ static uint32_t zend_compile_frameless_icall(znode *result, const zend_ast_list return zend_compile_frameless_icall_ex(result, args, fbc, frameless_function_info, type); } -static void zend_compile_ns_call(znode *result, const znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */ +static void zend_compile_ns_call(znode *result, const znode *name_node, zend_ast *args_ast, zend_ast *turbofish_ast, uint32_t lineno, uint32_t type) /* {{{ */ { int name_constants = zend_add_ns_func_name_literal(Z_STR(name_node->u.constant)); @@ -4849,7 +5374,7 @@ static void zend_compile_ns_call(znode *result, const znode *name_node, zend_ast opline->op2_type = IS_CONST; opline->op2.constant = name_constants; opline->result.num = zend_alloc_cache_slot(); - zend_compile_call_common(result, args_ast, NULL, lineno, type); + zend_compile_call_common(result, args_ast, NULL, lineno, type, turbofish_ast, ZEND_VERIFY_ARITY_KIND_CALL, NULL); /* Compile frameless call. */ if (frameless_function_info) { @@ -5195,10 +5720,10 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg znode call_result; switch (callback->kind) { case ZEND_AST_CALL: - zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_CALL, callback->child[0], call_args)); + zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_CALL, callback->child[0], call_args, NULL)); break; case ZEND_AST_STATIC_CALL: - zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_STATIC_CALL, callback->child[0], callback->child[1], call_args)); + zend_compile_expr(&call_result, zend_ast_create(ZEND_AST_STATIC_CALL, callback->child[0], callback->child[1], call_args, NULL)); break; } opline = zend_emit_op(NULL, ZEND_ADD_ARRAY_ELEMENT, &call_result, &key); @@ -5400,7 +5925,7 @@ static bool zend_compile_parent_property_hook_call(znode *result, const zend_ast opline->op1.constant = zend_add_literal_string(&property_name); opline->op2.num = hook_kind; - zend_compile_call_common(result, args_ast, NULL, zend_ast_get_lineno(method_ast), BP_VAR_R); + zend_compile_call_common(result, args_ast, NULL, zend_ast_get_lineno(method_ast), BP_VAR_R, NULL, ZEND_VERIFY_ARITY_KIND_CALL, NULL); return true; } @@ -5409,24 +5934,26 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) { zend_ast *name_ast = ast->child[0]; zend_ast *args_ast = ast->child[1]; + zend_ast *turbofish_ast = ast->child[2]; bool is_callable_convert = args_ast->kind == ZEND_AST_CALLABLE_CONVERT; znode name_node; if (name_ast->kind != ZEND_AST_ZVAL || Z_TYPE_P(zend_ast_get_zval(name_ast)) != IS_STRING) { zend_compile_expr(&name_node, name_ast); - zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno, type); + zend_compile_dynamic_call(result, &name_node, args_ast, turbofish_ast, ast->lineno, type); return; } { bool runtime_resolution = zend_compile_function_name(&name_node, name_ast); if (runtime_resolution) { - if (zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert") + if (turbofish_ast == NULL + && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "assert") && !is_callable_convert) { zend_compile_assert(result, zend_ast_get_list(args_ast), Z_STR(name_node.u.constant), NULL, ast->lineno, type); } else { - zend_compile_ns_call(result, &name_node, args_ast, ast->lineno, type); + zend_compile_ns_call(result, &name_node, args_ast, turbofish_ast, ast->lineno, type); } return; } @@ -5440,7 +5967,8 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) zend_op *opline; /* Special assert() handling should apply independently of compiler flags. */ - if (fbc && zend_string_equals_literal(lcname, "assert") && !is_callable_convert) { + if (turbofish_ast == NULL + && fbc && zend_string_equals_literal(lcname, "assert") && !is_callable_convert) { zend_compile_assert(result, zend_ast_get_list(args_ast), lcname, fbc, ast->lineno, type); zend_string_release(lcname); zval_ptr_dtor(&name_node.u.constant); @@ -5451,13 +5979,14 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) || !fbc_is_finalized(fbc) || zend_compile_ignore_function(fbc, CG(active_op_array)->filename)) { zend_string_release_ex(lcname, 0); - zend_compile_dynamic_call(result, &name_node, args_ast, ast->lineno, type); + zend_compile_dynamic_call(result, &name_node, args_ast, turbofish_ast, ast->lineno, type); return; } - if (!is_callable_convert && - zend_try_compile_special_func(result, lcname, - zend_ast_get_list(args_ast), fbc, type, ast->lineno) == SUCCESS + if (turbofish_ast == NULL + && !is_callable_convert + && zend_try_compile_special_func(result, lcname, + zend_ast_get_list(args_ast), fbc, type, ast->lineno) == SUCCESS ) { zend_string_release_ex(lcname, 0); zval_ptr_dtor(&name_node.u.constant); @@ -5476,7 +6005,7 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) Z_EXTRA_P(CT_CONSTANT(opline->op2)) = fbc_bucket - CG(function_table)->arData; } - zend_compile_call_common(result, args_ast, fbc, ast->lineno, type); + zend_compile_call_common(result, args_ast, fbc, ast->lineno, type, turbofish_ast, ZEND_VERIFY_ARITY_KIND_CALL, NULL); } } /* }}} */ @@ -5486,6 +6015,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type zend_ast *obj_ast = ast->child[0]; zend_ast *method_ast = ast->child[1]; zend_ast *args_ast = ast->child[2]; + zend_ast *turbofish_ast = ast->child[3]; znode obj_node, method_node; zend_op *opline; @@ -5540,7 +6070,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type } } - if (zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type)) { + if (zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type, turbofish_ast, ZEND_VERIFY_ARITY_KIND_CALL, NULL)) { if (short_circuiting_checkpoint != zend_short_circuiting_checkpoint()) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot combine nullsafe operator with Closure creation"); @@ -5588,6 +6118,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type zend_ast *class_ast = ast->child[0]; zend_ast *method_ast = ast->child[1]; zend_ast *args_ast = ast->child[2]; + zend_ast *turbofish_ast = ast->child[3]; znode class_node, method_node; zend_op *opline; @@ -5655,7 +6186,7 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type } } - zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type); + zend_compile_call_common(result, args_ast, fbc, zend_ast_get_lineno(method_ast), type, turbofish_ast, ZEND_VERIFY_ARITY_KIND_CALL, NULL); } /* }}} */ @@ -5665,6 +6196,7 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *class_ast = ast->child[0]; zend_ast *args_ast = ast->child[1]; + zend_ast *turbofish_ast = ast->child[2]; znode class_node, ctor_result; zend_op *opline; @@ -5711,7 +6243,7 @@ static void zend_compile_new(znode *result, zend_ast *ast) /* {{{ */ fbc = ce->constructor; } - zend_compile_call_common(&ctor_result, args_ast, fbc, ast->lineno, BP_VAR_R); + zend_compile_call_common(&ctor_result, args_ast, fbc, ast->lineno, BP_VAR_R, turbofish_ast, ZEND_VERIFY_ARITY_KIND_NEW, result); zend_do_free(&ctor_result); } /* }}} */ @@ -6804,23 +7336,23 @@ static void zend_compile_pipe(znode *result, zend_ast *ast, uint32_t type) && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT && zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { fcall_ast = zend_ast_create(ZEND_AST_CALL, - callable_ast->child[0], arg_list_ast); + callable_ast->child[0], arg_list_ast, NULL); /* Turn $foo |> bar::baz(...) into bar::baz($foo). */ } else if (callable_ast->kind == ZEND_AST_STATIC_CALL && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); + callable_ast->child[0], callable_ast->child[1], arg_list_ast, NULL); /* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */ } else if (callable_ast->kind == ZEND_AST_METHOD_CALL && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); + callable_ast->child[0], callable_ast->child[1], arg_list_ast, NULL); /* Turn $foo |> $expr into ($expr)($foo) */ } else { zend_compile_expr(&callable_result, callable_ast); callable_ast = zend_ast_create_znode(&callable_result); fcall_ast = zend_ast_create(ZEND_AST_CALL, - callable_ast, arg_list_ast); + callable_ast, arg_list_ast, NULL); } zend_do_extended_stmt(&operand_result); @@ -7364,6 +7896,55 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */ static zend_type zend_compile_single_typename(zend_ast *ast) { ZEND_ASSERT(!(ast->attr & ZEND_TYPE_NULLABLE)); + if (ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + ast = ast->child[0]; + } + /* Generic type parameter reference: erase to the parameter's bound (or `mixed` when unbounded). */ + { + zend_generic_parameter *param = zend_generic_lookup_name(ast); + if (param) { + for (zend_generic_scope_entry *e = CG(generic_scope); e; e = e->outer) { + if (e->self_compiling == param) { + zend_string *self = zend_string_copy(param->name); + zend_error_noreturn(E_COMPILE_ERROR, + "Type parameter %s cannot reference itself in its own bound or default outside of a generic type argument", + ZSTR_VAL(self)); + } + } + + if (ZEND_TYPE_IS_SET(param->bound)) { + zend_type result = param->bound; + if (ZEND_TYPE_HAS_NAME(result)) { + zend_string_addref(ZEND_TYPE_NAME(result)); + } else if (ZEND_TYPE_HAS_LIST(result)) { + zend_type_list *orig_list = ZEND_TYPE_LIST(result); + size_t list_size = ZEND_TYPE_LIST_SIZE(orig_list->num_types); + zend_type_list *copy = emalloc(list_size); + memcpy(copy, orig_list, list_size); + for (uint32_t i = 0; i < copy->num_types; i++) { + if (ZEND_TYPE_HAS_NAME(copy->types[i])) { + zend_string_addref(ZEND_TYPE_NAME(copy->types[i])); + } + } + ZEND_TYPE_SET_PTR(result, copy); + } + return result; + } + + return (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY); + } + + if (ast->kind == ZEND_AST_ZVAL + && (ast->attr & ZEND_NAME_NOT_FQ) == ZEND_NAME_NOT_FQ) { + const zval *zv = zend_ast_get_zval(ast); + if (Z_TYPE_P(zv) == IS_STRING + && zend_generic_lookup_forward(Z_STR_P(zv)) >= 0) { + zend_error_noreturn(E_COMPILE_ERROR, + "Type parameter %s referenced before declaration", + Z_STRVAL_P(zv)); + } + } + } if (ast->kind == ZEND_AST_TYPE) { if (ast->attr == IS_STATIC && !CG(active_class_entry) && zend_is_scope_known()) { zend_error_noreturn(E_COMPILE_ERROR, @@ -7830,6 +8411,7 @@ static void zend_compile_attributes( ? ZEND_ATTRIBUTE_STRICT_TYPES : 0; attr = zend_add_attribute( attributes, name, args ? args->children : 0, flags, offset, el->lineno); + attr->generic_arity = (uint8_t) el->attr; zend_string_release(name); /* Populate arguments */ @@ -8022,6 +8604,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 arg_infos->type = zend_compile_typename(return_type_ast); ZEND_TYPE_FULL_MASK(arg_infos->type) |= _ZEND_ARG_INFO_FLAGS( (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0, /* is_variadic */ 0, /* is_tentative */ 0); + if (zend_type_ast_has_generic_content(return_type_ast)) { + zend_type pre = zend_compile_pre_erasure_typename(return_type_ast); + zend_generic_type_table_set_return( + zend_generic_get_or_create_op_array_table(op_array), pre); + } } else { arg_infos->type = (zend_type) ZEND_TYPE_INIT_CODE(fallback_return_type, 0, 0); } @@ -8140,6 +8727,14 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; arg_info->type = zend_compile_typename_ex(type_ast, force_nullable, &forced_allow_nullable); + if (zend_type_ast_has_generic_content(type_ast)) { + zend_type pre = zend_compile_pre_erasure_typename(type_ast); + if (force_nullable || forced_allow_nullable) { + ZEND_TYPE_FULL_MASK(pre) |= _ZEND_TYPE_NULLABLE_BIT; + } + zend_generic_type_table_set_parameter( + zend_generic_get_or_create_op_array_table(op_array), i, pre); + } if (forced_allow_nullable) { zend_string *func_name = get_function_or_method_name((zend_function *) op_array); zend_error(E_DEPRECATED, @@ -8238,6 +8833,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 zend_type type = ZEND_TYPE_INIT_NONE(0); if (type_ast) { type = zend_compile_typename(type_ast); + if (zend_type_ast_has_generic_content(type_ast)) { + zend_type pre = zend_compile_pre_erasure_typename(type_ast); + zend_generic_type_table_set_property( + zend_generic_get_or_create_class_table(scope), name, pre); + } } /* Don't give the property an explicit default value. For typed properties this means @@ -8738,9 +9338,11 @@ static zend_op_array *zend_compile_func_decl_ex( zend_ast *uses_ast = decl->child[1]; zend_ast *stmt_ast = decl->child[2]; zend_ast *return_type_ast = decl->child[3]; + zend_ast *generic_params_ast = decl->child[5]; bool is_method = decl->kind == ZEND_AST_METHOD; zend_string *lcname = NULL; bool is_hook = decl->kind == ZEND_AST_PROPERTY_HOOK; + bool generic_scope_pushed = false; zend_class_entry *orig_class_entry = CG(active_class_entry); zend_op_array *orig_op_array = CG(active_op_array); @@ -8840,6 +9442,15 @@ static zend_op_array *zend_compile_func_decl_ex( zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var); } + /* Harvest function/method generic type parameters and push them into scope + * so that parameter, return, and body type annotations erase correctly. + * See GENERICS.md §6.9. */ + if (generic_params_ast) { + op_array->generic_parameters = zend_compile_generic_type_parameter_list(generic_params_ast); + zend_generic_scope_push(op_array->generic_parameters, ZEND_GENERIC_ORIGIN_FUNCTION_LIKE); + generic_scope_pushed = true; + } + zend_compile_params(params_ast, return_type_ast, is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { @@ -8923,6 +9534,10 @@ static zend_op_array *zend_compile_func_decl_ex( zend_string_release_ex(lcname, 0); } + if (generic_scope_pushed) { + zend_generic_scope_pop(); + } + CG(active_op_array) = orig_op_array; CG(active_class_entry) = orig_class_entry; @@ -9185,6 +9800,11 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f if (type_ast) { type = zend_compile_typename(type_ast); + if (zend_type_ast_has_generic_content(type_ast)) { + zend_type pre = zend_compile_pre_erasure_typename(type_ast); + zend_generic_type_table_set_property( + zend_generic_get_or_create_class_table(ce), name, pre); + } if (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_VOID|MAY_BE_NEVER|MAY_BE_CALLABLE)) { zend_string *str = zend_type_to_string(type); @@ -9439,15 +10059,24 @@ static void zend_compile_use_trait(const zend_ast *ast) /* {{{ */ zend_ast *trait_ast = traits->child[i]; if (ce->ce_flags & ZEND_ACC_INTERFACE) { - zend_string *name = zend_ast_get_str(trait_ast); + zend_string *name = trait_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE + ? zend_ast_get_str(trait_ast->child[0]) + : zend_ast_get_str(trait_ast); zend_error_noreturn(E_COMPILE_ERROR, "Cannot use traits inside of interfaces. " "%s is used in %s", ZSTR_VAL(name), ZSTR_VAL(ce->name)); } - ce->trait_names[ce->num_traits].name = + uint32_t trait_index = ce->num_traits; + ce->trait_names[trait_index].name = zend_resolve_const_class_name_reference(trait_ast, "trait name"); - ce->trait_names[ce->num_traits].lc_name = zend_string_tolower(ce->trait_names[ce->num_traits].name); + ce->trait_names[trait_index].lc_name = zend_string_tolower(ce->trait_names[trait_index].name); ce->num_traits++; + + if (trait_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + zend_type pre = zend_compile_pre_erasure_typename(trait_ast); + zend_generic_type_table_set_trait_use( + zend_generic_get_or_create_class_table(ce), trait_index, pre); + } } if (!adaptations) { @@ -9483,6 +10112,12 @@ static void zend_compile_implements(zend_ast *ast) /* {{{ */ interface_names[i].name = zend_resolve_const_class_name_reference(class_ast, "interface name"); interface_names[i].lc_name = zend_string_tolower(interface_names[i].name); + + if (class_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + zend_type pre = zend_compile_pre_erasure_typename(class_ast); + zend_generic_type_table_set_implements( + zend_generic_get_or_create_class_table(ce), i, pre); + } } ce->num_interfaces = list->children; @@ -9573,6 +10208,20 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top } zend_register_seen_symbol(lcname, ZEND_SYMBOL_CLASS); + + /* If a generic scope is active, this declaration shadows any generic + * type parameter with the same (case-insensitive) name. Register the + * unqualified lc_name in the innermost scope so subsequent lookups + * resolve to the class instead of the type parameter. */ + if (CG(generic_scope)) { + zend_string *unqualified_lc = zend_string_tolower(unqualified_name); + if (!CG(generic_scope)->shadowing_classes) { + ALLOC_HASHTABLE(CG(generic_scope)->shadowing_classes); + zend_hash_init(CG(generic_scope)->shadowing_classes, 4, NULL, NULL, 0); + } + zend_hash_add_empty_element(CG(generic_scope)->shadowing_classes, unqualified_lc); + zend_string_release(unqualified_lc); + } } else { /* Find an anon class name that is not in use yet. */ name = NULL; @@ -9613,9 +10262,24 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top ce->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; } + if (decl->child[5]) { + ZEND_ASSERT(!(decl->flags & ZEND_ACC_ANON_CLASS)); + ce->generic_parameters = zend_compile_generic_type_parameter_list(decl->child[5]); + zend_generic_scope_push(ce->generic_parameters, ZEND_GENERIC_ORIGIN_CLASS_LIKE); + } + if (extends_ast) { ce->parent_name = zend_resolve_const_class_name_reference(extends_ast, "class name"); + /* Capture the pre-erasure type when the extends clause specifies type + * arguments (e.g. `extends Box`). For an interface declaration + * this AST holds the list of parent interfaces; for class/trait it's + * the single parent class. */ + if (extends_ast->kind == ZEND_AST_GENERIC_NAMED_TYPE) { + zend_type pre = zend_compile_pre_erasure_typename(extends_ast); + zend_generic_type_table_set_extends( + zend_generic_get_or_create_class_table(ce), pre); + } } CG(active_class_entry) = ce; @@ -9645,6 +10309,10 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top zend_verify_abstract_class(ce); } + if (ce->generic_parameters) { + zend_generic_scope_pop(); + } + CG(active_class_entry) = original_ce; if (toplevel) { @@ -11220,7 +11888,7 @@ static void zend_compile_shell_exec(znode *result, const zend_ast *ast) /* {{{ * ZVAL_STRING(&fn_name, "shell_exec"); name_ast = zend_ast_create_zval(&fn_name); args_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, expr_ast); - call_ast = zend_ast_create(ZEND_AST_CALL, name_ast, args_ast); + call_ast = zend_ast_create(ZEND_AST_CALL, name_ast, args_ast, NULL); zend_compile_expr(result, call_ast); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0e31332c97f0..76a7b03dcce8 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -121,6 +121,75 @@ typedef struct _zend_file_context { HashTable seen_symbols; } zend_file_context; +/* Maximum number of generic type parameters or arguments at any single position. */ +#define ZEND_GENERIC_MAX_PARAMS 255 + +C23_ENUM(zend_generic_variance, uint8_t) { + ZEND_GENERIC_VARIANCE_INVARIANT = 0, + ZEND_GENERIC_VARIANCE_COVARIANT = 1, + ZEND_GENERIC_VARIANCE_CONTRAVARIANT = 2, +}; + +C23_ENUM(zend_generic_origin, uint8_t) { + ZEND_GENERIC_ORIGIN_CLASS_LIKE = 0, /* class, interface, trait, enum */ + ZEND_GENERIC_ORIGIN_FUNCTION_LIKE = 1, /* function, method, closure, arrow function */ +}; + +typedef struct _zend_generic_parameter { + zend_string *name; /* type-parameter name */ + zend_generic_variance variance; + zend_type bound; /* runtime erased; ZEND_TYPE_NONE if unbounded */ + zend_type bound_pre_erasure; /* pre-erasure; ZEND_TYPE_NONE if same as `bound` */ + zend_type default_type; /* runtime erased; ZEND_TYPE_NONE if no default */ + zend_type default_pre_erasure; /* pre-erasure; ZEND_TYPE_NONE if same as `default_type` */ +} zend_generic_parameter; + +typedef struct _zend_generic_parameter_list { + uint32_t count; + zend_generic_parameter parameters[1]; +} zend_generic_parameter_list; + +#define ZEND_GENERIC_PARAMETER_LIST_SIZE(count) \ + (sizeof(zend_generic_parameter_list) + ((count) - 1) * sizeof(zend_generic_parameter)) + +typedef struct _zend_generic_type_table { + zend_type *return_type; /* function/method return; NULL if equal to erased */ + zend_type *extends; /* class extends; NULL if equal */ + HashTable *parameters; /* parameter index -> zend_type * */ + HashTable *properties; /* zend_string * -> zend_type * */ + HashTable *class_constants; /* zend_string * -> zend_type * */ + HashTable *implements; /* implements index -> zend_type * */ + HashTable *trait_uses; /* trait-use index -> zend_type * */ +} zend_generic_type_table; + +/* Compile-time linked stack of in-scope generic type parameters. */ +typedef struct _zend_generic_scope_entry { + zend_generic_parameter_list *params; + uint32_t visible_count; /* number of parameters in `params` already declared */ + struct _zend_generic_parameter *self_compiling; /* param whose bound/default is being compiled, or NULL */ + HashTable *shadowing_classes; /* lc_names of class-likes declared inside this scope; lazy-allocated */ + zend_generic_origin origin; + struct _zend_generic_scope_entry *outer; +} zend_generic_scope_entry; + +ZEND_API zend_generic_parameter_list *zend_generic_parameter_list_alloc(uint32_t count, bool persistent); +ZEND_API void zend_generic_parameter_list_destroy(zend_generic_parameter_list *list); +ZEND_API zend_generic_type_table *zend_generic_type_table_alloc(void); +ZEND_API void zend_generic_type_table_destroy(zend_generic_type_table *table); +ZEND_API void zend_generic_type_table_set_return(zend_generic_type_table *t, zend_type type); +ZEND_API void zend_generic_type_table_set_extends(zend_generic_type_table *t, zend_type type); +ZEND_API void zend_generic_type_table_set_parameter(zend_generic_type_table *t, uint32_t idx, zend_type type); +ZEND_API void zend_generic_type_table_set_property(zend_generic_type_table *t, zend_string *name, zend_type type); +ZEND_API void zend_generic_type_table_set_class_constant(zend_generic_type_table *t, zend_string *name, zend_type type); +ZEND_API void zend_generic_type_table_set_implements(zend_generic_type_table *t, uint32_t idx, zend_type type); +ZEND_API void zend_generic_type_table_set_trait_use(zend_generic_type_table *t, uint32_t idx, zend_type type); + +ZEND_API void zend_check_generic_param_list_size(zend_ast *list_ast); +ZEND_API void zend_check_generic_arg_list_size(zend_ast *list_ast); + +ZEND_API void zend_check_generic_call_arity(const zend_function *fbc, uint32_t arity); +ZEND_API void zend_check_generic_new_arity(const zend_class_entry *ce, uint32_t arity); + typedef union _zend_parser_stack_elem { zend_ast *ast; zend_string *str; @@ -576,6 +645,10 @@ struct _zend_op_array { * referenced by index from opcodes. */ zend_op_array **dynamic_func_defs; + /* Generic-syntax metadata. NULL on non-generic functions/methods. */ + zend_generic_parameter_list *generic_parameters; + zend_generic_type_table *generic_types; + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 8257df32e831..b5875496cc43 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -164,6 +164,11 @@ struct _zend_compiler_globals { uint32_t internal_run_time_cache_size; zend_stack short_circuiting_opnums; + + uint32_t type_arg_depth; + int type_arg_residual_token; + + struct _zend_generic_scope_entry *generic_scope; #ifdef ZTS uint32_t copied_functions_count; #endif diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d70d08f5f1c4..bab27eda9ed0 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -34,6 +34,13 @@ ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; +static void zend_check_generic_link_arity( + const zend_class_entry *target_ce, uint32_t arity, + const char *clause, zend_string *child_name); +static void zend_validate_generic_inheritance_arities( + zend_class_entry *ce, zend_class_entry *parent_ce, + zend_class_entry **traits_and_interfaces); + /* Unresolved means that class declarations that are currently not available are needed to * determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated * as an ERROR. */ @@ -1838,6 +1845,15 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par zend_property_info *property_info; zend_string *key; + if (parent_ce + && !(ce->ce_flags & ZEND_ACC_INTERFACE) + && ce->generic_types + && ce->generic_types->extends + && ZEND_TYPE_HAS_NAMED_WITH_ARGS(*ce->generic_types->extends)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*ce->generic_types->extends); + zend_check_generic_link_arity(parent_ce, named->count, "extends", ce->name); + } + if (UNEXPECTED(ce->ce_flags & ZEND_ACC_INTERFACE)) { /* Interface can only inherit other interfaces */ if (UNEXPECTED(!(parent_ce->ce_flags & ZEND_ACC_INTERFACE))) { @@ -3482,6 +3498,78 @@ static zend_class_entry *zend_lazy_class_load(const zend_class_entry *pce) } while (0) #endif +static void zend_check_generic_link_arity( + const zend_class_entry *target_ce, + uint32_t arity, + const char *clause, + zend_string *child_name) +{ + uint32_t total = target_ce->generic_parameters ? target_ce->generic_parameters->count : 0; + uint32_t required = 0; + if (target_ce->generic_parameters) { + while (required < total + && !ZEND_TYPE_IS_SET(target_ce->generic_parameters->parameters[required].default_type)) { + required++; + } + } + + if (arity > total) { + zend_error_noreturn(E_COMPILE_ERROR, + "Too many generic type arguments to %s %s in %s, %u passed and %s %u expected", + clause, ZSTR_VAL(target_ce->name), ZSTR_VAL(child_name), arity, + required == total ? "exactly" : "at most", total); + } + + if (arity < required) { + zend_error_noreturn(E_COMPILE_ERROR, + "Too few generic type arguments to %s %s in %s, %u passed and %s %u expected", + clause, ZSTR_VAL(target_ce->name), ZSTR_VAL(child_name), arity, + required == total ? "exactly" : "at least", required); + } +} + +static void zend_validate_generic_inheritance_arities( + zend_class_entry *ce, + zend_class_entry *parent_ce, + zend_class_entry **traits_and_interfaces) +{ + (void) parent_ce; + if (!ce->generic_types) { + return; + } + + if (ce->generic_types->trait_uses && traits_and_interfaces) { + zval *zv; + zend_ulong idx; + ZEND_HASH_FOREACH_NUM_KEY_VAL(ce->generic_types->trait_uses, idx, zv) { + zend_type *boxed = (zend_type *) Z_PTR_P(zv); + if (!ZEND_TYPE_HAS_NAMED_WITH_ARGS(*boxed)) continue; + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*boxed); + if (idx >= ce->num_traits) continue; + zend_class_entry *trait_ce = traits_and_interfaces[idx]; + if (trait_ce) { + zend_check_generic_link_arity(trait_ce, named->count, "use", ce->name); + } + } ZEND_HASH_FOREACH_END(); + } + + if (ce->generic_types->implements && traits_and_interfaces) { + zval *zv; + zend_ulong idx; + ZEND_HASH_FOREACH_NUM_KEY_VAL(ce->generic_types->implements, idx, zv) { + zend_type *boxed = (zend_type *) Z_PTR_P(zv); + if (!ZEND_TYPE_HAS_NAMED_WITH_ARGS(*boxed)) continue; + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*boxed); + if (idx >= ce->num_interfaces) continue; + zend_class_entry *iface_ce = traits_and_interfaces[ce->num_traits + idx]; + if (iface_ce) { + const char *clause = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "extends" : "implements"; + zend_check_generic_link_arity(iface_ce, named->count, clause, ce->name); + } + } ZEND_HASH_FOREACH_END(); + } +} + ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, const zend_string *key) /* {{{ */ { /* Load parent/interface dependencies first, so we can still gracefully abort linking @@ -3564,6 +3652,8 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string } } + zend_validate_generic_inheritance_arities(ce, parent, traits_and_interfaces); + #ifndef ZEND_WIN32 if (ce->ce_flags & ZEND_ACC_ENUM) { /* We will add internal methods. */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b4dda00404ea..3b7baf946f84 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -50,6 +50,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %destructor { zend_ast_destroy($$); } %destructor { if ($$) zend_string_release_ex($$, 0); } +%precedence PREC_NO_TYPE_ARGS %precedence T_THROW %precedence PREC_ARROW_FUNCTION %precedence T_INCLUDE T_INCLUDE_ONCE T_REQUIRE T_REQUIRE_ONCE @@ -232,6 +233,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_DOLLAR_OPEN_CURLY_BRACES "'${'" %token T_CURLY_OPEN "'{$'" %token T_PAAMAYIM_NEKUDOTAYIM "'::'" +%token T_TURBOFISH "'::<'" %token T_NS_SEPARATOR "'\\'" %token T_ELLIPSIS "'...'" %token T_COALESCE "'??'" @@ -288,6 +290,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list clone_argument_list non_empty_clone_argument_list +%type optional_generic_type_parameter_list generic_type_parameter_list +%type generic_type_parameter_list_inner generic_type_parameter +%type optional_generic_type_parameter_bound optional_generic_type_parameter_default +%type optional_generic_type_argument_list generic_type_argument_list +%type generic_type_argument_list_inner +%type optional_call_type_argument_list call_type_argument_list call_type_argument_list_inner +%type bound_class_name bound_class_name_reference + +%type optional_generic_variance + %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers %type class_modifiers class_modifier anonymous_class_modifiers anonymous_class_modifiers_optional use_type backup_fn_flags @@ -364,10 +376,20 @@ name: ; attribute_decl: - class_name - { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); } - | class_name argument_list - { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, $2); } + class_name optional_call_type_argument_list + { + uint32_t _ga = $2 ? zend_ast_get_list($2)->children : 0; + if ($2) zend_ast_destroy($2); + $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); + $$->attr = (zend_ast_attr) _ga; + } + | class_name optional_call_type_argument_list argument_list + { + uint32_t _ga = $2 ? zend_ast_get_list($2)->children : 0; + if ($2) zend_ast_destroy($2); + $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, $3); + $$->attr = (zend_ast_attr) _ga; + } ; attribute_group: @@ -551,8 +573,8 @@ catch_list: ; catch_name_list: - class_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); } - | catch_name_list '|' class_name { $$ = zend_ast_list_add($1, $3); } + bound_class_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); } + | catch_name_list '|' bound_class_name { $$ = zend_ast_list_add($1, $3); } ; optional_variable: @@ -584,10 +606,10 @@ function_name: ; function_declaration_statement: - function returns_ref function_name backup_doc_comment '(' parameter_list ')' return_type + function returns_ref function_name optional_generic_type_parameter_list backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $13, $1, $4, - zend_ast_get_str($3), $6, NULL, $11, $8, NULL); CG(extra_fn_flags) = $9; } + { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $14, $1, $5, + zend_ast_get_str($3), $7, NULL, $12, $9, NULL, $4); CG(extra_fn_flags) = $10; } ; is_reference: @@ -602,11 +624,11 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING optional_generic_type_parameter_list extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, NULL, NULL, $5); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING optional_generic_type_parameter_list extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, NULL, NULL, $4); } ; class_modifiers: @@ -635,20 +657,20 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } - T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } + T_STRING optional_generic_type_parameter_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $5, zend_ast_get_str($3), NULL, NULL, $7, NULL, NULL, $4); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } - T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } + T_STRING optional_generic_type_parameter_list interface_extends_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, NULL, $4); } ; enum_declaration_statement: T_ENUM { $$ = CG(zend_lineno); } T_STRING enum_backing_type implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ENUM|ZEND_ACC_FINAL, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_ENUM|ZEND_ACC_FINAL, $2, $6, zend_ast_get_str($3), NULL, $5, $8, NULL, $4, NULL); } ; enum_backing_type: @@ -668,7 +690,7 @@ enum_case_expr: extends_from: %empty { $$ = NULL; } - | T_EXTENDS class_name { $$ = $2; } + | T_EXTENDS bound_class_name { $$ = $2; } ; interface_extends_list: @@ -830,6 +852,94 @@ optional_type_without_static: | type_expr_without_static { $$ = $1; } ; +optional_generic_type_parameter_list: + %empty { $$ = NULL; } + | generic_type_parameter_list { $$ = $1; } +; + +generic_type_parameter_list: + '<' { CG(type_arg_depth)++; } + generic_type_parameter_list_inner possible_comma '>' + { CG(type_arg_depth)--; zend_check_generic_param_list_size($3); $$ = $3; } +; + +generic_type_parameter_list_inner: + generic_type_parameter + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAMETER_LIST, $1); } + | generic_type_parameter_list_inner ',' generic_type_parameter + { $$ = zend_ast_list_add($1, $3); } +; + +generic_type_parameter: + optional_generic_variance T_STRING optional_generic_type_parameter_bound + optional_generic_type_parameter_default + { $$ = zend_ast_create_ex(ZEND_AST_GENERIC_TYPE_PARAMETER, $1, $2, $3, $4); } +; + +optional_generic_variance: + %empty { $$ = 0; } + | '+' { $$ = 1; } + | '-' { $$ = 2; } +; + +optional_generic_type_parameter_bound: + %empty { $$ = NULL; } + | ':' type_expr { $$ = $2; } +; + +optional_generic_type_parameter_default: + %empty { $$ = NULL; } + | '=' type_expr { $$ = $2; } +; + +optional_generic_type_argument_list: + %empty %prec PREC_NO_TYPE_ARGS { $$ = NULL; } + | generic_type_argument_list { $$ = $1; } +; + +generic_type_argument_list: + '<' { CG(type_arg_depth)++; } + generic_type_argument_list_inner possible_comma '>' + { CG(type_arg_depth)--; zend_check_generic_arg_list_size($3); $$ = $3; } +; + +generic_type_argument_list_inner: + type_expr + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_ARGUMENT_LIST, $1); } + | generic_type_argument_list_inner ',' type_expr + { $$ = zend_ast_list_add($1, $3); } +; + +optional_call_type_argument_list: + %empty { $$ = NULL; } + | call_type_argument_list { $$ = $1; } +; + +call_type_argument_list: + T_TURBOFISH { CG(type_arg_depth)++; } + call_type_argument_list_inner possible_comma '>' + { CG(type_arg_depth)--; zend_check_generic_arg_list_size($3); $$ = $3; } +; + +call_type_argument_list_inner: + type_expr + { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_CALL_TYPE_ARGUMENT_LIST, $1); } + | call_type_argument_list_inner ',' type_expr + { $$ = zend_ast_list_add($1, $3); } +; + +bound_class_name: + class_name optional_generic_type_argument_list + { $$ = $2 ? zend_ast_create(ZEND_AST_GENERIC_NAMED_TYPE, $1, $2) : $1; } +; + +bound_class_name_reference: + class_name optional_generic_type_argument_list + { $$ = $2 ? zend_ast_create(ZEND_AST_GENERIC_NAMED_TYPE, $1, $2) : $1; } + | new_variable { $$ = $1; } + | '(' expr ')' { $$ = $2; } +; + type_expr: type { $$ = $1; } | '?' type { $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; } @@ -839,7 +949,9 @@ type_expr: type: type_without_static { $$ = $1; } - | T_STATIC { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC); } + | T_STATIC optional_generic_type_argument_list + { zend_ast *bare = zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC); + $$ = $2 ? zend_ast_create(ZEND_AST_GENERIC_NAMED_TYPE, bare, $2) : bare; } ; union_type_element: @@ -870,9 +982,12 @@ type_expr_without_static: ; type_without_static: - T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } + T_ARRAY optional_generic_type_argument_list + { zend_ast *bare = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); + $$ = $2 ? zend_ast_create(ZEND_AST_GENERIC_NAMED_TYPE, bare, $2) : bare; } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name { $$ = $1; } + | name optional_generic_type_argument_list + { $$ = $2 ? zend_ast_create(ZEND_AST_GENERIC_NAMED_TYPE, $1, $2) : $1; } ; union_type_without_static_element: @@ -995,10 +1110,10 @@ attributed_class_statement: | class_const_modifiers T_CONST type_expr class_const_list ';' { $$ = zend_ast_create(ZEND_AST_CLASS_CONST_GROUP, $4, NULL, $3); $$->attr = $1; } - | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' + | method_modifiers function returns_ref identifier optional_generic_type_parameter_list backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags method_body backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, - zend_ast_get_str($4), $7, NULL, $11, $9, NULL); CG(extra_fn_flags) = $10; } + { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $13, $2, $6, + zend_ast_get_str($4), $8, NULL, $12, $10, NULL, $5); CG(extra_fn_flags) = $11; } | enum_case { $$ = $1; } ; @@ -1010,8 +1125,8 @@ class_statement: ; class_name_list: - class_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); } - | class_name_list ',' class_name { $$ = zend_ast_list_add($1, $3); } + bound_class_name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); } + | class_name_list ',' bound_class_name { $$ = zend_ast_list_add($1, $3); } ; trait_adaptations: @@ -1164,7 +1279,7 @@ property_hook: optional_parameter_list backup_fn_flags property_hook_body backup_fn_flags { $$ = zend_ast_create_decl( ZEND_AST_PROPERTY_HOOK, $1 | $2 | $9, $5, $4, zend_ast_get_str($3), - $6, NULL, $8, NULL, NULL); + $6, NULL, $8, NULL, NULL, NULL); CG(extra_fn_flags) = $7; } ; @@ -1230,14 +1345,14 @@ anonymous_class: extends_from implements_list backup_doc_comment '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS | $1, $3, $7, NULL, - $5, $6, $9, NULL, NULL); - $$ = zend_ast_create(ZEND_AST_NEW, decl, $4); + $5, $6, $9, NULL, NULL, NULL); + $$ = zend_ast_create(ZEND_AST_NEW, decl, $4, NULL); } ; new_dereferenceable: - T_NEW class_name_reference argument_list - { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } + T_NEW class_name_reference optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_NEW, $2, $4, $3); } | T_NEW anonymous_class { $$ = $2; } | T_NEW attributes anonymous_class @@ -1245,8 +1360,8 @@ new_dereferenceable: ; new_non_dereferenceable: - T_NEW class_name_reference - { $$ = zend_ast_create(ZEND_AST_NEW, $2, zend_ast_create_list(0, ZEND_AST_ARG_LIST)); } + T_NEW class_name_reference optional_call_type_argument_list + { $$ = zend_ast_create(ZEND_AST_NEW, $2, zend_ast_create_list(0, ZEND_AST_ARG_LIST), $3); } ; expr: @@ -1263,12 +1378,12 @@ expr: | T_CLONE clone_argument_list { zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); name->attr = ZEND_NAME_FQ; - $$ = zend_ast_create(ZEND_AST_CALL, name, $2); + $$ = zend_ast_create(ZEND_AST_CALL, name, $2, NULL); } | T_CLONE expr { zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE)); name->attr = ZEND_NAME_FQ; - $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2)); + $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2), NULL); } | variable T_PLUS_EQUAL expr { $$ = zend_ast_create_assign_op(ZEND_ADD, $1, $3); } @@ -1347,7 +1462,7 @@ expr: { $$ = zend_ast_create(ZEND_AST_GREATER_EQUAL, $1, $3); } | expr T_SPACESHIP expr { $$ = zend_ast_create_binary_op(ZEND_SPACESHIP, $1, $3); } - | expr T_INSTANCEOF class_name_reference + | expr T_INSTANCEOF bound_class_name_reference { $$ = zend_ast_create(ZEND_AST_INSTANCEOF, $1, $3); } | '(' expr ')' { $$ = $2; @@ -1373,7 +1488,7 @@ expr: | T_EXIT ctor_arguments { zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_EXIT)); name->attr = ZEND_NAME_FQ; - $$ = zend_ast_create(ZEND_AST_CALL, name, $2); + $$ = zend_ast_create(ZEND_AST_CALL, name, $2, NULL); } | '@' expr { $$ = zend_ast_create(ZEND_AST_SILENCE, $2); } | scalar { $$ = $1; } @@ -1394,16 +1509,16 @@ expr: inline_function: - function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type + function returns_ref backup_doc_comment optional_generic_type_parameter_list '(' parameter_list ')' lexical_vars return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, + { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $14, $1, $3, NULL, - $5, $7, $11, $8, NULL); CG(extra_fn_flags) = $9; } - | fn returns_ref backup_doc_comment '(' parameter_list ')' return_type + $6, $8, $12, $9, NULL, $4); CG(extra_fn_flags) = $10; } + | fn returns_ref backup_doc_comment optional_generic_type_parameter_list '(' parameter_list ')' return_type T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags - { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $3, - NULL, $5, NULL, $11, $7, NULL); - CG(extra_fn_flags) = $9; } + { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $13, $1, $3, + NULL, $6, NULL, $12, $8, NULL, $4); + CG(extra_fn_flags) = $10; } ; fn: @@ -1447,20 +1562,20 @@ lexical_var: ; function_call: - name argument_list - { $$ = zend_ast_create(ZEND_AST_CALL, $1, $2); } + name optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_CALL, $1, $3, $2); } | T_READONLY argument_list { zval zv; if (zend_lex_tstring(&zv, $1) == FAILURE) { YYABORT; } - $$ = zend_ast_create(ZEND_AST_CALL, zend_ast_create_zval(&zv), $2); + $$ = zend_ast_create(ZEND_AST_CALL, zend_ast_create_zval(&zv), $2, NULL); } - | class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list - { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); } - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name argument_list - { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $4); } - | callable_expr { $$ = CG(zend_lineno); } argument_list { - $$ = zend_ast_create(ZEND_AST_CALL, $1, $3); - $$->lineno = $2; + | class_name T_PAAMAYIM_NEKUDOTAYIM member_name optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $5, $4); } + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_STATIC_CALL, $1, $3, $5, $4); } + | callable_expr optional_call_type_argument_list { $$ = CG(zend_lineno); } argument_list { + $$ = zend_ast_create(ZEND_AST_CALL, $1, $4, $2); + $$->lineno = $3; } ; @@ -1571,10 +1686,10 @@ callable_variable: { $$ = zend_ast_create(ZEND_AST_VAR, $1); } | array_object_dereferenceable '[' optional_expr ']' { $$ = zend_ast_create(ZEND_AST_DIM, $1, $3); } - | array_object_dereferenceable T_OBJECT_OPERATOR property_name argument_list - { $$ = zend_ast_create(ZEND_AST_METHOD_CALL, $1, $3, $4); } - | array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name argument_list - { $$ = zend_ast_create(ZEND_AST_NULLSAFE_METHOD_CALL, $1, $3, $4); } + | array_object_dereferenceable T_OBJECT_OPERATOR property_name optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_METHOD_CALL, $1, $3, $5, $4); } + | array_object_dereferenceable T_NULLSAFE_OBJECT_OPERATOR property_name optional_call_type_argument_list argument_list + { $$ = zend_ast_create(ZEND_AST_NULLSAFE_METHOD_CALL, $1, $3, $5, $4); } | function_call { $$ = $1; } ; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 07f2d44cb5c6..cf6ccfe8bbe7 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1610,6 +1610,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_STR(T_STRING, 0); } +"::<" { + RETURN_TOKEN(T_TURBOFISH); +} + "::" { RETURN_TOKEN(T_PAAMAYIM_NEKUDOTAYIM); } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0c6b22473514..4de021b90336 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -91,6 +91,9 @@ void init_op_array(zend_op_array *op_array, zend_function_type type, int initial op_array->num_dynamic_func_defs = 0; op_array->dynamic_func_defs = NULL; + op_array->generic_parameters = NULL; + op_array->generic_types = NULL; + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); op_array->cache_size = zend_op_array_extension_handles * sizeof(void*); @@ -118,11 +121,166 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) { if (!ZEND_TYPE_USES_ARENA(type)) { pefree(ZEND_TYPE_LIST(type), persistent); } + } else if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(type)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(type); + if (named->name) { + zend_string_release(named->name); + } + for (uint32_t i = 0; i < named->count; i++) { + zend_type_release(named->args[i], persistent); + } + pefree(named, persistent); + } else if (ZEND_TYPE_HAS_TYPE_PARAMETER(type)) { + zend_type_parameter_ref *ref = ZEND_TYPE_TYPE_PARAMETER(type); + if (ref->name) { + zend_string_release(ref->name); + } + pefree(ref, persistent); } else if (ZEND_TYPE_HAS_NAME(type)) { zend_string_release(ZEND_TYPE_NAME(type)); } } +ZEND_API zend_generic_parameter_list *zend_generic_parameter_list_alloc(uint32_t count, bool persistent) { + ZEND_ASSERT(count > 0); + zend_generic_parameter_list *list = pemalloc(ZEND_GENERIC_PARAMETER_LIST_SIZE(count), persistent); + list->count = count; + for (uint32_t i = 0; i < count; i++) { + list->parameters[i].name = NULL; + list->parameters[i].variance = 0; + list->parameters[i].bound = (zend_type) ZEND_TYPE_INIT_NONE(0); + list->parameters[i].bound_pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + list->parameters[i].default_type = (zend_type) ZEND_TYPE_INIT_NONE(0); + list->parameters[i].default_pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + } + return list; +} + +ZEND_API void zend_generic_parameter_list_destroy(zend_generic_parameter_list *list) { + if (!list) { + return; + } + for (uint32_t i = 0; i < list->count; i++) { + zend_generic_parameter *param = &list->parameters[i]; + if (param->name) { + zend_string_release(param->name); + } + zend_type_release(param->bound, /* persistent */ false); + zend_type_release(param->bound_pre_erasure, /* persistent */ false); + zend_type_release(param->default_type, /* persistent */ false); + zend_type_release(param->default_pre_erasure, /* persistent */ false); + } + efree(list); +} + +static void zend_generic_type_table_value_dtor(zval *zv) { + zend_type *type = Z_PTR_P(zv); + zend_type_release(*type, /* persistent */ false); + efree(type); +} + +ZEND_API zend_generic_type_table *zend_generic_type_table_alloc(void) { + zend_generic_type_table *table = ecalloc(1, sizeof(zend_generic_type_table)); + return table; +} + +ZEND_API void zend_generic_type_table_destroy(zend_generic_type_table *table) { + if (!table) { + return; + } + if (table->return_type) { + zend_type_release(*table->return_type, /* persistent */ false); + efree(table->return_type); + } + if (table->extends) { + zend_type_release(*table->extends, /* persistent */ false); + efree(table->extends); + } + if (table->parameters) { + zend_hash_destroy(table->parameters); + FREE_HASHTABLE(table->parameters); + } + if (table->properties) { + zend_hash_destroy(table->properties); + FREE_HASHTABLE(table->properties); + } + if (table->class_constants) { + zend_hash_destroy(table->class_constants); + FREE_HASHTABLE(table->class_constants); + } + if (table->implements) { + zend_hash_destroy(table->implements); + FREE_HASHTABLE(table->implements); + } + if (table->trait_uses) { + zend_hash_destroy(table->trait_uses); + FREE_HASHTABLE(table->trait_uses); + } + efree(table); +} + +static HashTable *zend_generic_type_table_ensure_indexed(HashTable **slot) { + if (!*slot) { + HashTable *ht; + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, 4, NULL, zend_generic_type_table_value_dtor, /* persistent */ false); + *slot = ht; + } + return *slot; +} + +static HashTable *zend_generic_type_table_ensure_named(HashTable **slot) { + if (!*slot) { + HashTable *ht; + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, 4, NULL, zend_generic_type_table_value_dtor, /* persistent */ false); + *slot = ht; + } + return *slot; +} + +static zend_type *zend_type_box(zend_type type) { + zend_type *boxed = emalloc(sizeof(zend_type)); + *boxed = type; + return boxed; +} + +ZEND_API void zend_generic_type_table_set_return(zend_generic_type_table *t, zend_type type) { + if (t->return_type) { + zend_type_release(*t->return_type, /* persistent */ false); + efree(t->return_type); + } + t->return_type = zend_type_box(type); +} + +ZEND_API void zend_generic_type_table_set_extends(zend_generic_type_table *t, zend_type type) { + if (t->extends) { + zend_type_release(*t->extends, /* persistent */ false); + efree(t->extends); + } + t->extends = zend_type_box(type); +} + +ZEND_API void zend_generic_type_table_set_parameter(zend_generic_type_table *t, uint32_t idx, zend_type type) { + zend_hash_index_update_ptr(zend_generic_type_table_ensure_indexed(&t->parameters), idx, zend_type_box(type)); +} + +ZEND_API void zend_generic_type_table_set_property(zend_generic_type_table *t, zend_string *name, zend_type type) { + zend_hash_update_ptr(zend_generic_type_table_ensure_named(&t->properties), name, zend_type_box(type)); +} + +ZEND_API void zend_generic_type_table_set_class_constant(zend_generic_type_table *t, zend_string *name, zend_type type) { + zend_hash_update_ptr(zend_generic_type_table_ensure_named(&t->class_constants), name, zend_type_box(type)); +} + +ZEND_API void zend_generic_type_table_set_implements(zend_generic_type_table *t, uint32_t idx, zend_type type) { + zend_hash_index_update_ptr(zend_generic_type_table_ensure_indexed(&t->implements), idx, zend_type_box(type)); +} + +ZEND_API void zend_generic_type_table_set_trait_use(zend_generic_type_table *t, uint32_t idx, zend_type type) { + zend_hash_index_update_ptr(zend_generic_type_table_ensure_indexed(&t->trait_uses), idx, zend_type_box(type)); +} + ZEND_API void zend_free_internal_arg_info(zend_internal_function *function, bool persistent) { if (function->arg_info) { @@ -441,6 +599,12 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->backed_enum_table) { zend_hash_release(ce->backed_enum_table); } + if (ce->generic_parameters) { + zend_generic_parameter_list_destroy(ce->generic_parameters); + } + if (ce->generic_types) { + zend_generic_type_table_destroy(ce->generic_types); + } break; case ZEND_INTERNAL_CLASS: if (ce->doc_comment) { @@ -663,6 +827,12 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) } efree(op_array->dynamic_func_defs); } + if (op_array->generic_parameters) { + zend_generic_parameter_list_destroy(op_array->generic_parameters); + } + if (op_array->generic_types) { + zend_generic_type_table_destroy(op_array->generic_types); + } } static void zend_update_extended_stmts(zend_op_array *op_array) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index cfff3b942c4b..85a5857d20c2 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -127,14 +127,42 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 -#define _ZEND_TYPE_MASK ((1u << 25) - 1) +/* Generic type-parameter reference. */ +typedef struct _zend_type_parameter_ref { + zend_string *name; /* type-parameter source name (e.g. "T") */ + uint32_t index; /* position in declaring entity's parameter list */ + uint8_t origin; /* one of zend_generic_origin (see zend_compile.h) */ +} zend_type_parameter_ref; + +/* List of pre-erasure type arguments attached to a named pre-erasure zend_type. */ +typedef struct _zend_type_arguments { + uint32_t count; + zend_type arguments[1]; +} zend_type_arguments; + +/* Pre-erasure named type with type arguments. */ +typedef struct _zend_type_named_with_args { + zend_string *name; /* class name */ + uint32_t name_attr; /* ZEND_NAME_NOT_FQ / ZEND_NAME_FQ / ZEND_NAME_RELATIVE */ + uint32_t count; /* number of type arguments */ + zend_type args[1]; /* flexible array of pre-erasure type arguments */ +} zend_type_named_with_args; + +#define ZEND_TYPE_NAMED_WITH_ARGS_SIZE(count) \ + (sizeof(zend_type_named_with_args) + ((count) - 1) * sizeof(zend_type)) + +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 26 +#define _ZEND_TYPE_MASK (((1u << 26) - 1) | _ZEND_TYPE_NAMED_WITH_ARGS_BIT) +/* Pre-erasure named type with type arguments. Side-table only. */ +#define _ZEND_TYPE_NAMED_WITH_ARGS_BIT (1u << 31) +/* Generic type-parameter reference. */ +#define _ZEND_TYPE_TYPE_PARAMETER_BIT (1u << 25) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) // Used to signify that type.ptr is not a `zend_string*` but a `const char*`, #define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 23) #define _ZEND_TYPE_LIST_BIT (1u << 22) -#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) +#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT|_ZEND_TYPE_TYPE_PARAMETER_BIT|_ZEND_TYPE_NAMED_WITH_ARGS_BIT) /* For BC behaviour with iterable type */ #define _ZEND_TYPE_ITERABLE_BIT (1u << 21) /* Whether the type list is arena allocated */ @@ -165,6 +193,18 @@ typedef struct { #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) +#define ZEND_TYPE_HAS_TYPE_PARAMETER(t) \ + ((((t).type_mask) & _ZEND_TYPE_TYPE_PARAMETER_BIT) != 0) + +#define ZEND_TYPE_TYPE_PARAMETER(t) \ + ((zend_type_parameter_ref *) (t).ptr) + +#define ZEND_TYPE_HAS_NAMED_WITH_ARGS(t) \ + ((((t).type_mask) & _ZEND_TYPE_NAMED_WITH_ARGS_BIT) != 0) + +#define ZEND_TYPE_NAMED_WITH_ARGS(t) \ + ((zend_type_named_with_args *) (t).ptr) + #define ZEND_TYPE_IS_ITERABLE_FALLBACK(t) \ ((((t).type_mask) & _ZEND_TYPE_ITERABLE_BIT) != 0) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 05923bbc2b61..30bcffc9c408 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8928,6 +8928,40 @@ ZEND_VM_HOT_HANDLER(211, ZEND_TYPE_ASSERT, CONST, ANY, NUM) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +ZEND_VM_HANDLER(212, ZEND_VERIFY_GENERIC_ARITY, TMP|UNUSED, UNUSED) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + uint32_t arity = opline->op2.num; + + SAVE_OPLINE(); + + if (OP1_TYPE == IS_UNUSED) { + zend_check_generic_call_arity(call->func, arity); + } else { + zval *new_obj = EX_VAR(opline->op1.var); + zend_class_entry *ce = Z_OBJCE_P(new_obj); + zend_check_generic_new_arity(ce, arity); + } + + if (UNEXPECTED(EG(exception))) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_string_release_ex(call->func->common.function_name, 0); + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } + + EX(call) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + HANDLE_EXCEPTION(); + } + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_HANDLER(122, ZEND_DEFINED, CONST, ANY, CACHE_SLOT) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2b5b9e5fcd47..e977586fe3aa 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -21978,6 +21978,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_TM ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + uint32_t arity = opline->op2.num; + + SAVE_OPLINE(); + + if (IS_TMP_VAR == IS_UNUSED) { + zend_check_generic_call_arity(call->func, arity); + } else { + zval *new_obj = EX_VAR(opline->op1.var); + zend_class_entry *ce = Z_OBJCE_P(new_obj); + zend_check_generic_new_arity(ce, arity); + } + + if (UNEXPECTED(EG(exception))) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_string_release_ex(call->func->common.function_name, 0); + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } + + EX(call) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + HANDLE_EXCEPTION(); + } + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_COUNT_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -37289,6 +37323,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_YIELD_SPEC_UN ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + uint32_t arity = opline->op2.num; + + SAVE_OPLINE(); + + if (IS_UNUSED == IS_UNUSED) { + zend_check_generic_call_arity(call->func, arity); + } else { + zval *new_obj = EX_VAR(opline->op1.var); + zend_class_entry *ce = Z_OBJCE_P(new_obj); + zend_check_generic_new_arity(ce, arity); + } + + if (UNEXPECTED(EG(exception))) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_string_release_ex(call->func->common.function_name, 0); + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } + + EX(call) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + HANDLE_EXCEPTION(); + } + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -74434,6 +74502,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_TMP_UNU ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + uint32_t arity = opline->op2.num; + + SAVE_OPLINE(); + + if (IS_TMP_VAR == IS_UNUSED) { + zend_check_generic_call_arity(call->func, arity); + } else { + zval *new_obj = EX_VAR(opline->op1.var); + zend_class_entry *ce = Z_OBJCE_P(new_obj); + zend_check_generic_new_arity(ce, arity); + } + + if (UNEXPECTED(EG(exception))) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_string_release_ex(call->func->common.function_name, 0); + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } + + EX(call) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + HANDLE_EXCEPTION(); + } + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_COUNT_SPEC_TMP_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -89745,6 +89847,40 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_YIELD_SPEC_UNUSED_ ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + uint32_t arity = opline->op2.num; + + SAVE_OPLINE(); + + if (IS_UNUSED == IS_UNUSED) { + zend_check_generic_call_arity(call->func, arity); + } else { + zval *new_obj = EX_VAR(opline->op1.var); + zend_class_entry *ce = Z_OBJCE_P(new_obj); + zend_check_generic_new_arity(ce, arity); + } + + if (UNEXPECTED(EG(exception))) { + if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_string_release_ex(call->func->common.function_name, 0); + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } + + EX(call) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + HANDLE_EXCEPTION(); + } + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -109213,6 +109349,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -112945,6 +113086,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_YIELD_SPEC_TMP_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED): + VM_TRACE(ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED) + ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_COUNT_SPEC_TMP_UNUSED): VM_TRACE(ZEND_COUNT_SPEC_TMP_UNUSED) ZEND_COUNT_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -114050,6 +114196,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_YIELD_SPEC_UNUSED_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED) + ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED) ZEND_FETCH_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -118151,6 +118302,11 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_HANDLER, + ZEND_NULL_HANDLER, + ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_NULL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -121629,6 +121785,11 @@ void zend_vm_init(void) ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_TAILCALL_HANDLER, ZEND_RECV_NOTYPE_SPEC_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, @@ -122597,7 +122758,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3474, + 3479, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -122631,7 +122792,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3474, + 3479, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -122764,50 +122925,50 @@ void zend_vm_init(void) 2556, 2557, 2558, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, - 3474, + 2559 | SPEC_RULE_OP1, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, + 3479, }; #if 0 #elif (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) @@ -122984,7 +123145,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2567 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2572 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -122992,7 +123153,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2592 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2597 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123000,7 +123161,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2622 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -123011,17 +123172,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2642 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2647 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2667 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2672 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2697 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -123032,17 +123193,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2717 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2722 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2742 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2747 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2772 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -123053,16 +123214,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2797 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2872 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3092 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3097 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3098 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3103 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -123073,16 +123234,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2947 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3022 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3095 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3100 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3103 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3108 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -123093,12 +123254,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2792 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2797 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2867 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2872 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -123109,12 +123270,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2942 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2947 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3017 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3022 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -123122,12 +123283,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3108 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3113 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3183 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3188 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -123135,79 +123296,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3258 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3263 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3333 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3338 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3420 | SPEC_RULE_OP1; - } else if (op1_info == MAY_BE_DOUBLE) { spec = 3425 | SPEC_RULE_OP1; - } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { + } else if (op1_info == MAY_BE_DOUBLE) { spec = 3430 | SPEC_RULE_OP1; + } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { + spec = 3435 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3408 | SPEC_RULE_RETVAL; + spec = 3413 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3410 | SPEC_RULE_RETVAL; + spec = 3415 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3412 | SPEC_RULE_RETVAL; + spec = 3417 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3414 | SPEC_RULE_RETVAL; + spec = 3419 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3416; + spec = 3421; } else if (op1_info == MAY_BE_LONG) { - spec = 3417; + spec = 3422; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3418; + spec = 3423; } else if (op1_info == MAY_BE_LONG) { - spec = 3419; + spec = 3424; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2566; + spec = 2571; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2559; + spec = 2564; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2560; + spec = 2565; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3470; + spec = 3475; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3465 | SPEC_RULE_OP1; + spec = 3470 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3472 | SPEC_RULE_RETVAL; + spec = 3477 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -123215,22 +123376,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3435 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3440 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3471; + spec = 3476; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3460 | SPEC_RULE_OP1; + spec = 3465 | SPEC_RULE_OP1; } break; case ZEND_COUNT: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 2561 | SPEC_RULE_OP1; + spec = 2566 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 6f1595195450..141253b18844 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1087,507 +1087,509 @@ _(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ _(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \ - _(2559, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2560, ZEND_RECV_NOTYPE_SPEC) \ - _(2562, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ - _(2565, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2566, ZEND_JMP_FORWARD_SPEC) \ - _(2572, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2573, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2574, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2576, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2560, ZEND_VERIFY_GENERIC_ARITY_SPEC_TMP_UNUSED) \ + _(2562, ZEND_VERIFY_GENERIC_ARITY_SPEC_UNUSED_UNUSED) \ + _(2564, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2565, ZEND_RECV_NOTYPE_SPEC) \ + _(2567, ZEND_COUNT_ARRAY_SPEC_TMP_UNUSED) \ + _(2570, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2571, ZEND_JMP_FORWARD_SPEC) \ _(2577, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2578, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2579, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2581, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2587, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2588, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2589, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2597, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2598, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2599, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2601, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2582, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2583, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2584, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2586, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2592, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2593, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2594, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2602, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2603, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2604, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2606, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2612, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2613, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2614, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2622, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2623, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2624, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2626, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2607, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2608, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2609, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2611, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2617, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2618, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2619, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2627, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2628, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2629, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2631, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2637, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2638, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2639, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2644, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2646, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2647, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2651, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2632, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2633, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2634, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2636, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2642, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2643, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2644, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2648, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2649, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2651, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ _(2652, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2653, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2654, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2656, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2664, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2669, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2672, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2673, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2674, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2676, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2657, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2658, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2659, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2661, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2667, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2669, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2673, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2676, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ _(2677, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2678, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2679, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2681, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2687, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2688, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2689, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2694, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2696, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2697, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2698, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2699, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2701, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2682, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2683, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2684, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2686, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2692, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2693, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2694, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2698, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2699, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2701, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ _(2702, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2703, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2704, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2706, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2712, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2713, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2714, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2722, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2723, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2724, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2726, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2707, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2708, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2709, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2711, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2717, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2718, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2719, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2727, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2728, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2729, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2731, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2737, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2738, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2739, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2747, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2748, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2749, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2751, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2732, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2733, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2734, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2736, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2742, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2743, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2744, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2752, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2753, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2754, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2756, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2762, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2763, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2764, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2772, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2773, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2774, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2776, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2757, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2758, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2759, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2761, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2767, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2768, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2769, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2777, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2778, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2779, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2781, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2787, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2788, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2789, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2807, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2808, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2809, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2810, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2821, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2822, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2823, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2866, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2882, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2883, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2884, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2885, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2886, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2896, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2897, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2898, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2941, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2957, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2958, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2959, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2960, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2961, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2971, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2972, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2973, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3016, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3032, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3033, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3034, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3035, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3036, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3046, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3047, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3048, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3091, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3092, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3093, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3094, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3095, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3096, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3097, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3098, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3102, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3103, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3107, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3111, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3112, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3113, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3114, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3115, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3120, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3122, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3123, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3124, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3125, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3126, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3127, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3137, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3138, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3139, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3182, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3186, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3187, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3188, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3189, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3190, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3197, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3198, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3199, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3257, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3261, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3262, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3263, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3264, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3265, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3272, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3273, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3274, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3332, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3336, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3348, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3349, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3407, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3408, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3409, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3410, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3411, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3412, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3413, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3414, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3415, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3416, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3417, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3418, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3419, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3420, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3421, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3422, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3424, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3425, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3426, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3427, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3429, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3430, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3431, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3432, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3434, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3436, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3437, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3439, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3440, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3442, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3444, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(2782, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2783, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2784, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2786, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2792, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2793, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2794, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2812, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2813, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2814, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2815, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2816, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2817, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2818, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2819, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2820, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2824, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2825, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2860, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2861, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2862, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2863, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2869, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2870, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2871, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2887, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2888, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2889, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2890, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2891, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2892, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2893, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2894, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2895, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2899, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2900, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2935, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2936, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2937, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2938, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2944, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2945, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2946, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2962, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2963, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2964, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2965, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2966, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2967, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2968, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2969, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2970, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2974, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2975, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3010, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3011, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3012, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3013, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3019, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3020, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3021, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3037, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3038, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3039, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3040, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3041, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3042, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3043, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3044, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3045, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3049, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3050, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3085, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3086, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3087, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3088, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3094, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3095, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3096, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3097, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3098, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3099, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3100, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3101, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3102, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3103, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3107, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3108, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3112, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3116, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3117, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3118, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3119, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3120, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3121, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3125, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3126, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3127, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3128, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3129, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3130, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3131, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3132, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3133, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3134, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3135, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3136, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3140, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3141, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3176, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3177, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3178, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3179, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3185, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3186, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3187, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3191, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3192, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3193, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3194, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3195, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3196, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3200, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3201, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3202, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3203, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3204, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3251, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3252, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3253, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3254, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3260, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3261, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3262, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3266, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3267, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3268, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3269, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3270, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3271, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3275, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3276, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3277, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3278, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3279, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3326, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3327, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3328, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3335, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3336, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3337, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3343, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3344, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3345, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3350, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3351, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3352, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3353, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3354, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3401, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3402, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3403, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3404, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3410, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3411, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3412, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3413, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3414, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3415, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3416, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3417, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3418, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3419, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3420, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3421, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3422, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3423, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3424, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3425, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ + _(3426, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3427, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3429, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3430, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ + _(3431, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3432, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3434, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3435, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ + _(3436, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3437, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3439, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3441, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3442, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3444, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ _(3445, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(3446, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ _(3447, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ _(3449, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3457, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3462, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3464, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3467, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3469, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3470, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3471, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3472, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3473, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3473+1, ZEND_NULL) + _(3450, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3451, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3452, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3454, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3460, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ + _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3462, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3467, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3469, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3472, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3474, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3475, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3476, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3477, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3478, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3478+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 0ece3e6f0c66..5108ae544651 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -21,7 +21,7 @@ #include #include -static const char *zend_vm_opcodes_names[212] = { +static const char *zend_vm_opcodes_names[213] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,10 @@ static const char *zend_vm_opcodes_names[212] = { "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", "ZEND_TYPE_ASSERT", + "ZEND_VERIFY_GENERIC_ARITY", }; -static uint32_t zend_vm_opcodes_flags[212] = { +static uint32_t zend_vm_opcodes_flags[213] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -449,6 +450,7 @@ static uint32_t zend_vm_opcodes_flags[212] = { 0x01001103, 0x00000303, 0x01000003, + 0x00000101, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 92b46e6628f3..358861068b34 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -331,7 +331,8 @@ END_EXTERN_C() #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 #define ZEND_TYPE_ASSERT 211 +#define ZEND_VERIFY_GENERIC_ARITY 212 -#define ZEND_VM_LAST_OPCODE 211 +#define ZEND_VM_LAST_OPCODE 212 #endif diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index af59b9b2c34a..cd260226aa26 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -473,6 +473,28 @@ static void zend_file_cache_serialize_attribute(zval *zv, static void zend_file_cache_serialize_type( zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf) { + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*type)) { + zend_type_parameter_ref *ref = ZEND_TYPE_TYPE_PARAMETER(*type); + SERIALIZE_PTR(ref); + ZEND_TYPE_SET_PTR(*type, ref); + UNSERIALIZE_PTR(ref); + SERIALIZE_STR(ref->name); + return; + } + + if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(*type)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*type); + SERIALIZE_PTR(named); + ZEND_TYPE_SET_PTR(*type, named); + UNSERIALIZE_PTR(named); + SERIALIZE_STR(named->name); + for (uint32_t i = 0; i < named->count; i++) { + zend_file_cache_serialize_type(&named->args[i], script, info, buf); + } + + return; + } + if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); SERIALIZE_PTR(list); @@ -490,6 +512,79 @@ static void zend_file_cache_serialize_type( } } +static void zend_file_cache_serialize_generic_type_entry( + zval *zv, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf) +{ + zend_type *boxed = Z_PTR_P(zv); + SERIALIZE_PTR(Z_PTR_P(zv)); + UNSERIALIZE_PTR(boxed); + zend_file_cache_serialize_type(boxed, script, info, buf); +} + +static void zend_file_cache_serialize_generic_type_table_ht( + HashTable **ht_ptr, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf) +{ + HashTable *ht; + SERIALIZE_PTR(*ht_ptr); + ht = *ht_ptr; + UNSERIALIZE_PTR(ht); + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_generic_type_entry); +} + +static void zend_file_cache_serialize_generic_parameter_list( + zend_generic_parameter_list **list_ptr, zend_persistent_script *script, + zend_file_cache_metainfo *info, void *buf) +{ + zend_generic_parameter_list *list; + SERIALIZE_PTR(*list_ptr); + list = *list_ptr; + UNSERIALIZE_PTR(list); + for (uint32_t i = 0; i < list->count; i++) { + SERIALIZE_STR(list->parameters[i].name); + zend_file_cache_serialize_type(&list->parameters[i].bound, script, info, buf); + zend_file_cache_serialize_type(&list->parameters[i].bound_pre_erasure, script, info, buf); + zend_file_cache_serialize_type(&list->parameters[i].default_type, script, info, buf); + zend_file_cache_serialize_type(&list->parameters[i].default_pre_erasure, script, info, buf); + } +} + +static void zend_file_cache_serialize_generic_type_table( + zend_generic_type_table **table_ptr, zend_persistent_script *script, + zend_file_cache_metainfo *info, void *buf) +{ + zend_generic_type_table *table; + SERIALIZE_PTR(*table_ptr); + table = *table_ptr; + UNSERIALIZE_PTR(table); + if (table->return_type) { + SERIALIZE_PTR(table->return_type); + zend_type *t = table->return_type; + UNSERIALIZE_PTR(t); + zend_file_cache_serialize_type(t, script, info, buf); + } + if (table->extends) { + SERIALIZE_PTR(table->extends); + zend_type *t = table->extends; + UNSERIALIZE_PTR(t); + zend_file_cache_serialize_type(t, script, info, buf); + } + if (table->parameters) { + zend_file_cache_serialize_generic_type_table_ht(&table->parameters, script, info, buf); + } + if (table->properties) { + zend_file_cache_serialize_generic_type_table_ht(&table->properties, script, info, buf); + } + if (table->class_constants) { + zend_file_cache_serialize_generic_type_table_ht(&table->class_constants, script, info, buf); + } + if (table->implements) { + zend_file_cache_serialize_generic_type_table_ht(&table->implements, script, info, buf); + } + if (table->trait_uses) { + zend_file_cache_serialize_generic_type_table_ht(&table->trait_uses, script, info, buf); + } +} + static void zend_file_cache_serialize_op_array(zend_op_array *op_array, zend_persistent_script *script, zend_file_cache_metainfo *info, @@ -700,6 +795,14 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); SERIALIZE_PTR(op_array->prop_info); + + if (op_array->generic_parameters) { + zend_file_cache_serialize_generic_parameter_list(&op_array->generic_parameters, script, info, buf); + } + + if (op_array->generic_types) { + zend_file_cache_serialize_generic_type_table(&op_array->generic_types, script, info, buf); + } } } @@ -831,6 +934,14 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_STR(ce->info.user.filename); SERIALIZE_STR(ce->doc_comment); SERIALIZE_ATTRIBUTES(ce->attributes); + if (ce->generic_parameters) { + zend_file_cache_serialize_generic_parameter_list(&ce->generic_parameters, script, info, buf); + } + + if (ce->generic_types) { + zend_file_cache_serialize_generic_type_table(&ce->generic_types, script, info, buf); + } + zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); if (ce->properties_info_table) { @@ -1389,6 +1500,26 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri static void zend_file_cache_unserialize_type( zend_type *type, zend_class_entry *scope, zend_persistent_script *script, void *buf) { + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*type)) { + zend_type_parameter_ref *ref = ZEND_TYPE_TYPE_PARAMETER(*type); + UNSERIALIZE_PTR(ref); + ZEND_TYPE_SET_PTR(*type, ref); + UNSERIALIZE_STR(ref->name); + return; + } + + if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(*type)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*type); + UNSERIALIZE_PTR(named); + ZEND_TYPE_SET_PTR(*type, named); + UNSERIALIZE_STR(named->name); + for (uint32_t i = 0; i < named->count; i++) { + zend_file_cache_unserialize_type(&named->args[i], scope, script, buf); + } + + return; + } + if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); UNSERIALIZE_PTR(list); @@ -1410,6 +1541,79 @@ static void zend_file_cache_unserialize_type( } } +static zend_class_entry *zend_file_cache_generic_unserialize_scope = NULL; + +static void zend_file_cache_unserialize_generic_type_entry( + zval *zv, zend_persistent_script *script, void *buf) +{ + UNSERIALIZE_PTR(Z_PTR_P(zv)); + zend_file_cache_unserialize_type( + (zend_type *) Z_PTR_P(zv), zend_file_cache_generic_unserialize_scope, script, buf); +} + +static void zend_file_cache_unserialize_generic_type_table_ht( + HashTable **ht_ptr, zend_class_entry *scope, zend_persistent_script *script, void *buf) +{ + UNSERIALIZE_PTR(*ht_ptr); + HashTable *ht = *ht_ptr; + zend_class_entry *prev = zend_file_cache_generic_unserialize_scope; + zend_file_cache_generic_unserialize_scope = scope; + zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_generic_type_entry, NULL); + zend_file_cache_generic_unserialize_scope = prev; +} + +static void zend_file_cache_unserialize_generic_parameter_list( + zend_generic_parameter_list **list_ptr, zend_class_entry *scope, + zend_persistent_script *script, void *buf) +{ + UNSERIALIZE_PTR(*list_ptr); + zend_generic_parameter_list *list = *list_ptr; + for (uint32_t i = 0; i < list->count; i++) { + UNSERIALIZE_STR(list->parameters[i].name); + zend_file_cache_unserialize_type(&list->parameters[i].bound, scope, script, buf); + zend_file_cache_unserialize_type(&list->parameters[i].bound_pre_erasure, scope, script, buf); + zend_file_cache_unserialize_type(&list->parameters[i].default_type, scope, script, buf); + zend_file_cache_unserialize_type(&list->parameters[i].default_pre_erasure, scope, script, buf); + } +} + +static void zend_file_cache_unserialize_generic_type_table( + zend_generic_type_table **table_ptr, zend_class_entry *scope, + zend_persistent_script *script, void *buf) +{ + UNSERIALIZE_PTR(*table_ptr); + zend_generic_type_table *table = *table_ptr; + if (table->return_type) { + UNSERIALIZE_PTR(table->return_type); + zend_file_cache_unserialize_type(table->return_type, scope, script, buf); + } + + if (table->extends) { + UNSERIALIZE_PTR(table->extends); + zend_file_cache_unserialize_type(table->extends, scope, script, buf); + } + + if (table->parameters) { + zend_file_cache_unserialize_generic_type_table_ht(&table->parameters, scope, script, buf); + } + + if (table->properties) { + zend_file_cache_unserialize_generic_type_table_ht(&table->properties, scope, script, buf); + } + + if (table->class_constants) { + zend_file_cache_unserialize_generic_type_table_ht(&table->class_constants, scope, script, buf); + } + + if (table->implements) { + zend_file_cache_unserialize_generic_type_table_ht(&table->implements, scope, script, buf); + } + + if (table->trait_uses) { + zend_file_cache_unserialize_generic_type_table_ht(&table->trait_uses, scope, script, buf); + } +} + static void zend_file_cache_unserialize_op_array(zend_op_array *op_array, zend_persistent_script *script, void *buf) @@ -1596,6 +1800,16 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); UNSERIALIZE_PTR(op_array->prop_info); + + if (op_array->generic_parameters) { + zend_file_cache_unserialize_generic_parameter_list(&op_array->generic_parameters, + (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf); + } + + if (op_array->generic_types) { + zend_file_cache_unserialize_generic_type_table(&op_array->generic_types, + (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf); + } } } @@ -1720,6 +1934,14 @@ static void zend_file_cache_unserialize_class(zval *zv, UNSERIALIZE_STR(ce->info.user.filename); UNSERIALIZE_STR(ce->doc_comment); UNSERIALIZE_ATTRIBUTES(ce->attributes); + if (ce->generic_parameters) { + zend_file_cache_unserialize_generic_parameter_list(&ce->generic_parameters, ce, script, buf); + } + + if (ce->generic_types) { + zend_file_cache_unserialize_generic_type_table(&ce->generic_types, ce, script, buf); + } + zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 973e441edc80..985d31faf279 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -359,6 +359,26 @@ uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name) } static void zend_persist_type(zend_type *type) { + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*type)) { + zend_type_parameter_ref *ref = ZEND_TYPE_TYPE_PARAMETER(*type); + ref = zend_shared_memdup_put_free(ref, sizeof(*ref)); + zend_accel_store_interned_string(ref->name); + ZEND_TYPE_SET_PTR(*type, ref); + return; + } + + if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(*type)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*type); + named = zend_shared_memdup_put_free(named, ZEND_TYPE_NAMED_WITH_ARGS_SIZE(named->count)); + zend_accel_store_interned_string(named->name); + for (uint32_t i = 0; i < named->count; i++) { + zend_persist_type(&named->args[i]); + } + + ZEND_TYPE_SET_PTR(*type, named); + return; + } + if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); if (ZEND_TYPE_USES_ARENA(*type) || zend_accel_in_shm(list)) { @@ -376,6 +396,12 @@ static void zend_persist_type(zend_type *type) { zend_persist_type(single_type); continue; } + + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*single_type)) { + zend_persist_type(single_type); + continue; + } + if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_string *type_name = ZEND_TYPE_NAME(*single_type); zend_accel_store_interned_string(type_name); @@ -387,6 +413,98 @@ static void zend_persist_type(zend_type *type) { } ZEND_TYPE_FOREACH_END(); } +static zend_generic_parameter_list *zend_persist_generic_parameter_list(zend_generic_parameter_list *list) +{ + if (!list) { + return NULL; + } + + zend_generic_parameter_list *persisted = zend_shared_memdup_put_free( + list, + ZEND_GENERIC_PARAMETER_LIST_SIZE(list->count) + ); + + for (uint32_t i = 0; i < persisted->count; i++) { + zend_generic_parameter *param = &persisted->parameters[i]; + zend_accel_store_interned_string(param->name); + zend_persist_type(¶m->bound); + zend_persist_type(¶m->bound_pre_erasure); + zend_persist_type(¶m->default_type); + zend_persist_type(¶m->default_pre_erasure); + } + + return persisted; +} + +static HashTable *zend_persist_generic_type_table_ht(HashTable *ht) +{ + zend_hash_persist(ht); + if (HT_IS_PACKED(ht)) { + zval *v; + ZEND_HASH_PACKED_FOREACH_VAL(ht, v) { + zend_type *boxed = Z_PTR_P(v); + zend_type *copy = zend_shared_memdup_put_free(boxed, sizeof(zend_type)); + zend_persist_type(copy); + Z_PTR_P(v) = copy; + } ZEND_HASH_FOREACH_END(); + } else { + Bucket *p; + ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) { + if (p->key) { + zend_accel_store_interned_string(p->key); + } + zend_type *boxed = Z_PTR(p->val); + zend_type *copy = zend_shared_memdup_put_free(boxed, sizeof(zend_type)); + zend_persist_type(copy); + Z_PTR(p->val) = copy; + } ZEND_HASH_FOREACH_END(); + } + HashTable *ptr = zend_shared_memdup_put_free(ht, sizeof(HashTable)); + GC_SET_REFCOUNT(ptr, 2); + GC_TYPE_INFO(ptr) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT); + return ptr; +} + +static zend_generic_type_table *zend_persist_generic_type_table(zend_generic_type_table *table) +{ + if (!table) { + return NULL; + } + + zend_generic_type_table *persisted = zend_shared_memdup_put_free(table, sizeof(*table)); + if (persisted->return_type) { + persisted->return_type = zend_shared_memdup_put_free(persisted->return_type, sizeof(zend_type)); + zend_persist_type(persisted->return_type); + } + + if (persisted->extends) { + persisted->extends = zend_shared_memdup_put_free(persisted->extends, sizeof(zend_type)); + zend_persist_type(persisted->extends); + } + + if (persisted->parameters) { + persisted->parameters = zend_persist_generic_type_table_ht(persisted->parameters); + } + + if (persisted->properties) { + persisted->properties = zend_persist_generic_type_table_ht(persisted->properties); + } + + if (persisted->class_constants) { + persisted->class_constants = zend_persist_generic_type_table_ht(persisted->class_constants); + } + + if (persisted->implements) { + persisted->implements = zend_persist_generic_type_table_ht(persisted->implements); + } + + if (persisted->trait_uses) { + persisted->trait_uses = zend_persist_generic_type_table_ht(persisted->trait_uses); + } + + return persisted; +} + static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script) { zend_op *persist_ptr; @@ -700,6 +818,14 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } } + if (op_array->generic_parameters) { + op_array->generic_parameters = zend_persist_generic_parameter_list(op_array->generic_parameters); + } + + if (op_array->generic_types) { + op_array->generic_types = zend_persist_generic_type_table(op_array->generic_types); + } + ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); } @@ -1067,6 +1193,14 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) ce->attributes = zend_persist_attributes(ce->attributes); } + if (ce->generic_parameters) { + ce->generic_parameters = zend_persist_generic_parameter_list(ce->generic_parameters); + } + + if (ce->generic_types) { + ce->generic_types = zend_persist_generic_type_table(ce->generic_types); + } + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { uint32_t i = 0; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 9ff37079193b..68d6fbfcd345 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -197,6 +197,24 @@ static void zend_persist_attributes_calc(HashTable *attributes) static void zend_persist_type_calc(zend_type *type) { + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*type)) { + zend_type_parameter_ref *ref = ZEND_TYPE_TYPE_PARAMETER(*type); + ADD_SIZE(sizeof(*ref)); + ADD_INTERNED_STRING(ref->name); + return; + } + + if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(*type)) { + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*type); + ADD_SIZE(ZEND_TYPE_NAMED_WITH_ARGS_SIZE(named->count)); + ADD_INTERNED_STRING(named->name); + for (uint32_t i = 0; i < named->count; i++) { + zend_persist_type_calc(&named->args[i]); + } + + return; + } + if (ZEND_TYPE_HAS_LIST(*type)) { ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types)); } @@ -207,6 +225,12 @@ static void zend_persist_type_calc(zend_type *type) zend_persist_type_calc(single_type); continue; } + + if (ZEND_TYPE_HAS_TYPE_PARAMETER(*single_type)) { + zend_persist_type_calc(single_type); + continue; + } + if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_string *type_name = ZEND_TYPE_NAME(*single_type); ADD_INTERNED_STRING(type_name); @@ -215,6 +239,82 @@ static void zend_persist_type_calc(zend_type *type) } ZEND_TYPE_FOREACH_END(); } +static void zend_persist_generic_parameter_list_calc(zend_generic_parameter_list *list) +{ + if (!list) { + return; + } + + ADD_SIZE(ZEND_GENERIC_PARAMETER_LIST_SIZE(list->count)); + for (uint32_t i = 0; i < list->count; i++) { + ADD_INTERNED_STRING(list->parameters[i].name); + zend_persist_type_calc(&list->parameters[i].bound); + zend_persist_type_calc(&list->parameters[i].bound_pre_erasure); + zend_persist_type_calc(&list->parameters[i].default_type); + zend_persist_type_calc(&list->parameters[i].default_pre_erasure); + } +} + +static void zend_persist_generic_type_table_ht_calc(HashTable *ht) +{ + zend_hash_persist_calc(ht); + if (HT_IS_PACKED(ht)) { + zval *v; + ZEND_HASH_PACKED_FOREACH_VAL(ht, v) { + ADD_SIZE(sizeof(zend_type)); + zend_persist_type_calc((zend_type *) Z_PTR_P(v)); + } ZEND_HASH_FOREACH_END(); + } else { + Bucket *p; + ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) { + if (p->key) { + ADD_INTERNED_STRING(p->key); + } + ADD_SIZE(sizeof(zend_type)); + zend_persist_type_calc((zend_type *) Z_PTR(p->val)); + } ZEND_HASH_FOREACH_END(); + } + ADD_SIZE(sizeof(HashTable)); +} + +static void zend_persist_generic_type_table_calc(zend_generic_type_table *table) +{ + if (!table) { + return; + } + + ADD_SIZE(sizeof(*table)); + if (table->return_type) { + ADD_SIZE(sizeof(zend_type)); + zend_persist_type_calc(table->return_type); + } + + if (table->extends) { + ADD_SIZE(sizeof(zend_type)); + zend_persist_type_calc(table->extends); + } + + if (table->parameters) { + zend_persist_generic_type_table_ht_calc(table->parameters); + } + + if (table->properties) { + zend_persist_generic_type_table_ht_calc(table->properties); + } + + if (table->class_constants) { + zend_persist_generic_type_table_ht_calc(table->class_constants); + } + + if (table->implements) { + zend_persist_generic_type_table_ht_calc(table->implements); + } + + if (table->trait_uses) { + zend_persist_generic_type_table_ht_calc(table->trait_uses); + } +} + static void zend_persist_op_array_calc_ex(zend_op_array *op_array) { if (op_array->function_name) { @@ -344,6 +444,9 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } } + zend_persist_generic_parameter_list_calc(op_array->generic_parameters); + zend_persist_generic_type_table_calc(op_array->generic_types); + ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array))); } @@ -537,6 +640,9 @@ void zend_persist_class_entry_calc(zend_class_entry *ce) zend_persist_attributes_calc(ce->attributes); } + zend_persist_generic_parameter_list_calc(ce->generic_parameters); + zend_persist_generic_type_table_calc(ce->generic_types); + if (ce->num_interfaces) { uint32_t i; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 94d8bb7d149c..7a2a4dcd77dd 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -102,6 +102,9 @@ PHPAPI zend_class_entry *reflection_enum_unit_case_ptr; PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; PHPAPI zend_class_entry *reflection_fiber_ptr; PHPAPI zend_class_entry *reflection_constant_ptr; +PHPAPI zend_class_entry *reflection_generic_type_parameter_ptr; +PHPAPI zend_class_entry *reflection_type_parameter_reference_ptr; +PHPAPI zend_class_entry *reflection_generic_variance_ptr; PHPAPI zend_class_entry *reflection_property_hook_type_ptr; #define GET_REFLECTION_OBJECT() do { \ @@ -142,8 +145,22 @@ typedef struct _type_reference { zend_type type; /* Whether to use backwards compatible null representation */ bool legacy_behavior; + /* Optional pre-erasure form for generic-aware reflection. */ + zend_type pre_erasure; + /* Declaring entity for type-parameter resolution. */ + zend_class_entry *declaring_class; + zend_function *declaring_fn; } type_reference; +/* Struct for generic type-parameter reflection. Points back at a parameter slot + * inside the declaring entity's zend_generic_parameter_list. Lifetime is bound + * to the declaring class_entry / op_array. */ +typedef struct _generic_parameter_reference { + zend_generic_parameter *param; /* points into a zend_generic_parameter_list */ + uint32_t index; /* position in the list */ + zval declaring; /* zval-wrapped ReflectionClass / ReflectionFunctionAbstract */ +} generic_parameter_reference; + /* Struct for attributes */ typedef struct _attribute_reference { HashTable *attributes; @@ -162,7 +179,8 @@ typedef enum { REF_TYPE_TYPE, REF_TYPE_PROPERTY, REF_TYPE_CLASS_CONSTANT, - REF_TYPE_ATTRIBUTE + REF_TYPE_ATTRIBUTE, + REF_TYPE_GENERIC_PARAMETER } reflection_type_t; /* Struct for reflection objects */ @@ -264,6 +282,12 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ efree(intern->ptr); break; } + case REF_TYPE_GENERIC_PARAMETER: { + generic_parameter_reference *ref = intern->ptr; + zval_ptr_dtor(&ref->declaring); + efree(ref); + break; + } case REF_TYPE_GENERATOR: case REF_TYPE_FIBER: case REF_TYPE_CLASS_CONSTANT: @@ -1495,11 +1519,40 @@ static reflection_type_kind get_type_kind(zend_type type) { return NAMED_TYPE; } -/* {{{ reflection_type_factory */ -static void reflection_type_factory(zend_type type, zval *object, bool legacy_behavior) +/* {{{ reflection_type_factory_ex */ +static void reflection_type_factory_ex( + zend_type type, zval *object, bool legacy_behavior, + zend_type pre_erasure, + zend_class_entry *declaring_class, + zend_function *declaring_fn) { reflection_object *intern; type_reference *reference; + + if (ZEND_TYPE_HAS_NAMED_WITH_ARGS(type)) { + ZEND_ASSERT(!ZEND_TYPE_HAS_NAMED_WITH_ARGS(pre_erasure)); + pre_erasure = type; + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(type); + zend_string_addref(named->name); + type = (zend_type) ZEND_TYPE_INIT_CLASS(named->name, 0, 0); + } + + if (ZEND_TYPE_HAS_TYPE_PARAMETER(type)) { + object_init_ex(object, reflection_type_parameter_reference_ptr); + intern = Z_REFLECTION_P(object); + reference = (type_reference*) emalloc(sizeof(type_reference)); + reference->type = type; + reference->legacy_behavior = false; + reference->pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + reference->declaring_class = declaring_class; + reference->declaring_fn = declaring_fn; + intern->ptr = reference; + intern->ref_type = REF_TYPE_TYPE; + zend_type_parameter_ref *tp = ZEND_TYPE_TYPE_PARAMETER(type); + ZVAL_STR_COPY(reflection_prop_name(object), tp->name); + return; + } + reflection_type_kind type_kind = get_type_kind(type); bool is_mixed = ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY; bool is_only_null = (ZEND_TYPE_PURE_MASK(type) == MAY_BE_NULL && !ZEND_TYPE_IS_COMPLEX(type)); @@ -1521,6 +1574,9 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be reference = (type_reference*) emalloc(sizeof(type_reference)); reference->type = type; reference->legacy_behavior = legacy_behavior && type_kind == NAMED_TYPE && !is_mixed && !is_only_null; + reference->pre_erasure = pre_erasure; + reference->declaring_class = declaring_class; + reference->declaring_fn = declaring_fn; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; @@ -1535,6 +1591,11 @@ static void reflection_type_factory(zend_type type, zval *object, bool legacy_be } /* }}} */ +static void reflection_type_factory(zend_type type, zval *object, bool legacy_behavior) +{ + reflection_type_factory_ex(type, object, legacy_behavior, (zend_type) ZEND_TYPE_INIT_NONE(0), NULL, NULL); +} + /* {{{ reflection_function_factory */ static void reflection_function_factory(zend_function *function, zval *closure_object, zval *object) { @@ -2793,7 +2854,29 @@ ZEND_METHOD(ReflectionParameter, getType) if (!ZEND_TYPE_IS_SET(param->arg_info->type)) { RETURN_NULL(); } - reflection_type_factory(param->arg_info->type, return_value, true); + + zend_type pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + zend_class_entry *declaring_class = NULL; + zend_function *declaring_fn = NULL; + if (param->fptr->type == ZEND_USER_FUNCTION) { + zend_op_array *op = ¶m->fptr->op_array; + if (op->generic_types && op->generic_types->parameters) { + zend_type *boxed = zend_hash_index_find_ptr(op->generic_types->parameters, param->offset); + if (boxed) { + pre_erasure = *boxed; + declaring_fn = param->fptr; + } + } else if (op->generic_parameters) { + declaring_fn = param->fptr; + } + + if (op->scope && op->scope->generic_parameters) { + declaring_class = op->scope; + } + } + + reflection_type_factory_ex(param->arg_info->type, return_value, true, + pre_erasure, declaring_class, declaring_fn); } /* }}} */ @@ -3665,7 +3748,24 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) RETURN_NULL(); } - reflection_type_factory(fptr->common.arg_info[-1].type, return_value, true); + zend_type pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + zend_class_entry *declaring_class = NULL; + zend_function *declaring_fn = NULL; + if (fptr->type == ZEND_USER_FUNCTION) { + zend_op_array *op = &fptr->op_array; + if (op->generic_types && op->generic_types->return_type) { + pre_erasure = *op->generic_types->return_type; + } + if (op->generic_parameters) { + declaring_fn = fptr; + } + if (op->scope && op->scope->generic_parameters) { + declaring_class = op->scope; + } + } + + reflection_type_factory_ex(fptr->common.arg_info[-1].type, return_value, true, + pre_erasure, declaring_class, declaring_fn); } /* }}} */ @@ -6415,7 +6515,22 @@ ZEND_METHOD(ReflectionProperty, getType) RETURN_NULL(); } - reflection_type_factory(ref->prop->type, return_value, true); + zend_type pre_erasure = (zend_type) ZEND_TYPE_INIT_NONE(0); + zend_class_entry *declaring_class = NULL; + zend_class_entry *ce = ref->prop->ce; + if (ce && ce->generic_types && ce->generic_types->properties) { + zend_type *boxed = zend_hash_find_ptr(ce->generic_types->properties, ref->prop->name); + if (boxed) { + pre_erasure = *boxed; + } + } + + if (ce && ce->generic_parameters) { + declaring_class = ce; + } + + reflection_type_factory_ex(ref->prop->type, return_value, true, + pre_erasure, declaring_class, NULL); } /* }}} */ @@ -7608,6 +7723,13 @@ ZEND_METHOD(ReflectionAttribute, newInstance) } } + if (attr->data->generic_arity > 0) { + zend_check_generic_new_arity(ce, attr->data->generic_arity); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + } + zval obj; if (SUCCESS != zend_get_attribute_object(&obj, ce, attr->data, attr->scope, attr->filename)) { @@ -8156,6 +8278,534 @@ ZEND_METHOD(ReflectionConstant, __toString) RETURN_STR(smart_str_extract(&str)); } +ZEND_METHOD(ReflectionGenericTypeParameter, __construct) +{ + zend_throw_exception(reflection_exception_ptr, + "Cannot directly instantiate ReflectionGenericTypeParameter", 0); +} + +ZEND_METHOD(ReflectionTypeParameterReference, __construct) +{ + zend_throw_exception(reflection_exception_ptr, + "Cannot directly instantiate ReflectionTypeParameterReference", 0); +} + + +static void reflection_generic_type_parameter_factory( + zend_generic_parameter *param, uint32_t index, zval *declaring, zval *object) +{ + reflection_object *intern; + generic_parameter_reference *reference; + + object_init_ex(object, reflection_generic_type_parameter_ptr); + intern = Z_REFLECTION_P(object); + reference = emalloc(sizeof(generic_parameter_reference)); + reference->param = param; + reference->index = index; + ZVAL_COPY(&reference->declaring, declaring); + intern->ptr = reference; + intern->ref_type = REF_TYPE_GENERIC_PARAMETER; + + ZVAL_STR_COPY(reflection_prop_name(object), param->name); +} + +static void reflection_build_generic_parameters_array( + zend_generic_parameter_list *list, zval *declaring, zval *return_value) +{ + if (!list) { + array_init(return_value); + return; + } + array_init_size(return_value, list->count); + for (uint32_t i = 0; i < list->count; i++) { + zval entry; + reflection_generic_type_parameter_factory(&list->parameters[i], i, declaring, &entry); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &entry); + } +} + +ZEND_METHOD(ReflectionFunctionAbstract, isGeneric) +{ + reflection_object *intern; + zend_function *fptr; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(fptr); + + if (fptr->type != ZEND_USER_FUNCTION) { + RETURN_FALSE; + } + RETURN_BOOL(fptr->op_array.generic_parameters != NULL); +} + +ZEND_METHOD(ReflectionFunctionAbstract, getGenericParameters) +{ + reflection_object *intern; + zend_function *fptr; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(fptr); + + if (fptr->type != ZEND_USER_FUNCTION) { + array_init(return_value); + return; + } + reflection_build_generic_parameters_array(fptr->op_array.generic_parameters, ZEND_THIS, return_value); +} + +ZEND_METHOD(ReflectionClass, isGeneric) +{ + reflection_object *intern; + zend_class_entry *ce; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ce); + + RETURN_BOOL(ce->generic_parameters != NULL); +} + +ZEND_METHOD(ReflectionClass, getGenericParameters) +{ + reflection_object *intern; + zend_class_entry *ce; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ce); + + reflection_build_generic_parameters_array(ce->generic_parameters, ZEND_THIS, return_value); +} + +#define GET_GENERIC_PARAMETER_REFERENCE(intern, param_ref) \ + do { \ + (intern) = Z_REFLECTION_P(getThis()); \ + if ((intern)->ptr == NULL) { \ + zend_throw_error(NULL, "Internal error: Failed to retrieve the type parameter reference"); \ + RETURN_THROWS(); \ + } \ + (param_ref) = (generic_parameter_reference *) (intern)->ptr; \ + } while (0) + +ZEND_METHOD(ReflectionGenericTypeParameter, getName) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + RETURN_STR_COPY(ref->param->name); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, getPosition) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + RETURN_LONG((zend_long) ref->index); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, getVariance) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + + const char *case_name; + switch (ref->param->variance) { + case 1: case_name = "Covariant"; break; + case 2: case_name = "Contravariant"; break; + default: case_name = "Invariant"; break; + } + zend_object *case_obj = zend_enum_get_case_cstr(reflection_generic_variance_ptr, case_name); + if (!case_obj) { + zend_throw_error(NULL, "Internal error: ReflectionGenericVariance enum case not found"); + RETURN_THROWS(); + } + ZVAL_OBJ_COPY(return_value, case_obj); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, hasBound) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + RETURN_BOOL(ZEND_TYPE_IS_SET(ref->param->bound)); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, getBound) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + + if (!ZEND_TYPE_IS_SET(ref->param->bound)) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Type parameter %s has no bound", ZSTR_VAL(ref->param->name)); + RETURN_THROWS(); + } + zend_class_entry *declaring_class = NULL; + zend_function *declaring_fn = NULL; + if (Z_TYPE(ref->declaring) == IS_OBJECT) { + zend_object *obj = Z_OBJ(ref->declaring); + if (instanceof_function(obj->ce, reflection_class_ptr)) { + reflection_object *decl_intern = reflection_object_from_obj(obj); + declaring_class = decl_intern->ptr; + } else if (instanceof_function(obj->ce, reflection_function_abstract_ptr)) { + reflection_object *decl_intern = reflection_object_from_obj(obj); + declaring_fn = decl_intern->ptr; + } + } + zend_type primary = ZEND_TYPE_IS_SET(ref->param->bound_pre_erasure) + ? ref->param->bound_pre_erasure : ref->param->bound; + reflection_type_factory_ex(primary, return_value, false, + (zend_type) ZEND_TYPE_INIT_NONE(0), declaring_class, declaring_fn); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, hasDefault) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + RETURN_BOOL(ZEND_TYPE_IS_SET(ref->param->default_type)); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, getDefault) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + + if (!ZEND_TYPE_IS_SET(ref->param->default_type)) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Type parameter %s has no default", ZSTR_VAL(ref->param->name)); + RETURN_THROWS(); + } + + zend_class_entry *declaring_class = NULL; + zend_function *declaring_fn = NULL; + if (Z_TYPE(ref->declaring) == IS_OBJECT) { + zend_object *obj = Z_OBJ(ref->declaring); + if (instanceof_function(obj->ce, reflection_class_ptr)) { + reflection_object *decl_intern = reflection_object_from_obj(obj); + declaring_class = decl_intern->ptr; + } else if (instanceof_function(obj->ce, reflection_function_abstract_ptr)) { + reflection_object *decl_intern = reflection_object_from_obj(obj); + declaring_fn = decl_intern->ptr; + } + } + + zend_type primary = ZEND_TYPE_IS_SET(ref->param->default_pre_erasure) + ? ref->param->default_pre_erasure : ref->param->default_type; + reflection_type_factory_ex(primary, return_value, false, + (zend_type) ZEND_TYPE_INIT_NONE(0), declaring_class, declaring_fn); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, getDeclaringEntity) +{ + reflection_object *intern; + generic_parameter_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + ZVAL_COPY(return_value, &ref->declaring); +} + +ZEND_METHOD(ReflectionGenericTypeParameter, __toString) +{ + reflection_object *intern; + generic_parameter_reference *ref; + smart_str str = {0}; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_GENERIC_PARAMETER_REFERENCE(intern, ref); + + switch (ref->param->variance) { + case 1: + smart_str_appendc(&str, '+'); + break; + case 2: + smart_str_appendc(&str, '-'); + break; + default: + break; + } + smart_str_append(&str, ref->param->name); + if (ZEND_TYPE_IS_SET(ref->param->bound)) { + zend_string *bound = zend_type_to_string(ref->param->bound); + smart_str_appends(&str, " : "); + smart_str_append(&str, bound); + zend_string_release(bound); + } + if (ZEND_TYPE_IS_SET(ref->param->default_type)) { + zend_string *def = zend_type_to_string(ref->param->default_type); + smart_str_appends(&str, " = "); + smart_str_append(&str, def); + zend_string_release(def); + } + RETURN_NEW_STR(smart_str_extract(&str)); +} + +ZEND_METHOD(ReflectionTypeParameterReference, getName) +{ + reflection_object *intern; + type_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ZEND_TYPE_HAS_TYPE_PARAMETER(ref->type)) { + zend_throw_error(NULL, "Type parameter reference has no parameter"); + RETURN_THROWS(); + } + zend_type_parameter_ref *tp = ZEND_TYPE_TYPE_PARAMETER(ref->type); + RETURN_STR_COPY(tp->name); +} + +ZEND_METHOD(ReflectionTypeParameterReference, getTypeParameter) +{ + reflection_object *intern; + type_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ZEND_TYPE_HAS_TYPE_PARAMETER(ref->type)) { + zend_throw_error(NULL, "Type parameter reference has no parameter"); + RETURN_THROWS(); + } + + zend_type_parameter_ref *tp = ZEND_TYPE_TYPE_PARAMETER(ref->type); + + zend_generic_parameter_list *list = NULL; + zval declaring_zv; + ZVAL_UNDEF(&declaring_zv); + + if (tp->origin == ZEND_GENERIC_ORIGIN_CLASS_LIKE) { + if (!ref->declaring_class) { + zend_throw_error(NULL, + "Type parameter reference has no declaring class context"); + RETURN_THROWS(); + } + + list = ref->declaring_class->generic_parameters; + zend_reflection_class_factory(ref->declaring_class, &declaring_zv); + } else { + if (!ref->declaring_fn) { + zend_throw_error(NULL, + "Type parameter reference has no declaring function context"); + RETURN_THROWS(); + } + + list = ref->declaring_fn->op_array.generic_parameters; + if (ref->declaring_fn->op_array.scope) { + reflection_method_factory(ref->declaring_fn->op_array.scope, + ref->declaring_fn, NULL, &declaring_zv); + } else { + reflection_function_factory(ref->declaring_fn, NULL, &declaring_zv); + } + } + + if (!list || tp->index >= list->count) { + zval_ptr_dtor(&declaring_zv); + zend_throw_error(NULL, + "Type parameter index %u out of range for declaring entity", tp->index); + RETURN_THROWS(); + } + + reflection_generic_type_parameter_factory( + &list->parameters[tp->index], tp->index, &declaring_zv, return_value); + zval_ptr_dtor(&declaring_zv); +} + +ZEND_METHOD(ReflectionTypeParameterReference, allowsNull) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_TRUE; +} + +ZEND_METHOD(ReflectionTypeParameterReference, __toString) +{ + reflection_object *intern; + type_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ZEND_TYPE_HAS_TYPE_PARAMETER(ref->type)) { + RETURN_EMPTY_STRING(); + } + zend_type_parameter_ref *tp = ZEND_TYPE_TYPE_PARAMETER(ref->type); + RETURN_STR_COPY(tp->name); +} + +ZEND_METHOD(ReflectionNamedType, hasGenericArguments) +{ + reflection_object *intern; + type_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ref); + RETURN_BOOL(ZEND_TYPE_HAS_NAMED_WITH_ARGS(ref->pre_erasure)); +} + +ZEND_METHOD(ReflectionNamedType, getGenericArguments) +{ + reflection_object *intern; + type_reference *ref; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ref); + + if (!ZEND_TYPE_HAS_NAMED_WITH_ARGS(ref->pre_erasure)) { + array_init(return_value); + return; + } + + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(ref->pre_erasure); + array_init_size(return_value, named->count); + for (uint32_t i = 0; i < named->count; i++) { + zval entry; + reflection_type_factory_ex(named->args[i], &entry, false, + (zend_type) ZEND_TYPE_INIT_NONE(0), + ref->declaring_class, ref->declaring_fn); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &entry); + } +} + +static void reflection_build_named_args_list(zval *return_value, const zend_type *boxed, + zend_class_entry *declaring_class) +{ + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*boxed); + array_init_size(return_value, named->count); + for (uint32_t i = 0; i < named->count; i++) { + zval entry; + reflection_type_factory_ex(named->args[i], &entry, false, + (zend_type) ZEND_TYPE_INIT_NONE(0), + declaring_class, NULL); + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &entry); + } +} + +ZEND_METHOD(ReflectionClass, getGenericArgumentsForParentClass) +{ + reflection_object *intern; + zend_class_entry *ce; + + ZEND_PARSE_PARAMETERS_NONE(); + GET_REFLECTION_OBJECT_PTR(ce); + + bool has_parent = (ce->ce_flags & ZEND_ACC_LINKED) + ? ce->parent != NULL + : ce->parent_name != NULL; + if (!has_parent) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "Class %s has no parent class", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (ce->generic_types && ce->generic_types->extends) { + reflection_build_named_args_list(return_value, ce->generic_types->extends, ce); + return; + } + RETURN_EMPTY_ARRAY(); +} + +ZEND_METHOD(ReflectionClass, getGenericArgumentsForParentInterface) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(name) + ZEND_PARSE_PARAMETERS_END(); + GET_REFLECTION_OBJECT_PTR(ce); + + bool is_ancestor = false; + if (ce->ce_flags & ZEND_ACC_LINKED) { + for (uint32_t i = 0; i < ce->num_interfaces; i++) { + if (zend_string_equals_ci(ce->interfaces[i]->name, name)) { + is_ancestor = true; + break; + } + } + } else { + for (uint32_t i = 0; i < ce->num_interfaces; i++) { + if (zend_string_equals_ci(ce->interface_names[i].name, name)) { + is_ancestor = true; + break; + } + } + } + if (!is_ancestor) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "%s is not an ancestor interface of %s", ZSTR_VAL(name), ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (ce->generic_types && ce->generic_types->implements) { + zval *zv; + ZEND_HASH_FOREACH_VAL(ce->generic_types->implements, zv) { + zend_type *boxed = (zend_type *) Z_PTR_P(zv); + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*boxed); + if (zend_string_equals_ci(named->name, name)) { + reflection_build_named_args_list(return_value, boxed, ce); + return; + } + } ZEND_HASH_FOREACH_END(); + } + RETURN_EMPTY_ARRAY(); +} + +ZEND_METHOD(ReflectionClass, getGenericArgumentsForUsedTrait) +{ + reflection_object *intern; + zend_class_entry *ce; + zend_string *name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(name) + ZEND_PARSE_PARAMETERS_END(); + GET_REFLECTION_OBJECT_PTR(ce); + + bool is_used = false; + for (uint32_t i = 0; i < ce->num_traits; i++) { + if (zend_string_equals_ci(ce->trait_names[i].name, name)) { + is_used = true; + break; + } + } + if (!is_used) { + zend_throw_exception_ex(reflection_exception_ptr, 0, + "%s is not a trait used by %s", ZSTR_VAL(name), ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + if (ce->generic_types && ce->generic_types->trait_uses) { + zval *zv; + ZEND_HASH_FOREACH_VAL(ce->generic_types->trait_uses, zv) { + zend_type *boxed = (zend_type *) Z_PTR_P(zv); + zend_type_named_with_args *named = ZEND_TYPE_NAMED_WITH_ARGS(*boxed); + if (zend_string_equals_ci(named->name, name)) { + reflection_build_named_args_list(return_value, boxed, ce); + return; + } + } ZEND_HASH_FOREACH_END(); + } + RETURN_EMPTY_ARRAY(); +} + PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); @@ -8261,6 +8911,16 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_property_hook_type_ptr = register_class_PropertyHookType(); + reflection_generic_variance_ptr = register_class_ReflectionGenericVariance(); + + reflection_generic_type_parameter_ptr = register_class_ReflectionGenericTypeParameter(reflector_ptr); + reflection_generic_type_parameter_ptr->create_object = reflection_objects_new; + reflection_generic_type_parameter_ptr->default_object_handlers = &reflection_object_handlers; + + reflection_type_parameter_reference_ptr = register_class_ReflectionTypeParameterReference(reflection_type_ptr); + reflection_type_parameter_reference_ptr->create_object = reflection_objects_new; + reflection_type_parameter_reference_ptr->default_object_handlers = &reflection_object_handlers; + REFLECTION_G(key_initialized) = false; return SUCCESS; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index dd605100f8ba..d5ca9dbe0cd2 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -115,6 +115,11 @@ public function hasTentativeReturnType(): bool {} public function getTentativeReturnType(): ?ReflectionType {} public function getAttributes(?string $name = null, int $flags = 0): array {} + + public function isGeneric(): bool {} + + /** @return list */ + public function getGenericParameters(): array {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -436,6 +441,41 @@ public function getNamespaceName(): string {} public function getShortName(): string {} public function getAttributes(?string $name = null, int $flags = 0): array {} + + public function isGeneric(): bool {} + + /** @return list */ + public function getGenericParameters(): array {} + + /** + * Returns the type arguments this class supplies at the parent-class extends + * site, in source order. Returns an empty array if the extends clause + * specified no type arguments. + * + * @return list + * @throws ReflectionException if this class has no parent class + */ + public function getGenericArgumentsForParentClass(): array {} + + /** + * Returns the type arguments this class supplies for the named ancestor + * interface, in source order. Returns an empty array if no type arguments + * were specified at the use site. + * + * @return list + * @throws ReflectionException if $name is not an ancestor interface + */ + public function getGenericArgumentsForParentInterface(string $name): array {} + + /** + * Returns the type arguments this class supplies at the use site for trait + * $name, in source order. Returns an empty array if no type arguments were + * specified at the use site. + * + * @return list + * @throws ReflectionException if $name is not a directly-used trait + */ + public function getGenericArgumentsForUsedTrait(string $name): array {} } class ReflectionObject extends ReflectionClass @@ -741,6 +781,11 @@ public function getName(): string {} /** @tentative-return-type */ public function isBuiltin(): bool {} + + public function hasGenericArguments(): bool {} + + /** @return list */ + public function getGenericArguments(): array {} } class ReflectionUnionType extends ReflectionType @@ -753,6 +798,66 @@ class ReflectionIntersectionType extends ReflectionType public function getTypes(): array {} } +/** + * @strict-properties + * @not-serializable + */ +final class ReflectionTypeParameterReference extends ReflectionType +{ + public string $name; + + private function __construct() {} + + public function getName(): string {} + + public function getTypeParameter(): ReflectionGenericTypeParameter {} + + public function allowsNull(): bool {} + + public function __toString(): string {} +} + +enum ReflectionGenericVariance: int +{ + case Invariant = 0; + case Covariant = 1; + case Contravariant = 2; +} + +/** + * @strict-properties + * @not-serializable + */ +final class ReflectionGenericTypeParameter implements Reflector +{ + public string $name; + + private function __construct() {} + + /** @implementation-alias ReflectionClass::__clone */ + private function __clone(): void {} + + public function getName(): string {} + + public function getPosition(): int {} + + public function getVariance(): ReflectionGenericVariance {} + + public function hasBound(): bool {} + + /** @throws ReflectionException if this type parameter has no bound; check hasBound() first */ + public function getBound(): ReflectionType {} + + public function hasDefault(): bool {} + + /** @throws ReflectionException if this type parameter has no default; check hasDefault() first */ + public function getDefault(): ReflectionType {} + + public function getDeclaringEntity(): ReflectionClass|ReflectionFunctionAbstract {} + + public function __toString(): string {} +} + /** @not-serializable */ class ReflectionExtension implements Reflector { diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 65571f38d43c..ee6307cf4a54 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: c80946cc8c8215bb6527e09bb71b3a97a76a6a98 + * Stub hash: 3285061ba15d3312182782cace0c79f42915c7d6 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) @@ -87,6 +87,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionFunctionAbstract_isGeneric arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionFunctionAbstract_getGenericParameters arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) ZEND_ARG_OBJ_TYPE_MASK(0, function, Closure, MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() @@ -367,6 +371,18 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes +#define arginfo_class_ReflectionClass_isGeneric arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionClass_getGenericParameters arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables + +#define arginfo_class_ReflectionClass_getGenericArgumentsForParentClass arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_getGenericArgumentsForParentInterface, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionClass_getGenericArgumentsForUsedTrait arginfo_class_ReflectionClass_getGenericArgumentsForParentInterface + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionObject___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) ZEND_END_ARG_INFO() @@ -581,10 +597,51 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionNamedType_isBuiltin arginfo_class_ReflectionFunctionAbstract_inNamespace +#define arginfo_class_ReflectionNamedType_hasGenericArguments arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionNamedType_getGenericArguments arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables + #define arginfo_class_ReflectionUnionType_getTypes arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables #define arginfo_class_ReflectionIntersectionType_getTypes arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionTypeParameterReference___construct, 0, 0, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionTypeParameterReference_getName arginfo_class_ReflectionFunction___toString + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionTypeParameterReference_getTypeParameter, 0, 0, ReflectionGenericTypeParameter, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionTypeParameterReference_allowsNull arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionTypeParameterReference___toString arginfo_class_ReflectionFunction___toString + +#define arginfo_class_ReflectionGenericTypeParameter___construct arginfo_class_ReflectionTypeParameterReference___construct + +#define arginfo_class_ReflectionGenericTypeParameter___clone arginfo_class_ReflectionFunctionAbstract___clone + +#define arginfo_class_ReflectionGenericTypeParameter_getName arginfo_class_ReflectionFunction___toString + +#define arginfo_class_ReflectionGenericTypeParameter_getPosition arginfo_class_ReflectionGenerator_getExecutingLine + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionGenericTypeParameter_getVariance, 0, 0, ReflectionGenericVariance, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionGenericTypeParameter_hasBound arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionGenericTypeParameter_getBound, 0, 0, ReflectionType, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionGenericTypeParameter_hasDefault arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + +#define arginfo_class_ReflectionGenericTypeParameter_getDefault arginfo_class_ReflectionGenericTypeParameter_getBound + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_ReflectionGenericTypeParameter_getDeclaringEntity, 0, 0, ReflectionClass|ReflectionFunctionAbstract, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionGenericTypeParameter___toString arginfo_class_ReflectionFunction___toString + #define arginfo_class_ReflectionExtension___clone arginfo_class_ReflectionFunctionAbstract___clone ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionExtension___construct, 0, 0, 1) @@ -641,8 +698,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionReference___clone arginfo_class_ReflectionFunctionAbstract___clone -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionReference___construct, 0, 0, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionReference___construct arginfo_class_ReflectionTypeParameterReference___construct #define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionFunction___toString @@ -659,7 +715,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionAttribute___clone arginfo_class_ReflectionFunctionAbstract___clone -#define arginfo_class_ReflectionAttribute___construct arginfo_class_ReflectionReference___construct +#define arginfo_class_ReflectionAttribute___construct arginfo_class_ReflectionTypeParameterReference___construct #define arginfo_class_ReflectionEnum___construct arginfo_class_ReflectionClass___construct @@ -768,6 +824,8 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType); ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType); ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType); ZEND_METHOD(ReflectionFunctionAbstract, getAttributes); +ZEND_METHOD(ReflectionFunctionAbstract, isGeneric); +ZEND_METHOD(ReflectionFunctionAbstract, getGenericParameters); ZEND_METHOD(ReflectionFunction, __construct); ZEND_METHOD(ReflectionFunction, __toString); ZEND_METHOD(ReflectionFunction, isAnonymous); @@ -863,6 +921,11 @@ ZEND_METHOD(ReflectionClass, inNamespace); ZEND_METHOD(ReflectionClass, getNamespaceName); ZEND_METHOD(ReflectionClass, getShortName); ZEND_METHOD(ReflectionClass, getAttributes); +ZEND_METHOD(ReflectionClass, isGeneric); +ZEND_METHOD(ReflectionClass, getGenericParameters); +ZEND_METHOD(ReflectionClass, getGenericArgumentsForParentClass); +ZEND_METHOD(ReflectionClass, getGenericArgumentsForParentInterface); +ZEND_METHOD(ReflectionClass, getGenericArgumentsForUsedTrait); ZEND_METHOD(ReflectionObject, __construct); ZEND_METHOD(ReflectionProperty, __construct); ZEND_METHOD(ReflectionProperty, __toString); @@ -948,8 +1011,25 @@ ZEND_METHOD(ReflectionType, allowsNull); ZEND_METHOD(ReflectionType, __toString); ZEND_METHOD(ReflectionNamedType, getName); ZEND_METHOD(ReflectionNamedType, isBuiltin); +ZEND_METHOD(ReflectionNamedType, hasGenericArguments); +ZEND_METHOD(ReflectionNamedType, getGenericArguments); ZEND_METHOD(ReflectionUnionType, getTypes); ZEND_METHOD(ReflectionIntersectionType, getTypes); +ZEND_METHOD(ReflectionTypeParameterReference, __construct); +ZEND_METHOD(ReflectionTypeParameterReference, getName); +ZEND_METHOD(ReflectionTypeParameterReference, getTypeParameter); +ZEND_METHOD(ReflectionTypeParameterReference, allowsNull); +ZEND_METHOD(ReflectionTypeParameterReference, __toString); +ZEND_METHOD(ReflectionGenericTypeParameter, __construct); +ZEND_METHOD(ReflectionGenericTypeParameter, getName); +ZEND_METHOD(ReflectionGenericTypeParameter, getPosition); +ZEND_METHOD(ReflectionGenericTypeParameter, getVariance); +ZEND_METHOD(ReflectionGenericTypeParameter, hasBound); +ZEND_METHOD(ReflectionGenericTypeParameter, getBound); +ZEND_METHOD(ReflectionGenericTypeParameter, hasDefault); +ZEND_METHOD(ReflectionGenericTypeParameter, getDefault); +ZEND_METHOD(ReflectionGenericTypeParameter, getDeclaringEntity); +ZEND_METHOD(ReflectionGenericTypeParameter, __toString); ZEND_METHOD(ReflectionExtension, __construct); ZEND_METHOD(ReflectionExtension, __toString); ZEND_METHOD(ReflectionExtension, getName); @@ -1048,6 +1128,8 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_ME(ReflectionFunctionAbstract, hasTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getTentativeReturnType, arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getAttributes, arginfo_class_ReflectionFunctionAbstract_getAttributes, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, isGeneric, arginfo_class_ReflectionFunctionAbstract_isGeneric, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, getGenericParameters, arginfo_class_ReflectionFunctionAbstract_getGenericParameters, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1161,6 +1243,11 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, getNamespaceName, arginfo_class_ReflectionClass_getNamespaceName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getShortName, arginfo_class_ReflectionClass_getShortName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getAttributes, arginfo_class_ReflectionClass_getAttributes, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, isGeneric, arginfo_class_ReflectionClass_isGeneric, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, getGenericParameters, arginfo_class_ReflectionClass_getGenericParameters, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, getGenericArgumentsForParentClass, arginfo_class_ReflectionClass_getGenericArgumentsForParentClass, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, getGenericArgumentsForParentInterface, arginfo_class_ReflectionClass_getGenericArgumentsForParentInterface, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, getGenericArgumentsForUsedTrait, arginfo_class_ReflectionClass_getGenericArgumentsForUsedTrait, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1274,6 +1361,8 @@ static const zend_function_entry class_ReflectionType_methods[] = { static const zend_function_entry class_ReflectionNamedType_methods[] = { ZEND_ME(ReflectionNamedType, getName, arginfo_class_ReflectionNamedType_getName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionNamedType, isBuiltin, arginfo_class_ReflectionNamedType_isBuiltin, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionNamedType, hasGenericArguments, arginfo_class_ReflectionNamedType_hasGenericArguments, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionNamedType, getGenericArguments, arginfo_class_ReflectionNamedType_getGenericArguments, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1287,6 +1376,30 @@ static const zend_function_entry class_ReflectionIntersectionType_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ReflectionTypeParameterReference_methods[] = { + ZEND_ME(ReflectionTypeParameterReference, __construct, arginfo_class_ReflectionTypeParameterReference___construct, ZEND_ACC_PRIVATE) + ZEND_ME(ReflectionTypeParameterReference, getName, arginfo_class_ReflectionTypeParameterReference_getName, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionTypeParameterReference, getTypeParameter, arginfo_class_ReflectionTypeParameterReference_getTypeParameter, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionTypeParameterReference, allowsNull, arginfo_class_ReflectionTypeParameterReference_allowsNull, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionTypeParameterReference, __toString, arginfo_class_ReflectionTypeParameterReference___toString, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_ReflectionGenericTypeParameter_methods[] = { + ZEND_ME(ReflectionGenericTypeParameter, __construct, arginfo_class_ReflectionGenericTypeParameter___construct, ZEND_ACC_PRIVATE) + ZEND_RAW_FENTRY("__clone", zim_ReflectionClass___clone, arginfo_class_ReflectionGenericTypeParameter___clone, ZEND_ACC_PRIVATE, NULL, NULL) + ZEND_ME(ReflectionGenericTypeParameter, getName, arginfo_class_ReflectionGenericTypeParameter_getName, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, getPosition, arginfo_class_ReflectionGenericTypeParameter_getPosition, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, getVariance, arginfo_class_ReflectionGenericTypeParameter_getVariance, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, hasBound, arginfo_class_ReflectionGenericTypeParameter_hasBound, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, getBound, arginfo_class_ReflectionGenericTypeParameter_getBound, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, hasDefault, arginfo_class_ReflectionGenericTypeParameter_hasDefault, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, getDefault, arginfo_class_ReflectionGenericTypeParameter_getDefault, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, getDeclaringEntity, arginfo_class_ReflectionGenericTypeParameter_getDeclaringEntity, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionGenericTypeParameter, __toString, arginfo_class_ReflectionGenericTypeParameter___toString, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_ReflectionExtension_methods[] = { ZEND_RAW_FENTRY("__clone", zim_ReflectionClass___clone, arginfo_class_ReflectionExtension___clone, ZEND_ACC_PRIVATE, NULL, NULL) ZEND_ME(ReflectionExtension, __construct, arginfo_class_ReflectionExtension___construct, ZEND_ACC_PUBLIC) @@ -1808,6 +1921,54 @@ static zend_class_entry *register_class_ReflectionIntersectionType(zend_class_en return class_entry; } +static zend_class_entry *register_class_ReflectionTypeParameterReference(zend_class_entry *class_entry_ReflectionType) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionTypeParameterReference", class_ReflectionTypeParameterReference_methods); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_ReflectionType, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + return class_entry; +} + +static zend_class_entry *register_class_ReflectionGenericVariance(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("ReflectionGenericVariance", IS_LONG, NULL); + + zval enum_case_Invariant_value; + ZVAL_LONG(&enum_case_Invariant_value, 0); + zend_enum_add_case_cstr(class_entry, "Invariant", &enum_case_Invariant_value); + + zval enum_case_Covariant_value; + ZVAL_LONG(&enum_case_Covariant_value, 1); + zend_enum_add_case_cstr(class_entry, "Covariant", &enum_case_Covariant_value); + + zval enum_case_Contravariant_value; + ZVAL_LONG(&enum_case_Contravariant_value, 2); + zend_enum_add_case_cstr(class_entry, "Contravariant", &enum_case_Contravariant_value); + + return class_entry; +} + +static zend_class_entry *register_class_ReflectionGenericTypeParameter(zend_class_entry *class_entry_Reflector) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionGenericTypeParameter", class_ReflectionGenericTypeParameter_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_class_implements(class_entry, 1, class_entry_Reflector); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + return class_entry; +} + static zend_class_entry *register_class_ReflectionExtension(zend_class_entry *class_entry_Reflector) { zend_class_entry ce, *class_entry; diff --git a/ext/reflection/php_reflection_decl.h b/ext/reflection/php_reflection_decl.h index a87e1635419b..a11cc59ffe2a 100644 --- a/ext/reflection/php_reflection_decl.h +++ b/ext/reflection/php_reflection_decl.h @@ -1,12 +1,18 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: c80946cc8c8215bb6527e09bb71b3a97a76a6a98 */ + * Stub hash: 3285061ba15d3312182782cace0c79f42915c7d6 */ -#ifndef ZEND_PHP_REFLECTION_DECL_c80946cc8c8215bb6527e09bb71b3a97a76a6a98_H -#define ZEND_PHP_REFLECTION_DECL_c80946cc8c8215bb6527e09bb71b3a97a76a6a98_H +#ifndef ZEND_PHP_REFLECTION_DECL_3285061ba15d3312182782cace0c79f42915c7d6_H +#define ZEND_PHP_REFLECTION_DECL_3285061ba15d3312182782cace0c79f42915c7d6_H typedef enum zend_enum_PropertyHookType { ZEND_ENUM_PropertyHookType_Get = 1, ZEND_ENUM_PropertyHookType_Set = 2, } zend_enum_PropertyHookType; -#endif /* ZEND_PHP_REFLECTION_DECL_c80946cc8c8215bb6527e09bb71b3a97a76a6a98_H */ +typedef enum zend_enum_ReflectionGenericVariance { + ZEND_ENUM_ReflectionGenericVariance_Invariant = 1, + ZEND_ENUM_ReflectionGenericVariance_Covariant = 2, + ZEND_ENUM_ReflectionGenericVariance_Contravariant = 3, +} zend_enum_ReflectionGenericVariance; + +#endif /* ZEND_PHP_REFLECTION_DECL_3285061ba15d3312182782cace0c79f42915c7d6_H */ diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index fd5d83e91741..01415d08aacb 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -30,7 +30,7 @@ Class [ class ReflectionClass implements Stringable, Refle Property [ public string $name ] } - - Methods [64] { + - Methods [69] { Method [ private method __clone ] { - Parameters [0] { @@ -514,5 +514,42 @@ Class [ class ReflectionClass implements Stringable, Refle } - Return [ array ] } + + Method [ public method isGeneric ] { + + - Parameters [0] { + } + - Return [ bool ] + } + + Method [ public method getGenericParameters ] { + + - Parameters [0] { + } + - Return [ array ] + } + + Method [ public method getGenericArgumentsForParentClass ] { + + - Parameters [0] { + } + - Return [ array ] + } + + Method [ public method getGenericArgumentsForParentInterface ] { + + - Parameters [1] { + Parameter #0 [ string $name ] + } + - Return [ array ] + } + + Method [ public method getGenericArgumentsForUsedTrait ] { + + - Parameters [1] { + Parameter #0 [ string $name ] + } + - Return [ array ] + } } } diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 8ba243a503bd..e591c9b2f091 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECTF-- -array(26) { +array(29) { ["ReflectionException"]=> object(ReflectionClass)#%d (1) { ["name"]=> @@ -139,4 +139,19 @@ array(26) { ["name"]=> string(16) "PropertyHookType" } + ["ReflectionGenericVariance"]=> + object(ReflectionEnum)#%d (1) { + ["name"]=> + string(25) "ReflectionGenericVariance" + } + ["ReflectionGenericTypeParameter"]=> + object(ReflectionClass)#%d (1) { + ["name"]=> + string(30) "ReflectionGenericTypeParameter" + } + ["ReflectionTypeParameterReference"]=> + object(ReflectionClass)#%d (1) { + ["name"]=> + string(32) "ReflectionTypeParameterReference" + } } diff --git a/ext/reflection/tests/generics/ancestor_args_composite.phpt b/ext/reflection/tests/generics/ancestor_args_composite.phpt new file mode 100644 index 000000000000..dbd45bd29c6a --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_composite.phpt @@ -0,0 +1,19 @@ +--TEST-- +Reflection: composite (union/intersection) type args inside extends/implements +--FILE-- + {} + +class C extends Pair {} + +$args = (new ReflectionClass('C'))->getGenericArgumentsForParentClass(); +echo "count: ", count($args), "\n"; +echo "[0] kind: ", get_class($args[0]), "\n"; +echo "[1] kind: ", get_class($args[1]), "\n"; +?> +--EXPECT-- +count: 2 +[0] kind: ReflectionUnionType +[1] kind: ReflectionIntersectionType diff --git a/ext/reflection/tests/generics/ancestor_args_nested.phpt b/ext/reflection/tests/generics/ancestor_args_nested.phpt new file mode 100644 index 000000000000..3a85be230d0c --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_nested.phpt @@ -0,0 +1,39 @@ +--TEST-- +Reflection: nested generic arguments inside extends/implements/use are preserved +--FILE-- + {} +class Pair {} +interface I {} +trait T1 { public function noop(): void {} } + +class WithNested + extends Pair, string> + implements I> +{ + use T1>; +} + +$rc = new ReflectionClass('WithNested'); + +// Nested in extends +$args = $rc->getGenericArgumentsForParentClass(); +echo "parent[0]: ", $args[0]->getName(); +echo "<", $args[0]->getGenericArguments()[0]->getName(), ">\n"; +echo "parent[1]: ", $args[1]->getName(), "\n"; + +// Nested in implements +$args = $rc->getGenericArgumentsForParentInterface('I'); +echo "I[0]: ", $args[0]->getName(); +echo "<", $args[0]->getGenericArguments()[0]->getName(), ">\n"; + +// Nested in use +$args = $rc->getGenericArgumentsForUsedTrait('T1'); +echo "T1[0]: ", $args[0]->getName(); +echo "<", $args[0]->getGenericArguments()[0]->getName(), ">\n"; +?> +--EXPECT-- +parent[0]: Box +parent[1]: string +I[0]: Box +T1[0]: Box diff --git a/ext/reflection/tests/generics/ancestor_args_no_generics_class.phpt b/ext/reflection/tests/generics/ancestor_args_no_generics_class.phpt new file mode 100644 index 000000000000..b446b8b01789 --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_no_generics_class.phpt @@ -0,0 +1,47 @@ +--TEST-- +Reflection: classes with no generic content — empty array when ancestor without args, throw when not an ancestor +--FILE-- +getGenericArgumentsForParentClass(); + echo $cls, ": parent=", json_encode($p); + } catch (ReflectionException $e) { + echo $cls, ": parent=throw(", $e->getMessage(), ")"; + } + + try { + $i = $rc->getGenericArgumentsForParentInterface('IPlain'); + echo " iface=", json_encode($i); + } catch (ReflectionException $e) { + echo " iface=throw(", $e->getMessage(), ")"; + } + + try { + $t = $rc->getGenericArgumentsForUsedTrait('TPlain'); + echo " trait=", json_encode($t); + } catch (ReflectionException $e) { + echo " trait=throw(", $e->getMessage(), ")"; + } + + echo "\n"; +} + +foreach (['Plain', 'WithParent', 'WithIface', 'WithTrait'] as $cls) { + show($cls); +} +?> +--EXPECT-- +Plain: parent=throw(Class Plain has no parent class) iface=throw(IPlain is not an ancestor interface of Plain) trait=throw(TPlain is not a trait used by Plain) +WithParent: parent=[] iface=throw(IPlain is not an ancestor interface of WithParent) trait=throw(TPlain is not a trait used by WithParent) +WithIface: parent=throw(Class WithIface has no parent class) iface=[] trait=throw(TPlain is not a trait used by WithIface) +WithTrait: parent=throw(Class WithTrait has no parent class) iface=throw(IPlain is not an ancestor interface of WithTrait) trait=[] diff --git a/ext/reflection/tests/generics/ancestor_args_parent_class.phpt b/ext/reflection/tests/generics/ancestor_args_parent_class.phpt new file mode 100644 index 000000000000..cd7648a5bb28 --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_parent_class.phpt @@ -0,0 +1,39 @@ +--TEST-- +Reflection: getGenericArgumentsForParentClass returns args from extends clause, throws when no parent +--FILE-- + {} +class B {} + +class WithArgs extends A {} +class NoArgs extends A {} +class NoParent {} +class Multi extends B {} + +$cases = [ + 'WithArgs' => ['string'], + 'NoArgs' => [], + 'NoParent' => 'throw', + 'Multi' => ['int', 'float'], +]; + +foreach ($cases as $cls => $want) { + try { + $args = (new ReflectionClass($cls))->getGenericArgumentsForParentClass(); + } catch (ReflectionException $e) { + echo $cls, ": ", $want === 'throw' ? "throw OK ({$e->getMessage()})" : "FAIL (unexpected throw)", "\n"; + continue; + } + if ($want === 'throw') { + echo $cls, ": FAIL (expected throw)\n"; + } else { + $got = array_map(fn($t) => $t->getName(), $args); + echo $cls, ": ", $got === $want ? "OK" : ("FAIL got " . implode(",", $got)), "\n"; + } +} +?> +--EXPECT-- +WithArgs: OK +NoArgs: OK +NoParent: throw OK (Class NoParent has no parent class) +Multi: OK diff --git a/ext/reflection/tests/generics/ancestor_args_parent_interface.phpt b/ext/reflection/tests/generics/ancestor_args_parent_interface.phpt new file mode 100644 index 000000000000..33973c767b3d --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_parent_interface.phpt @@ -0,0 +1,43 @@ +--TEST-- +Reflection: getGenericArgumentsForParentInterface returns args from implements / interface-extends; throws when not ancestor +--FILE-- + {} +interface J {} +interface K1 extends I {} + +class WithArgs implements I {} +class WithoutArgs implements I {} +class NotImplements {} +class Multi implements I, J {} + +function show(string $cls, string $iface): void { + try { + $args = (new ReflectionClass($cls))->getGenericArgumentsForParentInterface($iface); + } catch (ReflectionException $e) { + echo "$cls/$iface: throw ({$e->getMessage()})\n"; + return; + } + if (!$args) { + echo "$cls/$iface: []\n"; + return; + } + echo "$cls/$iface: ", implode(",", array_map(fn($t)=>$t->getName(), $args)), "\n"; +} + +show('WithArgs', 'I'); +show('WithArgs', 'i'); // case insensitive +show('WithoutArgs', 'I'); +show('NotImplements', 'I'); +show('Multi', 'I'); +show('Multi', 'J'); +show('K1', 'I'); +?> +--EXPECT-- +WithArgs/I: int,string +WithArgs/i: int,string +WithoutArgs/I: [] +NotImplements/I: throw (I is not an ancestor interface of NotImplements) +Multi/I: bool,float +Multi/J: string +K1/I: int,string diff --git a/ext/reflection/tests/generics/ancestor_args_type_param_refs.phpt b/ext/reflection/tests/generics/ancestor_args_type_param_refs.phpt new file mode 100644 index 000000000000..e647933447ac --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_type_param_refs.phpt @@ -0,0 +1,24 @@ +--TEST-- +Reflection: type-parameter references inside extends/implements args resolve correctly +--FILE-- + {} +class Holder implements Container {} + +$rc = new ReflectionClass('Holder'); +$args = $rc->getGenericArgumentsForParentInterface('Container'); + +echo "count: ", count($args), "\n"; +echo "class: ", get_class($args[0]), "\n"; +echo "name: ", $args[0]->getName(), "\n"; + +$param = $args[0]->getTypeParameter(); +echo "type param: ", $param->getName(), "\n"; +echo "declaring: ", $param->getDeclaringEntity()->getName(), "\n"; +?> +--EXPECT-- +count: 1 +class: ReflectionTypeParameterReference +name: T +type param: T +declaring: Holder diff --git a/ext/reflection/tests/generics/ancestor_args_used_trait.phpt b/ext/reflection/tests/generics/ancestor_args_used_trait.phpt new file mode 100644 index 000000000000..76058e458b01 --- /dev/null +++ b/ext/reflection/tests/generics/ancestor_args_used_trait.phpt @@ -0,0 +1,46 @@ +--TEST-- +Reflection: getGenericArgumentsForUsedTrait returns args from use clause; throws when trait not used +--FILE-- + { public T $val; } +trait Pair { public K $k; public V $v; } +trait Plain { public int $x; } + +class WithArgs { use Holder; } +class NoArgs { use Holder; } +class NoTrait {} +class Multi { use Holder, Pair; } +class Combo { use Holder, Plain; } + +function show(string $cls, string $tr): void { + try { + $args = (new ReflectionClass($cls))->getGenericArgumentsForUsedTrait($tr); + } catch (ReflectionException $e) { + echo "$cls/$tr: throw ({$e->getMessage()})\n"; + return; + } + if (!$args) { + echo "$cls/$tr: []\n"; + return; + } + echo "$cls/$tr: ", implode(",", array_map(fn($t)=>$t->getName(), $args)), "\n"; +} + +show('WithArgs', 'Holder'); +show('NoArgs', 'Holder'); +show('NoTrait', 'Holder'); +show('Multi', 'Holder'); +show('Multi', 'Pair'); +show('Combo', 'Holder'); +show('Combo', 'Plain'); +show('Combo', 'holder'); // case insensitive +?> +--EXPECT-- +WithArgs/Holder: string +NoArgs/Holder: [] +NoTrait/Holder: throw (Holder is not a trait used by NoTrait) +Multi/Holder: bool +Multi/Pair: int,float +Combo/Holder: string +Combo/Plain: [] +Combo/holder: string diff --git a/ext/reflection/tests/generics/arrow_fn_get_generic_params.phpt b/ext/reflection/tests/generics/arrow_fn_get_generic_params.phpt new file mode 100644 index 000000000000..829cdaaa8d2f --- /dev/null +++ b/ext/reflection/tests/generics/arrow_fn_get_generic_params.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: getGenericParameters on arrow function +--FILE-- +(T $x): T => $x; +$r = new ReflectionFunction($f); +echo $r->isGeneric() ? "gen\n" : "not\n"; +echo $r->getGenericParameters()[0]->getName(), "\n"; +echo $r->getGenericParameters()[0]->getBound()->getName(), "\n"; +?> +--EXPECT-- +gen +T +int diff --git a/ext/reflection/tests/generics/class_isgeneric_false.phpt b/ext/reflection/tests/generics/class_isgeneric_false.phpt new file mode 100644 index 000000000000..52f5ce8130d1 --- /dev/null +++ b/ext/reflection/tests/generics/class_isgeneric_false.phpt @@ -0,0 +1,9 @@ +--TEST-- +Reflection: ReflectionClass::isGeneric() returns false for non-generic class +--FILE-- +isGeneric()); +?> +--EXPECT-- +bool(false) diff --git a/ext/reflection/tests/generics/class_isgeneric_true.phpt b/ext/reflection/tests/generics/class_isgeneric_true.phpt new file mode 100644 index 000000000000..82eaeaec2247 --- /dev/null +++ b/ext/reflection/tests/generics/class_isgeneric_true.phpt @@ -0,0 +1,9 @@ +--TEST-- +Reflection: ReflectionClass::isGeneric() returns true for generic class +--FILE-- + {} +var_dump((new ReflectionClass('G'))->isGeneric()); +?> +--EXPECT-- +bool(true) diff --git a/ext/reflection/tests/generics/closure_get_generic_params.phpt b/ext/reflection/tests/generics/closure_get_generic_params.phpt new file mode 100644 index 000000000000..8a133191fe72 --- /dev/null +++ b/ext/reflection/tests/generics/closure_get_generic_params.phpt @@ -0,0 +1,16 @@ +--TEST-- +Reflection: getGenericParameters on closure +--FILE-- +(T $x): T { return $x; }; +$r = new ReflectionFunction($cl); +echo $r->isGeneric() ? "gen\n" : "not\n"; +echo count($r->getGenericParameters()), "\n"; +echo $r->getGenericParameters()[0]->getName(), "\n"; +echo $r->getGenericParameters()[0]->getBound()->getName(), "\n"; +?> +--EXPECT-- +gen +1 +T +object diff --git a/ext/reflection/tests/generics/composite_bound.phpt b/ext/reflection/tests/generics/composite_bound.phpt new file mode 100644 index 000000000000..c28ae671d2ad --- /dev/null +++ b/ext/reflection/tests/generics/composite_bound.phpt @@ -0,0 +1,12 @@ +--TEST-- +Reflection: composite (union) bound returned +--FILE-- +(T $x): T { return $x; } +$p = (new ReflectionFunction('f'))->getGenericParameters()[0]; +$b = $p->getBound(); +echo get_class($b), "\n"; +?> +--EXPECT-- +ReflectionUnionType diff --git a/ext/reflection/tests/generics/composite_type_arg.phpt b/ext/reflection/tests/generics/composite_type_arg.phpt new file mode 100644 index 000000000000..a482246b9306 --- /dev/null +++ b/ext/reflection/tests/generics/composite_type_arg.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: composite type as generic argument +--FILE-- + {} +function f(Pair $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +$args = $pt->getGenericArguments(); +echo get_class($args[0]), "\n"; +echo get_class($args[1]), "\n"; +?> +--EXPECT-- +ReflectionUnionType +ReflectionIntersectionType diff --git a/ext/reflection/tests/generics/function_isgeneric.phpt b/ext/reflection/tests/generics/function_isgeneric.phpt new file mode 100644 index 000000000000..93998269ea55 --- /dev/null +++ b/ext/reflection/tests/generics/function_isgeneric.phpt @@ -0,0 +1,12 @@ +--TEST-- +Reflection: ReflectionFunction::isGeneric() +--FILE-- +(): void {} +function plain(): void {} +var_dump((new ReflectionFunction('gen'))->isGeneric()); +var_dump((new ReflectionFunction('plain'))->isGeneric()); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/reflection/tests/generics/get_generic_parameters_count.phpt b/ext/reflection/tests/generics/get_generic_parameters_count.phpt new file mode 100644 index 000000000000..2f9341e4eace --- /dev/null +++ b/ext/reflection/tests/generics/get_generic_parameters_count.phpt @@ -0,0 +1,16 @@ +--TEST-- +Reflection: getGenericParameters returns correct count +--FILE-- + {} +$ps = (new ReflectionClass('A'))->getGenericParameters(); +echo count($ps), "\n"; +echo $ps[0]->getName(), "\n"; +echo $ps[1]->getName(), "\n"; +echo $ps[2]->getName(), "\n"; +?> +--EXPECT-- +3 +X +Y +Z diff --git a/ext/reflection/tests/generics/get_generic_parameters_empty.phpt b/ext/reflection/tests/generics/get_generic_parameters_empty.phpt new file mode 100644 index 000000000000..987c0ef16e15 --- /dev/null +++ b/ext/reflection/tests/generics/get_generic_parameters_empty.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: getGenericParameters on non-generic returns empty array +--FILE-- +getGenericParameters()); +var_dump((new ReflectionFunction('f'))->getGenericParameters()); +?> +--EXPECT-- +array(0) { +} +array(0) { +} diff --git a/ext/reflection/tests/generics/method_get_generic_params.phpt b/ext/reflection/tests/generics/method_get_generic_params.phpt new file mode 100644 index 000000000000..72f019cde580 --- /dev/null +++ b/ext/reflection/tests/generics/method_get_generic_params.phpt @@ -0,0 +1,16 @@ +--TEST-- +Reflection: method type parameters separate from class type parameters +--FILE-- + { + public function f(): void {} +} +$rc = new ReflectionClass('C'); +$class_ps = $rc->getGenericParameters(); +$method_ps = $rc->getMethod('f')->getGenericParameters(); +echo count($class_ps), ": ", $class_ps[0]->getName(), "\n"; +echo count($method_ps), ": ", $method_ps[0]->getName(), "\n"; +?> +--EXPECT-- +1: X +1: U diff --git a/ext/reflection/tests/generics/method_isgeneric.phpt b/ext/reflection/tests/generics/method_isgeneric.phpt new file mode 100644 index 000000000000..ad2014844029 --- /dev/null +++ b/ext/reflection/tests/generics/method_isgeneric.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: ReflectionMethod::isGeneric() +--FILE-- +(): void {} + public function plain(): void {} +} +var_dump((new ReflectionClass('C'))->getMethod('gen')->isGeneric()); +var_dump((new ReflectionClass('C'))->getMethod('plain')->isGeneric()); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/reflection/tests/generics/named_type_get_name_erased.phpt b/ext/reflection/tests/generics/named_type_get_name_erased.phpt new file mode 100644 index 000000000000..cd89077b1547 --- /dev/null +++ b/ext/reflection/tests/generics/named_type_get_name_erased.phpt @@ -0,0 +1,13 @@ +--TEST-- +Reflection: ReflectionNamedType::getName() returns the erased name +--FILE-- + {} +function f(Box $x): Box { return $x; } +$r = new ReflectionFunction('f'); +echo $r->getParameters()[0]->getType()->getName(), "\n"; +echo $r->getReturnType()->getName(), "\n"; +?> +--EXPECT-- +Box +Box diff --git a/ext/reflection/tests/generics/named_type_has_args.phpt b/ext/reflection/tests/generics/named_type_has_args.phpt new file mode 100644 index 000000000000..d3e18b456259 --- /dev/null +++ b/ext/reflection/tests/generics/named_type_has_args.phpt @@ -0,0 +1,24 @@ +--TEST-- +Reflection: hasGenericArguments and getGenericArguments +--FILE-- + {} +function f(Box $x): Box { return $x; } +$r = new ReflectionFunction('f'); +$pt = $r->getParameters()[0]->getType(); +var_dump($pt->hasGenericArguments()); +$args = $pt->getGenericArguments(); +echo count($args), "\n"; +echo $args[0]->getName(), "\n"; + +$rt = $r->getReturnType(); +var_dump($rt->hasGenericArguments()); +$args2 = $rt->getGenericArguments(); +echo $args2[0]->getName(), "\n"; +?> +--EXPECT-- +bool(true) +1 +int +bool(true) +string diff --git a/ext/reflection/tests/generics/named_type_no_args.phpt b/ext/reflection/tests/generics/named_type_no_args.phpt new file mode 100644 index 000000000000..281e29060444 --- /dev/null +++ b/ext/reflection/tests/generics/named_type_no_args.phpt @@ -0,0 +1,12 @@ +--TEST-- +Reflection: hasGenericArguments returns false when no arguments +--FILE-- +getParameters()[0]->getType()->hasGenericArguments()); +var_dump($r->getReturnType()->hasGenericArguments()); +?> +--EXPECT-- +bool(false) +bool(false) diff --git a/ext/reflection/tests/generics/nested_args.phpt b/ext/reflection/tests/generics/nested_args.phpt new file mode 100644 index 000000000000..ad7c352a46bd --- /dev/null +++ b/ext/reflection/tests/generics/nested_args.phpt @@ -0,0 +1,17 @@ +--TEST-- +Reflection: nested generic arguments +--FILE-- + {} +function f(Box> $x): void {} +$pt = (new ReflectionFunction('f'))->getParameters()[0]->getType(); +echo $pt->getName(), "\n"; +$inner = $pt->getGenericArguments()[0]; +echo $inner->getName(), "\n"; +$inmost = $inner->getGenericArguments()[0]; +echo $inmost->getName(), "\n"; +?> +--EXPECT-- +Box +Box +int diff --git a/ext/reflection/tests/generics/parameter_constructor_throws.phpt b/ext/reflection/tests/generics/parameter_constructor_throws.phpt new file mode 100644 index 000000000000..d33d9de36190 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_constructor_throws.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: cannot instantiate ReflectionGenericTypeParameter directly +--FILE-- +newInstanceWithoutConstructor()->__construct(); + echo "no error\n"; +} catch (Throwable $e) { + echo "error: ", get_class($e), "\n"; +} +?> +--EXPECT-- +error: ReflectionException diff --git a/ext/reflection/tests/generics/parameter_get_bound.phpt b/ext/reflection/tests/generics/parameter_get_bound.phpt new file mode 100644 index 000000000000..c20c07ac2294 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_get_bound.phpt @@ -0,0 +1,18 @@ +--TEST-- +Reflection: ReflectionGenericTypeParameter::getBound() returns the bound type or throws when no bound +--FILE-- + {} +$ps = (new ReflectionClass('A'))->getGenericParameters(); +var_dump($ps[0]->hasBound()); +try { + $ps[0]->getBound(); +} catch (ReflectionException $e) { + echo $e->getMessage(), "\n"; +} +echo $ps[1]->getBound()->getName(), "\n"; +?> +--EXPECT-- +bool(false) +Type parameter X has no bound +object diff --git a/ext/reflection/tests/generics/parameter_get_declaring_class.phpt b/ext/reflection/tests/generics/parameter_get_declaring_class.phpt new file mode 100644 index 000000000000..006bb99922d6 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_get_declaring_class.phpt @@ -0,0 +1,13 @@ +--TEST-- +Reflection: getDeclaringEntity() returns the declaring class +--FILE-- + {} +$p = (new ReflectionClass('A'))->getGenericParameters()[0]; +$de = $p->getDeclaringEntity(); +echo get_class($de), "\n"; +echo $de->getName(), "\n"; +?> +--EXPECT-- +ReflectionClass +A diff --git a/ext/reflection/tests/generics/parameter_get_declaring_function.phpt b/ext/reflection/tests/generics/parameter_get_declaring_function.phpt new file mode 100644 index 000000000000..3ed7cfdc7625 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_get_declaring_function.phpt @@ -0,0 +1,13 @@ +--TEST-- +Reflection: getDeclaringEntity() returns the declaring function +--FILE-- +(): void {} +$p = (new ReflectionFunction('f'))->getGenericParameters()[0]; +$de = $p->getDeclaringEntity(); +echo get_class($de), "\n"; +echo $de->getName(), "\n"; +?> +--EXPECT-- +ReflectionFunction +f diff --git a/ext/reflection/tests/generics/parameter_get_position.phpt b/ext/reflection/tests/generics/parameter_get_position.phpt new file mode 100644 index 000000000000..babf13acbd9c --- /dev/null +++ b/ext/reflection/tests/generics/parameter_get_position.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: ReflectionGenericTypeParameter::getPosition() +--FILE-- + {} +$ps = (new ReflectionClass('A'))->getGenericParameters(); +foreach ($ps as $p) { + echo $p->getName(), ": ", $p->getPosition(), "\n"; +} +?> +--EXPECT-- +X: 0 +Y: 1 +Z: 2 diff --git a/ext/reflection/tests/generics/parameter_get_variance.phpt b/ext/reflection/tests/generics/parameter_get_variance.phpt new file mode 100644 index 000000000000..61d39c1d802c --- /dev/null +++ b/ext/reflection/tests/generics/parameter_get_variance.phpt @@ -0,0 +1,13 @@ +--TEST-- +Reflection: ReflectionGenericTypeParameter::getVariance() +--FILE-- + {} +foreach ((new ReflectionClass('A'))->getGenericParameters() as $p) { + echo $p->getName(), ": ", $p->getVariance()->name, "\n"; +} +?> +--EXPECT-- +X: Covariant +Y: Contravariant +Z: Invariant diff --git a/ext/reflection/tests/generics/parameter_has_bound.phpt b/ext/reflection/tests/generics/parameter_has_bound.phpt new file mode 100644 index 000000000000..2e1732073de3 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_has_bound.phpt @@ -0,0 +1,12 @@ +--TEST-- +Reflection: ReflectionGenericTypeParameter::hasBound() +--FILE-- + {} +$ps = (new ReflectionClass('A'))->getGenericParameters(); +echo $ps[0]->getName(), ": ", var_export($ps[0]->hasBound(), true), "\n"; +echo $ps[1]->getName(), ": ", var_export($ps[1]->hasBound(), true), "\n"; +?> +--EXPECT-- +X: false +Y: true diff --git a/ext/reflection/tests/generics/parameter_has_default.phpt b/ext/reflection/tests/generics/parameter_has_default.phpt new file mode 100644 index 000000000000..6e09af852391 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_has_default.phpt @@ -0,0 +1,20 @@ +--TEST-- +Reflection: hasDefault() and getDefault() +--FILE-- + {} +$ps = (new ReflectionClass('A'))->getGenericParameters(); +var_dump($ps[0]->hasDefault()); +try { + $ps[0]->getDefault(); +} catch (ReflectionException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($ps[1]->hasDefault()); +echo $ps[1]->getDefault()->getName(), "\n"; +?> +--EXPECT-- +bool(false) +Type parameter X has no default +bool(true) +int diff --git a/ext/reflection/tests/generics/parameter_to_string.phpt b/ext/reflection/tests/generics/parameter_to_string.phpt new file mode 100644 index 000000000000..60c441816533 --- /dev/null +++ b/ext/reflection/tests/generics/parameter_to_string.phpt @@ -0,0 +1,14 @@ +--TEST-- +Reflection: __toString on type parameter +--FILE-- + {} +foreach ((new ReflectionClass('A'))->getGenericParameters() as $p) { + echo $p, "\n"; +} +?> +--EXPECT-- +X +Y : object +Z = int ++W : object = stdClass diff --git a/ext/reflection/tests/generics/property_type_args.phpt b/ext/reflection/tests/generics/property_type_args.phpt new file mode 100644 index 000000000000..786200f43bfa --- /dev/null +++ b/ext/reflection/tests/generics/property_type_args.phpt @@ -0,0 +1,17 @@ +--TEST-- +Reflection: property type arguments via Reflection +--FILE-- + {} +class Holder { + public Box $b; +} +$rt = (new ReflectionClass('Holder'))->getProperty('b')->getType(); +echo $rt->getName(), "\n"; +echo count($rt->getGenericArguments()), "\n"; +echo $rt->getGenericArguments()[0]->getName(), "\n"; +?> +--EXPECT-- +Box +1 +int diff --git a/ext/reflection/tests/generics/type_parameter_ref_get_param.phpt b/ext/reflection/tests/generics/type_parameter_ref_get_param.phpt new file mode 100644 index 000000000000..642a5b7a79d3 --- /dev/null +++ b/ext/reflection/tests/generics/type_parameter_ref_get_param.phpt @@ -0,0 +1,19 @@ +--TEST-- +Reflection: ReflectionTypeParameterReference::getTypeParameter() +--FILE-- + {} +class Outer { + public Box $b; +} +$rt = (new ReflectionClass('Outer'))->getProperty('b')->getType(); +$ref = $rt->getGenericArguments()[0]; +$param = $ref->getTypeParameter(); +echo get_class($param), "\n"; +echo $param->getName(), "\n"; +echo $param->getDeclaringEntity()->getName(), "\n"; +?> +--EXPECT-- +ReflectionGenericTypeParameter +T +Outer diff --git a/ext/reflection/tests/generics/type_parameter_reference.phpt b/ext/reflection/tests/generics/type_parameter_reference.phpt new file mode 100644 index 000000000000..3ff5ae7d50e4 --- /dev/null +++ b/ext/reflection/tests/generics/type_parameter_reference.phpt @@ -0,0 +1,21 @@ +--TEST-- +Reflection: ReflectionTypeParameterReference appears inside getGenericArguments +--FILE-- + {} +class Outer { + public Box $b; +} +$rp = (new ReflectionClass('Outer'))->getProperty('b'); +$rt = $rp->getType(); +echo $rt->getName(), "\n"; +$args = $rt->getGenericArguments(); +echo count($args), "\n"; +echo get_class($args[0]), "\n"; +echo $args[0]->getName(), "\n"; +?> +--EXPECT-- +Box +1 +ReflectionTypeParameterReference +T diff --git a/ext/reflection/tests/generics/variance_enum.phpt b/ext/reflection/tests/generics/variance_enum.phpt new file mode 100644 index 000000000000..920c955d0d43 --- /dev/null +++ b/ext/reflection/tests/generics/variance_enum.phpt @@ -0,0 +1,12 @@ +--TEST-- +Reflection: ReflectionGenericVariance enum cases +--FILE-- +value, "\n"; +echo ReflectionGenericVariance::Covariant->value, "\n"; +echo ReflectionGenericVariance::Contravariant->value, "\n"; +?> +--EXPECT-- +0 +1 +2 diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index 87b15b8bb345..38f5c5910876 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -166,6 +166,7 @@ char *get_token_type_name(int token_type) case T_DOLLAR_OPEN_CURLY_BRACES: return "T_DOLLAR_OPEN_CURLY_BRACES"; case T_CURLY_OPEN: return "T_CURLY_OPEN"; case T_PAAMAYIM_NEKUDOTAYIM: return "T_DOUBLE_COLON"; + case T_TURBOFISH: return "T_TURBOFISH"; case T_NS_SEPARATOR: return "T_NS_SEPARATOR"; case T_ELLIPSIS: return "T_ELLIPSIS"; case T_COALESCE: return "T_COALESCE"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index 57c8edad8acb..e3dffb5e8979 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -717,6 +717,11 @@ * @cvalue T_PAAMAYIM_NEKUDOTAYIM */ const T_PAAMAYIM_NEKUDOTAYIM = UNKNOWN; +/** + * @var int + * @cvalue T_TURBOFISH + */ +const T_TURBOFISH = UNKNOWN; /** * @var int * @cvalue T_NS_SEPARATOR diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index b82842ede0f1..4c5f81240ae5 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit tokenizer_data.stub.php instead. - * Stub hash: c5235344b7c651d27c2c33c90696a418a9c96837 */ + * Stub hash: 34e6a9cb933770d8434820f2e7b567a05ed95c9e */ static void register_tokenizer_data_symbols(int module_number) { @@ -146,6 +146,7 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_DOLLAR_OPEN_CURLY_BRACES", T_DOLLAR_OPEN_CURLY_BRACES, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CURLY_OPEN", T_CURLY_OPEN, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_PAAMAYIM_NEKUDOTAYIM", T_PAAMAYIM_NEKUDOTAYIM, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_TURBOFISH", T_TURBOFISH, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_NS_SEPARATOR", T_NS_SEPARATOR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_ELLIPSIS", T_ELLIPSIS, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_COALESCE", T_COALESCE, CONST_PERSISTENT);