> 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
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to