@@ -3459,13 +3459,31 @@ static void flatten_trait_into_class(PHPLSPContext *ctx, CBMTypeRegistry *reg, c
34593459 t = lookup_type_with_project (ctx , trait_qn );
34603460 const char * canonical_trait_qn = t ? t -> qualified_name : trait_qn ;
34613461
3462+ /* A trait cannot meaningfully flatten into itself. PHP itself rejects
3463+ * `trait T { use T; }` ("Trait T cannot use itself"), and an aliased
3464+ * `use X as Y; use Y;` can resolve back onto the enclosing trait by short
3465+ * name. Without this guard the loop below copies the trait's own methods
3466+ * back onto it with receiver_type == canonical_trait_qn; because each copy
3467+ * then re-matches the loop's filter while cbm_registry_add_func() keeps
3468+ * growing reg->func_count, the iteration never terminates — it arena-
3469+ * allocates a fresh method every pass until the process exhausts all
3470+ * memory (observed: 40 GB+, freezing the host). See regression test
3471+ * phplsp_trait_self_use_terminates. */
3472+ if (strcmp (class_qn , canonical_trait_qn ) == 0 )
3473+ return ;
3474+
34623475 /* Iterate registry funcs whose receiver_type is the trait.
34633476 *
34643477 * Self-substitution: when the trait's method has a return type that
34653478 * names the trait itself (e.g. `tap(): self` registered as
34663479 * NAMED(trait_qn)), rewrite it to NAMED(using_class_qn) so chains
3467- * like `$c->tap()->classMethod()` resolve correctly. */
3468- for (int i = 0 ; i < reg -> func_count ; i ++ ) {
3480+ * like `$c->tap()->classMethod()` resolve correctly.
3481+ *
3482+ * Snapshot the count before iterating: cbm_registry_add_func() below
3483+ * appends to reg->funcs, so the loop must never visit entries it adds
3484+ * during iteration (a mutate-while-iterating hazard). */
3485+ const int func_count_before = reg -> func_count ;
3486+ for (int i = 0 ; i < func_count_before ; i ++ ) {
34693487 const CBMRegisteredFunc * src = & reg -> funcs [i ];
34703488 if (!src -> receiver_type || strcmp (src -> receiver_type , canonical_trait_qn ) != 0 ) {
34713489 continue ;
0 commit comments