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..1f01ee2b9e 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 {}; } @@ -2064,18 +2068,6 @@ 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); - } - // fall through case tok_final: case tok_abstract: if (cur_function->type == FunctionData::func_class_holder) { @@ -2231,39 +2223,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..3e57663294 100644 --- a/compiler/pipes/sort-and-inherit-classes.cpp +++ b/compiler/pipes/sort-and-inherit-classes.cpp @@ -280,10 +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())) { - 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); } }); } @@ -300,10 +299,27 @@ void SortAndInheritClassesF::inherit_class_from_interface(ClassPtr child_class, child_class->implements.emplace_back(interface_class); + 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); + } + }); + AutoLocker locker(&(*interface_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/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/