Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:79170 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 1391 invoked from network); 25 Nov 2014 17:08:35 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Nov 2014 17:08:35 -0000 Authentication-Results: pb1.pair.com smtp.mail=morrison.levi@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=morrison.levi@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.218.48 as permitted sender) X-PHP-List-Original-Sender: morrison.levi@gmail.com X-Host-Fingerprint: 209.85.218.48 mail-oi0-f48.google.com Received: from [209.85.218.48] ([209.85.218.48:61926] helo=mail-oi0-f48.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 04/E9-40624-197B4745 for ; Tue, 25 Nov 2014 12:08:33 -0500 Received: by mail-oi0-f48.google.com with SMTP id u20so712239oif.21 for ; Tue, 25 Nov 2014 09:08:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:sender:date:message-id:subject:from:to:content-type; bh=qyw1KgRhJqbUZkTpUUrE/e4/7+7J0rNwOc7mkkousWM=; b=IS7TW+I7oCwr0QEhfNGqAeQFcR+dA58tGQ9NV8Gn/3c0DXC/8qrGPB7iKlhXiFxaNL 8JFcKOtg1n2wev5qKOe9THjrpHMOhnp7VyDhe9JGUjxmuRnpP96vsPd+ZSN6j8xT5yfK C/l5KjzFptoKzRj+6aHMoNvKVdCYyQMhAMg3FDA72xErD9Q94YJmwujbGIVLUePVUn5s Caw/u595KJxquJWZHkXygmPDxSi8XCSBv5Zq4Q66KWwuePJ+vSBbmjmUb9EbDTQGZFJo ii9HU/OvGGF7Kq0n7A1QP0YdZbAVlCcc3Rx/Rt6+toOLumLTjl0XtGVGdksHEBwiLDl3 jOHQ== MIME-Version: 1.0 X-Received: by 10.182.98.168 with SMTP id ej8mr17260015obb.41.1416935310483; Tue, 25 Nov 2014 09:08:30 -0800 (PST) Sender: morrison.levi@gmail.com Received: by 10.76.89.237 with HTTP; Tue, 25 Nov 2014 09:08:30 -0800 (PST) Date: Tue, 25 Nov 2014 10:08:30 -0700 X-Google-Sender-Auth: H1JP07C4FSy1FU_hB8oPBcnZylY Message-ID: To: internals Content-Type: text/plain; charset=UTF-8 Subject: [RFC][Discussion] Return Type Variance Checking From: levim@php.net (Levi Morrison) 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?