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
rfc:nullsafe_operator [2020/07/12 13:36] – Disallow usage of ?-> by reference ilutovrfc:nullsafe_operator [2020/07/31 08:55] (current) – Close vote 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: Implemented
   * 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="true"> 
 +   * Yes 
 +   * No 
 +</doodle>
rfc/nullsafe_operator.1594560963.txt.gz · Last modified: 2020/07/12 13:36 by ilutov