Dear Internals, As you may remember, I closed the voting on the return types [RFC](https://wiki.php.net/rfc/returntypehinting) because of a design flaw that was found during the voting process. The purpose of this discussion thread is to explain the various options for checking return type compatibility with parent methods that I think are viable, to show some of the benefits and drawbacks of each option, and to solicit feedback on which options you prefer and why. As such, it's much longer than a normal message I would send.
The following code demonstrates the design flaw mentioned above: class A { function foo(): B { return new B; } } class B extends A { function foo(): C { return new C; } } class C extends B {} $b = new B; $c = $b->foo(); I've also used it because it can adequately show the differences in how each of the following options work: 1. Do covariant return types; check them at definition time 2. Do covariant return types; check them at runtime 3. Do invariant return types; check them at definition time Option 1: Covariant return types with definition time checking -------------------------------------------------------------- This is the option that is currently implemented in my pull request. This option catches all return type variance issues whether code is used or not; if it got included it is checked. This means return type variance issues can't bite you later. When class B is defined, the engine needs to check that the method `B::foo` has a compatible return type with `A::foo`, in this case that equates to checking that C is compatible with type B. This would trigger autoloading for C but it will fail in this case because C is defined in the same file. Note that there are ways we could fix this issue, but not reliably because of conditionally defined classes (defining classes in if blocks, for example) and other such dynamic behavior. Even if we could fix this issue, there is still an issue if A, B and C were defined in separate files and then included. You couldn't require them in any order for it to work; you would have to autoload. Option 2: Covariant return types with runtime checking ------------------------------------------------------ This option would do the variance check when the method is used (or potentially when the class is instantiated, or whichever comes first). Regardless of the exact details of how this method is implemented, the above code would work because the first time class B is used in any way occurs after C is defined. This would also work if you separated them out into different files This option would be slower than option 1, but cannot quantify it because it is not yet implemented. I suspect there would be a way to cache the result of the variance check to not have to do it on every instantiation or invocation, so this may be negligble. This option has the drawback that inheritance problems can exist in the code and won't be discovered until the code is ran. Let me repeat that to make sure that everyone understand it: if you have return type variance errors in your code, they would not be detected until you try to use the class or method. Option 3: Invariant return types with definition time checking -------------------------------------------------------------- This means that the declared types must *exactly match* after resolving aliases such as parent and self. The advantage of this option is that the inheritance check can be done at definition time in all cases without triggering an autoload. As such it is a bit simpler to implement and is slightly faster to do so. The obvious downside is that it can't support covariant return types. --- Note that C++ and Java (and most modern languages) support covariant return types, but C# supports only invariant return types. Which of these methods do you prefer the most, and which do you prefer the least, and why? -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php