Pavel,

On May 16, 2020, at 05:13, Pavel Patapau <algerd....@gmail.com> wrote:
> 
> Hello everyone,
> 
> I want to propose new syntax addition - guard statement, that executes code 
> only if expression equals false and must contain control-flow changing code, 
> and written a respective RFC:
> 
> https://wiki.php.net/rfc/guard_statement
> <https://wiki.php.net/rfc/unbundle_xmlprc>
> 
> Implementation in progress.
> 
> I started work before this proposal https://externals.io/message/110107 and 
> respected some moments in the RFC.
> 
> 
> Thanks for consideration,
> Pavel


Regarding this part of the proposal:

> Body of statement must contain code that changes control flow: return, throw, 
> goto. Also, in a loop context, you can use break or continue.
> 
> If compiler doesn't find any of those statements, it will search selection 
> type statements(if with else|try/catch/finally|switch) and ensures that every 
> element of these statements contains return, throw, goto.

I feel that from an implementation standpoint, this could be fairly complex, 
especially since code such as:

        guard (COND) else exit;
        guard (COND) else call_some_function_that_calls_exit();

ought to compile without warnings. (Exit was explicitly included in my original 
proposal on the mailing list.) Some languages allow this by marking functions 
with a noreturn attribute (C, Swift < 3). Swift >= 3 solves this problem by 
having a "Never" return type, which is validated by Swift's type checker, 
requiring all code paths to also call another function returning Never. (As an 
implementation detail, Never is an enum with no cases.)

Otherwise, you would have to write

        guard (COND) else {
                exit;
                return;
        }

to satisfy the parser, which is silly.

I would suggest an alternative that is conceptually simpler and allows for this 
use case.

We can observe that if we slightly loosen the restriction that the _compiler_ 
must be able to do a full call path analysis, a guard statement can also be 
thought of as:

        guard (COND) else {
                STATEMENTS;
                
                throw new \GuardFailureError;
        }

That is, the else clause ends with a compiler-generated fatal error. This means 
the compiler itself doesn't _have_ to do a full analysis of every call path in 
the STATEMENTS to ensure proper exit; the runtime can trap as well.

This allows us to be a bit more flexible: if the compiler can prove one way or 
the other, it can either fail compilation, or else omit generating the opcodes 
for throwing the GuardFailureError. But code that the compiler _can't_ prove 
(such as the previously mentioned code-that-calls-exit) should still be allowed.

If we later add language support for a noreturn attribute, or some other 
mechanism to account for that, then we are in a position to allow for that 
complexity in the parser and remove the runtime handling. It would obviously be 
preferable if the compiler can catch errors in all cases, but when it can't 
(and php's dynamicity makes this a hard problem), I think it's acceptable that 
the runtime can serve as a fallback.

-John

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to