Skip to content
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

RFC: Marking return values as important (#[\NoDiscard]) #17599

Merged
merged 13 commits into from
Apr 2, 2025

Conversation

TimWolla
Copy link
Member

@TimWolla TimWolla commented Jan 27, 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.

There could be some edge-cases with the optimizer. For example:

if (foo());

Should or should this not error? It will not without opcache, but will with.

@TimWolla
Copy link
Member Author

Should or should this not error? It will not without opcache, but will with.

Should not warn, but the RFC explicitly acknowledges OPcache optimizations (which is part of the reason for the explicit (void) cast): https://wiki.php.net/rfc/marking_return_value_as_important#void_cast_to_suppress_the_warning

@TimWolla TimWolla force-pushed the return-value-unused branch from 8fb967e to bb2125c Compare March 6, 2025 14:05
@TimWolla TimWolla force-pushed the return-value-unused branch 2 times, most recently from 812e8a5 to 101d0cf Compare March 19, 2025 15:47
@TimWolla TimWolla mentioned this pull request Mar 19, 2025
@TimWolla TimWolla force-pushed the return-value-unused branch 6 times, most recently from c893718 to ad4999b Compare March 26, 2025 16:53
@TimWolla TimWolla force-pushed the return-value-unused branch from ad4999b to 9ccd300 Compare March 27, 2025 09:41
@TimWolla TimWolla requested a review from iluuu1994 March 27, 2025 10:18
@TimWolla TimWolla marked this pull request as ready for review March 27, 2025 10:18
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

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

I don't see big problems.

Run-time performance should be the same (thanks to RETVAL specialization).
JIT code increase will affect only functions with NoDiscard attribute.
Right?

For optimizer, we may introduce an additional option that ignores NoDiscard attribute (disabled by default). We already have few similar "unsafe" bits in opcache.optimization_level.

@TimWolla
Copy link
Member Author

Thank you for the review.

JIT code increase will affect only functions with NoDiscard attribute.
Right?

Yes, this is my understanding. The tracing JIT should only emit additional code if both the attribute is set and if the result in unused. The function jit has the same code size and will just call a different helper, as suggested by Ilija in https://github.com/php/php-src/pull/17599/files#r1934186865.

For optimizer, we may introduce an additional option that ignores NoDiscard attribute (disabled by default). We already have few similar "unsafe" bits in opcache.optimization_level.

I tried to optimize the implementation for the “no warning happens” case, since users are expected to either use the value or to suppress it with (void), so I hope that this is not necessary in practice. For example the JIT will not emit any additional code.

Thus I would defer this to a later point when we have more data.

@dstogov
Copy link
Member

dstogov commented Mar 31, 2025

I tried to optimize the implementation for the “no warning happens” case, since users are expected to either use the value or to suppress it with (void), so I hope that this is not necessary in practice. For example the JIT will not emit any additional code.

You are right. It's not a big problem if we avoid inlining of functions with NoDisacard and with missing return value or (void) prefix.

@lubosdz
Copy link

lubosdz commented Mar 31, 2025

IMO - this attribute can be easily replaced with proper programming, e.g. returning array [$cntSuccess, $cntFailed] or whatever.

Aside remark - (sorry for interferring, I am not C-programmer and neither understand PHP-core stuff):
I do not really understand this craziness about the attributes - it's not programming anymore, it's configuring and with quite ugly very specific syntax. Almost everything what the attributes do can be done in a classic programming. So it seems to me like a shortcut for lazy programmers. One understandable argument might be "less boilerplate code", but that also means higher informational density, ergo steeper learning curve, thus possibly easier to miss errors. I always try to avoid attributes if possible and rather implement own solution. But that's me - other developers may say different .. Some research would be beneficial how much are attributes favourable amongst common developers - but pls do not ask only those 0.1% of framework or PHP core developers, ask other common userland developers.

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.

LGTM otherwise.

}
zend_update_property_ex(zend_ce_nodiscard, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value);

/* The assignment might fail due to 'readonly'. */
Copy link
Member

Choose a reason for hiding this comment

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

I find code like this noisy, this just checks the contract of RETURN_THROWS. I would prefer to just keep the comment and remove the check

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure I follow. Do you mean “remove the check” (thus keeping the RETURN_THROWS())? This would be incorrect if the assignment doesn't fail.

Or do you mean “remove the entire block, including the RETURN_THROWS()”? In that case the comment no longer makes sense.

In any case, this mirrors the constructor of Deprecated::__construct(), which has 2 readonly properties and where the RETURN_THROWS() is actually doing something, so I find it helpful for clarity. In production builds this should hopefully not result in any emitted assembly either.

see also #11293 (comment) and #11293 (comment).

Copy link
Member

Choose a reason for hiding this comment

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

I meant the check + the return, i.e. the entire block. I think the comment may still make sense to remind the reader that the property update can fail (but then the commit should probably placed a few lines up to just prior to the call).

@TimWolla TimWolla force-pushed the return-value-unused branch from 058597e to 609fcf5 Compare April 1, 2025 10:19
@TimWolla TimWolla requested a review from nielsdos April 1, 2025 12:27
Copy link
Member

@nielsdos nielsdos left a comment

Choose a reason for hiding this comment

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

Ok, see nit

@TimWolla TimWolla merged commit 5544be7 into php:master Apr 2, 2025
9 checks passed
@TimWolla TimWolla deleted the return-value-unused branch April 2, 2025 07:35
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.

5 participants