diff --git a/Zend/Optimizer/dce.c b/Zend/Optimizer/dce.c index 414abe01f96ac..dd8d4681eb8f0 100644 --- a/Zend/Optimizer/dce.c +++ b/Zend/Optimizer/dce.c @@ -107,7 +107,6 @@ static inline bool may_have_side_effects( case ZEND_IS_SMALLER_OR_EQUAL: case ZEND_CASE: case ZEND_CASE_STRICT: - case ZEND_CAST: case ZEND_ROPE_INIT: case ZEND_ROPE_ADD: case ZEND_INIT_ARRAY: @@ -127,6 +126,8 @@ static inline bool may_have_side_effects( case ZEND_ARRAY_KEY_EXISTS: /* No side effects */ return 0; + case ZEND_CAST: + return opline->extended_value == IS_UNDEF; case ZEND_ADD_ARRAY_ELEMENT: /* TODO: We can't free two vars. Keep instruction alive. "$b"]; */ if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) { diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index ce6c43afaedbe..1ba932f75d257 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -79,7 +79,7 @@ static void zend_delete_call_instructions(zend_op_array *op_array, zend_op *opli static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func) { if (func->type == ZEND_USER_FUNCTION - && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED)) + && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD)) /* TODO: function copied from trait may be inconsistent ??? */ && !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE)) && fcall->extended_value >= func->op_array.required_num_args diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index fc6b9b421b628..55c7fad2239cf 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -5287,6 +5287,8 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op return (t1 & MAY_BE_OBJECT); case IS_OBJECT: return 0; + case IS_UNDEF: + return 0; EMPTY_SWITCH_DEFAULT_CASE() } /* GCC is getting confused here for the Wimplicit-fallthrough warning with diff --git a/Zend/tests/attributes/nodiscard/001.phpt b/Zend/tests/attributes/nodiscard/001.phpt new file mode 100644 index 0000000000000..82fc49450cd1c --- /dev/null +++ b/Zend/tests/attributes/nodiscard/001.phpt @@ -0,0 +1,86 @@ +--TEST-- +#[\NoDiscard]: Basic test. +--FILE-- +test(); +$cls->test2(); +Clazz::test3(); + +call_user_func([$cls, "test"]); + +$closure(); + +$closure2(); + +?> +--EXPECTF-- +Warning: (B)The return value of function test() is expected to be consumed in %s on line %d + +Warning: (B)The return value of function test2() is expected to be consumed, this is important in %s on line %d + +Warning: (B)The return value of function test3() is expected to be consumed in %s on line %d + +Warning: (A)The return value of function test() is expected to be consumed in %s on line %d + +Warning: (A)The return value of function test() is expected to be consumed in %s on line %d + +Warning: (A)The return value of method Clazz::test() is expected to be consumed in %s on line %d + +Warning: (A)The return value of method Clazz::test2() is expected to be consumed, this is important in %s on line %d + +Warning: (B)The return value of method Clazz::test3() is expected to be consumed in %s on line %d + +Warning: (A)The return value of method Clazz::test() is expected to be consumed in %s on line %d + +Warning: (A)The return value of function {closure:%s:%d}() is expected to be consumed in %s on line %d + +Warning: (A)The return value of function {closure:%s:%d}() is expected to be consumed in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/002.phpt b/Zend/tests/attributes/nodiscard/002.phpt new file mode 100644 index 0000000000000..58e130e4cd460 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/002.phpt @@ -0,0 +1,44 @@ +--TEST-- +#[\NoDiscard]: __call(), __callStatic(), and __invoke(). +--FILE-- +test(); +Clazz::test(); +$cls('foo'); + +?> +--EXPECTF-- +Warning: (A)The return value of method Clazz::test() is expected to be consumed in %s on line %d +__call(test) + +Warning: (A)The return value of method Clazz::test() is expected to be consumed in %s on line %d +__callStatic(test) + +Warning: (A)The return value of method Clazz::__invoke() is expected to be consumed in %s on line %d +__invoke(foo) + diff --git a/Zend/tests/attributes/nodiscard/003.phpt b/Zend/tests/attributes/nodiscard/003.phpt new file mode 100644 index 0000000000000..23d838aa9f337 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/003.phpt @@ -0,0 +1,22 @@ +--TEST-- +#[\NoDiscard]: Taken from trait. +--FILE-- +test(); + +?> +--EXPECTF-- +Warning: (A)The return value of method Clazz::test() is expected to be consumed in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/005.phpt b/Zend/tests/attributes/nodiscard/005.phpt new file mode 100644 index 0000000000000..ddef8f4a972e9 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/005.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[\NoDiscard]: Native function and method. +--FILE-- +setTimestamp(0); + +?> +--EXPECTF-- +Warning: (B)The return value of function flock() is expected to be consumed, as locking the stream might have failed in %s on line %d + +Warning: (A)The return value of method DateTimeImmutable::setTimestamp() is expected to be consumed, as DateTimeImmutable::setTimestamp() does not modify the object itself in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/006.phpt b/Zend/tests/attributes/nodiscard/006.phpt new file mode 100644 index 0000000000000..61fee1c9562b3 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/006.phpt @@ -0,0 +1,20 @@ +--TEST-- +#[\NoDiscard]: execute_ex overwritten +--EXTENSIONS-- +zend_test +--INI-- +zend_test.replace_zend_execute_ex=1 +opcache.jit=disable +--FILE-- + +--EXPECTF-- +Warning: (A)The return value of function test() is expected to be consumed in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/007.phpt b/Zend/tests/attributes/nodiscard/007.phpt new file mode 100644 index 0000000000000..9f9eabd798d5f --- /dev/null +++ b/Zend/tests/attributes/nodiscard/007.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\NoDiscard]: execute_internal overwritten +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.execute_internal=1 +--FILE-- + +--EXPECTF-- + + + +Warning: (A)The return value of function flock() is expected to be consumed, as locking the stream might have failed in %s on line %d + + diff --git a/Zend/tests/attributes/nodiscard/008.phpt b/Zend/tests/attributes/nodiscard/008.phpt new file mode 100644 index 0000000000000..8fcbe7e468cb0 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/008.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\NoDiscard]: Combining with #[\Deprecated]. +--FILE-- + +--EXPECTF-- +Deprecated: Function test() is deprecated in %s on line %d + +Warning: (B)The return value of function test() is expected to be consumed in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/009.phpt b/Zend/tests/attributes/nodiscard/009.phpt new file mode 100644 index 0000000000000..8625e6b8a01e8 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/009.phpt @@ -0,0 +1,14 @@ +--TEST-- +#[\NoDiscard]: Combining with #[\Deprecated] (Internal). +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Deprecated: Function zend_test_deprecated_nodiscard() is deprecated, custom message in %s on line %d + +Warning: (B)The return value of function zend_test_deprecated_nodiscard() is expected to be consumed, custom message 2 in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/error_code_001.phpt b/Zend/tests/attributes/nodiscard/error_code_001.phpt new file mode 100644 index 0000000000000..2952587db7b87 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/error_code_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +#[\NoDiscard]: Code is E_USER_WARNING. +--FILE-- + +--EXPECT-- +int(512) +int(512) +bool(true) diff --git a/Zend/tests/attributes/nodiscard/property_readonly_001.phpt b/Zend/tests/attributes/nodiscard/property_readonly_001.phpt new file mode 100644 index 0000000000000..6f9779023126e --- /dev/null +++ b/Zend/tests/attributes/nodiscard/property_readonly_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +#[\NoDiscard]: NoDiscard::$message is readonly. +--FILE-- +message = 'bar'; + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/property_readonly_002.phpt b/Zend/tests/attributes/nodiscard/property_readonly_002.phpt new file mode 100644 index 0000000000000..f01594ca4b26c --- /dev/null +++ b/Zend/tests/attributes/nodiscard/property_readonly_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\NoDiscard]: __construct() respects that properties are readonly. +--FILE-- +__construct("bar"); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Cannot modify readonly property NoDiscard::$message in %s:%d +Stack trace: +#0 %s(%d): NoDiscard->__construct('bar') +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/suppress_assign.phpt b/Zend/tests/attributes/nodiscard/suppress_assign.phpt new file mode 100644 index 0000000000000..b09f1a39718a8 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/suppress_assign.phpt @@ -0,0 +1,66 @@ +--TEST-- +#[\NoDiscard]: Assigning to variable suppresses. +--FILE-- +test(); +$_ = $cls->test2(); +$_ = call_user_func([$cls, "test"]); +$_ = Clazz::test3(); + +$_ = $closure(); + +$_ = $closure2(); + +?> +DONE +--EXPECT-- +DONE diff --git a/Zend/tests/attributes/nodiscard/suppress_cast.phpt b/Zend/tests/attributes/nodiscard/suppress_cast.phpt new file mode 100644 index 0000000000000..2f639371cb3dd --- /dev/null +++ b/Zend/tests/attributes/nodiscard/suppress_cast.phpt @@ -0,0 +1,66 @@ +--TEST-- +#[\NoDiscard]: Casting to (void) suppresses. +--FILE-- +test(); +(void)$cls->test2(); +(void)call_user_func([$cls, "test"]); +(void)Clazz::test3(); + +(void)$closure(); + +(void)$closure2(); + +?> +DONE +--EXPECT-- +DONE diff --git a/Zend/tests/attributes/nodiscard/suppress_cast_destructor.phpt b/Zend/tests/attributes/nodiscard/suppress_cast_destructor.phpt new file mode 100644 index 0000000000000..272e37305ea96 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/suppress_cast_destructor.phpt @@ -0,0 +1,31 @@ +--TEST-- +#[\NoDiscard]: Casting to (void) destroys the value. +--FILE-- + +--EXPECT-- +Before +WithDestructor::__destruct +After diff --git a/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt b/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt new file mode 100644 index 0000000000000..89bd9f9ffba4c --- /dev/null +++ b/Zend/tests/attributes/nodiscard/throwing_error_handler_001.phpt @@ -0,0 +1,35 @@ +--TEST-- +#[\NoDiscard]: Throwing error handler. +--FILE-- +getMessage(), PHP_EOL; +} + +#[\NoDiscard] +function test2(): stdClass { + return new stdClass(); +} + +try { + test2(); +} catch (ErrorException $e) { + echo "Caught: ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Caught: (B)The return value of function test() is expected to be consumed +Caught: (B)The return value of function test2() is expected to be consumed diff --git a/Zend/tests/attributes/nodiscard/type_validation_001.phpt b/Zend/tests/attributes/nodiscard/type_validation_001.phpt new file mode 100644 index 0000000000000..6c4ed19e8a862 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/type_validation_001.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\NoDiscard]: Type validation of $message parameter with int. +--FILE-- + +--EXPECTF-- +Warning: (B)The return value of function test() is expected to be consumed, 1234 in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/type_validation_002.phpt b/Zend/tests/attributes/nodiscard/type_validation_002.phpt new file mode 100644 index 0000000000000..254fae15ac47a --- /dev/null +++ b/Zend/tests/attributes/nodiscard/type_validation_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +#[\NoDiscard]: Type validation of $message parameter with int and strict types. +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, int given in %s:%d +Stack trace: +#0 %s(%d): NoDiscard->__construct(1234) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/type_validation_003.phpt b/Zend/tests/attributes/nodiscard/type_validation_003.phpt new file mode 100644 index 0000000000000..0b66addaea187 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/type_validation_003.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\NoDiscard]: Type validation of $message parameter with array. +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, array given in %s:%d +Stack trace: +#0 %s(%d): NoDiscard->__construct(Array) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/type_validation_004.phpt b/Zend/tests/attributes/nodiscard/type_validation_004.phpt new file mode 100644 index 0000000000000..44d7c19b54863 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/type_validation_004.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\NoDiscard]: Type validation of $message parameter with native enum case. +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught TypeError: NoDiscard::__construct(): Argument #1 ($message) must be of type ?string, Random\IntervalBoundary given in %s:%d +Stack trace: +#0 %s(%d): NoDiscard->__construct(Random\IntervalBoundary::ClosedOpen) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_clone.phpt b/Zend/tests/attributes/nodiscard/unsupported_clone.phpt new file mode 100644 index 0000000000000..7fadfea40fa6a --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_clone.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\NoDiscard]: Not allowed on '__clone'. +--FILE-- + +--EXPECTF-- +Fatal error: Method Clazz::__clone cannot be #[\NoDiscard] in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt b/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt new file mode 100644 index 0000000000000..a11c6379938d3 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_constructor.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\NoDiscard]: Not allowed on '__construct'. +--FILE-- + +--EXPECTF-- +Fatal error: Method Clazz::__construct cannot be #[\NoDiscard] in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt b/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt new file mode 100644 index 0000000000000..ee7a81acbcde9 --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_never_function.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\NoDiscard]: Not allowed on never function. +--FILE-- + +--EXPECTF-- +Fatal error: A never returning function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt b/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt new file mode 100644 index 0000000000000..ccedf3ede315a --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_property_hook_get.phpt @@ -0,0 +1,20 @@ +--TEST-- +#[\NoDiscard]: Not allowed on 'get' property hook. +--FILE-- +test; + +?> +--EXPECTF-- +Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt b/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt new file mode 100644 index 0000000000000..351593f5250bb --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_property_hook_set.phpt @@ -0,0 +1,20 @@ +--TEST-- +#[\NoDiscard]: Not allowed on 'set' property hook. +--FILE-- +test = $value; + } + } +} + +$cls = new Foo(); +$cls->test = 'foo'; + +?> +--EXPECTF-- +Fatal error: #[\NoDiscard] is not supported for property hooks in %s on line %d diff --git a/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt b/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt new file mode 100644 index 0000000000000..3c45f255473bc --- /dev/null +++ b/Zend/tests/attributes/nodiscard/unsupported_void_function.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\NoDiscard]: Not allowed on void function. +--FILE-- + +--EXPECTF-- +Fatal error: A void function does not return a value, but #[\NoDiscard] requires a return value in %s on line %d diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 8c239a952f32b..8b4a02aacfb1d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2693,6 +2693,12 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_ static void zend_check_magic_method_return_type(const zend_class_entry *ce, const zend_function *fptr, int error_type, int return_type) { + if (return_type == MAY_BE_VOID) { + if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) { + zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name)); + } + } + if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { /* For backwards compatibility reasons, do not enforce the return type if it is not set. */ return; @@ -2752,6 +2758,10 @@ static void zend_check_magic_method_no_return_type( zend_error_noreturn(error_type, "Method %s::%s() cannot declare a return type", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name)); } + + if (fptr->common.fn_flags & ZEND_ACC_NODISCARD) { + zend_error_noreturn(error_type, "Method %s::%s cannot be #[\\NoDiscard]", ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name)); + } } ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */ diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index d7bcb1f54e889..48f79c12610f6 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -31,6 +31,7 @@ ZEND_API zend_class_entry *zend_ce_sensitive_parameter; ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value; ZEND_API zend_class_entry *zend_ce_override; ZEND_API zend_class_entry *zend_ce_deprecated; +ZEND_API zend_class_entry *zend_ce_nodiscard; static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; @@ -193,6 +194,29 @@ ZEND_METHOD(Deprecated, __construct) } } +ZEND_METHOD(NoDiscard, __construct) +{ + zend_string *message = NULL; + zval value; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(message) + ZEND_PARSE_PARAMETERS_END(); + + if (message) { + ZVAL_STR(&value, message); + } else { + ZVAL_NULL(&value); + } + zend_update_property_ex(zend_ce_nodiscard, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); + + /* The assignment might fail due to 'readonly'. */ + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } +} + static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) { if (attributes) { @@ -520,6 +544,9 @@ void zend_register_attribute_ce(void) zend_ce_deprecated = register_class_Deprecated(); attr = zend_mark_internal_attribute(zend_ce_deprecated); + + zend_ce_nodiscard = register_class_NoDiscard(); + attr = zend_mark_internal_attribute(zend_ce_nodiscard); } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 8a825247c00f8..468488800ebf8 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -47,6 +47,7 @@ extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter; extern ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value; extern ZEND_API zend_class_entry *zend_ce_override; extern ZEND_API zend_class_entry *zend_ce_deprecated; +extern ZEND_API zend_class_entry *zend_ce_nodiscard; typedef struct { zend_string *name; diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index 0a35b0c57cb44..6351ccd771838 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -84,3 +84,14 @@ final class Deprecated public function __construct(?string $message = null, ?string $since = null) {} } + +/** + * @strict-properties + */ +#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION)] +final class NoDiscard +{ + public readonly ?string $message; + + public function __construct(?string $message = null) {} +} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 018caa47d0ac5..aecb216291071 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2358a0d820edd06a1702c84104bfd545af08311c */ + * Stub hash: 6b54bc195be211caabb395b621380681953c1f5a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -29,6 +29,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Deprecated___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, since, IS_STRING, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + ZEND_METHOD(Attribute, __construct); ZEND_METHOD(ReturnTypeWillChange, __construct); ZEND_METHOD(AllowDynamicProperties, __construct); @@ -38,6 +42,7 @@ ZEND_METHOD(SensitiveParameterValue, getValue); ZEND_METHOD(SensitiveParameterValue, __debugInfo); ZEND_METHOD(Override, __construct); ZEND_METHOD(Deprecated, __construct); +ZEND_METHOD(NoDiscard, __construct); static const zend_function_entry class_Attribute_methods[] = { ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC) @@ -76,6 +81,11 @@ static const zend_function_entry class_Deprecated_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_NoDiscard_methods[] = { + ZEND_ME(NoDiscard, __construct, arginfo_class_NoDiscard___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -253,3 +263,24 @@ static zend_class_entry *register_class_Deprecated(void) return class_entry; } + +static zend_class_entry *register_class_NoDiscard(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "NoDiscard", class_NoDiscard_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval property_message_default_value; + ZVAL_UNDEF(&property_message_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + + zend_string *attribute_name_Attribute_class_NoDiscard_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_NoDiscard_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_NoDiscard_0, 1); + zend_string_release(attribute_name_Attribute_class_NoDiscard_0); + zval attribute_Attribute_class_NoDiscard_0_arg0; + ZVAL_LONG(&attribute_Attribute_class_NoDiscard_0_arg0, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION); + ZVAL_COPY_VALUE(&attribute_Attribute_class_NoDiscard_0->args[0].value, &attribute_Attribute_class_NoDiscard_0_arg0); + + return class_entry; +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index debf46d126de6..8e83424688c85 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3927,7 +3927,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* ZEND_ASSERT(!(fbc->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)); if (fbc->type == ZEND_INTERNAL_FUNCTION && !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS)) { if (init_op->opcode == ZEND_INIT_FCALL && !zend_execute_internal) { - if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) { + if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD))) { return ZEND_DO_ICALL; } else { return ZEND_DO_FCALL_BY_NAME; @@ -3935,7 +3935,7 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc) /* } } else if (!(CG(compiler_options) & ZEND_COMPILE_IGNORE_USER_FUNCTIONS)){ if (zend_execute_ex == execute_ex) { - if (!(fbc->common.fn_flags & ZEND_ACC_DEPRECATED)) { + if (!(fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD))) { return ZEND_DO_UCALL; } else { return ZEND_DO_FCALL_BY_NAME; @@ -8377,6 +8377,35 @@ static zend_op_array *zend_compile_func_decl_ex( } } + zend_attribute *nodiscard_attribute = zend_get_attribute_str( + op_array->attributes, + "nodiscard", + sizeof("nodiscard")-1 + ); + + if (nodiscard_attribute) { + if (is_hook) { + zend_error_noreturn(E_COMPILE_ERROR, "#[\\NoDiscard] is not supported for property hooks"); + } + + if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + zend_arg_info *return_info = CG(active_op_array)->arg_info - 1; + if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_VOID)) { + zend_error_noreturn(E_COMPILE_ERROR, + "A void %s does not return a value, but #[\\NoDiscard] requires a return value", + CG(active_class_entry) != NULL ? "method" : "function"); + } + + if (ZEND_TYPE_CONTAINS_CODE(return_info->type, IS_NEVER)) { + zend_error_noreturn(E_COMPILE_ERROR, + "A never returning %s does not return a value, but #[\\NoDiscard] requires a return value", + CG(active_class_entry) != NULL ? "method" : "function"); + } + } + + op_array->fn_flags |= ZEND_ACC_NODISCARD; + } + zend_compile_stmt(stmt_ast); if (is_method) { @@ -10218,6 +10247,9 @@ static void zend_compile_cast(znode *result, zend_ast *ast) /* {{{ */ opline = zend_emit_op_tmp(result, ZEND_BOOL, &expr_node, NULL); } else if (ast->attr == IS_NULL) { zend_error(E_COMPILE_ERROR, "The (unset) cast is no longer supported"); + } else if (ast->attr == IS_UNDEF) { + opline = zend_emit_op_tmp(NULL, ZEND_CAST, &expr_node, NULL); + opline->extended_value = IS_UNDEF; } else { opline = zend_emit_op_tmp(result, ZEND_CAST, &expr_node, NULL); opline->extended_value = ast->attr; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 63572ab6623cc..14134051ef8e3 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -333,7 +333,7 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ -/* Function Flags (unused: 29-30) | | | */ +/* Function Flags (unused: 30) | | | */ /* ============== | | | */ /* | | | */ /* deprecation flag | | | */ @@ -395,6 +395,9 @@ typedef struct _zend_oparray_context { /* has #[\Override] attribute | | | */ #define ZEND_ACC_OVERRIDE (1 << 28) /* | X | | */ /* | | | */ +/* has #[\NoDiscard] attribute | | | */ +#define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 44021b785ba95..9237ea794c9d5 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1902,6 +1902,90 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi zend_string_release(message_suffix); } +ZEND_COLD static zend_result ZEND_FASTCALL get_nodiscard_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) +{ + *message_suffix = ZSTR_EMPTY_ALLOC(); + + if (!attributes) { + return SUCCESS; + } + + zend_attribute *nodiscard = zend_get_attribute_str(attributes, "nodiscard", sizeof("nodiscard")-1); + + if (!nodiscard) { + return SUCCESS; + } + + if (nodiscard->argc == 0) { + return SUCCESS; + } + + zend_result result = FAILURE; + + zend_string *message = ZSTR_EMPTY_ALLOC(); + + zval obj; + ZVAL_UNDEF(&obj); + zval *z; + + /* Construct the NoDiscard object to correctly handle parameter processing. */ + if (FAILURE == zend_get_attribute_object(&obj, zend_ce_nodiscard, nodiscard, scope, NULL)) { + goto out; + } + + /* Extract the $message property. */ + z = zend_read_property_ex(zend_ce_nodiscard, Z_OBJ_P(&obj), ZSTR_KNOWN(ZEND_STR_MESSAGE), false, NULL); + ZEND_ASSERT(z != &EG(uninitialized_zval)); + if (Z_TYPE_P(z) == IS_STRING) { + message = zend_string_copy(Z_STR_P(z)); + } + + /* Construct the suffix. */ + *message_suffix = zend_strpprintf_unchecked( + 0, + "%s%S", + ZSTR_LEN(message) > 0 ? ", " : "", + message + ); + + result = SUCCESS; + + out: + + zend_string_release(message); + zval_ptr_dtor(&obj); + + return result; +} + +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(char *prefix, const zend_function *fbc) +{ + zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); + + if (get_nodiscard_suffix_from_attribute(fbc->common.attributes, fbc->common.scope, &message_suffix) == FAILURE) { + return; + } + + int code = fbc->type == ZEND_INTERNAL_FUNCTION ? E_WARNING : E_USER_WARNING; + + if (fbc->common.scope) { + zend_error_unchecked(code, "%sThe return value of method %s::%s() is expected to be consumed%S", + prefix, + ZSTR_VAL(fbc->common.scope->name), + ZSTR_VAL(fbc->common.function_name), + message_suffix + ); + } else { + zend_error_unchecked(code, "%sThe return value of function %s() is expected to be consumed%S", + prefix, + ZSTR_VAL(fbc->common.function_name), + message_suffix + ); + } + + zend_string_release(message_suffix); +} + ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name) { zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index a1fbe049f3f99..a0607c114f40a 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -62,6 +62,7 @@ extern ZEND_API const zend_internal_function zend_pass_function; ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(char *prefix, const zend_function *fbc); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void); ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index d2a29e670d8bf..dc26d63367c26 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -217,6 +217,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_OBJECT_CAST "'(object)'" %token T_BOOL_CAST "'(bool)'" %token T_UNSET_CAST "'(unset)'" +%token T_VOID_CAST "'(void)'" %token T_OBJECT_OPERATOR "'->'" %token T_NULLSAFE_OBJECT_OPERATOR "'?->'" %token T_DOUBLE_ARROW "'=>'" @@ -534,6 +535,7 @@ statement: { $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); } | T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); } | T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); } + | T_VOID_CAST expr ';' { $$ = zend_ast_create_cast(IS_UNDEF, $2); } ; catch_list: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7ae73875926eb..4c883b81c5f7d 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1657,6 +1657,10 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_UNSET_CAST); } +"("{TABS_AND_SPACES}("void"){TABS_AND_SPACES}")" { + RETURN_TOKEN(T_VOID_CAST); +} + "eval" { RETURN_TOKEN_WITH_IDENT(T_EVAL); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index c36261f0fb2c9..836273de910ca 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1617,7 +1617,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC - | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_DEPRECATED)); + | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD)); if (fbc->common.attributes) { func->attributes = fbc->common.attributes; GC_TRY_ADDREF(func->attributes); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7e471b5acd8b6..fba030b5ac510 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4156,8 +4156,15 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL,OBSERVER)) SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!RETURN_VALUE_USED(opline)) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(B)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { UNDEF_RESULT(); if (!RETURN_VALUE_USED(opline)) { @@ -4260,8 +4267,15 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!RETURN_VALUE_USED(opline)) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(A)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); @@ -6418,6 +6432,9 @@ ZEND_VM_COLD_CONST_HANDLER(51, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE) case IS_STRING: ZVAL_STR(result, zval_get_string(expr)); break; + case IS_UNDEF: + FREE_OP1(); + ZEND_VM_NEXT_OPCODE(); default: ZEND_ASSERT(opline->extended_value != _IS_BOOL && "Must use ZEND_BOOL instead"); if (OP1_TYPE & (IS_VAR|IS_CV)) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9209399a5cdbf..24f0ec47e969a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1552,8 +1552,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!0) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(B)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { UNDEF_RESULT(); if (!0) { @@ -1654,8 +1661,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!1) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(B)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { UNDEF_RESULT(); if (!1) { @@ -1756,8 +1770,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!RETURN_VALUE_USED(opline)) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(B)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { UNDEF_RESULT(); if (!RETURN_VALUE_USED(opline)) { @@ -1860,8 +1881,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!0) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(A)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); @@ -1978,8 +2006,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!1) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(A)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); @@ -2096,8 +2131,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_OBS SAVE_OPLINE(); EX(call) = call->prev_execute_data; - if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { - zend_deprecated_function(fbc); + const uint32_t no_discard = (!RETURN_VALUE_USED(opline)) * ZEND_ACC_NODISCARD; + + if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_DEPRECATED|no_discard)) != 0)) { + if ((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0) { + zend_deprecated_function(fbc); + } + if ((fbc->common.fn_flags & no_discard) != 0 && EG(exception) == NULL) { + zend_nodiscard_function("(A)", fbc); + } if (UNEXPECTED(EG(exception) != NULL)) { if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); @@ -5165,6 +5207,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H case IS_STRING: ZVAL_STR(result, zval_get_string(expr)); break; + case IS_UNDEF: + + ZEND_VM_NEXT_OPCODE(); default: ZEND_ASSERT(opline->extended_value != _IS_BOOL && "Must use ZEND_BOOL instead"); if (IS_CONST & (IS_VAR|IS_CV)) { @@ -20083,6 +20128,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC case IS_STRING: ZVAL_STR(result, zval_get_string(expr)); break; + case IS_UNDEF: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + ZEND_VM_NEXT_OPCODE(); default: ZEND_ASSERT(opline->extended_value != _IS_BOOL && "Must use ZEND_BOOL instead"); if (IS_TMP_VAR & (IS_VAR|IS_CV)) { @@ -22750,6 +22798,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC case IS_STRING: ZVAL_STR(result, zval_get_string(expr)); break; + case IS_UNDEF: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + ZEND_VM_NEXT_OPCODE(); default: ZEND_ASSERT(opline->extended_value != _IS_BOOL && "Must use ZEND_BOOL instead"); if (IS_VAR & (IS_VAR|IS_CV)) { @@ -40985,6 +41036,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO case IS_STRING: ZVAL_STR(result, zval_get_string(expr)); break; + case IS_UNDEF: + + ZEND_VM_NEXT_OPCODE(); default: ZEND_ASSERT(opline->extended_value != _IS_BOOL && "Must use ZEND_BOOL instead"); if (IS_CV & (IS_VAR|IS_CV)) { diff --git a/build/gen_stub.php b/build/gen_stub.php index aef737b37e526..f3cd13fe8a52f 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -1516,9 +1516,13 @@ private function getArginfoFlagsByPhpVersions(): array } foreach ($this->attributes as $attr) { - if ($attr->class === "Deprecated") { - $flags[] = "ZEND_ACC_DEPRECATED"; - break; + switch ($attr->class) { + case "Deprecated": + $flags[] = "ZEND_ACC_DEPRECATED"; + break; + case "NoDiscard": + $flags[] = "ZEND_ACC_NODISCARD"; + break; } } diff --git a/ext/date/php_date.stub.php b/ext/date/php_date.stub.php index d0119aac88ad5..f375c60ff0a4c 100644 --- a/ext/date/php_date.stub.php +++ b/ext/date/php_date.stub.php @@ -524,29 +524,38 @@ public function getMicrosecond(): int {} public function diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::modify() does not modify the object itself")] public function modify(string $modifier): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::add() does not modify the object itself")] public function add(DateInterval $interval): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::sub() does not modify the object itself")] public function sub(DateInterval $interval): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::setTimezone() does not modify the object itself")] public function setTimezone(DateTimeZone $timezone): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::setTime() does not modify the object itself")] public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::setDate() does not modify the object itself")] public function setDate(int $year, int $month, int $day): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::setISODate() does not modify the object itself")] public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTimeImmutable {} /** @tentative-return-type */ + #[\NoDiscard(message: "as DateTimeImmutable::setTimestamp() does not modify the object itself")] public function setTimestamp(int $timestamp): DateTimeImmutable {} + #[\NoDiscard(message: "as DateTimeImmutable::setMicrosecond() does not modify the object itself")] public function setMicrosecond(int $microsecond): static {} /** @tentative-return-type */ diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 8ce0114206cfe..82e9f0f1718ae 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d7a318f6fd85e23c6352323e03c323035a511738 */ + * Stub hash: 093743b4fe7a698d1262cc1a81b60a85064fdccb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) @@ -725,15 +725,15 @@ static const zend_function_entry class_DateTimeImmutable_methods[] = { ZEND_RAW_FENTRY("getTimestamp", zif_date_timestamp_get, arginfo_class_DateTimeImmutable_getTimestamp, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getMicrosecond", zim_DateTime_getMicrosecond, arginfo_class_DateTimeImmutable_getMicrosecond, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("diff", zif_date_diff, arginfo_class_DateTimeImmutable_diff, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC) - ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC) + ZEND_ME(DateTimeImmutable, modify, arginfo_class_DateTimeImmutable_modify, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, add, arginfo_class_DateTimeImmutable_add, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, sub, arginfo_class_DateTimeImmutable_sub, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setTimezone, arginfo_class_DateTimeImmutable_setTimezone, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setTime, arginfo_class_DateTimeImmutable_setTime, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setDate, arginfo_class_DateTimeImmutable_setDate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setISODate, arginfo_class_DateTimeImmutable_setISODate, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setTimestamp, arginfo_class_DateTimeImmutable_setTimestamp, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) + ZEND_ME(DateTimeImmutable, setMicrosecond, arginfo_class_DateTimeImmutable_setMicrosecond, ZEND_ACC_PUBLIC|ZEND_ACC_NODISCARD) ZEND_ME(DateTimeImmutable, createFromMutable, arginfo_class_DateTimeImmutable_createFromMutable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(DateTimeImmutable, createFromInterface, arginfo_class_DateTimeImmutable_createFromInterface, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_FE_END @@ -989,6 +989,88 @@ static zend_class_entry *register_class_DateTimeImmutable(zend_class_entry *clas class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); zend_class_implements(class_entry, 1, class_entry_DateTimeInterface); + + zend_string *attribute_name_NoDiscard_func_modify_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), attribute_name_NoDiscard_func_modify_0, 1); + zend_string_release(attribute_name_NoDiscard_func_modify_0); + zval attribute_NoDiscard_func_modify_0_arg0; + zend_string *attribute_NoDiscard_func_modify_0_arg0_str = zend_string_init("as DateTimeImmutable::modify() does not modify the object itself", strlen("as DateTimeImmutable::modify() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_modify_0_arg0, attribute_NoDiscard_func_modify_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_modify_0->args[0].value, &attribute_NoDiscard_func_modify_0_arg0); + attribute_NoDiscard_func_modify_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_add_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), attribute_name_NoDiscard_func_add_0, 1); + zend_string_release(attribute_name_NoDiscard_func_add_0); + zval attribute_NoDiscard_func_add_0_arg0; + zend_string *attribute_NoDiscard_func_add_0_arg0_str = zend_string_init("as DateTimeImmutable::add() does not modify the object itself", strlen("as DateTimeImmutable::add() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_add_0_arg0, attribute_NoDiscard_func_add_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_add_0->args[0].value, &attribute_NoDiscard_func_add_0_arg0); + attribute_NoDiscard_func_add_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_sub_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), attribute_name_NoDiscard_func_sub_0, 1); + zend_string_release(attribute_name_NoDiscard_func_sub_0); + zval attribute_NoDiscard_func_sub_0_arg0; + zend_string *attribute_NoDiscard_func_sub_0_arg0_str = zend_string_init("as DateTimeImmutable::sub() does not modify the object itself", strlen("as DateTimeImmutable::sub() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_sub_0_arg0, attribute_NoDiscard_func_sub_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_sub_0->args[0].value, &attribute_NoDiscard_func_sub_0_arg0); + attribute_NoDiscard_func_sub_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_settimezone_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), attribute_name_NoDiscard_func_settimezone_0, 1); + zend_string_release(attribute_name_NoDiscard_func_settimezone_0); + zval attribute_NoDiscard_func_settimezone_0_arg0; + zend_string *attribute_NoDiscard_func_settimezone_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimezone() does not modify the object itself", strlen("as DateTimeImmutable::setTimezone() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_settimezone_0_arg0, attribute_NoDiscard_func_settimezone_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimezone_0->args[0].value, &attribute_NoDiscard_func_settimezone_0_arg0); + attribute_NoDiscard_func_settimezone_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_settime_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), attribute_name_NoDiscard_func_settime_0, 1); + zend_string_release(attribute_name_NoDiscard_func_settime_0); + zval attribute_NoDiscard_func_settime_0_arg0; + zend_string *attribute_NoDiscard_func_settime_0_arg0_str = zend_string_init("as DateTimeImmutable::setTime() does not modify the object itself", strlen("as DateTimeImmutable::setTime() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_settime_0_arg0, attribute_NoDiscard_func_settime_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settime_0->args[0].value, &attribute_NoDiscard_func_settime_0_arg0); + attribute_NoDiscard_func_settime_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_setdate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), attribute_name_NoDiscard_func_setdate_0, 1); + zend_string_release(attribute_name_NoDiscard_func_setdate_0); + zval attribute_NoDiscard_func_setdate_0_arg0; + zend_string *attribute_NoDiscard_func_setdate_0_arg0_str = zend_string_init("as DateTimeImmutable::setDate() does not modify the object itself", strlen("as DateTimeImmutable::setDate() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_setdate_0_arg0, attribute_NoDiscard_func_setdate_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setdate_0->args[0].value, &attribute_NoDiscard_func_setdate_0_arg0); + attribute_NoDiscard_func_setdate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_setisodate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), attribute_name_NoDiscard_func_setisodate_0, 1); + zend_string_release(attribute_name_NoDiscard_func_setisodate_0); + zval attribute_NoDiscard_func_setisodate_0_arg0; + zend_string *attribute_NoDiscard_func_setisodate_0_arg0_str = zend_string_init("as DateTimeImmutable::setISODate() does not modify the object itself", strlen("as DateTimeImmutable::setISODate() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_setisodate_0_arg0, attribute_NoDiscard_func_setisodate_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setisodate_0->args[0].value, &attribute_NoDiscard_func_setisodate_0_arg0); + attribute_NoDiscard_func_setisodate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_settimestamp_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), attribute_name_NoDiscard_func_settimestamp_0, 1); + zend_string_release(attribute_name_NoDiscard_func_settimestamp_0); + zval attribute_NoDiscard_func_settimestamp_0_arg0; + zend_string *attribute_NoDiscard_func_settimestamp_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimestamp() does not modify the object itself", strlen("as DateTimeImmutable::setTimestamp() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_settimestamp_0_arg0, attribute_NoDiscard_func_settimestamp_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_settimestamp_0->args[0].value, &attribute_NoDiscard_func_settimestamp_0_arg0); + attribute_NoDiscard_func_settimestamp_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_setmicrosecond_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), attribute_name_NoDiscard_func_setmicrosecond_0, 1); + zend_string_release(attribute_name_NoDiscard_func_setmicrosecond_0); + zval attribute_NoDiscard_func_setmicrosecond_0_arg0; + zend_string *attribute_NoDiscard_func_setmicrosecond_0_arg0_str = zend_string_init("as DateTimeImmutable::setMicrosecond() does not modify the object itself", strlen("as DateTimeImmutable::setMicrosecond() does not modify the object itself"), 1); + ZVAL_STR(&attribute_NoDiscard_func_setmicrosecond_0_arg0, attribute_NoDiscard_func_setmicrosecond_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_setmicrosecond_0->args[0].value, &attribute_NoDiscard_func_setmicrosecond_0_arg0); + attribute_NoDiscard_func_setmicrosecond_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + return class_entry; } diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 1934610dc0fed..2ed8415ec3190 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2732,6 +2732,7 @@ function proc_nice(int $priority): bool {} * @param resource $stream * @param int $would_block */ +#[\NoDiscard(message: "as locking the stream might have failed")] function flock($stream, int $operation, &$would_block = null): bool {} /** diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 61feae1d88c68..65636a6eb0bcb 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: b0a0ccc94c6db2831f7f0b8e67562cd6a734fcdf */ + * Stub hash: 3c0741b456b5caca376d2e6a0780c0415bb28734 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -3205,7 +3205,7 @@ static const zend_function_entry ext_functions[] = { #if defined(HAVE_NICE) ZEND_FE(proc_nice, arginfo_proc_nice) #endif - ZEND_FE(flock, arginfo_flock) + ZEND_RAW_FENTRY("flock", zif_flock, arginfo_flock, ZEND_ACC_NODISCARD, NULL, NULL) ZEND_FE(get_meta_tags, arginfo_get_meta_tags) ZEND_FE(pclose, arginfo_pclose) ZEND_FE(popen, arginfo_popen) @@ -4039,6 +4039,15 @@ static void register_basic_functions_symbols(int module_number) ZVAL_COPY_VALUE(&attribute_Deprecated_func_utf8_decode_0->args[1].value, &attribute_Deprecated_func_utf8_decode_0_arg1); attribute_Deprecated_func_utf8_decode_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_string *attribute_name_NoDiscard_func_flock_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_flock_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "flock", sizeof("flock") - 1), attribute_name_NoDiscard_func_flock_0, 1); + zend_string_release(attribute_name_NoDiscard_func_flock_0); + zval attribute_NoDiscard_func_flock_0_arg0; + zend_string *attribute_NoDiscard_func_flock_0_arg0_str = zend_string_init("as locking the stream might have failed", strlen("as locking the stream might have failed"), 1); + ZVAL_STR(&attribute_NoDiscard_func_flock_0_arg0, attribute_NoDiscard_func_flock_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_flock_0->args[0].value, &attribute_NoDiscard_func_flock_0_arg0); + attribute_NoDiscard_func_flock_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_hash", sizeof("password_hash") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "password_verify", sizeof("password_verify") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); diff --git a/ext/standard/tests/file/userstreams_004.phpt b/ext/standard/tests/file/userstreams_004.phpt index 959f02f5b1cd7..96ea479b46af6 100644 --- a/ext/standard/tests/file/userstreams_004.phpt +++ b/ext/standard/tests/file/userstreams_004.phpt @@ -19,7 +19,7 @@ class test_wrapper extends test_wrapper_base { } function test($name, $fd, $mode) { echo "------ $name: -------\n"; - flock($fd, $mode); + $_ = flock($fd, $mode); $data = stream_get_meta_data($fd); var_dump($data['wrapper_data']->mode === $mode); } diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a046ab50e1498..a1e131032bcfb 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -153,6 +153,7 @@ char *get_token_type_name(int token_type) case T_OBJECT_CAST: return "T_OBJECT_CAST"; case T_BOOL_CAST: return "T_BOOL_CAST"; case T_UNSET_CAST: return "T_UNSET_CAST"; + case T_VOID_CAST: return "T_VOID_CAST"; case T_OBJECT_OPERATOR: return "T_OBJECT_OPERATOR"; case T_NULLSAFE_OBJECT_OPERATOR: return "T_NULLSAFE_OBJECT_OPERATOR"; case T_DOUBLE_ARROW: return "T_DOUBLE_ARROW"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index 45f3c89f2de3a..c1e1fd254dfaa 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -642,6 +642,11 @@ * @cvalue T_UNSET_CAST */ const T_UNSET_CAST = UNKNOWN; +/** + * @var int + * @cvalue T_VOID_CAST + */ +const T_VOID_CAST = UNKNOWN; /** * @var int * @cvalue T_OBJECT_OPERATOR diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 61f6ac1ec3659..9c488d19f1890 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: d917cab61a2b436a16d2227cdb438add45e42d69 */ + * Stub hash: 19d25d22098f46283b517352cbb302db962b50fd */ static void register_tokenizer_data_symbols(int module_number) { @@ -131,6 +131,7 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_OBJECT_CAST", T_OBJECT_CAST, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_BOOL_CAST", T_BOOL_CAST, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_UNSET_CAST", T_UNSET_CAST, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_VOID_CAST", T_VOID_CAST, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_OBJECT_OPERATOR", T_OBJECT_OPERATOR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_NULLSAFE_OBJECT_OPERATOR", T_NULLSAFE_OBJECT_OPERATOR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_DOUBLE_ARROW", T_DOUBLE_ARROW, CONST_PERSISTENT); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index bcc3351e03313..aad4128ed8ce2 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -123,6 +123,13 @@ static ZEND_FUNCTION(zend_test_deprecated_attr) ZEND_PARSE_PARAMETERS_NONE(); } +static ZEND_FUNCTION(zend_test_deprecated_nodiscard) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_LONG(1); +} + /* Create a string without terminating null byte. Must be terminated with * zend_terminate_string() before destruction, otherwise a warning is issued * in debug builds. */ diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 59cb9661e4e43..88007ae01fe56 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -218,6 +218,11 @@ function zend_test_deprecated(mixed $arg = null): void {} #[\Deprecated(message: "custom message")] function zend_test_deprecated_attr(): void {} + + #[\Deprecated(message: "custom message")] + #[\NoDiscard(message: "custom message 2")] + function zend_test_deprecated_nodiscard(): int {} + /** @alias zend_test_void_return */ function zend_test_aliased(): void {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index c558b58f65169..fc9a4e654bc7d 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3082e62e96d5f4383c98638513463c676a7c3a69 */ + * Stub hash: 9e70ab275967137c764d0983ea09a95f7594e799 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -22,6 +22,9 @@ ZEND_END_ARG_INFO() #define arginfo_zend_test_deprecated_attr arginfo_zend_test_void_return +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_deprecated_nodiscard, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_zend_test_aliased arginfo_zend_test_void_return #define arginfo_zend_test_deprecated_aliased arginfo_zend_test_void_return @@ -126,8 +129,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_is_string_marked_as_va ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_map_ptr_last, 0, 0, IS_LONG, 0) -ZEND_END_ARG_INFO() +#define arginfo_zend_get_map_ptr_last arginfo_zend_test_deprecated_nodiscard ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_crash, 0, 0, IS_VOID, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") @@ -185,7 +187,7 @@ ZEND_END_ARG_INFO() #define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_aliased_func arginfo_zend_test_void_return -#define arginfo_class__ZendTestClass_is_object arginfo_zend_get_map_ptr_last +#define arginfo_class__ZendTestClass_is_object arginfo_zend_test_deprecated_nodiscard #define arginfo_class__ZendTestClass___toString arginfo_zend_get_current_func_name @@ -260,6 +262,7 @@ static ZEND_FUNCTION(zend_test_void_return); static ZEND_FUNCTION(zend_test_compile_string); static ZEND_FUNCTION(zend_test_deprecated); static ZEND_FUNCTION(zend_test_deprecated_attr); +static ZEND_FUNCTION(zend_test_deprecated_nodiscard); static ZEND_FUNCTION(zend_create_unterminated_string); static ZEND_FUNCTION(zend_terminate_string); static ZEND_FUNCTION(zend_leak_variable); @@ -355,6 +358,11 @@ static const zend_function_entry ext_functions[] = { #else ZEND_RAW_FENTRY("zend_test_deprecated_attr", zif_zend_test_deprecated_attr, arginfo_zend_test_deprecated_attr, ZEND_ACC_DEPRECATED) #endif +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("zend_test_deprecated_nodiscard", zif_zend_test_deprecated_nodiscard, arginfo_zend_test_deprecated_nodiscard, ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD, NULL, NULL) +#else + ZEND_RAW_FENTRY("zend_test_deprecated_nodiscard", zif_zend_test_deprecated_nodiscard, arginfo_zend_test_deprecated_nodiscard, ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD) +#endif #if (PHP_VERSION_ID >= 80400) ZEND_RAW_FENTRY("zend_test_aliased", zif_zend_test_void_return, arginfo_zend_test_aliased, 0, NULL, NULL) #else @@ -560,6 +568,24 @@ static void register_test_symbols(int module_number) ZVAL_COPY_VALUE(&attribute_Deprecated_func_zend_test_deprecated_attr_0->args[0].value, &attribute_Deprecated_func_zend_test_deprecated_attr_0_arg0); attribute_Deprecated_func_zend_test_deprecated_attr_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_string *attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0 = zend_string_init_interned("Deprecated", sizeof("Deprecated") - 1, 1); + zend_attribute *attribute_Deprecated_func_zend_test_deprecated_nodiscard_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_deprecated_nodiscard", sizeof("zend_test_deprecated_nodiscard") - 1), attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0, 1); + zend_string_release(attribute_name_Deprecated_func_zend_test_deprecated_nodiscard_0); + zval attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0; + zend_string *attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0_str = zend_string_init("custom message", strlen("custom message"), 1); + ZVAL_STR(&attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0, attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_func_zend_test_deprecated_nodiscard_0->args[0].value, &attribute_Deprecated_func_zend_test_deprecated_nodiscard_0_arg0); + attribute_Deprecated_func_zend_test_deprecated_nodiscard_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_string *attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, 1); + zend_attribute *attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_deprecated_nodiscard", sizeof("zend_test_deprecated_nodiscard") - 1), attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1, 1); + zend_string_release(attribute_name_NoDiscard_func_zend_test_deprecated_nodiscard_1); + zval attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0; + zend_string *attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0_str = zend_string_init("custom message 2", strlen("custom message 2"), 1); + ZVAL_STR(&attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0, attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0_str); + ZVAL_COPY_VALUE(&attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1->args[0].value, &attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1_arg0); + attribute_NoDiscard_func_zend_test_deprecated_nodiscard_1->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zend_string *attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0 = zend_string_init_interned("ZendTestParameterAttribute", sizeof("ZendTestParameterAttribute") - 1, 1); zend_attribute *attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0 = zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_parameter_with_attribute", sizeof("zend_test_parameter_with_attribute") - 1), 0, attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0, 1); zend_string_release(attribute_name_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0);