rfc:nullsafe_operator

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:nullsafe_operator [2020/07/12 13:36] – Disallow usage of ?-> by reference ilutovrfc:nullsafe_operator [2020/07/17 09:01] – Also set status to voting ilutov
Line 3: Line 3:
   * Date: 2020-06-02   * Date: 2020-06-02
   * Author: Ilija Tovilo, tovilo.ilija@gmail.com   * Author: Ilija Tovilo, tovilo.ilija@gmail.com
-  * Status: Under discussion+  * Status: Voting
   * Target Version: PHP 8.0   * Target Version: PHP 8.0
   * Implementation: https://github.com/php/php-src/pull/5619   * Implementation: https://github.com/php/php-src/pull/5619
Line 10: Line 10:
 ===== Introduction ===== ===== Introduction =====
  
-This RFC proposes new operator nullsafe operator ''%%?->%%'' with full short circuiting.+This RFC proposes the new nullsafe operator ''%%?->%%'' with full short circuiting.
  
 ===== Proposal ===== ===== Proposal =====
Line 76: Line 76:
   * Nullsafe method call (''%%?->%%'')   * Nullsafe method call (''%%?->%%'')
   * Static method call (''%%::%%'')   * Static method call (''%%::%%'')
-  * Assignment (''%%=%%'', ''%%+=%%'', ''%%??=%%'', ''%%= &%%'', etc.) 
-  * Post/pre increment (''%%++%%'', ''%%--%%'') 
  
 The following elements will cause new sub-chains. The following elements will cause new sub-chains.
  
-  * Right hand side of an assignment 
   * Arguments in a function call   * Arguments in a function call
   * The expression in ''%%[]%%'' of an array access   * The expression in ''%%[]%%'' of an array access
Line 103: Line 100:
 //       --------  chain 2 //       --------  chain 2
 // If $c is null chain 2 is aborted, method d() isn't called, null is passed to `$a->b()` // If $c is null chain 2 is aborted, method d() isn't called, null is passed to `$a->b()`
- 
-   $foo?->bar = $a->b(); 
-// -------------------- chain 1 
-//              ------- chain 2 
-// If $foo is null chain 1 is aborted, `$a->b()` is not evaluated, the assignment is skipped 
- 
-   $foo?->bar++; 
-// ------------ chain 1 
-// If $foo is null, chain 1 is aborted, ++ is skipped 
 </code> </code>
 ==== Rationale ==== ==== Rationale ====
Line 121: Line 109:
 $foo?->bar(expensive_function()); $foo?->bar(expensive_function());
 </code> </code>
-The evaluation of ''%%expensive_function()%%'' is undesirable if ''%%$foo%%'' is ''%%null%%'' as its result will simply be discarded. If the function has side effects it could also lead to surpsises.+The evaluation of ''%%expensive_function()%%'' is undesirable if ''%%$foo%%'' is ''%%null%%'' as its result will simply be discarded. If the function has side effects it could also lead to surprises.
  
 **2. You can see which methods/properties return null** **2. You can see which methods/properties return null**
Line 131: Line 119:
 Without short circuiting every subsequent method call and property access in the chain will require using the nullsafe operator or you will get a “Call to a member function on null” error. With short circuiting this isn’t necessary which makes it more obvious which methods/properties might return ''%%null%%''. Without short circuiting every subsequent method call and property access in the chain will require using the nullsafe operator or you will get a “Call to a member function on null” error. With short circuiting this isn’t necessary which makes it more obvious which methods/properties might return ''%%null%%''.
  
-**3. Allows for nullsafe operator in write context** +**3. Mixing with other operators**
- +
-<code php> +
-$foo = null; +
-$foo?->bar = 'bar'; +
-var_dump($foo); +
- +
-// Without short circuiting: +
-// Fatal error: Can't use nullsafe result value in write context +
- +
-// With short circuiting: +
-// NULL +
-</code> +
-Without short circuiting the assignment to a nullsafe property would be illegal because it produces an r-value (a value that cannot be assigned to). With short circuiting if a nullsafe operation on the left hand side of the assignment fails the assignment is simply skipped. +
- +
-**4. Mixing with other operators**+
  
 <code php> <code php>
Line 189: Line 162:
 ===== Syntax choice ===== ===== Syntax choice =====
  
-The ''%%?%%'' in ''%%?->%%'' denotes the precise place in the code where the short circuiting occurs. It closesly resembles the syntax of every other language that implements a nullsafe operator.+The ''%%?%%'' in ''%%?->%%'' denotes the precise place in the code where the short circuiting occurs. It closely resembles the syntax of every other language that implements a nullsafe operator.
  
-===== Edge cases =====+===== Forbidden usages =====
  
-==== Nullsafe operator in parameters passed by reference ====+==== Nullsafe operator in write context ====
  
-The nullsafe operator is not allowed in parameters passed by reference, even if the resulting value is not ''%%null%%''.+Using the nullsafe operator in write context ist not allowed.
  
 <code php> <code php>
-takes_ref($foo?->bar); +$foo?->bar->baz = 'baz'; 
-// Error: Cannot pass parameter 1 by reference+// Can't use nullsafe operator in write context 
 + 
 +foreach ([1, 2, 3] as $foo?->bar->baz) {} 
 +// Can't use nullsafe operator in write context 
 + 
 +unset($foo?->bar->baz); 
 +// Can't use nullsafe operator in write context 
 + 
 +[$foo?->bar->baz] = 'baz'; 
 +// Assignments can only happen to writable values
 </code> </code>
-The code above could loosely be translated to the following code.+It was previously suggested to allow the nullsafe operator in the left hand side of assignments and skip the assignment if the left hand side of the nullsafe operator was ''%%null%%''. However, due to technical difficulties this is not a part of this RFC. It might be addressed in a later RFC. It is also not completely clear whether the right hand side of the assignment should always be evaluated or not. 
 + 
 +==== References ==== 
 + 
 +Taking the reference of a nullsafe chain is not allowed. This is because references require l-values (memory locations, like variables or properties) but the nullsafe operator can sometimes return the r-value ''%%null%%''.
  
 <code php> <code php>
 +$x = &$foo?->bar;
 +
 +// Could loosely be translated to
 +
 if ($foo !== null) { if ($foo !== null) {
-    takes_ref($foo->bar);+    $x = &$foo->bar;
 } else { } else {
-    takes_ref(null);+    $x = &null; 
 +    // Only variables should be assigned by reference
 } }
 </code> </code>
-This code is not validas it would throw a “Fatal error: Only variables can be passed by reference” error whenever ''%%$foo%%'' was ''%%null%%''.+For this reasonthe following examples are disallowed.
  
-==== Nullsafe operator in values returned by reference ====+<code php> 
 +// 1 
 +$x &$foo?->bar; 
 +// Compiler error: Cannot take reference of a nullsafe chain
  
-Trying to use the nullsafe operator in values returned by reference will emit a notice and return the result **by value**.+// 2 
 +takes_ref($foo?->bar); 
 +// Error: Cannot pass parameter 1 by reference
  
-<code php>+// 3
 function &return_by_ref($foo) { function &return_by_ref($foo) {
     return $foo?->bar;     return $foo?->bar;
 +    // Compiler error: Cannot take reference of a nullsafe chain
 } }
- 
-$barRef = &return_by_ref($foo); 
-// Notice: Only variable references should be returned by reference 
-$barRef = 'bar'; 
-var_dump($foo->bar); // bar was NOT set 
 </code> </code>
-This is consistent with trying to pass other r-values by reference.+Example 2 is a runtime error because we cannot know at compile time if the given parameter allows passing values by reference.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 240: Line 232:
 ===== Vote ===== ===== Vote =====
  
-+Voting starts 2020-07-17 and ends 2020-07-31.  
 + 
 +<doodle title="Add nullsafe operator to the language?" auth="ilutov" voteType="single" closed="false"> 
 +   * Yes 
 +   * No 
 +</doodle>
rfc/nullsafe_operator.txt · Last modified: 2020/07/31 08:55 by ilutov