On Mon, Dec 18, 2017 at 5:18 AM, Andreas Hennings <andr...@dqxtech.net> wrote:
> There were discussions about covariance and contravariance in the past.
> https://externals.io/message/98085#98105
> Unfortunately I was not subscribed back then, so I cannot respond to anything.
> So, here it goes again.
>
> WIth co- and contravariance, the following would be possible:
> - contravariance.php - https://3v4l.org/I3v0u
> - covariance.php - https://3v4l.org/i79O5
>
> (from guilhermeblanco's older email in "PHP's support to
> contravariance and covariance")
>
> The main problem was expressed by Levi Morrison in this older thread.
>
> Currently we do not autoload classes in type hints.
> https://3v4l.org/sFsDd
> In the example I can declare "UnknownClass" as a return type hint, and
> PHP won't care.
>
> However, to validate if a return type matches with the parent
> definition, the class must be autoloaded first.
> Or rather:
> - If the return type is identical with the parent, PHP can say "yes"
> with no class loading required.
> - If the return type is different from the parent:
> -- Currently, PHP simply says "no" (Fatal error: Declaration of
> C::foo(): C must be compatible with I::foo()).
> -- To support covariance, PHP would have to autoload the class in the
> type hint, and then check the hierarchy.
>
>
> ## Solutions proposed in old thread
>
> Levi Morrison:
>
>> You need to adjust the passes over the code to register symbols and
>> their declared relationships, and then in a separate pass validate
>> them. After that if the class isn't found then you trigger an
>> autoload.
>>
>> It's doable, it just hasn't been done.
>
> Christoph M. Becker:
>
>> An alternative might be forward class declarations:
>
>
> ## What I propose instead
>
> I think it is not so complicated actually, and can be done without BC
> break, and without forward type hints.
> This only gives us covariance. Contravariance is another story.
>
> When a class declaration is executed, do the following:
> - Parse the class AST (obviously).
> - Autoload all identifiers in "extends" and "implements".
> - Autoload all identifiers in return type hints that are not identical
> with the parent return type hint.
>
> If such a class is not found, report "Return type must either be
> identical with the parent, or it must be an existing or autoloadable
> class or interface."
> Well, or any message that clarifies why this one was autoloaded, while
> other type hint classes are not autoloaded.
>
> So, this behavior is the same as today, except for the case of return
> type hints that differ from the parent, which currently result in
> fatal error.
>
> Does this sound doable? Am I missing something else?

I believe your algorithm fails on this simple setup:

<?php

interface A {
    function foo(): X;
}

interface B extends A {
    function foo(): Y;
}

interface X {
    function bar(): A;
}

interface Y extends X {
    function bar(): B;
}

?>

If I correctly typed this from memory there is no way to order this
such that all units are defined ahead of time as needed for verifying
correctness. This means we trigger the autoloader even though the type
is defined in the same file. Even if we do some more complicated
compile-time passes we'd fail on things like this:

<?php // file1.php

interface A {
    function foo(): X;
}

interface B extends A {
    function foo(): Y;
}

if (getenv("ENABLE_X")) {
    interface X {
        function bar(): A;
    }
}
?>
<?php // file2.php

interface Y extends X {
    function bar(): B;
}

?>

This case shows that care needs to be taken to get the order down
correctly even if we autoload:

<?php
interface A {
    function foo(): X;
}

interface B extends A {
    function foo(): Y;
}
?>
<?php // X.php
interface X {
    function bar(): A;
}
?>
<?php // Y.php

interface Y extends X {
    function bar(): C;
}
// At this point the engine will need to verify A and C but we may not
have finished verifying A and B yet
?>

All-in-all I don't think we can resolve every case cleanly because we
do not have purely ahead-of-time compilation for all units involved. I
think every method of implementing this feature has drawbacks and we
need to thoughtfully evaluate them.

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

Reply via email to