-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
Support never types #6761
Conversation
You might also wanna add a terminating test (with |
Another small test you could add: --TEST--
noreturn in reflection
--FILE--
<?php
function foo(): noreturn {}
$noreturnType = (new ReflectionFunction('foo'))->getReturnType();
echo $noreturnType->getName() . "\n";
var_dump($noreturnType->isBuiltin());
?>
--EXPECT--
noreturn
bool(true) Here's the fix for opcache: Subject: [PATCH] Fix opcache for noreturn
---
Zend/Optimizer/dce.c | 1 +
Zend/Optimizer/pass1.c | 1 +
Zend/Optimizer/zend_cfg.c | 2 ++
3 files changed, 4 insertions(+)
diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c
index 940f1b6ee2..85b32a7e10 100644
--- a/Zend/Optimizer/dce.c
+++ b/Zend/Optimizer/dce.c
@@ -155,6 +155,7 @@ static inline bool may_have_side_effects(
case ZEND_TICKS:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
+ case ZEND_VERIFY_NORETURN_TYPE:
/* Intrinsic side effects */
return 1;
case ZEND_DO_FCALL:
diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c
index 86774afef4..fc42002813 100644
--- a/Zend/Optimizer/pass1.c
+++ b/Zend/Optimizer/pass1.c
@@ -677,6 +677,7 @@ constant_binary_op:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
case ZEND_JMP_NULL:
+ case ZEND_VERIFY_NORETURN_TYPE:
collect_constants = 0;
break;
}
diff --git a/Zend/Optimizer/zend_cfg.c b/Zend/Optimizer/zend_cfg.c
index 973a93ef6c..9d62563693 100644
--- a/Zend/Optimizer/zend_cfg.c
+++ b/Zend/Optimizer/zend_cfg.c
@@ -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_NORETURN_TYPE:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
@@ -514,6 +515,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_NORETURN_TYPE:
break;
case ZEND_JMP:
block->successors_count = 1;
-- I'm far from an opcache expert, there might be more things missing. We'll have to ask someone more experienced. |
Can a function that loops forever be marked as
|
@tuqqu Yep, that should work fine. Should be specified in the RFC though. Hard to test in a test 😜 |
|
||
try { | ||
foo(); | ||
} catch (Exception $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and below: exception variable can be omitted now.
foo(); | ||
?> | ||
--EXPECTF-- | ||
Fatal error: Uncaught TypeError: foo(): Nothing was expected to be returned in %s:%d |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope you don't mind my suggestions:
- Mention
noreturn
explicitly - Perhaps instead of "Nothing was expected to be returned" something like "Attempt to return from a noreturn function" should be clearer?
- It's not a type error, but rather a logic one. The
noreturn
"type" is merely how functions are marked as never returning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps instead of "Nothing was expected to be returned" something like "Attempt to return from a noreturn function" should be clearer?
This error is printed when the function ends without returning. So maybe something like "noreturn function was expected to throw, terminate or run infinitely". That's very explicit, maybe somebody has a better idea.
The
noreturn
"type" is merely how functions are marked as never returning.
noreturn
is an actual type in PHPs type system. We could add a different error but I wouldn't say TypeError
is wrong.
|
||
public function bar(): noreturn | ||
{ | ||
throw new UnexpectedValueException('child'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add an inheritance test for a static
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually meant one where noreturn
is returned from a static method 🙂 I wasn't sure if there might be some issues with the ==
equality type check.
Zend/zend_execute.c
Outdated
zend_verify_type_error_common( | ||
zf, arg_info, NULL, &fname, &fsep, &fclass, &need_msg, &given_msg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the reason for this check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As that's the only part you need, you can use get_function_or_method_name(zf)
to fetch the function/method prefix for the error.
Zend/zend_execute.c
Outdated
@@ -1232,6 +1232,22 @@ ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, zval * | |||
zend_string_release(need_msg); | |||
} | |||
|
|||
ZEND_API ZEND_COLD void zend_verify_noreturn_error(const zend_function *zf) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rename this into zend_noreturn_error() it always emits error.
Zend/zend_vm_def.h
Outdated
@@ -4237,6 +4237,13 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV | |||
} | |||
} | |||
|
|||
ZEND_VM_COLD_CONST_HANDLER(201, ZEND_VERIFY_NORETURN_TYPE, UNUSED, UNUSED) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rename this into ZEND_NORETURN or ZEND_NORETURN_ERROR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine. I'm missing a test for generator with never return type.
Zend/zend_execute.c
Outdated
zend_verify_type_error_common( | ||
zf, arg_info, NULL, &fname, &fsep, &fclass, &need_msg, &given_msg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As that's the only part you need, you can use get_function_or_method_name(zf)
to fetch the function/method prefix for the error.
@nikic I've added the test for a generator function returning |
@@ -16,7 +16,7 @@ | |||
|
|||
/* | |||
DO NOT EDIT THIS FILE! | |||
This file is generated using tokenizer_data_gen.php | |||
This file is generated using tokenizer_data_gen.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commits in this PR seems like they should be rebased and squashed - the changes to tokenizer_data.c ended up being unnecessary?
> **Never type** > > A new`return only type `never` has been added. This indicates that a function either `exit()`, throws an exception, or doesn't terminate. Includes unit tests. Refs: * https://wiki.php.net/rfc/noreturn_type * https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.never-type * php/php-src#6761 * php/php-src@6cd0b48
RFC
This provides a draft implementation of a
never
type, useful as a return type for functions that only everthrow
orexit
.