Re: [PHP-DEV] Planning an RFC to allow calls to global functions inconstant expressions

This is only part of a thread. view whole thread
February 9, 2020 00:55 (tyson andre)
> As you've already realized, the main problem here is the behavior for > functions that have side-effects or state. Currently we mostly get away > with the illusion that it doesn't matter when exactly constexpr > initializers are evaluated. Apart from the error location and (admittedly > quite a few) other details, you ostensibly can't distinguish whether the > expression is going to evaluated on declaration, on first access or on each > access.
Good point - If PHP ends up supporting an option where **any** global function can get called, It'd make sense to evaluate any expression *with a function call* every time for instance properties and parameter defaults (e.g. `public $id = generate_unique_int_id()`). (the optimizer can later optimize any functions where it won't be noticeable). I think that would require updates to my PR for caching, since immutable returned zvals (including false/true) get cached permanently.
> I'm not a big fan of whitelisting functions, especially as this runs into > the name resolution issue (you suggest that essentially the use of fully > qualified/imported names will be forced -- unlike everywhere else in PHP), > and because I've seen this "mark all the functions const/constexpr" race > play out one too many times in other languages, which have a much stronger > motivation for it than we do.
I'd agree with all of those drawbacks of whitelisting functions. This is still the best compromise I can think of for allowing those functions I've mentioned, if it turns out there are too many objections to this making constants too dynamic.
> If we want to expand the allowed operations inside constexpr initializers, > I think this needs to start by considering precise evaluation semantics in > the places where it matters. Those are primarily initializers for function > parameters and non-static properties, because both of these have a > reasonable expectation of evaluation at each use. I think the best way to > approach those two may well be to relax the constexpr restrictions on them > entirely (allowing say "public Foo $prop = new Foo()", something people > want anyway) and make sure that things are evaluated on each access (unless > they can be pre-evaluated without affecting behavior, of course).
For a more general proposal, the variable scope is a potential problem. For example, would the below example create a new class definition every time f() was called? (Creating a brand new scope with no variables, allowing everything except variables (and relying on zend_forbid_dynamic_call to blacklist get_defined_variables()), or always using the global scope might be ways of handling this.) ``` function f($x) { return fn() => new class() { public $var = $x; // or new Foo($x) }; } $factory1 = f(1); $factory2 = f(2); ``` That reminds me: I forgot about functions such as `compact()`, `extract()` and `get_defined_vars()`. (and `func_num_args()`/`func_get_args()`/`func_get_arg()`). It looks like all of them check `zend_forbid_dynamic_call()` to throw, but I need to add tests and possibly flag the call as dynamic.
> For the remaining cases (constant, class constant and static property > initializers) we can probably get away with the current semantics, which is > evaluation on first access, with a very broad definition of what "access" > means.
Agreed - I plan on keeping the existing semantics for those, but I think the semantics of global(non-class) constants are that they are currently always evaluated immediately, based on the tests I've wrote for this RFC. Thanks, - Tyson