Skip to content

zend_ast_tree_copy cannot copy closure with a default argument in zend_ast_process hook #19155

@KaseyJenkins

Description

@KaseyJenkins

Description

zend_ast_copy seems to be an API function to go when one wants to copy, let's say, function parameters.

Under the hood the function of interest is zend_ast_tree_copy.

We use the zend_ast_process hook to modify the AST after it is parsed and created.

So far zend_ast_tree_copy seems to have behaved correctly in such cases, however, with the advent of PHP 8.5.0alpha1 in general and 'Support for Closures in constant expressions' in particular it appears to no longer properly copy AST nodes.

The culprit is ostensibly ZEND_AST_OP_ARRAY, which does not seem to exist at the time of the zend_ast_process hook and appears only after zend_compile_const_expr -> zend_compile_const_expr_closure, which come later on:

zend_const_expr_to_zval (result=result@entry=0x7fffffff9d68, ast_ptr=ast_ptr@entry=0x7ffff448c1f0, allow_dynamic=allow_dynamic@entry=true)
    at php-src-php-8.5.0alpha1/Zend/zend_compile.c:11517
(gdb) p **ast_ptr
{kind = 69, attr = 0, lineno = 25, child = {0x1000000019}} // ZEND_AST_CLOSURE
zend_compile_const_expr(ast_ptr, &context);
p **ast_ptr
{kind = 66, attr = 0, lineno = 25, child = {0x7ffff4405320}} // ZEND_AST_OP_ARRAY
ZVAL_AST(&ast_zv, zend_ast_copy(*ast_ptr));

When one tries to use zend_ast_copy (or rather a function mimicking zend_ast_tree_copy's functionality) on a function parameter that is a Closure with a default argument it seems to fail to copy the ZEND_AST_CLOSURE node since zend_ast_get_num_children will detect it as zend_ast_is_special (or it will just return 0 according to ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT), hence no children will be copied (not to mention that the childless node created will be of (zend_ast*) type and not (zend_ast_decl*).

How should one copy function parameters (especially Closures with a default argument) when it comes to PHP 8.5 zend_ast_process hook? Should one no longer use zend_ast_copy (or functions mimicking zend_ast_tree_copy's functionality) or it was never intended to be used in such a way?

Thanks.

P.S. I reckon a partial answer is contained within the following statement:
For constant scalar expressions AST nodes need to be preserved after compilation. For this purpose they need to be copied from the arena into ZMM allocated memory. This is accomplished using the zend_ast_copy function.

PHP Version

PHP 8.5.0alpha1 (cli) (built: Jul  4 2025 10:17:12) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.0-dev, Copyright (c) Zend Technologies

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions