From 37e3ac3870e8e65885b044ef24092e09bbb5d773 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Thu, 14 Oct 2021 19:21:28 +0300 Subject: [PATCH 1/4] added support for final constants --- compiler/data/class-data.cpp | 2 +- compiler/data/class-members.cpp | 10 ++- compiler/data/class-members.h | 6 +- compiler/gentree.cpp | 87 +++++++++++-------- compiler/gentree.h | 4 +- compiler/pipes/sort-and-inherit-classes.cpp | 17 ++++ tests/phpt/access/constants/04_interface.php | 4 +- .../final_constants/001_final_constants.php | 7 ++ ...irited_interface_constant_in_interface.php | 18 ++++ .../final_constants/003_redeclaration.php | 11 +++ .../final_constants/004_private_final.php | 7 ++ .../005_redeclaration_in_interface.php | 10 +++ .../006_redeclaration_interface_const.php | 11 +++ ..._inhirited_interface_constant_in_class.php | 8 ++ .../final_constants/008_indirect_override.php | 12 +++ ...edeclaration_non_final_interface_const.php | 14 +++ 16 files changed, 183 insertions(+), 45 deletions(-) create mode 100644 tests/phpt/php8/final_constants/001_final_constants.php create mode 100644 tests/phpt/php8/final_constants/002_inhirited_interface_constant_in_interface.php create mode 100644 tests/phpt/php8/final_constants/003_redeclaration.php create mode 100644 tests/phpt/php8/final_constants/004_private_final.php create mode 100644 tests/phpt/php8/final_constants/005_redeclaration_in_interface.php create mode 100644 tests/phpt/php8/final_constants/006_redeclaration_interface_const.php create mode 100644 tests/phpt/php8/final_constants/007_inhirited_interface_constant_in_class.php create mode 100644 tests/phpt/php8/final_constants/008_indirect_override.php create mode 100644 tests/phpt/php8/final_constants/009_redeclaration_non_final_interface_const.php diff --git a/compiler/data/class-data.cpp b/compiler/data/class-data.cpp index a581c25098..ab69f8115c 100644 --- a/compiler/data/class-data.cpp +++ b/compiler/data/class-data.cpp @@ -132,7 +132,7 @@ FunctionPtr ClassData::add_virt_clone() { } void ClassData::add_class_constant() { - members.add_constant("class", GenTree::generate_constant_field_class_value(get_self()), AccessModifiers::public_); + members.add_constant("class", GenTree::generate_constant_field_class_value(get_self()), AccessModifiers::public_, false); } void ClassData::create_constructor_with_parent_call(DataStream &os) { diff --git a/compiler/data/class-members.cpp b/compiler/data/class-members.cpp index 11229413bd..3975c7d17e 100644 --- a/compiler/data/class-members.cpp +++ b/compiler/data/class-members.cpp @@ -110,9 +110,11 @@ const TypeData *ClassMemberInstanceField::get_inferred_type() const { return tinf::get_type(var); } -inline ClassMemberConstant::ClassMemberConstant(ClassPtr klass, const string &const_name, VertexPtr value, AccessModifiers access) : +inline ClassMemberConstant::ClassMemberConstant(ClassPtr klass, const string &const_name, VertexPtr value, AccessModifiers access, bool is_final) : + klass(klass), value(value), - access(access) { + access(access), + is_final(is_final) { define_name = "c#" + replace_backslashes(klass->name) + "$$" + const_name; } @@ -182,8 +184,8 @@ void ClassMembersContainer::add_instance_field(VertexAdaptor root, Verte append_member(ClassMemberInstanceField{klass, root, def_val, modifiers, phpdoc_str, type_hint}); } -void ClassMembersContainer::add_constant(const std::string &const_name, VertexPtr value, AccessModifiers access) { - append_member(ClassMemberConstant{klass, const_name, value, access}); +void ClassMembersContainer::add_constant(const std::string &const_name, VertexPtr value, AccessModifiers access, bool is_final) { + append_member(ClassMemberConstant{klass, const_name, value, access, is_final}); } void ClassMembersContainer::safe_add_instance_method(FunctionPtr function) { diff --git a/compiler/data/class-members.h b/compiler/data/class-members.h index d9f735afbb..d1fde6b04f 100644 --- a/compiler/data/class-members.h +++ b/compiler/data/class-members.h @@ -103,10 +103,12 @@ struct ClassMemberInstanceField { struct ClassMemberConstant { std::string define_name; + ClassPtr klass; VertexPtr value; AccessModifiers access; + bool is_final; - ClassMemberConstant(ClassPtr klass, const std::string &const_name, VertexPtr value, AccessModifiers access); + ClassMemberConstant(ClassPtr klass, const std::string &const_name, VertexPtr value, AccessModifiers access, bool is_final); vk::string_view global_name() const &; vk::string_view global_name() const && = delete; @@ -191,7 +193,7 @@ class ClassMembersContainer { void add_instance_method(FunctionPtr function); void add_static_field(VertexAdaptor root, VertexPtr def_val, FieldModifiers modifiers, vk::string_view phpdoc_str, const TypeHint *type_hint); void add_instance_field(VertexAdaptor root, VertexPtr def_val, FieldModifiers modifiers, vk::string_view phpdoc_str, const TypeHint *type_hint); - void add_constant(const std::string &const_name, VertexPtr value, AccessModifiers access); + void add_constant(const std::string &const_name, VertexPtr value, AccessModifiers access, bool is_final); void safe_add_instance_method(FunctionPtr function); diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index adb43bc437..3009832a8e 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1470,6 +1470,10 @@ VertexPtr GenTree::get_class_member(vk::string_view phpdoc_str) { modifiers.set_public(); } + if (test_expect(tok_const)) { + return get_class_const(modifiers); + } + if (test_expect(tok_function)) { return get_function(tok_function, phpdoc_str, modifiers); } @@ -1486,7 +1490,7 @@ VertexPtr GenTree::get_class_member(vk::string_view phpdoc_str) { return {}; } - kphp_error(0, "Expected 'function' or $var_name after class member modifiers"); + kphp_error(0, "Expected 'function', 'const' or $var_name after class member modifiers"); next_cur(); return {}; } @@ -2063,18 +2067,22 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { } case tok_protected: case tok_public: - case tok_private: - if (std::next(cur, 1)->type() == tok_const) { - next_cur(); - - auto access = AccessModifiers::public_; - if (type == tok_protected) { - access = AccessModifiers::protected_; - } else if (type == tok_private) { - access = AccessModifiers::private_; - } - return get_const_after_explicit_access_modifier(access); - } + case tok_private: { +// const auto next = std::next(cur, 1); +// const auto after_next = std::next(next, 1); +// +// if (next->type() == tok_const || after_next->type() == tok_const) { +// next_cur(); +// +// auto access = AccessModifiers::public_; +// if (type == tok_protected) { +// access = AccessModifiers::protected_; +// } else if (type == tok_private) { +// access = AccessModifiers::private_; +// } +// return get_class_const(access); +// } + } // fall through case tok_final: case tok_abstract: @@ -2231,39 +2239,50 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { kphp_fail(); } -VertexPtr GenTree::get_const_after_explicit_access_modifier(AccessModifiers access) { - CE(!kphp_error(cur_class, "Access modifier for const allowed in class only")); - if (cur_class->is_interface()) { - CE(!kphp_error(access == AccessModifiers::public_, "const access modifier in interface must be public")); +VertexPtr GenTree::get_class_const(ClassMemberModifiers modifiers) { + CE(!kphp_error(!modifiers.is_static(), fmt_format("Cannot use 'static' as constant modifier"))); + CE(!kphp_error(!modifiers.is_abstract(), fmt_format("Cannot use 'abstract' as constant modifier"))); + + if (modifiers.is_private() && modifiers.is_final()) { + kphp_error(0, "Private constant cannot be final as it is not visible to other classes"); } - return get_const(access); + if (cur_class->is_interface() && modifiers.access_modifier() != AccessModifiers::public_) { + CE(!kphp_error(0, fmt_format("Access type for interface constant must be public"))); + } + + return get_const(modifiers.access_modifier(), modifiers.is_final()); } -VertexPtr GenTree::get_const(AccessModifiers access) { - auto location = auto_location(); +VertexPtr GenTree::get_const(AccessModifiers access, bool is_final) { + const auto location = auto_location(); next_cur(); - bool const_in_global_scope = functions_stack.size() == 1 && !cur_class; - bool const_in_class = !!cur_class; + const auto in_class = (bool)cur_class; + const auto in_global_scope = functions_stack.size() == 1 && !in_class; - CE (!kphp_error(const_in_global_scope || const_in_class, "const expressions supported only inside classes and namespaces or in global scope")); - std::string const_name = get_identifier(); - CE (!const_name.empty()); - kphp_error(const_name != "class", "A class constant must not be called 'class'; it is reserved for class name fetching"); + if (!in_global_scope && !in_class) { + kphp_error(0, "Constant definition supported only inside classes and namespaces or in global scope"); + } - CE (expect(tok_eq1, "'='")); - VertexPtr v = get_expression(); - CE (check_statement_end()); + const auto name = get_identifier(); + CE(!name.empty()); + if (name == "class") { + kphp_error(0, "A class constant must not be called 'class'; it is reserved for class name fetching"); + } + + CE(expect(tok_eq1, "'='")); + const auto value_expr = get_expression(); + CE(check_statement_end()); - if (const_in_class) { - cur_class->members.add_constant(const_name, v, access); + if (in_class) { + cur_class->members.add_constant(name, value_expr, access, is_final); return VertexAdaptor::create(); } - auto name = VertexAdaptor::create(); - name->str_val = const_name; - return VertexAdaptor::create(name, v).set_location(location); + auto name_vertex = VertexAdaptor::create(); + name_vertex->str_val = name; + return VertexAdaptor::create(name_vertex, value_expr).set_location(location); } void GenTree::get_instance_var_list(vk::string_view phpdoc_str, FieldModifiers modifiers, const TypeHint *type_hint) { diff --git a/compiler/gentree.h b/compiler/gentree.h index 31fe78c097..d69e6e03e3 100644 --- a/compiler/gentree.h +++ b/compiler/gentree.h @@ -170,8 +170,8 @@ class GenTree { lambda_generators.clear(); } - VertexPtr get_const_after_explicit_access_modifier(AccessModifiers access); - VertexPtr get_const(AccessModifiers access); + VertexPtr get_class_const(ClassMemberModifiers modifiers); + VertexPtr get_const(AccessModifiers access, bool is_final = false); public: int line_num{0}; diff --git a/compiler/pipes/sort-and-inherit-classes.cpp b/compiler/pipes/sort-and-inherit-classes.cpp index cd511830d2..9afff63318 100644 --- a/compiler/pipes/sort-and-inherit-classes.cpp +++ b/compiler/pipes/sort-and-inherit-classes.cpp @@ -282,6 +282,11 @@ void SortAndInheritClassesF::inherit_child_class_from_parent(ClassPtr child_clas }); parent_class->members.for_each([&](const ClassMemberConstant &c) { if (auto child_const = child_class->get_constant(c.local_name())) { + if (c.is_final && child_const->klass == child_class) { + const auto child_name = child_class->name + "::" + child_const->local_name(); + const auto parent_name = parent_class->name + "::" + c.local_name(); + kphp_error(0, fmt_format("{} cannot override final constant {}", child_name, parent_name)); + } kphp_error(child_const->access == c.access, fmt_format("Can't change access type for constant {} in class {}", c.local_name(), child_class->name)); } @@ -300,6 +305,18 @@ void SortAndInheritClassesF::inherit_class_from_interface(ClassPtr child_class, child_class->implements.emplace_back(interface_class); + interface_class->members.for_each([&](const ClassMemberConstant &c) { + if (auto child_const = child_class->get_constant(c.local_name())) { + if (c.is_final && child_const->klass == child_class) { + const auto child_name = child_class->name + "::" + child_const->local_name(); + const auto parent_name = interface_class->name + "::" + c.local_name(); + kphp_error(0, fmt_format("{} cannot override final constant {}", child_name, parent_name)); + } + kphp_error(child_const->access == c.access, + fmt_format("Can't change access type for constant {} in class {}", c.local_name(), child_class->name)); + } + }); + AutoLocker locker(&(*interface_class)); interface_class->derived_classes.emplace_back(child_class); } diff --git a/tests/phpt/access/constants/04_interface.php b/tests/phpt/access/constants/04_interface.php index de38de042d..0d3e95b3ed 100644 --- a/tests/phpt/access/constants/04_interface.php +++ b/tests/phpt/access/constants/04_interface.php @@ -1,5 +1,5 @@ @kphp_should_fail -/const access modifier in interface must be public/ +/Access type for interface constant must be public/ Date: Thu, 14 Oct 2021 19:23:14 +0300 Subject: [PATCH 2/4] removed commented code --- compiler/gentree.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 3009832a8e..b2902d584e 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -2067,22 +2067,7 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { } case tok_protected: case tok_public: - case tok_private: { -// const auto next = std::next(cur, 1); -// const auto after_next = std::next(next, 1); -// -// if (next->type() == tok_const || after_next->type() == tok_const) { -// next_cur(); -// -// auto access = AccessModifiers::public_; -// if (type == tok_protected) { -// access = AccessModifiers::protected_; -// } else if (type == tok_private) { -// access = AccessModifiers::private_; -// } -// return get_class_const(access); -// } - } + case tok_private: // fall through case tok_final: case tok_abstract: From 03fefd86dac5c5d8db22ad00031bb2822b42bcb3 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Thu, 14 Oct 2021 19:23:51 +0300 Subject: [PATCH 3/4] remove unnecessary comment --- compiler/gentree.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index b2902d584e..1f01ee2b9e 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -2068,7 +2068,6 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { case tok_protected: case tok_public: case tok_private: - // fall through case tok_final: case tok_abstract: if (cur_function->type == FunctionData::func_class_holder) { From efacd1d6a8359e20fffb1d7003f0ae22c1039957 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Thu, 14 Oct 2021 21:32:34 +0300 Subject: [PATCH 4/4] refactored code and replaced php8.0 with php8.1 in python test runner --- compiler/pipes/sort-and-inherit-classes.cpp | 35 ++++++++++----------- compiler/pipes/sort-and-inherit-classes.h | 2 ++ tests/python/lib/file_utils.py | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/compiler/pipes/sort-and-inherit-classes.cpp b/compiler/pipes/sort-and-inherit-classes.cpp index 9afff63318..3e57663294 100644 --- a/compiler/pipes/sort-and-inherit-classes.cpp +++ b/compiler/pipes/sort-and-inherit-classes.cpp @@ -280,15 +280,9 @@ void SortAndInheritClassesF::inherit_child_class_from_parent(ClassPtr child_clas ); } }); - parent_class->members.for_each([&](const ClassMemberConstant &c) { - if (auto child_const = child_class->get_constant(c.local_name())) { - if (c.is_final && child_const->klass == child_class) { - const auto child_name = child_class->name + "::" + child_const->local_name(); - const auto parent_name = parent_class->name + "::" + c.local_name(); - kphp_error(0, fmt_format("{} cannot override final constant {}", child_name, parent_name)); - } - kphp_error(child_const->access == c.access, - fmt_format("Can't change access type for constant {} in class {}", c.local_name(), child_class->name)); + parent_class->members.for_each([&](ClassMemberConstant &parent_const) { + if (auto child_const = child_class->get_constant(parent_const.local_name())) { + check_const_inheritance(child_class, parent_class, &parent_const, child_const); } }); } @@ -305,15 +299,9 @@ void SortAndInheritClassesF::inherit_class_from_interface(ClassPtr child_class, child_class->implements.emplace_back(interface_class); - interface_class->members.for_each([&](const ClassMemberConstant &c) { - if (auto child_const = child_class->get_constant(c.local_name())) { - if (c.is_final && child_const->klass == child_class) { - const auto child_name = child_class->name + "::" + child_const->local_name(); - const auto parent_name = interface_class->name + "::" + c.local_name(); - kphp_error(0, fmt_format("{} cannot override final constant {}", child_name, parent_name)); - } - kphp_error(child_const->access == c.access, - fmt_format("Can't change access type for constant {} in class {}", c.local_name(), child_class->name)); + interface_class->members.for_each([&](const ClassMemberConstant &parent_const) { + if (auto child_const = child_class->get_constant(parent_const.local_name())) { + check_const_inheritance(child_class, interface_class, &parent_const, child_const); } }); @@ -321,6 +309,17 @@ void SortAndInheritClassesF::inherit_class_from_interface(ClassPtr child_class, interface_class->derived_classes.emplace_back(child_class); } +void SortAndInheritClassesF::check_const_inheritance(ClassPtr child_class, ClassPtr parent_class, const ClassMemberConstant *parent_const, const ClassMemberConstant *child_const) { + if (parent_const->is_final && child_const->klass == child_class) { + const auto child_name = child_class->name + "::" + child_const->local_name(); + const auto parent_name = parent_class->name + "::" + parent_const->local_name(); + kphp_error(0, fmt_format("{} cannot override final constant {}", child_name, parent_name)); + } + + kphp_error(child_const->access == parent_const->access, + fmt_format("Can't change access type for constant {} in class {}", parent_const->local_name(), child_class->name)); +} + void SortAndInheritClassesF::clone_members_from_traits(std::vector &&traits, ClassPtr ready_class, DataStream &function_stream) { for (size_t i = 0; i < traits.size(); ++i) { auto check_other_traits_doesnt_contain_method_and_clone = [&](FunctionPtr method) { diff --git a/compiler/pipes/sort-and-inherit-classes.h b/compiler/pipes/sort-and-inherit-classes.h index 02a13975b8..3169385c65 100644 --- a/compiler/pipes/sort-and-inherit-classes.h +++ b/compiler/pipes/sort-and-inherit-classes.h @@ -34,6 +34,8 @@ class SortAndInheritClassesF { decltype(ht)::HTNode *get_not_ready_dependency(ClassPtr klass); + static void check_const_inheritance(ClassPtr child_class, ClassPtr parent_class, const ClassMemberConstant *parent_const, const ClassMemberConstant *child_const); + public: void execute(ClassPtr klass, MultipleDataStreams &os); static void check_on_finish(DataStream &os); diff --git a/tests/python/lib/file_utils.py b/tests/python/lib/file_utils.py index ecfe499891..6736d84db7 100644 --- a/tests/python/lib/file_utils.py +++ b/tests/python/lib/file_utils.py @@ -127,5 +127,5 @@ def search_php_bin(php8_require=False): if sys.platform == "darwin": return shutil.which("php") if php8_require: - return shutil.which("php8.0") + return shutil.which("php8.1") return shutil.which("php7.4")