Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Zend/tests/type_declarations/gh14874.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-14874 (Incorrect lineno for property and class const variance check)
--FILE--
<?php

class C extends P {
public const int X = 42;
}

class P {
public const string X = 'X';
}

?>
--EXPECTF--
Fatal error: Type of C::X must be compatible with P::X of type string in %s on line 4
2 changes: 1 addition & 1 deletion Zend/tests/type_declarations/typed_properties_006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ class Bar extends Foo {
}
?>
--EXPECTF--
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7
2 changes: 1 addition & 1 deletion Zend/tests/type_declarations/typed_properties_007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ class Bar extends Foo {
}
?>
--EXPECTF--
Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 9
Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 10
2 changes: 1 addition & 1 deletion Zend/tests/type_declarations/typed_properties_008.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ class Bar extends Foo {
}
?>
--EXPECTF--
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7
2 changes: 2 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -4561,6 +4561,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z

property_info->name = zend_new_interned_string(property_info->name);
property_info->flags = access_type;
property_info->line = 0;
property_info->doc_comment = doc_comment;
property_info->attributes = NULL;
property_info->prototype = property_info;
Expand Down Expand Up @@ -4805,6 +4806,7 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry
c->attributes = NULL;
c->ce = ce;
c->type = type;
c->line = 0;

if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8264,6 +8264,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
scope, name, &default_value,
property_flags | (zend_property_is_virtual(scope, name, hooks_ast) ? ZEND_ACC_VIRTUAL : 0) | ZEND_ACC_PROMOTED,
doc_comment, type);
prop->line = param_ast->lineno;
if (hooks_ast) {
const zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
zend_compile_property_hooks(prop, name, type_ast, hooks);
Expand Down Expand Up @@ -9253,6 +9254,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
}

info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type);
info->line = prop_ast->lineno;

if (hooks_ast) {
zend_compile_property_hooks(info, name, type_ast, zend_ast_get_list(hooks_ast));
Expand Down Expand Up @@ -9339,6 +9341,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
}

c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type);
c->line = const_ast->lineno;

if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
Expand Down Expand Up @@ -9800,6 +9803,7 @@ static void zend_compile_enum_case(zend_ast *ast)

zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, doc_comment);
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
c->line = ast->lineno;
zend_ast_destroy(const_enum_init_ast);

zend_ast *attr_ast = ast->child[3];
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ typedef struct _zend_property_info {
uint32_t offset; /* property offset for object properties or
property index for static properties */
uint32_t flags;
uint32_t line;
zend_string *name;
zend_string *doc_comment;
HashTable *attributes;
Expand All @@ -493,6 +494,7 @@ typedef struct _zend_class_constant {
HashTable *attributes;
zend_class_entry *ce;
zend_type type;
uint32_t line;
} zend_class_constant;

#define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value)
Expand Down
75 changes: 53 additions & 22 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -1308,27 +1308,48 @@ static inheritance_status full_property_types_compatible(
static ZEND_COLD void emit_incompatible_property_error(
const zend_property_info *child, const zend_property_info *parent, prop_variance variance) {
zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce);
zend_error_noreturn(E_COMPILE_ERROR,
"Type of %s::$%s must be %s%s (as in class %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
variance == PROP_INVARIANT ? "" :
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
ZSTR_VAL(type_str),
ZSTR_VAL(parent->ce->name));
if (child->line && child->ce->type == ZEND_USER_CLASS) {
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
"Type of %s::$%s must be %s%s (as in class %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
variance == PROP_INVARIANT ? "" :
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
ZSTR_VAL(type_str),
ZSTR_VAL(parent->ce->name));
} else {
zend_error_noreturn(E_COMPILE_ERROR,
"Type of %s::$%s must be %s%s (as in class %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
variance == PROP_INVARIANT ? "" :
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
ZSTR_VAL(type_str),
ZSTR_VAL(parent->ce->name));
}
}

static ZEND_COLD void emit_set_hook_type_error(const zend_property_info *child, const zend_property_info *parent)
{
zend_type set_type = parent->hooks[ZEND_PROPERTY_HOOK_SET]->common.arg_info[0].type;
zend_string *type_str = zend_type_to_string_resolved(set_type, parent->ce);
zend_error_noreturn(E_COMPILE_ERROR,
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
ZSTR_VAL(type_str),
zend_get_object_type_case(parent->ce, false),
ZSTR_VAL(parent->ce->name));
if (child->line && child->ce->type == ZEND_USER_CLASS) {
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
ZSTR_VAL(type_str),
zend_get_object_type_case(parent->ce, false),
ZSTR_VAL(parent->ce->name));
} else {
zend_error_noreturn(E_COMPILE_ERROR,
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
ZSTR_VAL(child->ce->name),
zend_get_unmangled_property_name(child->name),
ZSTR_VAL(type_str),
zend_get_object_type_case(parent->ce, false),
ZSTR_VAL(parent->ce->name));
}
}

static inheritance_status verify_property_type_compatibility(
Expand Down Expand Up @@ -1621,13 +1642,23 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
static void emit_incompatible_class_constant_error(
const zend_class_constant *child, const zend_class_constant *parent, const zend_string *const_name) {
zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce);
zend_error_noreturn(E_COMPILE_ERROR,
"Type of %s::%s must be compatible with %s::%s of type %s",
ZSTR_VAL(child->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(parent->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(type_str));
if (child->line && child->ce->type == ZEND_USER_CLASS) {
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
"Type of %s::%s must be compatible with %s::%s of type %s",
ZSTR_VAL(child->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(parent->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(type_str));
} else {
zend_error_noreturn(E_COMPILE_ERROR,
"Type of %s::%s must be compatible with %s::%s of type %s",
ZSTR_VAL(child->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(parent->ce->name),
ZSTR_VAL(const_name),
ZSTR_VAL(type_str));
}
}

static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)
Expand Down
Loading