Skip to content

Partials v2 gen#22

Draft
arnaud-lb wants to merge 435 commits intomasterfrom
partials-v2-gen
Draft

Partials v2 gen#22
arnaud-lb wants to merge 435 commits intomasterfrom
partials-v2-gen

Conversation

@arnaud-lb
Copy link
Copy Markdown
Owner

@arnaud-lb arnaud-lb commented Sep 8, 2025

PFAs are created by generating the AST of a Closure at runtime and compiling it. The compiled op_array can be cached similarly to linked classes.

About caching

There are three levels of caching:

  • The global opcache (ZCG(hash))
  • EG(partial_function_application_cache)
  • The caller's run time cache

The cache key in ZCG(hash) is composed of the address of the declaring opline, the address of the function being called, and a prefix.

EG(partial_function_application_cache) is used when opcache is disabled, or when the PFA can not be cached in SHM (e.g. because the actual function itself is not cached). This is mostly useful in CLI (opcache is disabled by default) for polymorphic PFAs (inline cache less useful).

TODO

  • Improve PFA caching when opcache is not enabled, and fix cases that are broken when opcache is not enabled
  • Reorganize tests
    • Update RFC-example tests
  • PFAs in constant expressions
  • bindTo() is a no-op for PFA of closures
  • assert(), compact(), extract(), func_get_arg(), get_defined_vars()
  • Pre-bound literal optimization
  • Test preloading
  • Test race between fetch pfa / compile pfa
  • Test hooks
  • ACC_NEVER_CACHE
  • UNKNOWN default value handling

@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 3 times, most recently from 7062e66 to f65bf45 Compare September 13, 2025 10:37
Comment thread Zend/tests/partial_application/errors_001.phpt
Comment thread Zend/tests/partial_application/clone.phpt Outdated
Comment thread Zend/tests/partial_application/compile_errors_001.phpt Outdated
@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 2 times, most recently from d820a4f to 75dbd22 Compare November 3, 2025 17:04
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get a test case where the function has optional arguments and an incorrect number is given (just to see the error message once).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I just realize that the error messages for userland and native functions are already inconsistent with each other:

https://3v4l.org/3G8EO

Comment thread Zend/tests/partial_application/variation_invoke_001.phpt Outdated
Comment thread Zend/tests/partial_application/variation_invoke_001.phpt Outdated
Comment thread Zend/tests/partial_application/variation_nocall_002.phpt Outdated
Comment thread Zend/zend_ast.c Outdated
Comment thread Zend/zend_ast.c Outdated
Comment thread Zend/zend_ast.c Outdated
@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 2 times, most recently from bf4b4f6 to b753b0c Compare November 3, 2025 18:55
Comment thread Zend/tests/partial_application/references_001.phpt Outdated
Comment thread Zend/tests/partial_application/this_001.phpt Outdated
Comment thread Zend/tests/partial_application/variation_ex_001.phpt Outdated
Comment thread Zend/tests/partial_application/superfluous_args_are_forwarded.phpt Outdated
Comment thread Zend/tests/partial_application/superfluous_args_are_forwarded.phpt Outdated
Comment thread Zend/zend_object_handlers.c Outdated
Comment thread Zend/zend_execute.c
Comment on lines +1236 to +1241
ZEND_API bool zend_check_type_ex(
const zend_type *type, zval *arg, zend_class_entry *scope,
bool is_return_type, bool is_internal)
{
return zend_check_type(type, arg, scope, is_return_type, is_internal);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this? Wouldn't it work to expose zend_check_type() directly?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that easy, as the function is always_inline, and uses always_inline functions from this file as well. zend_check_type() is critical, I didn't want to take the risk of performance regression.

So we have the following options:

  • Move the function and a bunch of functions it uses to a header file (zend_check_type, zend_check_type_slow, zend_check_intersection_type_from_list, probably others), so we don't lose inline-ability
  • Make the function extern inline, but we don't do that (there was a thread about that in a PR).
  • Export a wrapper, like I did here

I will think about about the other options

Comment thread Zend/tests/partial_application/reflection_005.phpt Outdated
Comment thread ext/opcache/ZendAccelerator.c Outdated
@arnaud-lb arnaud-lb changed the base branch from master to tmp-master December 10, 2025 17:35
@arnaud-lb arnaud-lb changed the base branch from tmp-master to master December 10, 2025 17:35
Comment thread Zend/tests/partial_application/fuzz_003.phpt Outdated
Comment thread Zend/tests/partial_application/fuzz_004.phpt Outdated
Comment thread Zend/tests/partial_application/reflection_004.phpt Outdated
Comment thread Zend/tests/partial_application/rfc_examples_010.phpt Outdated
@arnaud-lb arnaud-lb changed the base branch from master to tmp-master December 16, 2025 12:00
@arnaud-lb arnaud-lb changed the base branch from tmp-master to master December 16, 2025 12:00
@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 2 times, most recently from 0b7a4a5 to 2cf2ae1 Compare January 8, 2026 11:32
@arnaud-lb arnaud-lb force-pushed the partials-v2-gen branch 4 times, most recently from a3a4549 to 8514af4 Compare January 19, 2026 12:18
* PHP-8.4:
  Fix phpGH-21698: memory leak in ZipArchive::addGlob on early returns.
iluuu1994 and others added 29 commits April 21, 2026 14:47
* PHP-8.5:
  [skip ci] Sort paths-ignore and remove cirrus
  [skip ci] Tweak paths-ignore
* PHP-8.2:
  [skip ci] Mark curl/bug71523.phpt as online test
* PHP-8.3:
  [skip ci] Mark curl/bug71523.phpt as online test
* PHP-8.4:
  [skip ci] Mark curl/bug71523.phpt as online test
* PHP-8.5:
  [skip ci] Mark curl/bug71523.phpt as online test
…e` checks (php#21820)

The phar_archive_data->alias field is only ever assigned a newly allocated char* or NULL, as such it can never be equal to the phar_archive_data->fname field, thus those comparisons are just confusing.
* PHP-8.4:
  PHP 8.4 is now for PHP 8.4.22-dev
* PHP-8.5:
  PHP 8.4 is now for PHP 8.4.22-dev
Clang supports __has_attribute(cold) but not __attribute__((optimize(...))). The cold and optimize branches need to be split.
…ntersection type list (php#21717)

When generating a union or intersection type list with multiple class
types, the variable holding each zend_string* was declared using
toVarEscapedName() (backslashes replaced by underscores), but the
subsequent ZEND_TYPE_INIT_CLASS() reference used toEscapedName()
(backslashes escaped as \\), producing an invalid C identifier.

Backported to PHP 8.5.
* PHP-8.5:
  gen_stub: fix invalid C variable name for namespaced types in union/intersection type list (php#21717)
* PHP-8.5:
  PHP-8.5 is now for PHP 8.5.7-dev
This char* is derived from the fname char* field.
As this is derived from a live char* pointer where we don't have the ownership.
… on DOM-built documents.

For programmatically built documents (e.g. createElementNS), namespaces
live on node->ns without corresponding xmlns attributes. Create temporary
synthetic nsDef entries so C14N can find them, complementing the existing
xmlns attribute to nsDef conversion.

close phpGH-21561
* PHP-8.4:
  Fix phpGH-21544: Dom\XMLDocument::C14N() drops namespace declarations on DOM-built documents.
* PHP-8.5:
  Fix phpGH-21544: Dom\XMLDocument::C14N() drops namespace declarations on DOM-built documents.
Change zend_function_entry.flags to a uint64_t to that both ZEND_ACC_ and
ZEND_ACC2_ flags can be represented.

Introduce ZEND_FENTRY_FLAGS(flags, flags2) to pass ZEND_ACC2_ flags to
ZEND_RAW_FENTRY(), ZEND_FENTRY().

Source-level backwards compatibility is maintained, as passing raw ZEND_ACC_
flags to ZEND_RAW_FENTRY(), ZEND_FENTRY() still works.
Functions that use zend_forbid_dynamic_call() must be flagged with
ZEND_ACC2_FORBID_DYN_CALLS. In stubs, this is done by using
@forbid-dynamic-calls.

To ensure consistency, we assert that the flag exists in
zend_forbid_dynamic_call(), and we assert that flagged functions thrown after a
dynamic call.

Closes phpGH-21818
RFC: https://wiki.php.net/rfc/partial_function_application_v2

Co-authored-by: Joe Watkins <krakjoe@php.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.