@@ -438,10 +438,11 @@ void zend_init_compiler_data_structures(void) /* {{{ */
438438}
439439/* }}} */
440440
441- static void zend_generic_scope_push (zend_generic_parameter_list * params ) /* {{{ */
441+ static void zend_generic_scope_push (zend_generic_parameter_list * params , uint8_t origin ) /* {{{ */
442442{
443443 zend_generic_scope_entry * entry = emalloc (sizeof (zend_generic_scope_entry ));
444444 entry -> params = params ;
445+ entry -> origin = origin ;
445446 entry -> outer = CG (generic_scope );
446447 CG (generic_scope ) = entry ;
447448}
@@ -456,12 +457,15 @@ static void zend_generic_scope_pop(void) /* {{{ */
456457}
457458/* }}} */
458459
459- static zend_generic_parameter * zend_generic_lookup (const zend_string * name ) /* {{{ */
460+ static zend_generic_parameter * zend_generic_lookup_full (
461+ const zend_string * name , uint8_t * origin_out , uint32_t * index_out ) /* {{{ */
460462{
461463 for (zend_generic_scope_entry * e = CG (generic_scope ); e ; e = e -> outer ) {
462464 zend_generic_parameter_list * params = e -> params ;
463465 for (uint32_t i = 0 ; i < params -> count ; i ++ ) {
464466 if (zend_string_equals (params -> parameters [i ].name , name )) {
467+ if (origin_out ) * origin_out = e -> origin ;
468+ if (index_out ) * index_out = i ;
465469 return & params -> parameters [i ];
466470 }
467471 }
@@ -470,6 +474,12 @@ static zend_generic_parameter *zend_generic_lookup(const zend_string *name) /* {
470474}
471475/* }}} */
472476
477+ static zend_generic_parameter * zend_generic_lookup (const zend_string * name ) /* {{{ */
478+ {
479+ return zend_generic_lookup_full (name , NULL , NULL );
480+ }
481+ /* }}} */
482+
473483static zend_generic_parameter_list * zend_compile_generic_type_parameter_list (zend_ast * list_ast ) /* {{{ */
474484{
475485 if (!list_ast ) {
@@ -509,6 +519,139 @@ static zend_generic_parameter_list *zend_compile_generic_type_parameter_list(zen
509519}
510520/* }}} */
511521
522+ /* Returns true if the AST contains any generic-aware constructs (a type-parameter
523+ * reference or a named type with type arguments) that need pre-erasure capture. */
524+ static bool zend_type_ast_has_generic_content (zend_ast * ast )
525+ {
526+ if (!ast ) return false;
527+ zend_ast_attr orig = ast -> attr ;
528+ ast -> attr &= ~ZEND_TYPE_NULLABLE ;
529+ bool result = false;
530+
531+ if (ast -> kind == ZEND_AST_GENERIC_NAMED_TYPE ) {
532+ result = true;
533+ } else if (ast -> kind == ZEND_AST_TYPE_UNION || ast -> kind == ZEND_AST_TYPE_INTERSECTION ) {
534+ zend_ast_list * list = zend_ast_get_list (ast );
535+ for (uint32_t i = 0 ; i < list -> children ; i ++ ) {
536+ if (zend_type_ast_has_generic_content (list -> child [i ])) {
537+ result = true;
538+ break ;
539+ }
540+ }
541+ } else if (ast -> kind == ZEND_AST_ZVAL ) {
542+ if ((ast -> attr & ZEND_NAME_NOT_FQ ) == ZEND_NAME_NOT_FQ ) {
543+ const zval * zv = zend_ast_get_zval (ast );
544+ if (Z_TYPE_P (zv ) == IS_STRING && zend_generic_lookup (Z_STR_P (zv ))) {
545+ result = true;
546+ }
547+ }
548+ }
549+
550+ ast -> attr = orig ;
551+ return result ;
552+ }
553+
554+ /* Build a pre-erasure zend_type from a type-expression AST. The returned type
555+ * may carry type-parameter references (TYPE_PARAMETER bit) or named-with-args
556+ * payloads (NAMED_WITH_ARGS bit). Used only for the side table; the runtime
557+ * never sees this form. */
558+ static zend_type zend_compile_pre_erasure_typename (zend_ast * ast )
559+ {
560+ bool is_marked_nullable = ast -> attr & ZEND_TYPE_NULLABLE ;
561+ zend_ast_attr orig_attr = ast -> attr ;
562+ if (is_marked_nullable ) {
563+ ast -> attr &= ~ZEND_TYPE_NULLABLE ;
564+ }
565+
566+ zend_type result = ZEND_TYPE_INIT_NONE (0 );
567+
568+ if (ast -> kind == ZEND_AST_TYPE_UNION || ast -> kind == ZEND_AST_TYPE_INTERSECTION ) {
569+ zend_ast_list * list = zend_ast_get_list (ast );
570+ zend_type_list * type_list = emalloc (ZEND_TYPE_LIST_SIZE (list -> children ));
571+ type_list -> num_types = list -> children ;
572+ for (uint32_t i = 0 ; i < list -> children ; i ++ ) {
573+ type_list -> types [i ] = zend_compile_pre_erasure_typename (list -> child [i ]);
574+ }
575+ ZEND_TYPE_SET_PTR (result , type_list );
576+ ZEND_TYPE_FULL_MASK (result ) |= _ZEND_TYPE_LIST_BIT |
577+ (ast -> kind == ZEND_AST_TYPE_UNION ? _ZEND_TYPE_UNION_BIT : _ZEND_TYPE_INTERSECTION_BIT );
578+ } else if (ast -> kind == ZEND_AST_GENERIC_NAMED_TYPE ) {
579+ zend_ast * name_ast = ast -> child [0 ];
580+ zend_ast_list * args_list = zend_ast_get_list (ast -> child [1 ]);
581+ zend_type_named_with_args * payload = emalloc (ZEND_TYPE_NAMED_WITH_ARGS_SIZE (args_list -> children ));
582+ payload -> name = zend_string_copy (zval_make_interned_string (zend_ast_get_zval (name_ast )));
583+ payload -> name_attr = name_ast -> attr ;
584+ payload -> count = args_list -> children ;
585+ for (uint32_t i = 0 ; i < args_list -> children ; i ++ ) {
586+ payload -> args [i ] = zend_compile_pre_erasure_typename (args_list -> child [i ]);
587+ }
588+ ZEND_TYPE_SET_PTR (result , payload );
589+ ZEND_TYPE_FULL_MASK (result ) |= _ZEND_TYPE_NAMED_WITH_ARGS_BIT ;
590+ } else if (ast -> kind == ZEND_AST_TYPE ) {
591+ /* Builtin pseudo-type: same as erased. */
592+ result = (zend_type ) ZEND_TYPE_INIT_CODE (ast -> attr , 0 , 0 );
593+ } else if (ast -> kind == ZEND_AST_ZVAL ) {
594+ uint8_t origin ;
595+ uint32_t index ;
596+ zend_generic_parameter * param = NULL ;
597+ if ((ast -> attr & ZEND_NAME_NOT_FQ ) == ZEND_NAME_NOT_FQ ) {
598+ const zval * zv = zend_ast_get_zval (ast );
599+ if (Z_TYPE_P (zv ) == IS_STRING ) {
600+ param = zend_generic_lookup_full (Z_STR_P (zv ), & origin , & index );
601+ }
602+ }
603+ if (param ) {
604+ zend_type_parameter_ref * ref = emalloc (sizeof (* ref ));
605+ ref -> name = zend_string_copy (param -> name );
606+ ref -> index = index ;
607+ ref -> origin = origin ;
608+ ZEND_TYPE_SET_PTR (result , ref );
609+ ZEND_TYPE_FULL_MASK (result ) |= _ZEND_TYPE_TYPE_PARAMETER_BIT ;
610+ } else {
611+ zend_string * name = zval_make_interned_string (zend_ast_get_zval (ast ));
612+ uint8_t code = zend_lookup_builtin_type_by_name (name );
613+ if (code != 0 ) {
614+ if (code == IS_ITERABLE ) {
615+ zend_type iterable = (zend_type ) ZEND_TYPE_INIT_CLASS_MASK (
616+ ZSTR_KNOWN (ZEND_STR_TRAVERSABLE ),
617+ (MAY_BE_ARRAY |_ZEND_TYPE_ITERABLE_BIT ));
618+ result = iterable ;
619+ } else {
620+ result = (zend_type ) ZEND_TYPE_INIT_CODE (code , 0 , 0 );
621+ }
622+ } else {
623+ zend_string_addref (name );
624+ result = (zend_type ) ZEND_TYPE_INIT_CLASS (name , 0 , 0 );
625+ }
626+ }
627+ } else {
628+ ZEND_UNREACHABLE ();
629+ }
630+
631+ if (is_marked_nullable ) {
632+ ZEND_TYPE_FULL_MASK (result ) |= _ZEND_TYPE_NULLABLE_BIT ;
633+ }
634+ ast -> attr = orig_attr ;
635+ return result ;
636+ }
637+
638+ /* Ensure op_array->generic_types or ce->generic_types is allocated. */
639+ static zend_generic_type_table * zend_generic_get_or_create_op_array_table (zend_op_array * op_array )
640+ {
641+ if (!op_array -> generic_types ) {
642+ op_array -> generic_types = zend_generic_type_table_alloc ();
643+ }
644+ return op_array -> generic_types ;
645+ }
646+
647+ static zend_generic_type_table * zend_generic_get_or_create_class_table (zend_class_entry * ce )
648+ {
649+ if (!ce -> generic_types ) {
650+ ce -> generic_types = zend_generic_type_table_alloc ();
651+ }
652+ return ce -> generic_types ;
653+ }
654+
512655static zend_generic_parameter * zend_generic_lookup_name (const zend_ast * ast ) /* {{{ */
513656{
514657 if (!CG (generic_scope ) || ast -> kind != ZEND_AST_ZVAL ) {
@@ -7499,7 +7642,22 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
74997642 zend_generic_parameter * param = zend_generic_lookup_name (ast );
75007643 if (param ) {
75017644 if (ZEND_TYPE_IS_SET (param -> bound )) {
7502- return param -> bound ;
7645+ zend_type result = param -> bound ;
7646+ if (ZEND_TYPE_HAS_NAME (result )) {
7647+ zend_string_addref (ZEND_TYPE_NAME (result ));
7648+ } else if (ZEND_TYPE_HAS_LIST (result )) {
7649+ zend_type_list * orig_list = ZEND_TYPE_LIST (result );
7650+ size_t list_size = ZEND_TYPE_LIST_SIZE (orig_list -> num_types );
7651+ zend_type_list * copy = emalloc (list_size );
7652+ memcpy (copy , orig_list , list_size );
7653+ for (uint32_t i = 0 ; i < copy -> num_types ; i ++ ) {
7654+ if (ZEND_TYPE_HAS_NAME (copy -> types [i ])) {
7655+ zend_string_addref (ZEND_TYPE_NAME (copy -> types [i ]));
7656+ }
7657+ }
7658+ ZEND_TYPE_SET_PTR (result , copy );
7659+ }
7660+ return result ;
75037661 }
75047662
75057663 return (zend_type ) ZEND_TYPE_INIT_MASK (MAY_BE_ANY );
@@ -8163,6 +8321,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
81638321 arg_infos -> type = zend_compile_typename (return_type_ast );
81648322 ZEND_TYPE_FULL_MASK (arg_infos -> type ) |= _ZEND_ARG_INFO_FLAGS (
81658323 (op_array -> fn_flags & ZEND_ACC_RETURN_REFERENCE ) != 0 , /* is_variadic */ 0 , /* is_tentative */ 0 );
8324+ if (zend_type_ast_has_generic_content (return_type_ast )) {
8325+ zend_type pre = zend_compile_pre_erasure_typename (return_type_ast );
8326+ zend_generic_type_table_set_return (
8327+ zend_generic_get_or_create_op_array_table (op_array ), pre );
8328+ }
81668329 } else {
81678330 arg_infos -> type = (zend_type ) ZEND_TYPE_INIT_CODE (fallback_return_type , 0 , 0 );
81688331 }
@@ -8281,6 +8444,14 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
82818444
82828445 op_array -> fn_flags |= ZEND_ACC_HAS_TYPE_HINTS ;
82838446 arg_info -> type = zend_compile_typename_ex (type_ast , force_nullable , & forced_allow_nullable );
8447+ if (zend_type_ast_has_generic_content (type_ast )) {
8448+ zend_type pre = zend_compile_pre_erasure_typename (type_ast );
8449+ if (force_nullable || forced_allow_nullable ) {
8450+ ZEND_TYPE_FULL_MASK (pre ) |= _ZEND_TYPE_NULLABLE_BIT ;
8451+ }
8452+ zend_generic_type_table_set_parameter (
8453+ zend_generic_get_or_create_op_array_table (op_array ), i , pre );
8454+ }
82848455 if (forced_allow_nullable ) {
82858456 zend_string * func_name = get_function_or_method_name ((zend_function * ) op_array );
82868457 zend_error (E_DEPRECATED ,
@@ -8379,6 +8550,11 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
83798550 zend_type type = ZEND_TYPE_INIT_NONE (0 );
83808551 if (type_ast ) {
83818552 type = zend_compile_typename (type_ast );
8553+ if (zend_type_ast_has_generic_content (type_ast )) {
8554+ zend_type pre = zend_compile_pre_erasure_typename (type_ast );
8555+ zend_generic_type_table_set_property (
8556+ zend_generic_get_or_create_class_table (scope ), name , pre );
8557+ }
83828558 }
83838559
83848560 /* Don't give the property an explicit default value. For typed properties this means
@@ -8988,7 +9164,7 @@ static zend_op_array *zend_compile_func_decl_ex(
89889164 * See GENERICS.md §6.9. */
89899165 if (generic_params_ast ) {
89909166 op_array -> generic_parameters = zend_compile_generic_type_parameter_list (generic_params_ast );
8991- zend_generic_scope_push (op_array -> generic_parameters );
9167+ zend_generic_scope_push (op_array -> generic_parameters , /* origin */ 1 );
89929168 generic_scope_pushed = true;
89939169 }
89949170
@@ -9341,6 +9517,11 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
93419517
93429518 if (type_ast ) {
93439519 type = zend_compile_typename (type_ast );
9520+ if (zend_type_ast_has_generic_content (type_ast )) {
9521+ zend_type pre = zend_compile_pre_erasure_typename (type_ast );
9522+ zend_generic_type_table_set_property (
9523+ zend_generic_get_or_create_class_table (ce ), name , pre );
9524+ }
93449525
93459526 if (ZEND_TYPE_FULL_MASK (type ) & (MAY_BE_VOID |MAY_BE_NEVER |MAY_BE_CALLABLE )) {
93469527 zend_string * str = zend_type_to_string (type );
@@ -9772,7 +9953,7 @@ static void zend_compile_class_decl(znode *result, const zend_ast *ast, bool top
97729953 if (decl -> child [5 ]) {
97739954 ZEND_ASSERT (!(decl -> flags & ZEND_ACC_ANON_CLASS ));
97749955 ce -> generic_parameters = zend_compile_generic_type_parameter_list (decl -> child [5 ]);
9775- zend_generic_scope_push (ce -> generic_parameters );
9956+ zend_generic_scope_push (ce -> generic_parameters , /* origin */ 0 );
97769957 }
97779958
97789959 if (extends_ast ) {
0 commit comments