Skip to content

Commit adfdfb2

Browse files
authored
Improvements in modifier parsing (php#9926)
Use a shared non-terminal for all class modifiers. This avoids conflicts when adding modifiers that are only valid for certain targets. This change is necessary for asymmetric visibility but might be useful for other future additions. Closes phpGH-9926
1 parent 2cf03b6 commit adfdfb2

21 files changed

+179
-76
lines changed

Zend/tests/access_modifiers_007.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class test {
1010
echo "Done\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Cannot use the final modifier on an abstract class member in %s on line %d
13+
Fatal error: Cannot use the final modifier on an abstract method in %s on line %d

Zend/tests/ctor_promotion_additional_modifiers.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ class Test {
99

1010
?>
1111
--EXPECTF--
12-
Parse error: syntax error, unexpected token "static", expecting variable in %s on line %d
12+
Fatal error: Cannot use the static modifier on a promoted property in %s on line %d

Zend/tests/errmsg_037.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class test {
1010
echo "Done\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Properties cannot be declared abstract in %s on line %d
13+
Fatal error: Cannot use the abstract modifier on a property in %s on line %d

Zend/tests/errmsg_038.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class test {
1010
echo "Done\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods, classes, and class constants in %s on line %d
13+
Fatal error: Cannot use the final modifier on a property in %s on line %d

Zend/tests/readonly_props/readonly_const.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ class Test {
99

1010
?>
1111
--EXPECTF--
12-
Fatal error: Cannot use 'readonly' as constant modifier in %s on line %d
12+
Fatal error: Cannot use the readonly modifier on a class constant in %s on line %d

Zend/tests/readonly_props/readonly_method.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ class Test {
99

1010
?>
1111
--EXPECTF--
12-
Fatal error: Cannot use 'readonly' as method modifier in %s on line %d
12+
Fatal error: Cannot use the readonly modifier on a method in %s on line %d

Zend/tests/readonly_props/readonly_method_trait.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ class Test {
99

1010
?>
1111
--EXPECTF--
12-
Fatal error: Cannot use 'readonly' as method modifier in %s on line %d
12+
Fatal error: Cannot use the readonly modifier on a method in %s on line %d

Zend/tests/traits/error_013.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ var_dump($x->test());
1616

1717
?>
1818
--EXPECTF--
19-
Fatal error: Cannot use 'static' as method modifier in %s on line %d
19+
Fatal error: Cannot use "static" as method modifier in trait alias in %s on line %d

Zend/tests/traits/language018.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ class C1 {
1212
}
1313
?>
1414
--EXPECTF--
15-
Fatal error: Cannot use 'abstract' as method modifier in %s on line %d
15+
Fatal error: Cannot use "abstract" as method modifier in trait alias in %s on line %d

Zend/tests/traits/language019.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ class C1 {
1212
}
1313
?>
1414
--EXPECTF--
15-
Fatal error: Cannot use 'final' as method modifier in %s on line %d
15+
Fatal error: Cannot use "final" as method modifier in trait alias in %s on line %d

Zend/tests/type_declarations/static_type_param.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ class Test {
1212

1313
?>
1414
--EXPECTF--
15-
Parse error: syntax error, unexpected token "static", expecting variable in %s on line %d
15+
Fatal error: Cannot use the static modifier on a promoted property in %s on line %d

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum _zend_ast_kind {
6666
ZEND_AST_ATTRIBUTE_LIST,
6767
ZEND_AST_ATTRIBUTE_GROUP,
6868
ZEND_AST_MATCH_ARM_LIST,
69+
ZEND_AST_MODIFIER_LIST,
6970

7071
/* 0 child nodes */
7172
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,

Zend/zend_compile.c

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,96 @@ static void zend_do_free(znode *op1) /* {{{ */
787787
}
788788
/* }}} */
789789

790+
791+
static char *zend_modifier_token_to_string(uint32_t token)
792+
{
793+
switch (token) {
794+
case T_PUBLIC:
795+
return "public";
796+
case T_PROTECTED:
797+
return "protected";
798+
case T_PRIVATE:
799+
return "private";
800+
case T_STATIC:
801+
return "static";
802+
case T_FINAL:
803+
return "final";
804+
case T_READONLY:
805+
return "readonly";
806+
case T_ABSTRACT:
807+
return "abstract";
808+
EMPTY_SWITCH_DEFAULT_CASE()
809+
}
810+
}
811+
812+
uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t token)
813+
{
814+
switch (token) {
815+
case T_PUBLIC:
816+
return ZEND_ACC_PUBLIC;
817+
case T_PROTECTED:
818+
return ZEND_ACC_PROTECTED;
819+
case T_PRIVATE:
820+
return ZEND_ACC_PRIVATE;
821+
case T_READONLY:
822+
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_CPP) {
823+
return ZEND_ACC_READONLY;
824+
}
825+
break;
826+
case T_ABSTRACT:
827+
if (target == ZEND_MODIFIER_TARGET_METHOD) {
828+
return ZEND_ACC_ABSTRACT;
829+
}
830+
break;
831+
case T_FINAL:
832+
if (target == ZEND_MODIFIER_TARGET_METHOD || target == ZEND_MODIFIER_TARGET_CONSTANT) {
833+
return ZEND_ACC_FINAL;
834+
}
835+
break;
836+
case T_STATIC:
837+
if (target == ZEND_MODIFIER_TARGET_PROPERTY || target == ZEND_MODIFIER_TARGET_METHOD) {
838+
return ZEND_ACC_STATIC;
839+
}
840+
break;
841+
}
842+
843+
char *member;
844+
if (target == ZEND_MODIFIER_TARGET_PROPERTY) {
845+
member = "property";
846+
} else if (target == ZEND_MODIFIER_TARGET_METHOD) {
847+
member = "method";
848+
} else if (target == ZEND_MODIFIER_TARGET_CONSTANT) {
849+
member = "class constant";
850+
} else if (target == ZEND_MODIFIER_TARGET_CPP) {
851+
member = "promoted property";
852+
} else {
853+
ZEND_UNREACHABLE();
854+
}
855+
856+
zend_throw_exception_ex(zend_ce_compile_error, 0,
857+
"Cannot use the %s modifier on a %s", zend_modifier_token_to_string(token), member);
858+
return 0;
859+
}
860+
861+
uint32_t zend_modifier_list_to_flags(zend_modifier_target target, zend_ast *modifiers)
862+
{
863+
uint32_t flags = 0;
864+
zend_ast_list *modifier_list = zend_ast_get_list(modifiers);
865+
866+
for (uint32_t i = 0; i < modifier_list->children; i++) {
867+
uint32_t new_flag = zend_modifier_token_to_flag(target, (uint32_t) Z_LVAL_P(zend_ast_get_zval(modifier_list->child[i])));
868+
if (!new_flag) {
869+
return 0;
870+
}
871+
flags = zend_add_member_modifier(flags, new_flag, target);
872+
if (!flags) {
873+
return 0;
874+
}
875+
}
876+
877+
return flags;
878+
}
879+
790880
uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
791881
{
792882
uint32_t new_flags = flags | new_flag;
@@ -812,7 +902,7 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
812902
}
813903
/* }}} */
814904

815-
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
905+
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag, zend_modifier_target target) /* {{{ */
816906
{
817907
uint32_t new_flags = flags | new_flag;
818908
if ((flags & ZEND_ACC_PPP_MASK) && (new_flag & ZEND_ACC_PPP_MASK)) {
@@ -837,9 +927,9 @@ uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
837927
"Multiple readonly modifiers are not allowed", 0);
838928
return 0;
839929
}
840-
if ((new_flags & ZEND_ACC_ABSTRACT) && (new_flags & ZEND_ACC_FINAL)) {
930+
if (target == ZEND_MODIFIER_TARGET_METHOD && (new_flags & ZEND_ACC_ABSTRACT) && (new_flags & ZEND_ACC_FINAL)) {
841931
zend_throw_exception(zend_ce_compile_error,
842-
"Cannot use the final modifier on an abstract class member", 0);
932+
"Cannot use the final modifier on an abstract method", 0);
843933
return 0;
844934
}
845935
return new_flags;
@@ -7474,10 +7564,6 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
74747564
zend_error_noreturn(E_COMPILE_ERROR, "Enum %s cannot include properties", ZSTR_VAL(ce->name));
74757565
}
74767566

7477-
if (flags & ZEND_ACC_ABSTRACT) {
7478-
zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract");
7479-
}
7480-
74817567
for (i = 0; i < children; ++i) {
74827568
zend_property_info *info;
74837569
zend_ast *prop_ast = list->child[i];
@@ -7505,12 +7591,6 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
75057591
doc_comment = zend_string_copy(zend_ast_get_str(doc_comment_ast));
75067592
}
75077593

7508-
if (flags & ZEND_ACC_FINAL) {
7509-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, "
7510-
"the final modifier is allowed only for methods, classes, and class constants",
7511-
ZSTR_VAL(ce->name), ZSTR_VAL(name));
7512-
}
7513-
75147594
if (zend_hash_exists(&ce->properties_info, name)) {
75157595
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare %s::$%s",
75167596
ZSTR_VAL(ce->name), ZSTR_VAL(name));
@@ -7583,16 +7663,14 @@ static void zend_compile_prop_group(zend_ast *ast) /* {{{ */
75837663
}
75847664
/* }}} */
75857665

7586-
static void zend_check_const_and_trait_alias_attr(uint32_t attr, const char* entity) /* {{{ */
7666+
static void zend_check_trait_alias_modifiers(uint32_t attr) /* {{{ */
75877667
{
75887668
if (attr & ZEND_ACC_STATIC) {
7589-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'static' as %s modifier", entity);
7669+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"static\" as method modifier in trait alias");
75907670
} else if (attr & ZEND_ACC_ABSTRACT) {
7591-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'abstract' as %s modifier", entity);
7671+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"abstract\" as method modifier in trait alias");
75927672
} else if (attr & ZEND_ACC_FINAL) {
7593-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'final' as %s modifier", entity);
7594-
} else if (attr & ZEND_ACC_READONLY) {
7595-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use 'readonly' as %s modifier", entity);
7673+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use \"final\" as method modifier in trait alias");
75967674
}
75977675
}
75987676
/* }}} */
@@ -7613,10 +7691,6 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
76137691
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
76147692
zval value_zv;
76157693

7616-
if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_READONLY))) {
7617-
zend_check_const_and_trait_alias_attr(flags, "constant");
7618-
}
7619-
76207694
if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) {
76217695
zend_error_noreturn(
76227696
E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is not visible to other classes",
@@ -7687,7 +7761,7 @@ static void zend_compile_trait_alias(zend_ast *ast) /* {{{ */
76877761

76887762
zend_trait_alias *alias;
76897763

7690-
zend_check_const_and_trait_alias_attr(modifiers, "method");
7764+
zend_check_trait_alias_modifiers(modifiers);
76917765

76927766
alias = emalloc(sizeof(zend_trait_alias));
76937767
zend_compile_method_ref(method_ref_ast, &alias->trait_method);

Zend/zend_compile.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,11 +806,22 @@ ZEND_API binary_op_type get_binary_op(int opcode);
806806
void zend_stop_lexing(void);
807807
void zend_emit_final_return(bool return_one);
808808

809+
typedef enum {
810+
ZEND_MODIFIER_TARGET_PROPERTY = 0,
811+
ZEND_MODIFIER_TARGET_METHOD,
812+
ZEND_MODIFIER_TARGET_CONSTANT,
813+
ZEND_MODIFIER_TARGET_CPP,
814+
} zend_modifier_target;
815+
809816
/* Used during AST construction */
810817
zend_ast *zend_ast_append_str(zend_ast *left, zend_ast *right);
811818
zend_ast *zend_negate_num_string(zend_ast *ast);
812819
uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag);
813-
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag);
820+
uint32_t zend_add_member_modifier(uint32_t flags, uint32_t new_flag, zend_modifier_target target);
821+
822+
uint32_t zend_modifier_token_to_flag(zend_modifier_target target, uint32_t flags);
823+
uint32_t zend_modifier_list_to_flags(zend_modifier_target target, zend_ast *modifiers);
824+
814825
bool zend_handle_encoding_declaration(zend_ast *ast);
815826

816827
ZEND_API zend_class_entry *zend_bind_class_in_slot(

0 commit comments

Comments
 (0)