Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support never types #6761

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d5d2d03
Support noreturn types
muglug Mar 7, 2021
d24d458
Add more tests
muglug Mar 7, 2021
a763287
Attempt opcode adjustment
muglug Mar 7, 2021
4dd3c32
Add covariance checks
muglug Mar 8, 2021
db5b67c
Fix test formatting
muglug Mar 9, 2021
0395486
Do straightforward noreturn comparison
muglug Mar 9, 2021
f59b56a
Do straightforward noreturn comparison
muglug Mar 9, 2021
03a9ede
Fix test description
muglug Mar 9, 2021
984de02
Make exception more precise
muglug Mar 9, 2021
3ec058f
Don’t emit return type check if noreturn is given
muglug Mar 9, 2021
38449b7
Add a reflection test
muglug Mar 10, 2021
e3033ae
Incorporate opcache suggestions from @iluuu1994
muglug Mar 10, 2021
56ae52e
Add combinations of return and throw as tests
muglug Mar 10, 2021
6d3a76d
Add specific return-by-ref test and remove unnecessary check
muglug Mar 19, 2021
1e7ce1f
Add another test for noreturn as the bottom type
muglug Mar 19, 2021
8763f7f
Add a test for __toString noreturn
muglug Mar 30, 2021
30f2013
Merge remote-tracking branch 'upstream/master' into support-noreturn
muglug Apr 9, 2021
1c05283
Use never instead of noreturn
muglug Apr 9, 2021
fd5bfa3
Migrate more to never
muglug Apr 15, 2021
47af885
Update message for never-returning function
muglug Apr 15, 2021
b087460
Fix spacing
muglug Apr 15, 2021
f59ec6d
Improve some language and other PR changes
muglug Apr 16, 2021
f16cb38
Merge remote-tracking branch 'upstream/master' into support-noreturn
muglug Apr 16, 2021
1b68ae6
Add return type for generator returning never
muglug Apr 16, 2021
0def0c9
Simplify zend_verify_never_error
muglug Apr 16, 2021
044207b
Release memory
muglug Apr 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/Optimizer/dce.c
Expand Up @@ -155,6 +155,7 @@ static inline bool may_have_side_effects(
case ZEND_TICKS:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
case ZEND_VERIFY_NEVER_TYPE:
/* Intrinsic side effects */
return 1;
case ZEND_DO_FCALL:
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/pass1.c
Expand Up @@ -677,6 +677,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
case ZEND_JMP_NULL:
case ZEND_VERIFY_NEVER_TYPE:
collect_constants = 0;
break;
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_cfg.c
Expand Up @@ -301,6 +301,7 @@ ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, u
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_MATCH_ERROR:
case ZEND_VERIFY_NEVER_TYPE:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
Expand Down Expand Up @@ -515,6 +516,7 @@ ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, u
case ZEND_EXIT:
case ZEND_THROW:
case ZEND_MATCH_ERROR:
case ZEND_VERIFY_NEVER_TYPE:
break;
case ZEND_JMP:
block->successors_count = 1;
Expand Down
3 changes: 3 additions & 0 deletions Zend/Optimizer/zend_dump.c
Expand Up @@ -471,6 +471,9 @@ ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block
case IS_VOID:
fprintf(stderr, " (void)");
break;
case IS_NEVER:
fprintf(stderr, " (never)");
break;
default:
fprintf(stderr, " (\?\?\?)");
break;
Expand Down
29 changes: 29 additions & 0 deletions Zend/tests/return_types/never_allowed.phpt
@@ -0,0 +1,29 @@
--TEST--
never return type: acceptable cases
--FILE--
<?php

function foo(): never {
throw new Exception('bad');
}

try {
foo();
} catch (Exception $e) {
// do nothing
}

function calls_foo(): never {
foo();
}

try {
calls_foo();
} catch (Exception $e) {
// do nothing
}

echo "OK!", PHP_EOL;
?>
--EXPECT--
OK!
78 changes: 78 additions & 0 deletions Zend/tests/return_types/never_covariance.phpt
@@ -0,0 +1,78 @@
--TEST--
never return type: acceptable covariance cases
--FILE--
<?php

class A
{
public function foo(): string
{
return "hello";
}

public function bar(): never
{
throw new UnexpectedValueException('parent');
}

public function &baz()
{
}

public function someReturningStaticMethod() : static
{
}
}

class B extends A
{
public function foo(): never
{
throw new UnexpectedValueException('bad');
}

public function bar(): never
{
throw new UnexpectedValueException('child');
}

public function &baz(): never
{
throw new UnexpectedValueException('child');
}

public function someReturningStaticMethod(): never
{
throw new UnexpectedValueException('child');
}
}

try {
(new B)->foo();
} catch (UnexpectedValueException $e) {
// do nothing
}

try {
(new B)->bar();
} catch (UnexpectedValueException $e) {
// do nothing
}

try {
(new B)->baz();
} catch (UnexpectedValueException $e) {
// do nothing
}

try {
(new B)->someReturningStaticMethod();
} catch (UnexpectedValueException $e) {
// do nothing
}

echo "OK!", PHP_EOL;

?>
--EXPECT--
OK!
13 changes: 13 additions & 0 deletions Zend/tests/return_types/never_disallowed1.phpt
@@ -0,0 +1,13 @@
--TEST--
never return type: unacceptable cases: any return
--FILE--
<?php

function foo(): never {
return "hello"; // not permitted in a never function
}

// Note the lack of function call: function validated at compile-time
?>
--EXPECTF--
Fatal error: A never function must not return in %s on line %d
13 changes: 13 additions & 0 deletions Zend/tests/return_types/never_disallowed2.phpt
@@ -0,0 +1,13 @@
--TEST--
never return type: unacceptable cases: empty return
--FILE--
<?php

function foo(): never {
return; // not permitted in a never function
}

// Note the lack of function call: function validated at compile-time
?>
--EXPECTF--
Fatal error: A never function must not return in %s on line %d
19 changes: 19 additions & 0 deletions Zend/tests/return_types/never_disallowed3.phpt
@@ -0,0 +1,19 @@
--TEST--
never return type: unacceptable cases: implicit return
--FILE--
<?php

function foo(): never {
if (false) {
throw new Exception('bad');
}
}

foo();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: foo(): Nothing was expected to be returned in %s:%d
Stack trace:
#0 %s(%d): foo()
#1 {main}
thrown in %s on line %d
muglug marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 17 additions & 0 deletions Zend/tests/return_types/never_finally_return.phpt
@@ -0,0 +1,17 @@
--TEST--
never return type: never cannot return from finally
--FILE--
<?php

function foo() : never {
try {
throw new Exception('bad');
} finally {
return;
}
}

// Note the lack of function call: function validated at compile-time
?>
--EXPECTF--
Fatal error: A never function must not return in %s on line %d
26 changes: 26 additions & 0 deletions Zend/tests/return_types/never_no_variance.phpt
@@ -0,0 +1,26 @@
--TEST--
never return type: prevent unacceptable cases
--FILE--
<?php

class A
{
public function bar(): never
{
throw new \Exception('parent');
}
}

class B extends A
{
public function bar(): string
{
return "hello";
}
}

(new B)->bar();

?>
--EXPECTF--
Fatal error: Declaration of B::bar(): string must be compatible with A::bar(): never in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/return_types/never_parameter.phpt
@@ -0,0 +1,9 @@
--TEST--
never return type: not valid as a parameter type
--FILE--
<?php

function foobar(never $a) {}
?>
--EXPECTF--
Fatal error: never cannot be used as a parameter type in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/return_types/never_reflected.phpt
@@ -0,0 +1,16 @@
--TEST--
never in reflection
--FILE--
<?php

function foo(): never {}

$neverType = (new ReflectionFunction('foo'))->getReturnType();

echo $neverType->getName() . "\n";
var_dump($neverType->isBuiltin());

?>
--EXPECT--
never
bool(true)
13 changes: 13 additions & 0 deletions Zend/tests/return_types/never_return_throw.phpt
@@ -0,0 +1,13 @@
--TEST--
never return type: never cannot return from throw expression
--FILE--
<?php

function foo() : never {
return throw new Exception('bad');
}

// Note the lack of function call: function validated at compile-time
?>
--EXPECTF--
Fatal error: A never function must not return in %s on line %d
28 changes: 28 additions & 0 deletions Zend/tests/return_types/never_tostring.phpt
@@ -0,0 +1,28 @@
--TEST--
never type of __toString method
--FILE--
<?php

class A implements Stringable {
public function __toString(): string {
return "hello";
}
}

class B extends A {
public function __toString(): never {
throw new \Exception('not supported');
}
}

try {
echo (string) (new B());
} catch (Exception $e) {
// do nothing
}

echo "done";

?>
--EXPECT--
done
12 changes: 12 additions & 0 deletions Zend/tests/type_declarations/typed_properties_109.phpt
@@ -0,0 +1,12 @@
--TEST--
Test typed properties disallow never
--FILE--
<?php
class Foo {
public never $int;
}

$foo = new Foo();
?>
--EXPECTF--
Fatal error: Property Foo::$int cannot have type never in %s on line 3
10 changes: 10 additions & 0 deletions Zend/tests/type_declarations/union_types/never_with_class.phpt
@@ -0,0 +1,10 @@
--TEST--
Combining never with class type
--FILE--
<?php

function test(): T|never {}

?>
--EXPECTF--
Fatal error: never can only be used as a standalone type in %s on line %d