-
Notifications
You must be signed in to change notification settings - Fork 7.8k
[RFC] Add support for attributes on compile-time constants #16952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There are going to be some memory issues that CI will report, I couldn't figure those out, asked for help on the mailing list, https://news-web.php.net/php.internals/126065 |
So there are also opcache failures, not just the failures I had locally - I guess a data op isn't the right way to send a pointer to the attributes from the compilation to the runtime - I'll see if I can have it send the raw AST and delay the attribute compilation until runtime |
So for the life of me, I can't figure out why I'm unable to get the cleanup in free_zend_constant() to run properly - I added debugging code diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 8b92650816..ade2efb618 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -41,6 +41,8 @@ void free_zend_constant(zval *zv)
{
zend_constant *c = Z_PTR_P(zv);
+ fprintf(stderr, "Freeing constant %s\n", ZSTR_VAL(c->name));
+
if (!(ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)) {
zval_ptr_dtor_nogc(&c->value);
if (c->name) {
@@ -50,7 +52,7 @@ void free_zend_constant(zval *zv)
zend_string_release_ex(c->filename, 0);
} and none of the compile-time constants in my test script are listed as being freed, though plenty of php-provided ones are. If the cleanup is never reached, then it makes sense that the attributes are not properly freed, but why isn't the cleanup reached? |
From f411ddf4c984ed8d614d003e9eb209387b904f9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@tideways-gmbh.com>
Date: Wed, 27 Nov 2024 17:32:48 +0100
Subject: [PATCH] Fix constant attribute leak
---
Zend/zend_execute_API.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 9ebc15f3a4..f4adeb855f 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -302,6 +302,9 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown)
if (c->filename) {
zend_string_release_ex(c->filename, 0);
}
+ if (c->attributes) {
+ zend_hash_release(c->attributes);
+ }
efree(c);
zend_string_release_ex(key, 0);
} ZEND_HASH_MAP_FOREACH_END_DEL();
--
2.43.0 This appears to fix the leak for me. I did not verify if it is possible to call |
Thanks - I forgot there was a second place that constants are freed, though I should have remembered it from adding |
So on circleci the deprecation messages don't seem to be properly found, and on windows there are failures with exit code 1073741819, but only for the Reflection tests |
@DanielEScherzer This is not related to CircleCI / ARM, but to JIT. You should be able to reproduce the issue locally with:
see: #11293 (comment) |
I have taken the liberty to push a fix into your PR. |
I can reproduce issues for the 3 tests that fail on Windows by using a non-debug ZTS build and running with Valgrind. Specifically when I configure as follows:
And run the tests as follows:
Running
|
RFC filed: https://wiki.php.net/rfc/attributes-on-constants |
e8f731b
to
50f88e5
Compare
Rebased to update for #17101 fix, then squashed and added UPGRADING |
50f88e5
to
819ed7e
Compare
819ed7e
to
5b65cd9
Compare
Fixed conflict from #17306 |
I've rebased and squashed this patch. I plan to merge in around a week if there are no further review comments |
@DanielEScherzer Please wait, I haven't gotten around the final review. Sorry for the delay. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments. Did not have an in-depth look at this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this still misses support for opcache file cache. Tests fail with --file-cache-prime
.
@@ -130,6 +130,10 @@ PHP 8.5 UPGRADE NOTES | |||
RFC: https://wiki.php.net/rfc/marking_return_value_as_important | |||
. Added asymmetric visibility support for static properties. | |||
RFC: https://wiki.php.net/rfc/static-aviz | |||
. Added support for attributes on compile-time non-class constants. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
. Added support for attributes on compile-time non-class constants. | |
. Added support for attributes on global constants. |
Global constants are not actually compile time in PHP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They aren't created at compile time, but they are declared at compile time, unlike those with define()
. https://www.php.net/manual/en/language.constants.syntax.php
As opposed to defining constants using define(), constants defined using the const keyword must be declared at the top-level scope because they are defined at compile-time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the UPGRADING
file keeps causing merge conflicts, I've removed my changes to that file for now, but the language I'm planning to include is:
. Added support for attributes on compile-time non-class constants.
RFC: https://wiki.php.net/rfc/attributes-on-constants
. The #[\Deprecated] attribute can now be used on constants.
RFC: https://wiki.php.net/rfc/attributes-on-constants
if (list->children > 2) { | ||
zend_error_noreturn( | ||
E_COMPILE_ERROR, | ||
"Cannot apply attributes to multiple constants at once" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this is inconsistent with how attributes work on other lists. https://3v4l.org/01Nhv I prefer the error, but optimally they should be aligned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was pointed out before the RFC #16952 (comment) and I explained my reasoning at the time, it was voted on as part of the RFC
To prevent confusing code, attributes are only allowed to be applied to constants that are declared in their own statement:
with an example demonstrating this behavior. Personally I would prefer to deprecate having attributes applied to multiple properties/constants in other places
Can you document how to reproduce this? I ran in order
and they all passed |
You need |
I tried with that too and wasn't able to get failures, what is the exact command you were using? |
Are you sure you have opcache enabled? It should error right away. E.g.
|
Thanks, figured it Reproduced with And in GDB: Should be fixed now |
Hint: You can easily spawn a single (failed) test in gdb by doing: |
You're missing the equivalent in |
Tested with --file-cache-use now too, thanks for bearing with me |
No worries, this part of the code is not easy to understand. I would prefer if not all |
I had poked around in GDB and didn't see any way to tell that it was an attribute (or not) but given that so far there was nothing that would be IS_PTR it seems that at least for now the only thing that can be IS_PTR is attributes. -- |
We can't tell from the zval alone, see how |
Except that here the values are stored in
So we would need to somehow add that information to the parsing of ->literals Any objections to merging this as is? (I'll update UPGRADING, rebase, and squash) |
The same goes for persist. See how it's done there. The zval persisting skips the IS_PTR, the pointer is then handled when looping over the oparray, because only then we know what the content of the pointer actually is. |
I assumed that the point of this was to ensure that we didn't just assume that all IS_PTR zvals were pointing to attributes, but even if we come back after looping through the oparray, how do we know that any given literal is associated with the declaration of attributes? It seems we would just move the assumption Until the assumption is incorrect, is there any harm in leaving this as-is? |
From ext/opcache/zend_persist.c: if (opline->opcode == ZEND_OP_DATA && (opline-1)->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) {
zval *literal = RT_CONSTANT(opline, opline->op1);
HashTable *attributes = Z_PTR_P(literal);
attributes = zend_persist_attributes(attributes);
ZVAL_PTR(literal, attributes);
} This changes the assumption from "all IS_PTR literals are attributes" to "DECLARE_ATTRIBUTED_CONST OP_DATA literals are attributes". That's quite a different assumption.
Then, the question is, why do we make the assumption only half the time (i.e. not persist and persist_calc)? If that's really the preferred way, then it should at least be the same everywhere. |
I didn't realize I could also access the literal from there, that makes more sense, will do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I no longer have any complaints
Oh, just as i approved I saw the arm failure. This look related, no? |
This comment was marked as outdated.
This comment was marked as outdated.
I spent way too long to figure out that the issue was that in |
Tests now all pass |
d57801e
to
84be69d
Compare
https://wiki.php.net/rfc/attributes-on-constants