Skip to content

Fix AST export crash for closures in const-expressions #17120

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

Closed
wants to merge 5 commits into from

Conversation

TimWolla
Copy link
Member

Fixes GH-17096.

@iluuu1994
Copy link
Member

Tbh, I would prefer a simple solution, e.g. by printing a placeholder in the ast dumper. Accurate ast dumping is not very important.

@TimWolla
Copy link
Member Author

That would also be fine with me.

Nevertheless I would suggest to keep the first commit (I can send a separate PR for that), as it it definitely fixes a bug in handling of zend_ast_decl nodes that is not (easily) detectable with sanitizers. It will just work on garbage values.

@mvorisek

This comment was marked as off-topic.

@TimWolla TimWolla force-pushed the ast-op-array-original-ast branch from 05e868f to 45057db Compare December 11, 2024 12:55
@bwoebi
Copy link
Member

bwoebi commented Dec 12, 2024

@iluuu1994 I actually like the full AST being there. While it's not fundamentally necessary for usability, it's nice to have I think.

@iluuu1994
Copy link
Member

One difference imo is that closures may grow large, in comparison to singular expressions. This ast is almost never used, but will still take up shm space with little benefit.

@bwoebi
Copy link
Member

bwoebi commented Dec 12, 2024

@iluuu1994 I doubt the amount of closures within attributes will be significant enough to warrant considerations in that regard.

@iluuu1994
Copy link
Member

@bwoebi You're right, probably not. However, it's also a weird exception. E.g. we don't attempt to preserve the AST in other cases. Without opcache, the AST is discarded after the expression has been evaluated, and a lot of time expressions get folded into some concrete value at compile time. So, I'm just wondering why we need to make an exception here.

@bwoebi
Copy link
Member

bwoebi commented Dec 15, 2024

@iluuu1994 But it's also a weird exception to not preserve Closure contents, but everything else in an attribute.

@iluuu1994
Copy link
Member

iluuu1994 commented Dec 16, 2024

@bwoebi But again, "preserve" is a stretch, compile-time optimizations still apply. I don't believe we preserve the AST solely for the purposes of printing, rather than evaluating. Even assert() collapses the AST at compile-time.

But ok, I don't object strongly to this, so feel free to go with this approach if you prefer.

@TimWolla
Copy link
Member Author

@iluuu1994 I don't have a strong opinion either way. Shall I extract the first commit (which I consider to be a bugfix) into a separate PR to allow for an independent review?

@iluuu1994
Copy link
Member

iluuu1994 commented Dec 16, 2024

@TimWolla If we decide not to store the AST in this case, it might be better to just add some ZEND_ASSERT(!zend_ast_is_decl(ast)); conditions. WDYT? Otherwise we're just adding unused and untested code.

@TimWolla
Copy link
Member Author

WDYT? Otherwise we're just adding unused and untested code.

That makes sense to me. With the assertions being there in all the locations, (re)doing the implementation is straight forward. The complicated part was finding all the locations.

@arnaud-lb
Copy link
Member

I don't know what would be the most useful between displaying the source code or displaying a placeholder with the closure location (filename / lineno).

Currently we don't expose a closure's source code in var_dump() or in Reflection, and it's usually fine.

Otherwise, given that we primarily store the AST for evaluation purposes, and the AST is already evaluated in this case (to an op_array), maybe we can store a string instead? This would take less space, and would be comparable to a doc comment. I believe that this is what we do for assert().

@edorian
Copy link
Member

edorian commented Dec 18, 2024

I'd be good with just producing a placeholder and not adding more bits to the runtime memory for this.

But maybe I'm missing some context and don't understand the benefits or potential use cases for this, and I'm overlooking some reason why this is useful.

  • If PHP wants to support restoring/creating a PHP script from runtime data (without the source files) via reflection and bits of AST (or stringified AST) then this would obviously make sense, but I'm not aware of this being a goal.
  • Projects like php-parser or static analysers also shouldn't benefit much from this?
  • Are there error messages that would be worse for this having the code?

So without a concrete use case at hand, I'd argue for dropping it, and otherwise I'm happy to change my mind.

@TimWolla TimWolla force-pushed the ast-op-array-original-ast branch 2 times, most recently from c5ad440 to 29977f0 Compare January 3, 2025 08:51
@TimWolla
Copy link
Member Author

TimWolla commented Jan 3, 2025

Okay, I've opted to show a placeholder there, but left commits that store the AST as a string and the original implementation that stores the full AST in the commit history, should we want to decide differently.

Regarding the “store as string” variant, it should be noted that it naturally cannot take the indent and priority parameter of zend_ast_export_ex into account and thus it might also be subtly wrong. Thus the obvious placeholder value might be preferable after all.

@TimWolla TimWolla requested a review from arnaud-lb January 3, 2025 13:56
@TimWolla TimWolla changed the title Preserve the original AST for closures in const-expressions Support AST export for closures in const-expressions Jan 3, 2025
@TimWolla TimWolla changed the title Support AST export for closures in const-expressions Fix AST export crash for closures in const-expressions Jan 3, 2025
Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

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

Code looks correct 👍 Not necessarily a fan of more ast types, but not objecting.

Previously `zend_ast_decl` AST nodes were handled as if they were regular
`zend_ast` nodes.

This was not observable, because copying an AST only happened when evaluating
const-expressions, which could not include declarations until the “Support
Closures in constant expressions” RFC.
This is necessary when stringifying a `ReflectionAttribute` that has a closure
as a parameter.
This is unreachable / untestable now.
@TimWolla TimWolla force-pushed the ast-op-array-original-ast branch from 6ee4bad to f029cc1 Compare January 8, 2025 11:30
TimWolla added a commit to TimWolla/php-src that referenced this pull request Jan 8, 2025
As per the discussion in phpGH-17120, we are printing a placeholder value only.
The commit history of that PR also includes alternative implementations, should
a different decision be desirable.

Fixes phpGH-17096
Closes phpGH-17120
@TimWolla TimWolla closed this in d8d1cb4 Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SIGABRT/SIGTRAP when using closures in attributes in PHP 8.5
6 participants