From 14fc348e39f3c96851d25bfa658c7af806ef52bd Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 21 Apr 2023 19:06:31 +0300 Subject: [PATCH 01/41] Add php8* versions in python tests --- tests/python/lib/file_utils.py | 25 +++++++++++++++++++++---- tests/python/lib/testcase.py | 6 +++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/python/lib/file_utils.py b/tests/python/lib/file_utils.py index 263385aa7a..8b584b6400 100644 --- a/tests/python/lib/file_utils.py +++ b/tests/python/lib/file_utils.py @@ -3,7 +3,16 @@ import re import sys import shutil +import enum +class PhpVersion(enum.Enum): + v7_4 = "php7.4" + v8 = "php8" + v8_1 = "php8.1" + v8_2 = "php8.2" + + def major(self): + return self.value[3] def _check_file(file_name, file_dir, file_checker): file_path = os.path.join(file_dir, file_name) @@ -103,9 +112,17 @@ def can_ignore_sanitizer_log(sanitizer_log_file): return ignore_sanitizer -def search_php_bin(php8_require=False): +def search_php_bin(required_php_ver=PhpVersion.v7_4): if sys.platform == "darwin": return shutil.which("php") - if php8_require: - return shutil.which("php8.1") or shutil.which("php8") - return shutil.which("php7.4") + ordered_version = [PhpVersion.v8_1, PhpVersion.v8, PhpVersion.v7_4] + for php_ver in ordered_version: + if php_ver == required_php_ver: + return shutil.which(php_ver.value) + if php_ver.major() != required_php_ver.major(): + continue + php_path = shutil.which(php_ver.value) + if php_path: + return php_path + # invalid php version + return shutil.which(PhpVersion.v7_4.value) \ No newline at end of file diff --git a/tests/python/lib/testcase.py b/tests/python/lib/testcase.py index 1490ec2434..c853e7ab39 100644 --- a/tests/python/lib/testcase.py +++ b/tests/python/lib/testcase.py @@ -11,7 +11,7 @@ from .kphp_server import KphpServer from .kphp_builder import KphpBuilder from .kphp_run_once import KphpRunOnce -from .file_utils import search_combined_tlo, can_ignore_sanitizer_log, search_php_bin +from .file_utils import search_combined_tlo, can_ignore_sanitizer_log, search_php_bin, PhpVersion from .nocc_for_kphp_tester import nocc_env, nocc_start_daemon_in_background logging.disable(logging.DEBUG) @@ -280,7 +280,7 @@ class KphpCompilerAutoTestCase(BaseTestCase): def __init__(self, method_name): super().__init__(method_name) - self.require_php8 = False + self.php_version = PhpVersion.v7_4 @classmethod def extra_class_setup(cls): @@ -318,7 +318,7 @@ def make_kphp_once_runner(self, php_script_path): php_script_path=os.path.join(self.test_dir, php_script_path), artifacts_dir=self.kphp_server_working_dir, working_dir=self.kphp_build_working_dir, - php_bin=search_php_bin(php8_require=self.require_php8), + php_bin=search_php_bin(required_php_ver=self.php_version), use_nocc=self.should_use_nocc(), ) self.once_runner_trash_bin.append(once_runner) From 69ad8e5c5d7d2b17ebca3bcd9e59597dfc63f2d5 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 21 Apr 2023 19:31:56 +0300 Subject: [PATCH 02/41] Add php8* versions in phpt --- tests/kphp_tester.py | 13 ++++++++----- tests/python/lib/file_utils.py | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/kphp_tester.py b/tests/kphp_tester.py index 309913ff4e..1f3a5cc859 100755 --- a/tests/kphp_tester.py +++ b/tests/kphp_tester.py @@ -10,7 +10,7 @@ from multiprocessing.dummy import Pool as ThreadPool from python.lib.colors import red, green, yellow, blue -from python.lib.file_utils import search_php_bin +from python.lib.file_utils import search_php_bin, PhpVersion from python.lib.nocc_for_kphp_tester import nocc_start_daemon_in_background from python.lib.kphp_run_once import KphpRunOnce @@ -42,8 +42,11 @@ def is_kphp_runtime_should_warn(self): def is_kphp_runtime_should_not_warn(self): return "kphp_runtime_should_not_warn" in self.tags - def is_php8(self): - return "php8" in self.tags + def php_version(self): + for version in PhpVersion: + if version.value in self.tags: + return version + return PhpVersion.v7_4 def make_kphp_once_runner(self, use_nocc, cxx_name): tester_dir = os.path.abspath(os.path.dirname(__file__)) @@ -51,7 +54,7 @@ def make_kphp_once_runner(self, use_nocc, cxx_name): php_script_path=self.file_path, working_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "working_dir")), artifacts_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "artifacts")), - php_bin=search_php_bin(php8_require=self.is_php8()), + php_bin=search_php_bin(required_php_ver=self.php_version()), extra_include_dirs=[os.path.join(tester_dir, "php_include")], vkext_dir=os.path.abspath(os.path.join(tester_dir, os.path.pardir, "objs", "vkext")), use_nocc=use_nocc, @@ -322,7 +325,7 @@ def run_test(use_nocc, cxx_name, test: TestFile): runner = test.make_kphp_once_runner(use_nocc, cxx_name) runner.remove_artifacts_dir() - if test.is_php8() and runner._php_bin is None: # if php8 doesn't exist on a machine + if test.php_version() != PhpVersion.v7_4 and runner._php_bin is None: # if requested php version doesn't exist on a machine test_result = TestResult.skipped(test) elif test.is_kphp_should_fail(): test_result = run_fail_test(test, runner) diff --git a/tests/python/lib/file_utils.py b/tests/python/lib/file_utils.py index 8b584b6400..aa386a7051 100644 --- a/tests/python/lib/file_utils.py +++ b/tests/python/lib/file_utils.py @@ -125,4 +125,4 @@ def search_php_bin(required_php_ver=PhpVersion.v7_4): if php_path: return php_path # invalid php version - return shutil.which(PhpVersion.v7_4.value) \ No newline at end of file + return shutil.which(PhpVersion.v7_4.value) From f7a58f92a6268f7ce161ecd6c7d67506258c9806 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 24 Apr 2023 23:28:01 +0300 Subject: [PATCH 03/41] Add wrap in InlineDefinesUsage --- compiler/const-manipulations.h | 20 ++++++++++++++++++-- compiler/pipes/inline-defines-usages.cpp | 5 ++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/const-manipulations.h b/compiler/const-manipulations.h index ba763ae2c8..2037671220 100644 --- a/compiler/const-manipulations.h +++ b/compiler/const-manipulations.h @@ -47,6 +47,8 @@ struct ConstManipulations { virtual T on_define_val(VertexAdaptor v) { return on_non_const(v); } + virtual T on_func_call(VertexAdaptor v) { return on_non_const(v); } + virtual T on_non_const([[maybe_unused]] VertexPtr vertex) { return T(); } virtual T on_array(VertexAdaptor v) { @@ -131,12 +133,17 @@ struct ConstManipulations { case op_define_val: return on_define_val(v.as()); + case op_func_call: + return on_func_call(v.as()); + default: return on_non_const(v); } } }; +// CheckConst is used as Base class below +// And in name-gen.cpp, to check -- whether we should hash and utilize as const variable or not struct CheckConst : ConstManipulations { public: @@ -190,12 +197,14 @@ struct CheckConst return visit(key); } - bool on_define_val(VertexAdaptor v) final { + bool on_define_val(VertexAdaptor v) override { return visit(v->value()); } - }; + +// CheckConstWithDefines is used in CalcRealDefinesValues pass +// To choose correct `def->val->const_type` (cnst_const_val or def_var) struct CheckConstWithDefines final : CheckConst { public: @@ -217,6 +226,11 @@ struct CheckConstWithDefines final return false; } + bool on_func_call(VertexAdaptor v) override { + // Allows use objects in const context + return v->get_string() == "__construct"; + } + bool on_non_const(VertexPtr v) final { if (v->type() == op_concat || v->type() == op_string_build) { in_concat++; @@ -278,6 +292,8 @@ static inline std::string collect_string_concatenation(VertexPtr v, bool allow_d return {}; } +// MakeConst is used in CalcRealDefinesValues pass +// To choose correct `def->val->const_type` (cnst_const_val or def_var) struct MakeConst final : ConstManipulations { public: diff --git a/compiler/pipes/inline-defines-usages.cpp b/compiler/pipes/inline-defines-usages.cpp index 9bdf0fa6f0..b15963e4c9 100644 --- a/compiler/pipes/inline-defines-usages.cpp +++ b/compiler/pipes/inline-defines-usages.cpp @@ -60,7 +60,10 @@ VertexPtr InlineDefinesUsagesPass::on_enter_vertex(VertexPtr root) { auto access_class = def->class_id; check_access(class_id, lambda_class_id, FieldModifiers{def->access}, access_class, "const", def->as_human_readable()); } - root = def->val.clone().set_location_recursively(root); + auto wrapped = VertexAdaptor::create(def->val.clone()); + wrapped->value().set_location_recursively(root); + wrapped->define_id = def; + root = wrapped; } } From deaf855aed2929a1388072caae1381bc95f6ac8e Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 25 Apr 2023 15:28:13 +0300 Subject: [PATCH 04/41] Fix errors in CI --- compiler/class-assumptions.cpp | 2 ++ compiler/pipes/check-func-calls-and-vararg.cpp | 1 + .../pipes/deduce-implicit-types-and-casts.cpp | 17 +++++++++++------ compiler/pipes/inline-defines-usages.cpp | 4 +--- compiler/pipes/preprocess-eq3.cpp | 10 ++++++---- compiler/vertex-util.cpp | 7 +++++++ compiler/vertex-util.h | 1 + 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/compiler/class-assumptions.cpp b/compiler/class-assumptions.cpp index ffa5eaaf46..099626041d 100644 --- a/compiler/class-assumptions.cpp +++ b/compiler/class-assumptions.cpp @@ -790,6 +790,8 @@ Assumption assume_class_of_expr(FunctionPtr f, VertexPtr root, VertexPtr stop_at case op_false: case op_true: return Assumption(TypeHintPrimitive::create(tp_bool)); + case op_define_val: + return assume_class_of_expr(f, root.as()->value(), stop_at); default: return {}; diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 5d6871da46..450f3b8298 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -45,6 +45,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA // at first, convert f(1, ...[2, ...[3]], ...$all, ...[5]) to f(1,2,3,...$all,5) std::function flatten_call_varg = [&flattened_call_args, &flatten_call_varg](VertexPtr inner) { + inner = VertexUtil::get_actual_value(inner); if (auto as_array = inner.try_as()) { for (VertexPtr item : as_array->args()) { kphp_error(item->type() != op_double_arrow, "Passed unpacked ...[array] must be a vector, without => keys"); diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 8e2a9c5232..6e866ac743 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -244,10 +244,13 @@ void patch_rhs_casting_to_callable(VertexPtr &rhs, const TypeHintCallable *as_ca v_lambda->lambda_class = typed_interface; rhs = v_lambda; } - - } else if (rhs->type() == op_array && rhs->size() == 2 && rhs->front()->type() == op_string && rhs->back()->type() == op_string) { + } else if (rhs->type() == op_array && rhs->size() == 2 && VertexUtil::unwrap_inlined_define(rhs->front())->type() == op_string + && VertexUtil::unwrap_inlined_define(rhs->back())->type() == op_string) { // ['A', 'staticMethod'] - std::string func_name = rhs->front()->get_string() + "$$" + rhs->back()->get_string(); + auto unwrapped_front = VertexUtil::unwrap_inlined_define(rhs->front()); + auto unwrapped_back = VertexUtil::unwrap_inlined_define(rhs->back()); + + std::string func_name = unwrapped_front->get_string() + "$$" + unwrapped_back->back()->get_string(); func_name = replace_backslashes(func_name[0] == '\\' ? func_name.substr(1) : func_name); if (is_extern_func_param) { @@ -261,16 +264,18 @@ void patch_rhs_casting_to_callable(VertexPtr &rhs, const TypeHintCallable *as_ca rhs = v_lambda; } - } else if (rhs->type() == op_array && rhs->size() == 2 && rhs->back()->type() == op_string) { + } else if (rhs->type() == op_array && rhs->size() == 2 && VertexUtil::unwrap_inlined_define(rhs->back())->type() == op_string) { // HERE // [$obj, 'method'] or [getObject(), 'method'] or similar // pay attention, that $obj (rhs->front()) is captured in an auto-created wrapping lambda or in a c++ lambda in case of extern + auto unwrapped_back = VertexUtil::unwrap_inlined_define(rhs->back()); + if (is_extern_func_param) { auto v_callback = VertexAdaptor::create(rhs->front()).set_location(rhs); - v_callback->str_val = rhs->back()->get_string(); + v_callback->str_val = unwrapped_back->get_string(); rhs = v_callback; } else { auto v_lambda = VertexAdaptor::create().set_location(rhs); - v_lambda->func_id = generate_lambda_from_string_or_array(stage::get_function(), rhs->back()->get_string(), rhs->front(), rhs); + v_lambda->func_id = generate_lambda_from_string_or_array(stage::get_function(), unwrapped_back->get_string(), rhs->front(), rhs); v_lambda->lambda_class = typed_interface; rhs = v_lambda; } diff --git a/compiler/pipes/inline-defines-usages.cpp b/compiler/pipes/inline-defines-usages.cpp index b15963e4c9..240023497a 100644 --- a/compiler/pipes/inline-defines-usages.cpp +++ b/compiler/pipes/inline-defines-usages.cpp @@ -4,7 +4,6 @@ #include "compiler/pipes/inline-defines-usages.h" -#include "compiler/data/class-data.h" #include "compiler/data/define-data.h" #include "compiler/modulite-check-rules.h" #include "compiler/name-gen.h" @@ -60,8 +59,7 @@ VertexPtr InlineDefinesUsagesPass::on_enter_vertex(VertexPtr root) { auto access_class = def->class_id; check_access(class_id, lambda_class_id, FieldModifiers{def->access}, access_class, "const", def->as_human_readable()); } - auto wrapped = VertexAdaptor::create(def->val.clone()); - wrapped->value().set_location_recursively(root); + auto wrapped = VertexAdaptor::create(def->val.clone()).set_location_recursively(root); wrapped->define_id = def; root = wrapped; } diff --git a/compiler/pipes/preprocess-eq3.cpp b/compiler/pipes/preprocess-eq3.cpp index 43325092f2..75c2226efe 100644 --- a/compiler/pipes/preprocess-eq3.cpp +++ b/compiler/pipes/preprocess-eq3.cpp @@ -3,9 +3,9 @@ // Distributed under the GPL v3 License, see LICENSE.notice.txt #include "compiler/pipes/preprocess-eq3.h" +#include "compiler/vertex-util.h" #include "common/algorithms/contains.h" -#include "common/wrappers/likely.h" VertexPtr PreprocessEq3Pass::on_exit_vertex(VertexPtr root) { if (root->type() == op_eq3) { @@ -29,11 +29,13 @@ inline VertexPtr PreprocessEq3Pass::convert_eq3_null_to_isset(VertexPtr eq_op, V while (v->type() == op_index) { v = v.as()->array(); } - if (vk::any_of_equal(v->type(), op_var, op_instance_prop, op_array)) { + + auto unwrapped = VertexUtil::get_actual_value(v); + if (vk::any_of_equal(unwrapped->type(), op_var, op_instance_prop, op_array)) { // it's a kludge to make "real world" PHP code compile // TODO: can we get rid of this? - if (v->has_get_string() && - (v->get_string() == "connection" || vk::contains(v->get_string(), "MC"))) { + if (unwrapped->has_get_string() && + (unwrapped->get_string() == "connection" || vk::contains(unwrapped->get_string(), "MC"))) { return eq_op; } diff --git a/compiler/vertex-util.cpp b/compiler/vertex-util.cpp index f7d7da8903..7f6d1606c8 100644 --- a/compiler/vertex-util.cpp +++ b/compiler/vertex-util.cpp @@ -23,6 +23,13 @@ VertexPtr VertexUtil::get_actual_value(VertexPtr v) { return v; } +VertexPtr VertexUtil::unwrap_inlined_define(VertexPtr v) { + if (auto as_op_def = v.try_as()) { + return unwrap_inlined_define(as_op_def->value()); + } + return v; +} + const std::string *VertexUtil::get_constexpr_string(VertexPtr v) { v = VertexUtil::get_actual_value(v); if (auto conv_vertex = v.try_as()) { diff --git a/compiler/vertex-util.h b/compiler/vertex-util.h index 62db70fce0..24bedc2a5f 100644 --- a/compiler/vertex-util.h +++ b/compiler/vertex-util.h @@ -14,6 +14,7 @@ class VertexUtil { public: static VertexPtr get_actual_value(VertexPtr v); + static VertexPtr unwrap_inlined_define(VertexPtr v); static const std::string *get_constexpr_string(VertexPtr v); static VertexPtr get_call_arg_ref(int arg_num, VertexPtr v_func_call); static VertexAdaptor add_call_arg(VertexPtr to_add, VertexAdaptor call, bool prepend); From eb23d468bb61faa9b4c3a857d5c3b66929d3a6f1 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 25 Apr 2023 19:30:19 +0300 Subject: [PATCH 05/41] Fix --- compiler/pipes/deduce-implicit-types-and-casts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 6e866ac743..50e76c08dc 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -250,7 +250,7 @@ void patch_rhs_casting_to_callable(VertexPtr &rhs, const TypeHintCallable *as_ca auto unwrapped_front = VertexUtil::unwrap_inlined_define(rhs->front()); auto unwrapped_back = VertexUtil::unwrap_inlined_define(rhs->back()); - std::string func_name = unwrapped_front->get_string() + "$$" + unwrapped_back->back()->get_string(); + std::string func_name = unwrapped_front->get_string() + "$$" + unwrapped_back->get_string(); func_name = replace_backslashes(func_name[0] == '\\' ? func_name.substr(1) : func_name); if (is_extern_func_param) { From 613a33f2ce992306225a433e5597edf382c34cc6 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 1 May 2023 15:22:41 +0300 Subject: [PATCH 06/41] Allow constructors with const args to be `cnst_const_val` --- compiler/pipes/calc-const-types.cpp | 10 ++++++++++ compiler/pipes/collect-const-vars.cpp | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index 5ea0716c0e..40fc89679a 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -32,10 +32,20 @@ void CalcConstTypePass::calc_const_type_of_class_fields(ClassPtr klass) { VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { if (auto as_define_val = v.try_as()) { + auto val = as_define_val->value(); + // check if it's an object in define + if (auto as_func_call = val.try_as(); + as_func_call && as_func_call->func_id && as_func_call->func_id->is_constructor()) { + bool has_nonconst_son = vk::any_of(*as_func_call, [](VertexPtr son) { return son->const_type == cnst_nonconst_val; }); + as_func_call->const_type = has_nonconst_son ? cnst_nonconst_val : cnst_const_val; + return v; + } as_define_val->const_type = as_define_val->value()->const_type; kphp_error(as_define_val->const_type == cnst_const_val, "Non-constant in const context"); return v; } + // for now only pure functions and + // constructors in defines can be `cnst_const_val`-ed if (auto as_func_call = v.try_as()) { if (!as_func_call->func_id || !as_func_call->func_id->is_pure) { v->const_type = cnst_nonconst_val; diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 60c3c7d200..908a81c3f5 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -78,8 +78,13 @@ struct ShouldStoreOnTopDown : public VertexVisitor { return vk::any_of_equal(expr->type(), op_string, op_concat, op_string_build); } + static bool on_func_call(VertexAdaptor v) { + // const constructors are handles in on_define_val + return v->func_id && v->func_id->is_pure; + } + static bool fallback(VertexPtr v) { - return vk::any_of_equal(v->type(), op_concat, op_string_build, op_func_call); + return vk::any_of_equal(v->type(), op_concat, op_string_build); // op func call must be pure } }; From 8960d906eb36ef707653e291ecfe0f854a43ec33 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 2 May 2023 10:50:59 +0300 Subject: [PATCH 07/41] Add name generating --- compiler/const-manipulations.h | 68 ++++++++++++++++++++------- compiler/name-gen.cpp | 11 +++++ compiler/name-gen.h | 2 + compiler/pipes/calc-const-types.cpp | 4 +- compiler/pipes/collect-const-vars.cpp | 24 +++++++++- 5 files changed, 87 insertions(+), 22 deletions(-) diff --git a/compiler/const-manipulations.h b/compiler/const-manipulations.h index 2037671220..83806a3bfd 100644 --- a/compiler/const-manipulations.h +++ b/compiler/const-manipulations.h @@ -49,6 +49,8 @@ struct ConstManipulations { virtual T on_func_call(VertexAdaptor v) { return on_non_const(v); } + virtual T on_alloc(VertexAdaptor v) { return on_non_const(v); } + virtual T on_non_const([[maybe_unused]] VertexPtr vertex) { return T(); } virtual T on_array(VertexAdaptor v) { @@ -136,6 +138,9 @@ struct ConstManipulations { case op_func_call: return on_func_call(v.as()); + case op_alloc: + return on_alloc(v.as()); + default: return on_non_const(v); } @@ -375,15 +380,13 @@ struct MakeConst final return v; } -}; + VertexPtr on_func_call(VertexAdaptor v) final { + return v; + } -struct ArrayHash final +}; +struct CommonHash : ConstManipulations { - static uint64_t calc_hash(VertexPtr v) { - ArrayHash array_hash; - array_hash.visit(VertexUtil::get_actual_value(v)); - return array_hash.cur_hash; - } void feed_hash(uint64_t val) { cur_hash = cur_hash * HASH_MULT + val; @@ -394,7 +397,7 @@ struct ArrayHash final } protected: - void on_trivial(VertexPtr v) final { + void on_trivial(VertexPtr v) override { std::string s = OpInfo::str(v->type()); if (v->has_get_string()) { @@ -404,19 +407,19 @@ struct ArrayHash final feed_hash_string(s); } - void on_conv(VertexAdaptor v) final { + void on_conv(VertexAdaptor v) override { feed_hash_string(OpInfo::str(v->type())); return visit(v->expr()); } - void on_unary(VertexAdaptor v) final { + void on_unary(VertexAdaptor v) override { std::string type_str = OpInfo::str(v->type()); feed_hash_string(type_str); return visit(v->expr()); } - void on_binary(VertexAdaptor v) final { + void on_binary(VertexAdaptor v) override { VertexPtr key = v->lhs(); VertexPtr value = v->rhs(); @@ -425,7 +428,7 @@ struct ArrayHash final visit(value); } - void on_double_arrow(VertexAdaptor v) final { + void on_double_arrow(VertexAdaptor v) override { VertexPtr key = VertexUtil::get_actual_value(v->key()); VertexPtr value = VertexUtil::get_actual_value(v->value()); @@ -434,7 +437,7 @@ struct ArrayHash final visit(value); } - void on_array(VertexAdaptor v) final { + void on_array(VertexAdaptor v) override { feed_hash(v->args().size()); feed_hash(MAGIC1); @@ -445,27 +448,58 @@ struct ArrayHash final feed_hash(MAGIC2); } - void on_var(VertexAdaptor v) final { + void on_var(VertexAdaptor v) override { return visit(VertexUtil::get_actual_value(v)); } - void on_non_const(VertexPtr v) final { + void on_non_const(VertexPtr v) override { std::string msg = "unsupported type for hashing: " + OpInfo::str(v->type()); kphp_assert_msg(false, msg.c_str()); } - bool on_index_key(VertexPtr key) final { + bool on_index_key(VertexPtr key) override { visit(key); return true; } -private: + void on_func_call(VertexAdaptor v) override { + if (v->func_id && v->func_id->is_constructor()) + for (auto son : v->args()) { + visit(son); + } + } + + void on_define_val(VertexAdaptor v) override { + auto define_hasher = DefinePtr::Hash(); + feed_hash(static_cast(define_hasher(v->define_id))); + visit(v->value()); + } + + void on_alloc(VertexAdaptor v) override { + feed_hash_string(v->allocated_class_name); + } + uint64_t cur_hash = 0; static const uint64_t HASH_MULT = 56235515617499ULL; static const uint64_t MAGIC1 = 536536536536960ULL; static const uint64_t MAGIC2 = 288288288288069ULL; }; +struct ArrayHash final : CommonHash { + static uint64_t calc_hash(VertexPtr v) { + ArrayHash array_hash; + array_hash.visit(VertexUtil::get_actual_value(v)); + return array_hash.cur_hash; + } +}; + +struct ObjectHash final : CommonHash { + static uint64_t calc_hash(VertexPtr v) { + ObjectHash object_hash; + object_hash.visit(v); + return object_hash.cur_hash; + } +}; struct VertexPtrFormatter final : ConstManipulations { static std::string to_string(VertexPtr v) { diff --git a/compiler/name-gen.cpp b/compiler/name-gen.cpp index 612ebe6233..cf59459003 100644 --- a/compiler/name-gen.cpp +++ b/compiler/name-gen.cpp @@ -35,6 +35,17 @@ bool is_array_suitable_for_hashing(VertexPtr vertex) { return vertex->type() == op_array && CheckConst::is_const(vertex); } +bool is_object_suitable_for_hashing(VertexPtr vertex) { + bool ss = vertex->type() == op_define_val && vertex.as()->value()->type() == op_func_call && vertex->const_type == cnst_const_val; + return ss; +} + +std::string gen_const_object_name(const VertexAdaptor &def) { + kphp_error(def->value()->type() == op_func_call, "Internal error: expected op_define_val "); + auto obj_hash = ObjectHash::calc_hash(def); + return fmt_format("const_obj$us{:x}", obj_hash); +} + std::string gen_const_array_name(const VertexAdaptor &array) { return fmt_format("const_array$us{:x}", ArrayHash::calc_hash(array)); } diff --git a/compiler/name-gen.h b/compiler/name-gen.h index 1c081d5f7f..cf8ad75534 100644 --- a/compiler/name-gen.h +++ b/compiler/name-gen.h @@ -14,6 +14,8 @@ std::string gen_anonymous_function_name(FunctionPtr parent_function); std::string gen_unique_name(const std::string& prefix, FunctionPtr function = FunctionPtr{}); std::string gen_const_string_name(const std::string &str); std::string gen_const_regexp_name(const std::string &str); +bool is_object_suitable_for_hashing(VertexPtr vertex); +std::string gen_const_object_name(const VertexAdaptor &array); bool is_array_suitable_for_hashing(VertexPtr vertex); std::string gen_const_array_name(const VertexAdaptor &array); diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index 40fc89679a..14defc6c7f 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -7,7 +7,6 @@ #include "compiler/data/class-data.h" #include "compiler/data/src-file.h" #include "compiler/data/var-data.h" -#include "compiler/vertex-util.h" void CalcConstTypePass::on_start() { if (current_function->type == FunctionData::func_class_holder) { @@ -38,9 +37,8 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { as_func_call && as_func_call->func_id && as_func_call->func_id->is_constructor()) { bool has_nonconst_son = vk::any_of(*as_func_call, [](VertexPtr son) { return son->const_type == cnst_nonconst_val; }); as_func_call->const_type = has_nonconst_son ? cnst_nonconst_val : cnst_const_val; - return v; } - as_define_val->const_type = as_define_val->value()->const_type; + as_define_val->const_type = val->const_type; kphp_error(as_define_val->const_type == cnst_const_val, "Non-constant in const context"); return v; } diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 908a81c3f5..3d3fbc859a 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -28,6 +28,8 @@ struct VertexVisitor { return Derived::on_func_call(v.as()); case op_string_build: return Derived::on_string_build(v.as()); + case op_define_val: + return Derived::on_define_val(v.as()); default: return Derived::fallback(v); } @@ -57,6 +59,10 @@ struct VertexVisitor { return Derived::fallback(v); } + static ResultType on_define_val(VertexAdaptor v) { + return Derived::fallback(v); + } + static ResultType fallback(VertexPtr v [[maybe_unused]]) { kphp_assert_msg(false, "Internal error: invalid visitor in CollectConstVars pass!"); } @@ -80,7 +86,8 @@ struct ShouldStoreOnTopDown : public VertexVisitor { static bool on_func_call(VertexAdaptor v) { // const constructors are handles in on_define_val - return v->func_id && v->func_id->is_pure; + auto res = v->func_id && v->func_id->is_pure; + return res; } static bool fallback(VertexPtr v) { @@ -89,6 +96,12 @@ struct ShouldStoreOnTopDown : public VertexVisitor { }; struct ShouldStoreOnBottomUp : public VertexVisitor { + static bool on_define_val(VertexAdaptor v) { + auto val = v->value().try_as(); + auto res = val && val->func_id && val->func_id->is_constructor(); + return res; + } + static bool fallback(VertexPtr v) { return vk::any_of_equal(v->type(), op_string, op_array); } @@ -101,6 +114,13 @@ struct NameGenerator : public VertexVisitor { return gen_unique_name(prefix); } + static std::string on_define_val(VertexAdaptor v) { + if (is_object_suitable_for_hashing(v)) { + return gen_const_object_name(v); + } + return fallback(v); + } + static std::string on_string(VertexAdaptor v) { return gen_const_string_name(v->str_val); } @@ -218,7 +238,7 @@ VertexPtr CollectConstVarsPass::create_const_variable(VertexPtr root, Location l var->extra_type = op_ex_var_const; var->location = loc; - VarPtr var_id = G->get_global_var(name, VarData::var_const_t, root); + VarPtr var_id = G->get_global_var(name, VarData::var_const_t, VertexUtil::unwrap_inlined_define(root)); set_var_dep_level(var_id); if (composite_const_depth_ > 0) { From 670760042c7fae9ae2ee26db523c3d733b93d6c2 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 2 May 2023 14:55:17 +0300 Subject: [PATCH 08/41] Fix includes and externs --- compiler/code-gen/files/vars-cpp.cpp | 19 +++++++++++++++++++ compiler/code-gen/includes.cpp | 4 ++++ compiler/pipes/collect-const-vars.cpp | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/compiler/code-gen/files/vars-cpp.cpp b/compiler/code-gen/files/vars-cpp.cpp index 978efd98b5..88bfa22bc1 100644 --- a/compiler/code-gen/files/vars-cpp.cpp +++ b/compiler/code-gen/files/vars-cpp.cpp @@ -52,6 +52,24 @@ static void add_dependent_declarations(VertexPtr vertex, std::set &depen } } +static void add_dependent_includes(VertexPtr vertex, IncludesCollector & includes) { + if (!vertex) { + return; + } + for (auto child : *vertex) { + add_dependent_includes(child, includes); + } + if (auto as_func_call = vertex.try_as()) { + includes.add_raw_filename_include(as_func_call->func_id->header_full_name); + includes.add_function_signature_depends(as_func_call->func_id); + } + if (auto as_var = vertex.try_as()) { + includes.add_var_signature_depends(as_var->var_id); + } +} + + + void compile_raw_array(CodeGenerator &W, const VarPtr &var, int shift) { if (shift == -1) { W << InitVar(var); @@ -77,6 +95,7 @@ static void compile_vars_part(CodeGenerator &W, const std::vector &vars, for (auto var : vars) { if (!G->settings().is_static_lib_mode() || !var->is_builtin_global()) { includes.add_var_signature_depends(var); + add_dependent_includes(var->init_val, includes); } } W << includes; diff --git a/compiler/code-gen/includes.cpp b/compiler/code-gen/includes.cpp index 82c815b63c..2423da52bc 100644 --- a/compiler/code-gen/includes.cpp +++ b/compiler/code-gen/includes.cpp @@ -62,6 +62,10 @@ void IncludesCollector::add_function_body_depends(const FunctionPtr &function) { } } + for(auto const_var : function->explicit_const_var_ids) { + add_var_signature_depends(const_var); + } + for (auto to_include : function->class_dep) { add_class_include(to_include); } diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 3d3fbc859a..cff02ea972 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -69,6 +69,11 @@ struct VertexVisitor { }; struct IsComposite : public VertexVisitor { + static bool on_define_val(VertexAdaptor v) { + auto val = v->value().try_as(); + return val && val->func_id && val->func_id->is_constructor(); + } + static bool on_array(VertexAdaptor v [[maybe_unused]]) { return true; } From 9ac1a5ab7ccd450a0dbd95f132fc3b7fad5f7e6c Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 2 May 2023 20:33:20 +0300 Subject: [PATCH 09/41] Fix includes x2 --- compiler/code-gen/files/vars-cpp.cpp | 7 ++++++- compiler/pipes/calc-const-types.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/code-gen/files/vars-cpp.cpp b/compiler/code-gen/files/vars-cpp.cpp index 88bfa22bc1..923fa82dae 100644 --- a/compiler/code-gen/files/vars-cpp.cpp +++ b/compiler/code-gen/files/vars-cpp.cpp @@ -60,7 +60,12 @@ static void add_dependent_includes(VertexPtr vertex, IncludesCollector & include add_dependent_includes(child, includes); } if (auto as_func_call = vertex.try_as()) { - includes.add_raw_filename_include(as_func_call->func_id->header_full_name); + if (as_func_call->func_id) { + const auto& header_full_name = as_func_call->func_id->header_full_name; + if (!header_full_name.empty()) { + includes.add_raw_filename_include(as_func_call->func_id->header_full_name); + } + } includes.add_function_signature_depends(as_func_call->func_id); } if (auto as_var = vertex.try_as()) { diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index 14defc6c7f..bbd8c32bd4 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -39,7 +39,7 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { as_func_call->const_type = has_nonconst_son ? cnst_nonconst_val : cnst_const_val; } as_define_val->const_type = val->const_type; - kphp_error(as_define_val->const_type == cnst_const_val, "Non-constant in const context"); + kphp_error(as_define_val->const_type == cnst_const_val, "Non-const expression in const context"); return v; } // for now only pure functions and From ee96d5d38939bec41960ef6219670ffb97f51695 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Wed, 3 May 2023 10:29:28 +0300 Subject: [PATCH 10/41] Tests & c-tor as const --- compiler/pipes/calc-const-types.cpp | 8 ++----- compiler/pipes/collect-const-vars.cpp | 5 ++--- tests/phpt/constants/003_object_as_const.php | 9 ++++++++ tests/phpt/constants/004_object_in_array.php | 18 ++++++++++++++++ tests/phpt/constants/005_object_ref.php | 21 +++++++++++++++++++ .../constants/006_object_as_class_const.php | 15 +++++++++++++ tests/phpt/constants/007_nested_object.php | 19 +++++++++++++++++ .../constants/008_not_const_ctor_params.php | 13 ++++++++++++ .../009_not_const_ctor_params_nested.php | 12 +++++++++++ tests/phpt/constants/Classes/A.php | 13 ++++++++++++ tests/phpt/constants/Classes/B.php | 16 ++++++++++++++ 11 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 tests/phpt/constants/003_object_as_const.php create mode 100644 tests/phpt/constants/004_object_in_array.php create mode 100644 tests/phpt/constants/005_object_ref.php create mode 100644 tests/phpt/constants/006_object_as_class_const.php create mode 100644 tests/phpt/constants/007_nested_object.php create mode 100644 tests/phpt/constants/008_not_const_ctor_params.php create mode 100644 tests/phpt/constants/009_not_const_ctor_params_nested.php create mode 100644 tests/phpt/constants/Classes/A.php create mode 100644 tests/phpt/constants/Classes/B.php diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index bbd8c32bd4..abb0c0abdf 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -33,11 +33,6 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { if (auto as_define_val = v.try_as()) { auto val = as_define_val->value(); // check if it's an object in define - if (auto as_func_call = val.try_as(); - as_func_call && as_func_call->func_id && as_func_call->func_id->is_constructor()) { - bool has_nonconst_son = vk::any_of(*as_func_call, [](VertexPtr son) { return son->const_type == cnst_nonconst_val; }); - as_func_call->const_type = has_nonconst_son ? cnst_nonconst_val : cnst_const_val; - } as_define_val->const_type = val->const_type; kphp_error(as_define_val->const_type == cnst_const_val, "Non-const expression in const context"); return v; @@ -45,7 +40,8 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { // for now only pure functions and // constructors in defines can be `cnst_const_val`-ed if (auto as_func_call = v.try_as()) { - if (!as_func_call->func_id || !as_func_call->func_id->is_pure) { + const bool can_be_const = as_func_call->func_id && (as_func_call->func_id->is_constructor() || as_func_call->func_id->is_pure); + if (!can_be_const) { v->const_type = cnst_nonconst_val; return v; } diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index cff02ea972..54cb44bdf0 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -69,9 +69,8 @@ struct VertexVisitor { }; struct IsComposite : public VertexVisitor { - static bool on_define_val(VertexAdaptor v) { - auto val = v->value().try_as(); - return val && val->func_id && val->func_id->is_constructor(); + static bool on_func_call(VertexAdaptor v) { + return v && v->func_id && v->func_id->is_constructor(); } static bool on_array(VertexAdaptor v [[maybe_unused]]) { diff --git a/tests/phpt/constants/003_object_as_const.php b/tests/phpt/constants/003_object_as_const.php new file mode 100644 index 0000000000..f37da5cc9b --- /dev/null +++ b/tests/phpt/constants/003_object_as_const.php @@ -0,0 +1,9 @@ +@php8.1 ok +x = $x; + } + public function __toString() { + return strval($this->x); + } + } diff --git a/tests/phpt/constants/Classes/B.php b/tests/phpt/constants/Classes/B.php new file mode 100644 index 0000000000..155a39f51a --- /dev/null +++ b/tests/phpt/constants/Classes/B.php @@ -0,0 +1,16 @@ +a = $a; + } + + public function __toString() { + return strval($this->a); + } +} From 4db23550fe736a36cfef4d028b0f950b490a0f52 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Wed, 3 May 2023 19:19:48 +0300 Subject: [PATCH 11/41] Fix includes x3 --- compiler/pipes/collect-const-vars.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 54cb44bdf0..0e8cd9ef87 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -245,15 +245,15 @@ VertexPtr CollectConstVarsPass::create_const_variable(VertexPtr root, Location l VarPtr var_id = G->get_global_var(name, VarData::var_const_t, VertexUtil::unwrap_inlined_define(root)); set_var_dep_level(var_id); - if (composite_const_depth_ > 0) { - current_function->implicit_const_var_ids.insert(var_id); - } else { +// if (composite_const_depth_ > 0) { +// current_function->implicit_const_var_ids.insert(var_id); +// } else { if (in_param_list_ > 0) { current_function->explicit_header_const_var_ids.insert(var_id); } else { current_function->explicit_const_var_ids.insert(var_id); } - } +// } var->var_id = var_id; return var; From c697c19cfebe154a0193ed368755a604ff2b5eb0 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 11:32:13 +0300 Subject: [PATCH 12/41] Add inlined defines counter in CalcConstTypes pass --- compiler/pipes/calc-const-types.cpp | 9 ++++++++- compiler/pipes/calc-const-types.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index abb0c0abdf..509145e7e8 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -31,6 +31,7 @@ void CalcConstTypePass::calc_const_type_of_class_fields(ClassPtr klass) { VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { if (auto as_define_val = v.try_as()) { + inlined_define_cnt--; auto val = as_define_val->value(); // check if it's an object in define as_define_val->const_type = val->const_type; @@ -40,7 +41,7 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { // for now only pure functions and // constructors in defines can be `cnst_const_val`-ed if (auto as_func_call = v.try_as()) { - const bool can_be_const = as_func_call->func_id && (as_func_call->func_id->is_constructor() || as_func_call->func_id->is_pure); + const bool can_be_const = as_func_call->func_id && ((as_func_call->func_id->is_constructor() && inlined_define_cnt > 0)|| as_func_call->func_id->is_pure); if (!can_be_const) { v->const_type = cnst_nonconst_val; return v; @@ -68,3 +69,9 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { } return v; } +VertexPtr CalcConstTypePass::on_enter_vertex(VertexPtr v) { + if (v->type() == op_define_val) { + inlined_define_cnt++; + } + return v; +} diff --git a/compiler/pipes/calc-const-types.h b/compiler/pipes/calc-const-types.h index b033a9b9d0..73af344bb9 100644 --- a/compiler/pipes/calc-const-types.h +++ b/compiler/pipes/calc-const-types.h @@ -9,7 +9,7 @@ /*** Calculate const_type for all nodes ***/ class CalcConstTypePass final : public FunctionPassBase { void calc_const_type_of_class_fields(ClassPtr klass); - + int inlined_define_cnt{0}; public: void on_start() override; @@ -19,4 +19,5 @@ class CalcConstTypePass final : public FunctionPassBase { } VertexPtr on_exit_vertex(VertexPtr v) override; + VertexPtr on_enter_vertex(VertexPtr v) override; }; From 2838e7817ee77bd2f62e635760746b0a1320564e Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 14:35:01 +0300 Subject: [PATCH 13/41] Revert implicit_const_vars --- compiler/pipes/collect-const-vars.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 0e8cd9ef87..54cb44bdf0 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -245,15 +245,15 @@ VertexPtr CollectConstVarsPass::create_const_variable(VertexPtr root, Location l VarPtr var_id = G->get_global_var(name, VarData::var_const_t, VertexUtil::unwrap_inlined_define(root)); set_var_dep_level(var_id); -// if (composite_const_depth_ > 0) { -// current_function->implicit_const_var_ids.insert(var_id); -// } else { + if (composite_const_depth_ > 0) { + current_function->implicit_const_var_ids.insert(var_id); + } else { if (in_param_list_ > 0) { current_function->explicit_header_const_var_ids.insert(var_id); } else { current_function->explicit_const_var_ids.insert(var_id); } -// } + } var->var_id = var_id; return var; From 4c85275531e0d0ec0ddcba0af95dede9c496b616 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 15:48:30 +0300 Subject: [PATCH 14/41] Revert "Add php8* versions in phpt" This reverts commit 81b042aaae923bb7c0baec9e68482e9089db3916. --- tests/kphp_tester.py | 13 +++++-------- tests/python/lib/file_utils.py | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/kphp_tester.py b/tests/kphp_tester.py index 1f3a5cc859..309913ff4e 100755 --- a/tests/kphp_tester.py +++ b/tests/kphp_tester.py @@ -10,7 +10,7 @@ from multiprocessing.dummy import Pool as ThreadPool from python.lib.colors import red, green, yellow, blue -from python.lib.file_utils import search_php_bin, PhpVersion +from python.lib.file_utils import search_php_bin from python.lib.nocc_for_kphp_tester import nocc_start_daemon_in_background from python.lib.kphp_run_once import KphpRunOnce @@ -42,11 +42,8 @@ def is_kphp_runtime_should_warn(self): def is_kphp_runtime_should_not_warn(self): return "kphp_runtime_should_not_warn" in self.tags - def php_version(self): - for version in PhpVersion: - if version.value in self.tags: - return version - return PhpVersion.v7_4 + def is_php8(self): + return "php8" in self.tags def make_kphp_once_runner(self, use_nocc, cxx_name): tester_dir = os.path.abspath(os.path.dirname(__file__)) @@ -54,7 +51,7 @@ def make_kphp_once_runner(self, use_nocc, cxx_name): php_script_path=self.file_path, working_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "working_dir")), artifacts_dir=os.path.abspath(os.path.join(self.test_tmp_dir, "artifacts")), - php_bin=search_php_bin(required_php_ver=self.php_version()), + php_bin=search_php_bin(php8_require=self.is_php8()), extra_include_dirs=[os.path.join(tester_dir, "php_include")], vkext_dir=os.path.abspath(os.path.join(tester_dir, os.path.pardir, "objs", "vkext")), use_nocc=use_nocc, @@ -325,7 +322,7 @@ def run_test(use_nocc, cxx_name, test: TestFile): runner = test.make_kphp_once_runner(use_nocc, cxx_name) runner.remove_artifacts_dir() - if test.php_version() != PhpVersion.v7_4 and runner._php_bin is None: # if requested php version doesn't exist on a machine + if test.is_php8() and runner._php_bin is None: # if php8 doesn't exist on a machine test_result = TestResult.skipped(test) elif test.is_kphp_should_fail(): test_result = run_fail_test(test, runner) diff --git a/tests/python/lib/file_utils.py b/tests/python/lib/file_utils.py index aa386a7051..8b584b6400 100644 --- a/tests/python/lib/file_utils.py +++ b/tests/python/lib/file_utils.py @@ -125,4 +125,4 @@ def search_php_bin(required_php_ver=PhpVersion.v7_4): if php_path: return php_path # invalid php version - return shutil.which(PhpVersion.v7_4.value) + return shutil.which(PhpVersion.v7_4.value) \ No newline at end of file From 95b85814338089a68de87f8d47c85ea856d997b5 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 15:48:47 +0300 Subject: [PATCH 15/41] Revert "Add php8* versions in python tests" This reverts commit bce7936a97daa9f8176497492c76cc55ab6b3bbf. --- tests/python/lib/file_utils.py | 25 ++++--------------------- tests/python/lib/testcase.py | 6 +++--- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/tests/python/lib/file_utils.py b/tests/python/lib/file_utils.py index 8b584b6400..263385aa7a 100644 --- a/tests/python/lib/file_utils.py +++ b/tests/python/lib/file_utils.py @@ -3,16 +3,7 @@ import re import sys import shutil -import enum -class PhpVersion(enum.Enum): - v7_4 = "php7.4" - v8 = "php8" - v8_1 = "php8.1" - v8_2 = "php8.2" - - def major(self): - return self.value[3] def _check_file(file_name, file_dir, file_checker): file_path = os.path.join(file_dir, file_name) @@ -112,17 +103,9 @@ def can_ignore_sanitizer_log(sanitizer_log_file): return ignore_sanitizer -def search_php_bin(required_php_ver=PhpVersion.v7_4): +def search_php_bin(php8_require=False): if sys.platform == "darwin": return shutil.which("php") - ordered_version = [PhpVersion.v8_1, PhpVersion.v8, PhpVersion.v7_4] - for php_ver in ordered_version: - if php_ver == required_php_ver: - return shutil.which(php_ver.value) - if php_ver.major() != required_php_ver.major(): - continue - php_path = shutil.which(php_ver.value) - if php_path: - return php_path - # invalid php version - return shutil.which(PhpVersion.v7_4.value) \ No newline at end of file + if php8_require: + return shutil.which("php8.1") or shutil.which("php8") + return shutil.which("php7.4") diff --git a/tests/python/lib/testcase.py b/tests/python/lib/testcase.py index c853e7ab39..1490ec2434 100644 --- a/tests/python/lib/testcase.py +++ b/tests/python/lib/testcase.py @@ -11,7 +11,7 @@ from .kphp_server import KphpServer from .kphp_builder import KphpBuilder from .kphp_run_once import KphpRunOnce -from .file_utils import search_combined_tlo, can_ignore_sanitizer_log, search_php_bin, PhpVersion +from .file_utils import search_combined_tlo, can_ignore_sanitizer_log, search_php_bin from .nocc_for_kphp_tester import nocc_env, nocc_start_daemon_in_background logging.disable(logging.DEBUG) @@ -280,7 +280,7 @@ class KphpCompilerAutoTestCase(BaseTestCase): def __init__(self, method_name): super().__init__(method_name) - self.php_version = PhpVersion.v7_4 + self.require_php8 = False @classmethod def extra_class_setup(cls): @@ -318,7 +318,7 @@ def make_kphp_once_runner(self, php_script_path): php_script_path=os.path.join(self.test_dir, php_script_path), artifacts_dir=self.kphp_server_working_dir, working_dir=self.kphp_build_working_dir, - php_bin=search_php_bin(required_php_ver=self.php_version), + php_bin=search_php_bin(php8_require=self.require_php8), use_nocc=self.should_use_nocc(), ) self.once_runner_trash_bin.append(once_runner) From c78e98ef67de8ef0eee332fc5eb7a3140edfa0e6 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 15:50:24 +0300 Subject: [PATCH 16/41] Fix php versions --- tests/phpt/constants/003_object_as_const.php | 2 +- tests/phpt/constants/004_object_in_array.php | 2 +- tests/phpt/constants/005_object_ref.php | 2 +- tests/phpt/constants/006_object_as_class_const.php | 2 +- tests/phpt/constants/007_nested_object.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/phpt/constants/003_object_as_const.php b/tests/phpt/constants/003_object_as_const.php index f37da5cc9b..d6f26dddef 100644 --- a/tests/phpt/constants/003_object_as_const.php +++ b/tests/phpt/constants/003_object_as_const.php @@ -1,4 +1,4 @@ -@php8.1 ok +@php8 Date: Thu, 4 May 2023 17:42:52 +0300 Subject: [PATCH 17/41] Fix deduce (string const) -> callable --- compiler/pipes/deduce-implicit-types-and-casts.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 50e76c08dc..17405b439d 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -229,7 +229,8 @@ void patch_rhs_casting_to_callable(VertexPtr &rhs, const TypeHintCallable *as_ca } rhs.as()->lambda_class = typed_interface; // save for the next pass - } else if (rhs->type() == op_string) { + } else if (VertexUtil::unwrap_inlined_define(rhs)->type() == op_string) { + rhs = VertexUtil::unwrap_inlined_define(rhs); // 'strlen' or 'A::staticMethod' std::string func_name = replace_characters(rhs->get_string(), ':', '$'); func_name = replace_backslashes(func_name[0] == '\\' ? func_name.substr(1) : func_name); From 1bc8ef3d9008605fd58970127e693af881ea740f Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 4 May 2023 18:07:27 +0300 Subject: [PATCH 18/41] Fix const as default vars --- compiler/pipes/check-abstract-function-defaults.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pipes/check-abstract-function-defaults.cpp b/compiler/pipes/check-abstract-function-defaults.cpp index 8ba7d453c8..1521fb1b25 100644 --- a/compiler/pipes/check-abstract-function-defaults.cpp +++ b/compiler/pipes/check-abstract-function-defaults.cpp @@ -38,7 +38,7 @@ void CheckAbstractFunctionDefaults::execute(FunctionPtr interface_function, Data for (auto arg_id = interface_function->get_min_argn(); arg_id < interface_params.size(); ++arg_id) { auto derived_default = get_default(derived_params[arg_id]); auto interface_default = get_default(interface_params[arg_id]); - kphp_error(Vertex::deep_equal(derived_default, interface_default), + kphp_error(Vertex::deep_equal( VertexUtil::unwrap_inlined_define(derived_default), VertexUtil::unwrap_inlined_define(interface_default)), fmt_format("default value of interface parameter:`{}` may not differ from value of derived parameter: `{}`, in function: {}", TermStringFormat::paint(VertexPtrFormatter::to_string(interface_default), TermStringFormat::green), TermStringFormat::paint(VertexPtrFormatter::to_string(derived_default), TermStringFormat::green), From 50fcbe84f13b0f3d7a8961a514fd700a825c6f28 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 11 May 2023 17:22:51 +0300 Subject: [PATCH 19/41] Fix eq3 optimization with const objects --- compiler/pipes/final-check.cpp | 1 - compiler/pipes/preprocess-eq3.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/pipes/final-check.cpp b/compiler/pipes/final-check.cpp index 10e5e6ef64..c0f36ed414 100644 --- a/compiler/pipes/final-check.cpp +++ b/compiler/pipes/final-check.cpp @@ -631,7 +631,6 @@ VertexPtr FinalCheckPass::on_enter_vertex(VertexPtr vertex) { } } } else { - kphp_error(!var->is_constant(), "Can't use isset on const variable"); const TypeData *type_info = tinf::get_type(var); kphp_error(type_info->can_store_null(), fmt_format("isset({}) will be always true for {}", var->as_human_readable(), type_info->as_human_readable())); diff --git a/compiler/pipes/preprocess-eq3.cpp b/compiler/pipes/preprocess-eq3.cpp index 75c2226efe..d09f325687 100644 --- a/compiler/pipes/preprocess-eq3.cpp +++ b/compiler/pipes/preprocess-eq3.cpp @@ -31,7 +31,7 @@ inline VertexPtr PreprocessEq3Pass::convert_eq3_null_to_isset(VertexPtr eq_op, V } auto unwrapped = VertexUtil::get_actual_value(v); - if (vk::any_of_equal(unwrapped->type(), op_var, op_instance_prop, op_array)) { + if (vk::any_of_equal(unwrapped->type(), op_var, op_instance_prop, op_array) || (unwrapped->type() == op_func_call && unwrapped.try_as()->extra_type == op_ex_constructor_call)) { // it's a kludge to make "real world" PHP code compile // TODO: can we get rid of this? if (unwrapped->has_get_string() && From 30f536961275dd70dbea81aa76d4bdbd66fd40ae Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 11 May 2023 18:44:29 +0300 Subject: [PATCH 20/41] Fix eq3 optimization with const objects x2 --- compiler/pipes/preprocess-eq3.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/pipes/preprocess-eq3.cpp b/compiler/pipes/preprocess-eq3.cpp index d09f325687..03a11c537c 100644 --- a/compiler/pipes/preprocess-eq3.cpp +++ b/compiler/pipes/preprocess-eq3.cpp @@ -13,8 +13,10 @@ VertexPtr PreprocessEq3Pass::on_exit_vertex(VertexPtr root) { VertexPtr a = eq_op->lhs(); VertexPtr b = eq_op->rhs(); - if (a->type() == op_null || b->type() == op_null) { - root = convert_eq3_null_to_isset(root, a->type() == op_null ? b : a); + auto a_unwrapped = VertexUtil::unwrap_inlined_define(a); + auto b_unwrapped = VertexUtil::unwrap_inlined_define(b); + if (a_unwrapped->type() == op_null || b_unwrapped->type() == op_null) { + root = convert_eq3_null_to_isset(root, a_unwrapped->type() == op_null ? b : a); } } From 91ebefd335b4576468a9a52986cf5f256e253fc4 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 12 May 2023 12:29:17 +0300 Subject: [PATCH 21/41] Fix runtime rule --- compiler/rewrite-rules/rules_runtime.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/rewrite-rules/rules_runtime.cpp b/compiler/rewrite-rules/rules_runtime.cpp index 062715e2ee..fa87644b59 100644 --- a/compiler/rewrite-rules/rules_runtime.cpp +++ b/compiler/rewrite-rules/rules_runtime.cpp @@ -183,7 +183,9 @@ static bool is_safe_simple_expr(VertexPtr v) { } return false; } - + case op_define_val: { + return is_safe_simple_expr(v.as()->value()); + } default: return false; } @@ -228,10 +230,16 @@ bool contains_var(VertexPtr tree, VertexPtr var) { } VertexPtr to_tmp_string_expr(VertexPtr v, bool safe) { + v = VertexUtil::unwrap_string_value(v); if (v->type() != op_func_call) { return {}; } + + if (v.as()->func_id->name == "substr") { + puts("here"); + } + auto as_call = v.as(); if (as_call->args().empty()) { return {}; @@ -243,6 +251,9 @@ VertexPtr to_tmp_string_expr(VertexPtr v, bool safe) { if (safe) { // the $subject string is always the first argument auto subject_arg = VertexUtil::unwrap_string_value(as_call->args()[0]); + puts("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); + subject_arg.debugPrint(); + puts("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); if (!is_safe_simple_expr(subject_arg)) { return {}; } From 0f24f06af9fcd46f2e7225430a57763f1d9dddf6 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 12 May 2023 12:29:33 +0300 Subject: [PATCH 22/41] Fix tests --- tests/phpt/constants/003_object_as_const.php | 2 +- tests/phpt/constants/004_object_in_array.php | 2 +- tests/phpt/constants/005_object_ref.php | 2 +- tests/phpt/constants/006_object_as_class_const.php | 2 +- tests/phpt/constants/007_nested_object.php | 2 +- tests/phpt/constants/008_not_const_ctor_params.php | 2 +- tests/phpt/constants/009_not_const_ctor_params_nested.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/phpt/constants/003_object_as_const.php b/tests/phpt/constants/003_object_as_const.php index d6f26dddef..383ad31135 100644 --- a/tests/phpt/constants/003_object_as_const.php +++ b/tests/phpt/constants/003_object_as_const.php @@ -1,4 +1,4 @@ -@php8 +@ok php8 Date: Fri, 12 May 2023 12:41:01 +0300 Subject: [PATCH 23/41] Remove debug prints --- compiler/rewrite-rules/rules_runtime.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/compiler/rewrite-rules/rules_runtime.cpp b/compiler/rewrite-rules/rules_runtime.cpp index fa87644b59..829a78ca78 100644 --- a/compiler/rewrite-rules/rules_runtime.cpp +++ b/compiler/rewrite-rules/rules_runtime.cpp @@ -230,16 +230,10 @@ bool contains_var(VertexPtr tree, VertexPtr var) { } VertexPtr to_tmp_string_expr(VertexPtr v, bool safe) { - v = VertexUtil::unwrap_string_value(v); if (v->type() != op_func_call) { return {}; } - - if (v.as()->func_id->name == "substr") { - puts("here"); - } - auto as_call = v.as(); if (as_call->args().empty()) { return {}; @@ -251,9 +245,6 @@ VertexPtr to_tmp_string_expr(VertexPtr v, bool safe) { if (safe) { // the $subject string is always the first argument auto subject_arg = VertexUtil::unwrap_string_value(as_call->args()[0]); - puts("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"); - subject_arg.debugPrint(); - puts("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); if (!is_safe_simple_expr(subject_arg)) { return {}; } From ea2361debbcb4049d9eb236aa47e81678cc3b8fe Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 12 May 2023 15:12:11 +0300 Subject: [PATCH 24/41] Fix review notes --- compiler/code-gen/files/vars-cpp.cpp | 25 +------------------ compiler/code-gen/includes.cpp | 23 ++++++++++++++++- compiler/code-gen/includes.h | 3 +++ compiler/const-manipulations.h | 3 ++- compiler/name-gen.cpp | 7 +++--- compiler/pipes/calc-const-types.cpp | 1 + .../check-abstract-function-defaults.cpp | 2 +- compiler/pipes/collect-const-vars.cpp | 2 +- .../009_not_const_ctor_params_nested.php | 6 +++-- 9 files changed, 39 insertions(+), 33 deletions(-) diff --git a/compiler/code-gen/files/vars-cpp.cpp b/compiler/code-gen/files/vars-cpp.cpp index 923fa82dae..30814647ce 100644 --- a/compiler/code-gen/files/vars-cpp.cpp +++ b/compiler/code-gen/files/vars-cpp.cpp @@ -52,29 +52,6 @@ static void add_dependent_declarations(VertexPtr vertex, std::set &depen } } -static void add_dependent_includes(VertexPtr vertex, IncludesCollector & includes) { - if (!vertex) { - return; - } - for (auto child : *vertex) { - add_dependent_includes(child, includes); - } - if (auto as_func_call = vertex.try_as()) { - if (as_func_call->func_id) { - const auto& header_full_name = as_func_call->func_id->header_full_name; - if (!header_full_name.empty()) { - includes.add_raw_filename_include(as_func_call->func_id->header_full_name); - } - } - includes.add_function_signature_depends(as_func_call->func_id); - } - if (auto as_var = vertex.try_as()) { - includes.add_var_signature_depends(as_var->var_id); - } -} - - - void compile_raw_array(CodeGenerator &W, const VarPtr &var, int shift) { if (shift == -1) { W << InitVar(var); @@ -100,7 +77,7 @@ static void compile_vars_part(CodeGenerator &W, const std::vector &vars, for (auto var : vars) { if (!G->settings().is_static_lib_mode() || !var->is_builtin_global()) { includes.add_var_signature_depends(var); - add_dependent_includes(var->init_val, includes); + includes.add_vertex_depends(var->init_val); } } W << includes; diff --git a/compiler/code-gen/includes.cpp b/compiler/code-gen/includes.cpp index 2423da52bc..bcff98bdb2 100644 --- a/compiler/code-gen/includes.cpp +++ b/compiler/code-gen/includes.cpp @@ -62,7 +62,7 @@ void IncludesCollector::add_function_body_depends(const FunctionPtr &function) { } } - for(auto const_var : function->explicit_const_var_ids) { + for (VarPtr const_var : function->explicit_const_var_ids) { add_var_signature_depends(const_var); } @@ -133,6 +133,27 @@ void IncludesCollector::add_raw_filename_include(const std::string &file_name) { internal_headers_.emplace(file_name); } +void IncludesCollector::add_vertex_depends(VertexPtr v) { + if (!v) { + return; + } + for (VertexPtr child : *v) { + add_vertex_depends(child); + } + if (auto as_func_call = v.try_as()) { + if (as_func_call->func_id) { + const auto &header_full_name = as_func_call->func_id->header_full_name; + if (!header_full_name.empty()) { + add_raw_filename_include(as_func_call->func_id->header_full_name); + } + } + add_function_signature_depends(as_func_call->func_id); + } + if (auto as_var = v.try_as()) { + add_var_signature_depends(as_var->var_id); + } +} + void IncludesCollector::compile(CodeGenerator &W) const { for (const auto &lib_header : lib_headers_) { if (!prev_headers_.count(lib_header.first)) { diff --git a/compiler/code-gen/includes.h b/compiler/code-gen/includes.h index 34f2d5ba52..aa202ca7f9 100644 --- a/compiler/code-gen/includes.h +++ b/compiler/code-gen/includes.h @@ -5,6 +5,7 @@ #pragma once #include "compiler/code-gen/code-generator.h" +#include "compiler/data/vertex-adaptor.h" #include "compiler/inferring/type-data.h" struct ExternInclude { @@ -33,6 +34,8 @@ struct IncludesCollector { void add_function_body_depends(const FunctionPtr &function); void add_function_signature_depends(const FunctionPtr &function); void add_var_signature_depends(const VarPtr &var); + void add_vertex_depends(VertexPtr v); + void add_class_forward_declaration(const ClassPtr &klass); void add_var_signature_forward_declarations(const VarPtr &var); diff --git a/compiler/const-manipulations.h b/compiler/const-manipulations.h index 83806a3bfd..df57c22479 100644 --- a/compiler/const-manipulations.h +++ b/compiler/const-manipulations.h @@ -233,7 +233,7 @@ struct CheckConstWithDefines final bool on_func_call(VertexAdaptor v) override { // Allows use objects in const context - return v->get_string() == "__construct"; + return v->extra_type == op_ex_constructor_call; } bool on_non_const(VertexPtr v) final { @@ -500,6 +500,7 @@ struct ObjectHash final : CommonHash { return object_hash.cur_hash; } }; + struct VertexPtrFormatter final : ConstManipulations { static std::string to_string(VertexPtr v) { diff --git a/compiler/name-gen.cpp b/compiler/name-gen.cpp index cf59459003..378bd43922 100644 --- a/compiler/name-gen.cpp +++ b/compiler/name-gen.cpp @@ -35,13 +35,14 @@ bool is_array_suitable_for_hashing(VertexPtr vertex) { return vertex->type() == op_array && CheckConst::is_const(vertex); } +// checks that inlined as define' value constructor is suitable to be stored as constant var bool is_object_suitable_for_hashing(VertexPtr vertex) { - bool ss = vertex->type() == op_define_val && vertex.as()->value()->type() == op_func_call && vertex->const_type == cnst_const_val; - return ss; + return vertex->type() == op_define_val && vertex.as()->value()->type() == op_func_call + && vertex.as()->value()->extra_type == op_ex_constructor_call && vertex->const_type == cnst_const_val; } std::string gen_const_object_name(const VertexAdaptor &def) { - kphp_error(def->value()->type() == op_func_call, "Internal error: expected op_define_val "); + kphp_assert_msg(def->value()->type() == op_func_call, "Internal error: expected op_define_val "); auto obj_hash = ObjectHash::calc_hash(def); return fmt_format("const_obj$us{:x}", obj_hash); } diff --git a/compiler/pipes/calc-const-types.cpp b/compiler/pipes/calc-const-types.cpp index 509145e7e8..345d9d11b3 100644 --- a/compiler/pipes/calc-const-types.cpp +++ b/compiler/pipes/calc-const-types.cpp @@ -69,6 +69,7 @@ VertexPtr CalcConstTypePass::on_exit_vertex(VertexPtr v) { } return v; } + VertexPtr CalcConstTypePass::on_enter_vertex(VertexPtr v) { if (v->type() == op_define_val) { inlined_define_cnt++; diff --git a/compiler/pipes/check-abstract-function-defaults.cpp b/compiler/pipes/check-abstract-function-defaults.cpp index 1521fb1b25..b4c6732aeb 100644 --- a/compiler/pipes/check-abstract-function-defaults.cpp +++ b/compiler/pipes/check-abstract-function-defaults.cpp @@ -38,7 +38,7 @@ void CheckAbstractFunctionDefaults::execute(FunctionPtr interface_function, Data for (auto arg_id = interface_function->get_min_argn(); arg_id < interface_params.size(); ++arg_id) { auto derived_default = get_default(derived_params[arg_id]); auto interface_default = get_default(interface_params[arg_id]); - kphp_error(Vertex::deep_equal( VertexUtil::unwrap_inlined_define(derived_default), VertexUtil::unwrap_inlined_define(interface_default)), + kphp_error(Vertex::deep_equal(VertexUtil::unwrap_inlined_define(derived_default), VertexUtil::unwrap_inlined_define(interface_default)), fmt_format("default value of interface parameter:`{}` may not differ from value of derived parameter: `{}`, in function: {}", TermStringFormat::paint(VertexPtrFormatter::to_string(interface_default), TermStringFormat::green), TermStringFormat::paint(VertexPtrFormatter::to_string(derived_default), TermStringFormat::green), diff --git a/compiler/pipes/collect-const-vars.cpp b/compiler/pipes/collect-const-vars.cpp index 54cb44bdf0..001e734f0b 100644 --- a/compiler/pipes/collect-const-vars.cpp +++ b/compiler/pipes/collect-const-vars.cpp @@ -89,7 +89,7 @@ struct ShouldStoreOnTopDown : public VertexVisitor { } static bool on_func_call(VertexAdaptor v) { - // const constructors are handles in on_define_val + // const constructors are handled in on_define_val auto res = v->func_id && v->func_id->is_pure; return res; } diff --git a/tests/phpt/constants/009_not_const_ctor_params_nested.php b/tests/phpt/constants/009_not_const_ctor_params_nested.php index dd02c502e3..dae6307fb2 100644 --- a/tests/phpt/constants/009_not_const_ctor_params_nested.php +++ b/tests/phpt/constants/009_not_const_ctor_params_nested.php @@ -1,12 +1,14 @@ -@ok kphp_should_fail +@kphp_should_fail +/Non-const expression in const context/ Date: Mon, 15 May 2023 12:22:24 +0300 Subject: [PATCH 25/41] Fix arrow access for const objects --- compiler/name-gen.cpp | 4 ++++ tests/phpt/constants/010_arrow_access.php | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/phpt/constants/010_arrow_access.php diff --git a/compiler/name-gen.cpp b/compiler/name-gen.cpp index 378bd43922..b41a788441 100644 --- a/compiler/name-gen.cpp +++ b/compiler/name-gen.cpp @@ -219,6 +219,10 @@ ClassPtr resolve_class_of_arrow_access(FunctionPtr function, VertexPtr lhs, Vert case op_set: return resolve_class_of_arrow_access(function, lhs.as()->rhs(), v); + // inlined constant value + case op_define_val: + return resolve_class_of_arrow_access(function, lhs.as()->value(), v); + default: break; } diff --git a/tests/phpt/constants/010_arrow_access.php b/tests/phpt/constants/010_arrow_access.php new file mode 100644 index 0000000000..f2c217e8d6 --- /dev/null +++ b/tests/phpt/constants/010_arrow_access.php @@ -0,0 +1,21 @@ +@ok php8 +value = $x; + } + public function getValue() { + return $this->value; + } +} + +const b = new B(42); + +echo b->getValue(); +echo b->value; + From 3b3c58d0c59db210e0641839138565084ea58385 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 16 May 2023 17:31:39 +0300 Subject: [PATCH 26/41] Fix review notes --- compiler/class-assumptions.cpp | 4 ++-- compiler/pipes/deduce-implicit-types-and-casts.cpp | 2 +- compiler/pipes/preprocess-eq3.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/class-assumptions.cpp b/compiler/class-assumptions.cpp index 099626041d..1ea3928f4a 100644 --- a/compiler/class-assumptions.cpp +++ b/compiler/class-assumptions.cpp @@ -777,6 +777,8 @@ Assumption assume_class_of_expr(FunctionPtr f, VertexPtr root, VertexPtr stop_at return assume_class_of_expr(f, root.as()->expr(), stop_at); case op_set: return assume_class_of_expr(f, root.as()->rhs(), stop_at); + case op_define_val: + return assume_class_of_expr(f, root.as()->value(), stop_at); // assumptions for primitives aren't saved to $local_vars and don't help to resolve ->arrows, // but they are useful for generics reification @@ -790,8 +792,6 @@ Assumption assume_class_of_expr(FunctionPtr f, VertexPtr root, VertexPtr stop_at case op_false: case op_true: return Assumption(TypeHintPrimitive::create(tp_bool)); - case op_define_val: - return assume_class_of_expr(f, root.as()->value(), stop_at); default: return {}; diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 17405b439d..df43e83df2 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -265,7 +265,7 @@ void patch_rhs_casting_to_callable(VertexPtr &rhs, const TypeHintCallable *as_ca rhs = v_lambda; } - } else if (rhs->type() == op_array && rhs->size() == 2 && VertexUtil::unwrap_inlined_define(rhs->back())->type() == op_string) { // HERE + } else if (rhs->type() == op_array && rhs->size() == 2 && VertexUtil::unwrap_inlined_define(rhs->back())->type() == op_string) { // [$obj, 'method'] or [getObject(), 'method'] or similar // pay attention, that $obj (rhs->front()) is captured in an auto-created wrapping lambda or in a c++ lambda in case of extern auto unwrapped_back = VertexUtil::unwrap_inlined_define(rhs->back()); diff --git a/compiler/pipes/preprocess-eq3.cpp b/compiler/pipes/preprocess-eq3.cpp index 03a11c537c..e40a475382 100644 --- a/compiler/pipes/preprocess-eq3.cpp +++ b/compiler/pipes/preprocess-eq3.cpp @@ -32,7 +32,7 @@ inline VertexPtr PreprocessEq3Pass::convert_eq3_null_to_isset(VertexPtr eq_op, V v = v.as()->array(); } - auto unwrapped = VertexUtil::get_actual_value(v); + auto unwrapped = VertexUtil::unwrap_inlined_define(v); if (vk::any_of_equal(unwrapped->type(), op_var, op_instance_prop, op_array) || (unwrapped->type() == op_func_call && unwrapped.try_as()->extra_type == op_ex_constructor_call)) { // it's a kludge to make "real world" PHP code compile // TODO: can we get rid of this? From d6cc98ec5fe1a6d74cfb375ac3af5042a16d59ae Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 15 May 2023 12:09:18 +0300 Subject: [PATCH 27/41] Implement basic things --- compiler/data/class-data.cpp | 2 + compiler/debug.cpp | 1 + compiler/gentree.cpp | 144 +++++++++++++++++++++++++++++++++++ compiler/gentree.h | 2 + compiler/keywords.gperf | 1 + compiler/token.h | 1 + 6 files changed, 151 insertions(+) diff --git a/compiler/data/class-data.cpp b/compiler/data/class-data.cpp index f832fab683..e360bd050b 100644 --- a/compiler/data/class-data.cpp +++ b/compiler/data/class-data.cpp @@ -426,6 +426,8 @@ void ClassData::add_str_dependent(FunctionPtr cur_function, ClassType type, vk:: void ClassData::register_defines() const { members.for_each([this](const ClassMemberConstant &c) { + printf("In register define: %s\n", c.global_name().data()); + fflush(stdout); auto *data = new DefineData(std::string{c.global_name()}, c.value, DefineData::def_unknown); data->file_id = file_id; data->access = c.access; diff --git a/compiler/debug.cpp b/compiler/debug.cpp index 795ed12fd9..56420d4118 100644 --- a/compiler/debug.cpp +++ b/compiler/debug.cpp @@ -64,6 +64,7 @@ std::string debugTokenName(TokenType t) { {tok_class, "tok_class"}, {tok_interface, "tok_interface"}, {tok_trait, "tok_trait"}, + {tok_enum, "tok_enum"}, {tok_extends, "tok_extends"}, {tok_implements, "tok_implements"}, {tok_namespace, "tok_namespace"}, diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 11b70840e5..bb2a9e6af9 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1744,6 +1744,144 @@ VertexPtr GenTree::get_class(const PhpDocComment *phpdoc, ClassType class_type) return {}; } +VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { + CE(cur->type() == tok_enum); + next_cur(); + + CE (!kphp_error(test_expect(tok_func_name), "Enum name expected")); + + const auto name_str = static_cast(cur->str_val); + std::string full_class_name = processing_file->namespace_name.empty() ? std::string{name_str} : processing_file->namespace_name + "\\" + name_str; + + kphp_error(processing_file->namespace_uses.find(name_str) == processing_file->namespace_uses.end(), + "Enum name is the same as one of 'use' at the top of the file"); + + // TODO parse_extends_implements(); + + const auto class_ptr = ClassPtr(new ClassData{ClassType::klass}); + StackPushPop c_alive(class_stack, cur_class, class_ptr); + StackPushPop f_alive(functions_stack, cur_function, cur_class->gen_holder_function(full_class_name)); + + cur_class->modifiers.set_final(); + cur_class->file_id = processing_file; + cur_class->set_name_and_src_name(full_class_name); // with full namespaces and slashes + cur_class->phpdoc = phpdoc; + cur_class->is_immutable = true; + cur_class->location_line_num = line_num; + + bool registered = G->register_class(cur_class); + if (registered) { + ++G->stats.total_classes; + } + + cur_class->add_class_constant(); + if (registered) { + G->register_and_require_function(cur_function, parsed_os, true); // push the class down the pipeline + } + + // generate body + next_cur(); + CE(cur->type() == tok_opbrc); + + VertexPtr body_vertex = get_statement(); + kphp_assert_msg(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); + const auto body_seq = body_vertex.try_as(); + + std::vector cases; // do i need it? + + // add $name field + { + auto modifiers_field_public = FieldModifiers(); + modifiers_field_public.set_public(); + + auto name_vertex = VertexAdaptor::create(); + name_vertex->str_val = "name"; + name_vertex->is_const = true; + cur_class->members.add_instance_field(name_vertex, + VertexAdaptor::create(), + modifiers_field_public, + nullptr, + nullptr); + } + + // generating constructor + { + auto param_var = VertexAdaptor::create(); + param_var->str_val = "name_"; + const auto param = VertexAdaptor::create(param_var.clone()); + + auto this_vertex = VertexAdaptor::create(); + this_vertex->str_val = "this"; + auto inst_prop = VertexAdaptor::create(this_vertex.clone()); + inst_prop->str_val = "name"; + const auto body = VertexAdaptor::create( + std::vector{VertexAdaptor::create(inst_prop, param_var.clone()), VertexAdaptor::create(this_vertex.clone())}); + + auto func = VertexAdaptor::create(VertexAdaptor::create(std::vector{param}), body); + + std::string func_name = replace_backslashes(cur_class->name) + "$$" + ClassData::NAME_OF_CONSTRUCT; + auto this_param = cur_class->gen_param_this(func->get_location()); + func->param_list_ref() = VertexAdaptor::create(this_param, func->param_list()->params()); + VertexUtil::func_force_return(func, cur_class->gen_vertex_this(func->location)); + auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); + + auto f_alive2 = StackPushPop(functions_stack, cur_function, ctor_function); + + ctor_function->update_location_in_body(); + ctor_function->is_inline = true; + ctor_function->modifiers = FunctionModifiers::instance_private(); + ctor_function->phpdoc = phpdoc; + cur_class->members.add_instance_method(ctor_function); + G->register_and_require_function(ctor_function, parsed_os, true); + } + + // generating constants + for (const auto &stmt : body_seq->args()) { + if (const auto case_vertex = stmt.try_as()) { + auto cve = case_vertex->expr(); + const auto case_name_vertex = cve.try_as(); + + if (!case_name_vertex) { + puts("Blya"); + } + + assert(case_name_vertex); + const auto case_name = case_name_vertex->get_string(); + cases.push_back(case_name); + + VertexAdaptor cns = VertexAdaptor::create(); + cns->str_val = case_name.data(); + + const auto ctor_call = gen_constructor_call_with_args(cur_class, std::vector{cns}, auto_location()); + + cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); + + } + kphp_error(stmt->type() != op_var, "Fields are no allowed in enums"); + } + + return {}; +} + +VertexAdaptor GenTree::get_enum_case() { + + /* + enum { + ... + case ENUM_CASE; <----- parse such a construction + } + */ + CE(cur->type() == tok_case); + next_cur(); + CE (!kphp_error(test_expect(tok_func_name), "Enum case name expected")); + auto val = VertexAdaptor::create().set_location(auto_location()); + val->str_val = cur->str_val; + auto response = VertexAdaptor::create(val, VertexAdaptor::create()); + + + + return response; +} VertexAdaptor GenTree::gen_constructor_call_with_args(const std::string &allocated_class_name, std::vector args, const Location &location) { auto alloc = VertexAdaptor::create().set_location(location); @@ -2021,6 +2159,8 @@ VertexPtr GenTree::get_statement(const PhpDocComment *phpdoc) { CE (check_seq_end()); return res; } + case tok_case: + return get_enum_case(); case tok_return: return get_return(); case tok_continue: @@ -2232,6 +2372,8 @@ VertexPtr GenTree::get_statement(const PhpDocComment *phpdoc) { kphp_error(processing_file->namespace_uses.empty(), "Usage of operator `use`(Aliasing/Importing) with traits is temporarily prohibited"); return res; } + case tok_enum: + return get_enum(phpdoc); default: { auto res = get_expression(); CE (check_statement_end()); @@ -2270,6 +2412,8 @@ VertexPtr GenTree::get_const(AccessModifiers access) { CE (check_statement_end()); if (const_in_class) { +// printf("Add to class: %s\n", const_name.c_str()); +// fflush(stdout); cur_class->members.add_constant(const_name, v, access); return VertexAdaptor::create(); } diff --git a/compiler/gentree.h b/compiler/gentree.h index 31e5fe6314..87873593b0 100644 --- a/compiler/gentree.h +++ b/compiler/gentree.h @@ -119,6 +119,8 @@ class GenTree { static VertexAdaptor auto_capture_this_in_lambda(FunctionPtr f_lambda); VertexPtr get_class(const PhpDocComment *phpdoc, ClassType class_type); + VertexPtr get_enum(const PhpDocComment *phpdoc); + VertexAdaptor get_enum_case(); void parse_extends_implements(); static VertexPtr process_arrow(VertexPtr lhs, VertexPtr rhs); diff --git a/compiler/keywords.gperf b/compiler/keywords.gperf index e1260987d0..21ba7123af 100644 --- a/compiler/keywords.gperf +++ b/compiler/keywords.gperf @@ -31,6 +31,7 @@ switch, tok_switch class, tok_class interface, tok_interface trait, tok_trait +enum, tok_enum const, tok_const default, tok_default do, tok_do diff --git a/compiler/token.h b/compiler/token.h index 8303527035..9b229474da 100644 --- a/compiler/token.h +++ b/compiler/token.h @@ -47,6 +47,7 @@ enum TokenType { tok_class, tok_interface, tok_trait, + tok_enum, tok_extends, tok_implements, tok_namespace, From 229599a0ceb01a867acc8afdd4d1a531cf2a27db Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 15 May 2023 12:28:45 +0300 Subject: [PATCH 28/41] Add first test --- tests/phpt/enums/001_basic.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/phpt/enums/001_basic.php diff --git a/tests/phpt/enums/001_basic.php b/tests/phpt/enums/001_basic.php new file mode 100644 index 0000000000..65af07fef7 --- /dev/null +++ b/tests/phpt/enums/001_basic.php @@ -0,0 +1,18 @@ +@ok php8 +name; + +const c = MyBool::F; +echo c->name; + +echo MyBool::F->name; +echo MyBool::T->name; + +// TODO add 002_comparison \ No newline at end of file From d8711f7a75efcbb0c0f8382ff5d8d08b57528ae7 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 15 May 2023 17:01:22 +0300 Subject: [PATCH 29/41] Add tests; Add `implements; Fix problem with namespaces --- compiler/data/class-data.cpp | 2 - compiler/gentree.cpp | 61 ++++++++++++++++---- tests/phpt/enums/002_comparison.php | 24 ++++++++ tests/phpt/enums/003_cases.php | 30 ++++++++++ tests/phpt/enums/004_instance_method.php | 14 +++++ tests/phpt/enums/005_const_context.php | 16 +++++ tests/phpt/enums/006_func_param.php | 17 ++++++ tests/phpt/enums/007_instanceof.php | 16 +++++ tests/phpt/enums/008_namespace_interface.php | 21 +++++++ tests/phpt/enums/009_namespace_enum.php | 9 +++ tests/phpt/enums/101_cannot_extend.php | 10 ++++ tests/phpt/enums/102_not_implementation.php | 21 +++++++ tests/phpt/enums/enums/MyBool.php | 8 +++ tests/phpt/enums/interfaces/Printer.php | 7 +++ 14 files changed, 242 insertions(+), 14 deletions(-) create mode 100644 tests/phpt/enums/002_comparison.php create mode 100644 tests/phpt/enums/003_cases.php create mode 100644 tests/phpt/enums/004_instance_method.php create mode 100644 tests/phpt/enums/005_const_context.php create mode 100644 tests/phpt/enums/006_func_param.php create mode 100644 tests/phpt/enums/007_instanceof.php create mode 100644 tests/phpt/enums/008_namespace_interface.php create mode 100644 tests/phpt/enums/009_namespace_enum.php create mode 100644 tests/phpt/enums/101_cannot_extend.php create mode 100644 tests/phpt/enums/102_not_implementation.php create mode 100644 tests/phpt/enums/enums/MyBool.php create mode 100644 tests/phpt/enums/interfaces/Printer.php diff --git a/compiler/data/class-data.cpp b/compiler/data/class-data.cpp index e360bd050b..f832fab683 100644 --- a/compiler/data/class-data.cpp +++ b/compiler/data/class-data.cpp @@ -426,8 +426,6 @@ void ClassData::add_str_dependent(FunctionPtr cur_function, ClassType type, vk:: void ClassData::register_defines() const { members.for_each([this](const ClassMemberConstant &c) { - printf("In register define: %s\n", c.global_name().data()); - fflush(stdout); auto *data = new DefineData(std::string{c.global_name()}, c.value, DefineData::def_unknown); data->file_id = file_id; data->access = c.access; diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index bb2a9e6af9..d096328ea2 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1756,12 +1756,23 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { kphp_error(processing_file->namespace_uses.find(name_str) == processing_file->namespace_uses.end(), "Enum name is the same as one of 'use' at the top of the file"); - // TODO parse_extends_implements(); const auto class_ptr = ClassPtr(new ClassData{ClassType::klass}); StackPushPop c_alive(class_stack, cur_class, class_ptr); StackPushPop f_alive(functions_stack, cur_function, cur_class->gen_holder_function(full_class_name)); + next_cur(); + kphp_error(!test_expect(tok_extends), "Enums cannot extend"); + + if (test_expect(tok_implements)) { // TODO separate parse_extends_implements into different functions + do { + next_cur(); + kphp_error(test_expect(tok_func_name), "Class name expected after 'implements'"); + cur_class->add_str_dependent(cur_function, ClassType::interface, cur->str_val); + next_cur(); + } while (test_expect(tok_comma)); + } + cur_class->modifiers.set_final(); cur_class->file_id = processing_file; cur_class->set_name_and_src_name(full_class_name); // with full namespaces and slashes @@ -1780,14 +1791,12 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { } // generate body - next_cur(); CE(cur->type() == tok_opbrc); VertexPtr body_vertex = get_statement(); kphp_assert_msg(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); - const auto body_seq = body_vertex.try_as(); - std::vector cases; // do i need it? + std::vector cases; // add $name field { @@ -1829,11 +1838,12 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { ctor_function->update_location_in_body(); ctor_function->is_inline = true; - ctor_function->modifiers = FunctionModifiers::instance_private(); + ctor_function->modifiers = FunctionModifiers::instance_public(); ctor_function->phpdoc = phpdoc; cur_class->members.add_instance_method(ctor_function); G->register_and_require_function(ctor_function, parsed_os, true); } + const auto body_seq = body_vertex.try_as(); // generating constants for (const auto &stmt : body_seq->args()) { @@ -1841,25 +1851,52 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { auto cve = case_vertex->expr(); const auto case_name_vertex = cve.try_as(); - if (!case_name_vertex) { - puts("Blya"); - } - assert(case_name_vertex); const auto case_name = case_name_vertex->get_string(); cases.push_back(case_name); VertexAdaptor cns = VertexAdaptor::create(); - cns->str_val = case_name.data(); - - const auto ctor_call = gen_constructor_call_with_args(cur_class, std::vector{cns}, auto_location()); + cns->str_val = case_name; + const auto ctor_call = gen_constructor_call_with_args(cur_class->name, std::vector{cns}, auto_location()); + ctor_call->args()[0].as()->allocated_class_name = "self"; cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); } kphp_error(stmt->type() != op_var, "Fields are no allowed in enums"); } + + // generating cases() + { + std::vector> arr_args; + + std::transform(cases.begin(), cases.end(), std::back_inserter(arr_args), [](const std::string &case_name) { + auto item = VertexAdaptor::create(); + item->str_val = "self::" + case_name; + return item; + }); + + auto response = VertexAdaptor::create(std::move(arr_args)); + + const auto params = VertexAdaptor::create(std::vector>{}); + const auto body = VertexAdaptor::create(std::vector{VertexAdaptor::create(response)}); + auto func = VertexAdaptor::create(params, body); + std::string func_name = replace_backslashes(cur_class->name) + "$$cases"; + auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); + auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); + + + cases_fun->update_location_in_body(); + cases_fun->is_inline = true; + cases_fun->modifiers = FunctionModifiers::nonmember(); + cases_fun->modifiers.set_public(); + cases_fun->modifiers.set_final(); + cur_class->members.add_static_method(cases_fun); + G->register_and_require_function(cases_fun, parsed_os, true); + } + + return {}; } diff --git a/tests/phpt/enums/002_comparison.php b/tests/phpt/enums/002_comparison.php new file mode 100644 index 0000000000..1df80262c1 --- /dev/null +++ b/tests/phpt/enums/002_comparison.php @@ -0,0 +1,24 @@ +@ok php8 +name); +} + +enum Foo { + case Bar; + case Baz; +} + +enum Single { + case Single; +} + +enum EmptyEnum { +} + +var_dump(count(Foo::cases())); +var_dump(count(Single::cases())); +var_dump(count(EmptyEnum::cases())); + diff --git a/tests/phpt/enums/004_instance_method.php b/tests/phpt/enums/004_instance_method.php new file mode 100644 index 0000000000..98df98f52a --- /dev/null +++ b/tests/phpt/enums/004_instance_method.php @@ -0,0 +1,14 @@ +@ok php8 +name); + } +} + +Foo::Bar->dump(); +Foo::Baz->dump(); diff --git a/tests/phpt/enums/005_const_context.php b/tests/phpt/enums/005_const_context.php new file mode 100644 index 0000000000..e357c02f17 --- /dev/null +++ b/tests/phpt/enums/005_const_context.php @@ -0,0 +1,16 @@ +@ok php8 +name); + } +} + +const CONST_VAR = Foo::Bar; +$var = Foo::Baz; + +CONST_VAR->dump(); +$var->dump(); diff --git a/tests/phpt/enums/006_func_param.php b/tests/phpt/enums/006_func_param.php new file mode 100644 index 0000000000..7a35a69980 --- /dev/null +++ b/tests/phpt/enums/006_func_param.php @@ -0,0 +1,17 @@ +@ok php8 +name); + } +} + +function print_Foo(Foo $var) { + $var->dump(); +} + +print_Foo(Foo::Bar); +print_Foo(Foo::Baz); diff --git a/tests/phpt/enums/007_instanceof.php b/tests/phpt/enums/007_instanceof.php new file mode 100644 index 0000000000..aaca95d306 --- /dev/null +++ b/tests/phpt/enums/007_instanceof.php @@ -0,0 +1,16 @@ +@ok php8 +print(); +MyBool::F->print(); + +// TODO tests with namespaces, different dirs etc diff --git a/tests/phpt/enums/009_namespace_enum.php b/tests/phpt/enums/009_namespace_enum.php new file mode 100644 index 0000000000..e1dc1cb1b1 --- /dev/null +++ b/tests/phpt/enums/009_namespace_enum.php @@ -0,0 +1,9 @@ +@ok php8 +name; +echo AnotherBool::F->name; \ No newline at end of file diff --git a/tests/phpt/enums/101_cannot_extend.php b/tests/phpt/enums/101_cannot_extend.php new file mode 100644 index 0000000000..5ef61918ca --- /dev/null +++ b/tests/phpt/enums/101_cannot_extend.php @@ -0,0 +1,10 @@ +@kphp_should_fail +/Enums cannot extend/ + Date: Mon, 15 May 2023 17:39:51 +0300 Subject: [PATCH 30/41] Add test with trait --- tests/phpt/enums/010_with_traits.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/phpt/enums/010_with_traits.php diff --git a/tests/phpt/enums/010_with_traits.php b/tests/phpt/enums/010_with_traits.php new file mode 100644 index 0000000000..4fcb11e53b --- /dev/null +++ b/tests/phpt/enums/010_with_traits.php @@ -0,0 +1,18 @@ +@ok php8 +log("sample"); From 70afe97d8028dedd67aa7c4feeace890b20866db Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 16 May 2023 14:31:28 +0300 Subject: [PATCH 31/41] Add UnitEnum to runtime --- builtin-functions/_functions.txt | 4 ++++ compiler/data/lib-data.cpp | 1 + compiler/gentree.cpp | 7 ++++++- runtime/class_instance.inl | 1 + runtime/enum.h | 26 ++++++++++++++++++++++++++ tests/phpt/enums/011_unit_enum.php | 15 +++++++++++++++ 6 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 runtime/enum.h create mode 100644 tests/phpt/enums/011_unit_enum.php diff --git a/builtin-functions/_functions.txt b/builtin-functions/_functions.txt index 026a828877..bcae0c7906 100644 --- a/builtin-functions/_functions.txt +++ b/builtin-functions/_functions.txt @@ -67,6 +67,10 @@ class Error implements Throwable { final private function __clone() ::: void; } +interface UnitEnum { + public static function cases(); +} + interface Memcache { public function get (string|string[] $key) ::: mixed; public function delete (string $key) ::: bool; diff --git a/compiler/data/lib-data.cpp b/compiler/data/lib-data.cpp index 476df61f42..53e03db523 100644 --- a/compiler/data/lib-data.cpp +++ b/compiler/data/lib-data.cpp @@ -86,6 +86,7 @@ const char *LibData::headers_tmp_dir() { return "lib_headers/"; } + const char *LibData::functions_txt_tmp_file() { return "lib_functions.txt"; } diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index d096328ea2..137d5e4156 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1779,6 +1779,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cur_class->phpdoc = phpdoc; cur_class->is_immutable = true; cur_class->location_line_num = line_num; + cur_class->add_str_dependent(cur_function, ClassType::interface, "\\UnitEnum"); bool registered = G->register_class(cur_class); if (registered) { @@ -1794,7 +1795,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { CE(cur->type() == tok_opbrc); VertexPtr body_vertex = get_statement(); - kphp_assert_msg(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); + kphp_error(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); std::vector cases; @@ -1883,9 +1884,13 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { const auto body = VertexAdaptor::create(std::vector{VertexAdaptor::create(response)}); auto func = VertexAdaptor::create(params, body); std::string func_name = replace_backslashes(cur_class->name) + "$$cases"; + + auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); +// const auto *hint = TypeHintArray::create_array_of_any(); +// cases_fun->return_typehint = hint; cases_fun->update_location_in_body(); cases_fun->is_inline = true; diff --git a/runtime/class_instance.inl b/runtime/class_instance.inl index bed1014eb1..53249ad927 100644 --- a/runtime/class_instance.inl +++ b/runtime/class_instance.inl @@ -4,6 +4,7 @@ #error "this file must be included only from kphp_core.h" #endif + template class_instance &class_instance::operator=(const Optional &null) noexcept { php_assert(null.value_state() == OptionalState::null_value); diff --git a/runtime/enum.h b/runtime/enum.h new file mode 100644 index 0000000000..b93a085d76 --- /dev/null +++ b/runtime/enum.h @@ -0,0 +1,26 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2023 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + + +#include "runtime/memory_usage.h" +#include "runtime/refcountable_php_classes.h" +#include "runtime/to-array-processor.h" +#include "runtime/instance-copy-processor.h" + +struct C$UnitEnum : public abstract_refcountable_php_interface { + virtual const char *get_class() const noexcept = 0; + virtual int get_hash() const noexcept = 0; + + virtual void accept(ToArrayVisitor &) noexcept {} + virtual void accept(InstanceMemoryEstimateVisitor &) noexcept {} + virtual void accept(InstanceReferencesCountingVisitor &) noexcept {} + virtual void accept(InstanceDeepCopyVisitor &) noexcept {} + virtual void accept(InstanceDeepDestroyVisitor &) noexcept {} + + C$UnitEnum() __attribute__((always_inline)) = default; + ~C$UnitEnum() __attribute__((always_inline)) = default; +}; + diff --git a/tests/phpt/enums/011_unit_enum.php b/tests/phpt/enums/011_unit_enum.php new file mode 100644 index 0000000000..785d7d5fb7 --- /dev/null +++ b/tests/phpt/enums/011_unit_enum.php @@ -0,0 +1,15 @@ +@ok php8 + Date: Wed, 17 May 2023 11:17:14 +0300 Subject: [PATCH 32/41] Improve diagnostics --- compiler/data/class-data.h | 2 ++ compiler/gentree.cpp | 8 ++++---- compiler/pipes/code-gen.cpp | 1 + compiler/pipes/sort-and-inherit-classes.cpp | 8 ++++++++ dev_kphp/index.php | 12 ++++++++++++ tests/phpt/enums/103_trait_with_property.php | 15 +++++++++++++++ tests/phpt/enums/104_extends_enum.php | 9 +++++++++ tests/phpt/enums/105_class_case.php | 12 ++++++++++++ 8 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 dev_kphp/index.php create mode 100644 tests/phpt/enums/103_trait_with_property.php create mode 100644 tests/phpt/enums/104_extends_enum.php create mode 100644 tests/phpt/enums/105_class_case.php diff --git a/compiler/data/class-data.h b/compiler/data/class-data.h index 0887dd2691..63027a2730 100644 --- a/compiler/data/class-data.h +++ b/compiler/data/class-data.h @@ -27,6 +27,7 @@ enum class ClassType { klass, interface, trait, + enumeration, ffi_scope, ffi_cdata, }; @@ -122,6 +123,7 @@ class ClassData : public Lockable { } bool is_class() const { return class_type == ClassType::klass; } + bool is_enum() const { return class_type == ClassType::enumeration; } bool is_interface() const { return class_type == ClassType::interface; } bool is_trait() const { return class_type == ClassType::trait; } bool is_ffi_scope() const { return class_type == ClassType::ffi_scope; } diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 137d5e4156..87ed8c2227 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1757,7 +1757,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { "Enum name is the same as one of 'use' at the top of the file"); - const auto class_ptr = ClassPtr(new ClassData{ClassType::klass}); + const auto class_ptr = ClassPtr(new ClassData{ClassType::enumeration}); StackPushPop c_alive(class_stack, cur_class, class_ptr); StackPushPop f_alive(functions_stack, cur_function, cur_class->gen_holder_function(full_class_name)); @@ -1889,9 +1889,6 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); -// const auto *hint = TypeHintArray::create_array_of_any(); -// cases_fun->return_typehint = hint; - cases_fun->update_location_in_body(); cases_fun->is_inline = true; cases_fun->modifiers = FunctionModifiers::nonmember(); @@ -1914,6 +1911,9 @@ VertexAdaptor GenTree::get_enum_case() { } */ CE(cur->type() == tok_case); + + kphp_error(cur_class->is_enum(), "'case' expressions are available only in enum declaration"); + next_cur(); CE (!kphp_error(test_expect(tok_func_name), "Enum case name expected")); auto val = VertexAdaptor::create().set_location(auto_location()); diff --git a/compiler/pipes/code-gen.cpp b/compiler/pipes/code-gen.cpp index b039074221..4f1010adb4 100644 --- a/compiler/pipes/code-gen.cpp +++ b/compiler/pipes/code-gen.cpp @@ -72,6 +72,7 @@ void CodeGenF::on_finish(DataStream> &os) { } switch (c->class_type) { + case ClassType::enumeration: case ClassType::klass: code_gen_start_root_task(os, std::make_unique(c)); break; diff --git a/compiler/pipes/sort-and-inherit-classes.cpp b/compiler/pipes/sort-and-inherit-classes.cpp index 5cf22573fe..43df751da6 100644 --- a/compiler/pipes/sort-and-inherit-classes.cpp +++ b/compiler/pipes/sort-and-inherit-classes.cpp @@ -204,6 +204,8 @@ void SortAndInheritClassesF::inherit_child_class_from_parent(ClassPtr child_clas stage::set_file(child_class->file_id); stage::set_function(FunctionPtr{}); + kphp_error_return(!parent_class->is_enum(), fmt_format("Inheritance from enum is not allowed ({} extends {})", child_class->name, parent_class->name)); + if (parent_class->is_ffi_cdata() || parent_class->name == "FFI\\Scope") { kphp_error_return(child_class->is_builtin() || child_class->is_ffi_scope(), "FFI classes should not be extended"); @@ -283,6 +285,11 @@ void SortAndInheritClassesF::clone_members_from_traits(std::vector &&t traits[i]->members.for_each([&](ClassMemberInstanceMethod &m) { check_other_traits_doesnt_contain_method_and_clone(m.function); }); traits[i]->members.for_each([&](ClassMemberStaticMethod &m) { check_other_traits_doesnt_contain_method_and_clone(m.function); }); + if (ready_class->is_enum()) { + kphp_error_return(!traits[i]->members.has_any_instance_var() && !traits[i]->members.has_any_static_var(), + fmt_format("Traits used in an enum must not contain properties (enum {} uses trait {})", ready_class->name, traits[i]->name)); + } + traits[i]->members.for_each([&](const ClassMemberInstanceField &f) { ready_class->members.add_instance_field(f.root.clone(), f.var->init_val.clone(), f.modifiers, f.phpdoc, f.type_hint); }); @@ -319,6 +326,7 @@ void SortAndInheritClassesF::on_class_ready(ClassPtr klass, DataStreamget_class(dep.class_name); switch (dep.type) { + case ClassType::enumeration: case ClassType::klass: inherit_child_class_from_parent(klass, dep_class, function_stream); break; diff --git a/dev_kphp/index.php b/dev_kphp/index.php new file mode 100644 index 0000000000..dfc40e1743 --- /dev/null +++ b/dev_kphp/index.php @@ -0,0 +1,12 @@ +@kphp_should_fail +/'case' expressions are available only in enum declaration/ +Print(); diff --git a/tests/phpt/enums/104_extends_enum.php b/tests/phpt/enums/104_extends_enum.php new file mode 100644 index 0000000000..3d95b2a867 --- /dev/null +++ b/tests/phpt/enums/104_extends_enum.php @@ -0,0 +1,9 @@ +@kphp_should_fail + Date: Fri, 19 May 2023 11:35:20 +0300 Subject: [PATCH 33/41] Add backed enum --- builtin-functions/_functions.txt | 5 + compiler/gentree.cpp | 258 +++++++++++++++--- runtime/enum.h | 7 + tests/phpt/enums/007_instanceof.php | 2 +- tests/phpt/enums/012_backed_basic.php | 21 ++ tests/phpt/enums/013_backed_try_from.php | 13 + tests/phpt/enums/014_backed_from.php | 12 + tests/phpt/enums/015_failed_from.php | 13 + .../phpt/enums/017_backed_enum_interface.php | 16 ++ 9 files changed, 306 insertions(+), 41 deletions(-) create mode 100644 tests/phpt/enums/012_backed_basic.php create mode 100644 tests/phpt/enums/013_backed_try_from.php create mode 100644 tests/phpt/enums/014_backed_from.php create mode 100644 tests/phpt/enums/015_failed_from.php create mode 100644 tests/phpt/enums/017_backed_enum_interface.php diff --git a/builtin-functions/_functions.txt b/builtin-functions/_functions.txt index bcae0c7906..380af74ff5 100644 --- a/builtin-functions/_functions.txt +++ b/builtin-functions/_functions.txt @@ -71,6 +71,11 @@ interface UnitEnum { public static function cases(); } +interface BackedEnum implements UnitEnum { + public static function from(int | string $value); + public static function tryFrom(int | string $value); +} + interface Memcache { public function get (string|string[] $key) ::: mixed; public function delete (string $key) ::: bool; diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 87ed8c2227..e6fe97e844 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1764,10 +1764,40 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { next_cur(); kphp_error(!test_expect(tok_extends), "Enums cannot extend"); + enum class EnumType { + Empty, + Pure, + BackedString, + BackedInt, + }; + + auto is_pure_enum_case = [](VertexAdaptor value) -> bool { + return value->args().empty(); + }; + + EnumType enum_type = EnumType::Empty; + + if (test_expect(tok_colon)) { + next_cur(); +// cur->debugPrint(); + + if (cur->type() == tok_int) { + enum_type = EnumType::BackedInt; + } else if (cur->type() == tok_string) { + enum_type = EnumType::BackedString; + } + + next_cur(); + } else { + enum_type = EnumType::Pure; + } + +// printf("Enum type = %d\n", (int)enum_type); + if (test_expect(tok_implements)) { // TODO separate parse_extends_implements into different functions do { next_cur(); - kphp_error(test_expect(tok_func_name), "Class name expected after 'implements'"); + kphp_error(test_expect(tok_func_name), "Interface name expected after 'implements'"); cur_class->add_str_dependent(cur_function, ClassType::interface, cur->str_val); next_cur(); } while (test_expect(tok_comma)); @@ -1779,19 +1809,18 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cur_class->phpdoc = phpdoc; cur_class->is_immutable = true; cur_class->location_line_num = line_num; - cur_class->add_str_dependent(cur_function, ClassType::interface, "\\UnitEnum"); + cur_class->add_str_dependent(cur_function, ClassType::interface, enum_type == EnumType::Pure ? "\\UnitEnum" : "\\BackedEnum"); bool registered = G->register_class(cur_class); if (registered) { ++G->stats.total_classes; } - cur_class->add_class_constant(); if (registered) { G->register_and_require_function(cur_function, parsed_os, true); // push the class down the pipeline } - // generate body + // parse body CE(cur->type() == tok_opbrc); VertexPtr body_vertex = get_statement(); @@ -1799,6 +1828,45 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { std::vector cases; + + const auto body_seq = body_vertex.try_as(); + + // generating constants + for (const auto &stmt : body_seq->args()) { + if (const auto case_vertex = stmt.try_as()) { + auto cve = case_vertex->expr(); + const auto case_name_vertex = cve.try_as(); + + assert(case_name_vertex); + const auto case_name = case_name_vertex->get_string(); + cases.push_back(case_name); + + VertexAdaptor cns = VertexAdaptor::create(); + cns->str_val = case_name; + + auto is_pure_case = is_pure_enum_case(case_vertex->cmd()); + + kphp_error( + (is_pure_case && enum_type == EnumType::Pure) || + (!is_pure_case && vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)), + fmt_format("Not appropriate case \"{}\" for enum \"{}\"", case_name, cur_class->name)); + + std::vector args{cns}; + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + kphp_error(!case_vertex->cmd()->args().empty(), "You cannot use pure enum' case in backed enum"); + args.push_back(case_vertex->cmd()->args()[0]); + } + +// printf("cur class name: %s\n", cur_class->name.data()); + const auto ctor_call = gen_constructor_call_with_args(cur_class->name, args, auto_location()); + ctor_call->args()[0].as()->allocated_class_name = "self"; + cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); + + } + kphp_error(stmt->type() != op_var, "Fields are no allowed in enums"); + } + cur_class->add_class_constant(); + // add $name field { auto modifiers_field_public = FieldModifiers(); @@ -1808,26 +1876,63 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { name_vertex->str_val = "name"; name_vertex->is_const = true; cur_class->members.add_instance_field(name_vertex, - VertexAdaptor::create(), + VertexPtr{}, modifiers_field_public, nullptr, - nullptr); + TypeHintPrimitive::create(PrimitiveType::tp_string)); } + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + auto modifiers_field_public = FieldModifiers(); + modifiers_field_public.set_public(); + + auto name_vertex = VertexAdaptor::create(); + name_vertex->str_val = "value"; + name_vertex->is_const = true; + + cur_class->members.add_instance_field(name_vertex, + VertexPtr{}, + modifiers_field_public, + nullptr, + TypeHintPrimitive::create(enum_type == EnumType::BackedInt ? tp_int : tp_string)); + } + + #if 1 + // generating constructor { auto param_var = VertexAdaptor::create(); param_var->str_val = "name_"; - const auto param = VertexAdaptor::create(param_var.clone()); + auto param = VertexAdaptor::create(param_var.clone()); + + auto params_vec = std::vector{param}; auto this_vertex = VertexAdaptor::create(); this_vertex->str_val = "this"; auto inst_prop = VertexAdaptor::create(this_vertex.clone()); inst_prop->str_val = "name"; - const auto body = VertexAdaptor::create( - std::vector{VertexAdaptor::create(inst_prop, param_var.clone()), VertexAdaptor::create(this_vertex.clone())}); + auto ctor_body_seq = std::vector{ VertexAdaptor::create(inst_prop, param_var.clone())}; + + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + auto param_var2 = VertexAdaptor::create(); + param_var2->str_val = "value_"; + auto param2= VertexAdaptor::create(param_var2.clone()); - auto func = VertexAdaptor::create(VertexAdaptor::create(std::vector{param}), body); + params_vec.emplace_back(param2); + + auto this_vertex2 = VertexAdaptor::create(); + this_vertex2->str_val = "this"; + auto inst_prop2 = VertexAdaptor::create(this_vertex2.clone()); + inst_prop2->str_val = "value"; + + ctor_body_seq.emplace_back(VertexAdaptor::create(inst_prop2, param_var2.clone())); + } + + ctor_body_seq.emplace_back(VertexAdaptor::create(this_vertex.clone())); + + const auto body = VertexAdaptor::create(ctor_body_seq); + + auto func = VertexAdaptor::create(VertexAdaptor::create(params_vec), body); std::string func_name = replace_backslashes(cur_class->name) + "$$" + ClassData::NAME_OF_CONSTRUCT; auto this_param = cur_class->gen_param_this(func->get_location()); @@ -1835,7 +1940,6 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { VertexUtil::func_force_return(func, cur_class->gen_vertex_this(func->location)); auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); - auto f_alive2 = StackPushPop(functions_stack, cur_function, ctor_function); ctor_function->update_location_in_body(); ctor_function->is_inline = true; @@ -1844,30 +1948,8 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cur_class->members.add_instance_method(ctor_function); G->register_and_require_function(ctor_function, parsed_os, true); } - const auto body_seq = body_vertex.try_as(); - - // generating constants - for (const auto &stmt : body_seq->args()) { - if (const auto case_vertex = stmt.try_as()) { - auto cve = case_vertex->expr(); - const auto case_name_vertex = cve.try_as(); - - assert(case_name_vertex); - const auto case_name = case_name_vertex->get_string(); - cases.push_back(case_name); - - VertexAdaptor cns = VertexAdaptor::create(); - cns->str_val = case_name; - - const auto ctor_call = gen_constructor_call_with_args(cur_class->name, std::vector{cns}, auto_location()); - ctor_call->args()[0].as()->allocated_class_name = "self"; - cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); - - } - kphp_error(stmt->type() != op_var, "Fields are no allowed in enums"); - } - - + #endif +#if 1 // generating cases() { std::vector> arr_args; @@ -1887,7 +1969,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); - auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); + // auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); cases_fun->update_location_in_body(); cases_fun->is_inline = true; @@ -1897,7 +1979,88 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cur_class->members.add_static_method(cases_fun); G->register_and_require_function(cases_fun, parsed_os, true); } +#endif + #if 1 + if (vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)) { + auto param_var = VertexAdaptor::create(); + param_var->str_val = "value_"; + auto params = VertexAdaptor::create(std::vector{VertexAdaptor::create(param_var)}); + + + VertexPtr foreach_vert; + // generating foreach + { + // generating foreach param + VertexAdaptor cases_call = VertexAdaptor::create(); + cases_call->str_val = "self::cases"; + + VertexAdaptor cur_case = VertexAdaptor::create(); + cur_case->str_val = "cur_case"; + + VertexAdaptor tmp_var = VertexAdaptor::create(); + tmp_var->str_val = "tmp_var"; + tmp_var->extra_type = op_ex_var_superlocal; + + VertexAdaptor fe_param = VertexAdaptor::create(cases_call, cur_case, tmp_var); + + // generating body + auto inst_prop = VertexAdaptor::create(cur_case.clone()); + inst_prop->str_val = "value"; + auto cmp_vert = VertexAdaptor::create(VertexAdaptor::create(inst_prop.clone(), param_var.clone())); + auto ret_vert = VertexUtil::embrace(VertexAdaptor::create(cur_case.clone())); + auto if_vert = VertexAdaptor::create(cmp_vert, ret_vert); + foreach_vert = VertexAdaptor::create(fe_param, VertexUtil::embrace(if_vert)); + } + auto func_body = VertexUtil::embrace(foreach_vert); + auto tryFrom_func = VertexAdaptor::create(params, func_body); + [[maybe_unused]]auto from_func = tryFrom_func.clone(); + + auto tf_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$tryFrom", tryFrom_func, FunctionData::func_local); + + VertexUtil::func_force_return(tryFrom_func, VertexAdaptor::create()); + + // auto f_alive2 = StackPushPop(functions_stack, cur_function, tf_fun); + + tf_fun->update_location_in_body(); + tf_fun->is_inline = true; + tf_fun->modifiers = FunctionModifiers::nonmember(); + tf_fun->modifiers.set_public(); + tf_fun->modifiers.set_final(); + cur_class->members.add_static_method(tf_fun); + G->register_and_require_function(tf_fun, parsed_os, true); + + + // from func + { + VertexPtr cmd = from_func->cmd(); + assert (cmd->type() == op_seq); + + VertexAdaptor text = VertexAdaptor::create(); + text->str_val = fmt_format("Not a valid backing value for enum \"{}\"", cur_class->name); + auto code = VertexAdaptor::create(); + code->set_string("0"); + + VertexPtr node = gen_constructor_call_with_args("\\ValueError", std::vector{text, code}, auto_location()); + node = VertexAdaptor::create(node); + + std::vector next = cmd->get_next(); + next.push_back(node); + from_func->cmd_ref() = VertexAdaptor::create(next); + + auto f_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$from", from_func, FunctionData::func_local); + + + f_fun->update_location_in_body(); + f_fun->is_inline = true; + f_fun->modifiers = FunctionModifiers::nonmember(); + f_fun->modifiers.set_public(); + f_fun->modifiers.set_final(); + cur_class->members.add_static_method(f_fun); + G->register_and_require_function(f_fun, parsed_os, true); + } + } + #endif return {}; } @@ -1907,7 +2070,7 @@ VertexAdaptor GenTree::get_enum_case() { /* enum { ... - case ENUM_CASE; <----- parse such a construction + case ENUM_CASE (= "value"); <----- parse such a construction } */ CE(cur->type() == tok_case); @@ -1916,10 +2079,25 @@ VertexAdaptor GenTree::get_enum_case() { next_cur(); CE (!kphp_error(test_expect(tok_func_name), "Enum case name expected")); - auto val = VertexAdaptor::create().set_location(auto_location()); - val->str_val = cur->str_val; - auto response = VertexAdaptor::create(val, VertexAdaptor::create()); + auto case_name = VertexAdaptor::create().set_location(auto_location()); + case_name->str_val = cur->str_val; + VertexAdaptor response; + + next_cur(); + if (test_expect(tok_semicolon)) { + response = VertexAdaptor::create(case_name, VertexAdaptor::create()); + } else { + CE (!kphp_error(test_expect(tok_eq1), "\"=\" must be after backed enum case")); + response = VertexAdaptor::create(case_name, VertexAdaptor::create()); + + next_cur(); // WTF why I need it? + + auto expr = get_expression(); +// puts("Enum value:\n"); +// expr.debugPrint(); + response = VertexAdaptor::create(case_name, VertexAdaptor::create(expr)); + } return response; diff --git a/runtime/enum.h b/runtime/enum.h index b93a085d76..776098b388 100644 --- a/runtime/enum.h +++ b/runtime/enum.h @@ -24,3 +24,10 @@ struct C$UnitEnum : public abstract_refcountable_php_interface { ~C$UnitEnum() __attribute__((always_inline)) = default; }; +struct C$BackedEnum : public C$UnitEnum { + virtual const char *get_class() const noexcept override = 0; + virtual int get_hash() const noexcept override = 0; + + C$BackedEnum() __attribute__((always_inline)) = default; + ~C$BackedEnum() __attribute__((always_inline)) = default; +}; diff --git a/tests/phpt/enums/007_instanceof.php b/tests/phpt/enums/007_instanceof.php index aaca95d306..5303f9a33c 100644 --- a/tests/phpt/enums/007_instanceof.php +++ b/tests/phpt/enums/007_instanceof.php @@ -13,4 +13,4 @@ enum Baz { var_dump(Baz::Qux instanceof Baz); var_dump(Foo::Bar instanceof Baz); -var_dump(Baz::Qux instanceof Foo); \ No newline at end of file +var_dump(Baz::Qux instanceof Foo); diff --git a/tests/phpt/enums/012_backed_basic.php b/tests/phpt/enums/012_backed_basic.php new file mode 100644 index 0000000000..0b17ef678b --- /dev/null +++ b/tests/phpt/enums/012_backed_basic.php @@ -0,0 +1,21 @@ +@ok php8 +name; + echo $v->value; +} + +$v = HttpResponse::Ok; +var_dump($v->value); +var_dump($v->name); + +const c = HttpResponse::ServerError; +var_dump($v->value); +var_dump($v->name); diff --git a/tests/phpt/enums/013_backed_try_from.php b/tests/phpt/enums/013_backed_try_from.php new file mode 100644 index 0000000000..497cc171be --- /dev/null +++ b/tests/phpt/enums/013_backed_try_from.php @@ -0,0 +1,13 @@ +@ok php8 +name); +var_dump(Status::tryFrom(404)->name); +var_dump(Status::tryFrom(500)->name); +var_dump(Status::tryFrom(0) === null); diff --git a/tests/phpt/enums/014_backed_from.php b/tests/phpt/enums/014_backed_from.php new file mode 100644 index 0000000000..5ad7d34e1e --- /dev/null +++ b/tests/phpt/enums/014_backed_from.php @@ -0,0 +1,12 @@ +@ok php8 +name); +var_dump(Status::from(404)->name); +var_dump(Status::from(500)->name); diff --git a/tests/phpt/enums/015_failed_from.php b/tests/phpt/enums/015_failed_from.php new file mode 100644 index 0000000000..10ac364124 --- /dev/null +++ b/tests/phpt/enums/015_failed_from.php @@ -0,0 +1,13 @@ +@ok php8 +getCode() . "\n"; +} diff --git a/tests/phpt/enums/017_backed_enum_interface.php b/tests/phpt/enums/017_backed_enum_interface.php new file mode 100644 index 0000000000..9c48b6b8ab --- /dev/null +++ b/tests/phpt/enums/017_backed_enum_interface.php @@ -0,0 +1,16 @@ +@ok php8 +enum Status : int { + case Ok = 1; + case Fail = 0; +} + +echo Status::Ok instanceof \BackedEnum; +echo Status::Ok instanceof \UnitEnum; +echo Status::Fail instanceof \BackedEnum; +echo Status::Fail instanceof \UnitEnum; + +enum A { + case B; +} + +echo A::B instanceof \BackedEnum; \ No newline at end of file From a32bc0efd6181cd18664f03771f5fe8fa47e3cd1 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 19 May 2023 13:09:55 +0300 Subject: [PATCH 34/41] Improve diagnostics * TypeHint for cases() return value and arg of from/tryFrom() * Extension/implementation of UnitEnum and BackedEnum is prohibited --- compiler/gentree.cpp | 9 ++++++--- compiler/pipes/collect-required-and-classes.cpp | 8 ++++++++ dev_kphp/index.php | 14 +++++++------- .../106_extends_implements_enum_interfaces.php | 12 ++++++++++++ tests/phpt/enums/107_try_from_wrong_type.php | 12 ++++++++++++ 5 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 tests/phpt/enums/106_extends_implements_enum_interfaces.php create mode 100644 tests/phpt/enums/107_try_from_wrong_type.php diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index e6fe97e844..539d385faa 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1902,7 +1902,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { // generating constructor { auto param_var = VertexAdaptor::create(); - param_var->str_val = "name_"; + param_var->str_val = "name"; auto param = VertexAdaptor::create(param_var.clone()); auto params_vec = std::vector{param}; @@ -1976,6 +1976,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cases_fun->modifiers = FunctionModifiers::nonmember(); cases_fun->modifiers.set_public(); cases_fun->modifiers.set_final(); + cases_fun->return_typehint = TypeHintArray::create(cur_class->type_hint); cur_class->members.add_static_method(cases_fun); G->register_and_require_function(cases_fun, parsed_os, true); } @@ -1984,8 +1985,10 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { #if 1 if (vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)) { auto param_var = VertexAdaptor::create(); - param_var->str_val = "value_"; - auto params = VertexAdaptor::create(std::vector{VertexAdaptor::create(param_var)}); + param_var->str_val = "value"; + auto param_pampam = VertexAdaptor::create(param_var); + param_pampam->type_hint = TypeHintPipe::create(std::vector{TypeHintPrimitive::create(tp_int), TypeHintPrimitive::create(tp_string)}); + auto params = VertexAdaptor::create(std::vector{param_pampam}); VertexPtr foreach_vert; diff --git a/compiler/pipes/collect-required-and-classes.cpp b/compiler/pipes/collect-required-and-classes.cpp index 78305f26b8..1e5f47948d 100644 --- a/compiler/pipes/collect-required-and-classes.cpp +++ b/compiler/pipes/collect-required-and-classes.cpp @@ -74,6 +74,14 @@ class CollectRequiredPass final : public FunctionPassBase { } } + if (cur_class->name != "BackedEnum" && !cur_class->is_enum() && (dep.class_name == "UnitEnum" || dep.class_name == "BackedEnum")) { + if (cur_class->is_interface()) { + kphp_error(false, fmt_format("Interface {} cannot extend UnitEnum or BackedEnum", cur_class->name)); + } else { + kphp_error(false, fmt_format("Class {} cannot implement UnitEnum or BackedEnum", cur_class->name)); + } + } + require_class(dep.class_name); } // class constant values may contain other class constants that may need require_class() diff --git a/dev_kphp/index.php b/dev_kphp/index.php index dfc40e1743..e1bc39dcee 100644 --- a/dev_kphp/index.php +++ b/dev_kphp/index.php @@ -1,12 +1,12 @@ @kphp_should_fail -/'case' expressions are available only in enum declaration/ +/pass A to argument $value of Status::tryFrom +but it's declared as @param mixed/ Date: Fri, 26 May 2023 11:38:43 +0300 Subject: [PATCH 35/41] Small fix --- compiler/gentree.cpp | 5 +---- dev_kphp/index.php | 12 ------------ ...m_interface.php => 016_backed_enum_interface.php} | 1 + 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 dev_kphp/index.php rename tests/phpt/enums/{017_backed_enum_interface.php => 016_backed_enum_interface.php} (97%) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 539d385faa..c711fb55da 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1779,7 +1779,6 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { if (test_expect(tok_colon)) { next_cur(); -// cur->debugPrint(); if (cur->type() == tok_int) { enum_type = EnumType::BackedInt; @@ -1792,9 +1791,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { enum_type = EnumType::Pure; } -// printf("Enum type = %d\n", (int)enum_type); - - if (test_expect(tok_implements)) { // TODO separate parse_extends_implements into different functions + if (test_expect(tok_implements)) { do { next_cur(); kphp_error(test_expect(tok_func_name), "Interface name expected after 'implements'"); diff --git a/dev_kphp/index.php b/dev_kphp/index.php deleted file mode 100644 index e1bc39dcee..0000000000 --- a/dev_kphp/index.php +++ /dev/null @@ -1,12 +0,0 @@ -@kphp_should_fail -/pass A to argument $value of Status::tryFrom -but it's declared as @param mixed/ - Date: Fri, 26 May 2023 14:58:08 +0300 Subject: [PATCH 36/41] Refactoring x1 --- compiler/gentree.cpp | 446 +++++++++++++++++++-------------------- compiler/gentree.h | 19 +- compiler/vertex-util.cpp | 6 + compiler/vertex-util.h | 2 + 4 files changed, 241 insertions(+), 232 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index c711fb55da..ca26874114 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1644,9 +1644,8 @@ bool GenTree::check_statement_end() { } return expect (tok_semicolon, "';'"); } - -void GenTree::parse_extends_implements() { - if (test_expect(tok_extends)) { // extends comes before 'implements', the order is fixed +void GenTree::parse_extends() { + if (test_expect(tok_extends)) { do { next_cur(); // (same as in PHP) kphp_error_return(test_expect(tok_func_name), "Class name expected after 'extends'"); @@ -1655,7 +1654,8 @@ void GenTree::parse_extends_implements() { next_cur(); } while (cur_class->is_interface() && test_expect(tok_comma)); } - +} +void GenTree::parse_implements() { if (test_expect(tok_implements)) { do { next_cur(); @@ -1666,6 +1666,11 @@ void GenTree::parse_extends_implements() { } } +void GenTree::parse_extends_implements() { + parse_extends(); // extends comes before 'implements', the order is fixed + parse_implements(); +} + VertexPtr GenTree::get_class(const PhpDocComment *phpdoc, ClassType class_type) { ClassModifiers modifiers; if (test_expect(tok_abstract)) { @@ -1744,7 +1749,7 @@ VertexPtr GenTree::get_class(const PhpDocComment *phpdoc, ClassType class_type) return {}; } -VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { +VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { CE(cur->type() == tok_enum); next_cur(); @@ -1764,41 +1769,9 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { next_cur(); kphp_error(!test_expect(tok_extends), "Enums cannot extend"); - enum class EnumType { - Empty, - Pure, - BackedString, - BackedInt, - }; + EnumType enum_type = get_enum_type(); - auto is_pure_enum_case = [](VertexAdaptor value) -> bool { - return value->args().empty(); - }; - - EnumType enum_type = EnumType::Empty; - - if (test_expect(tok_colon)) { - next_cur(); - - if (cur->type() == tok_int) { - enum_type = EnumType::BackedInt; - } else if (cur->type() == tok_string) { - enum_type = EnumType::BackedString; - } - - next_cur(); - } else { - enum_type = EnumType::Pure; - } - - if (test_expect(tok_implements)) { - do { - next_cur(); - kphp_error(test_expect(tok_func_name), "Interface name expected after 'implements'"); - cur_class->add_str_dependent(cur_function, ClassType::interface, cur->str_val); - next_cur(); - } while (test_expect(tok_comma)); - } + parse_implements(); cur_class->modifiers.set_final(); cur_class->file_id = processing_file; @@ -1806,7 +1779,9 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { cur_class->phpdoc = phpdoc; cur_class->is_immutable = true; cur_class->location_line_num = line_num; - cur_class->add_str_dependent(cur_function, ClassType::interface, enum_type == EnumType::Pure ? "\\UnitEnum" : "\\BackedEnum"); + cur_class->add_str_dependent(cur_function, ClassType::interface, enum_type == EnumType::Pure ? "\\UnitEnum" : "\\BackedEnum"); + + cur_class->add_class_constant(); bool registered = G->register_class(cur_class); if (registered) { @@ -1817,256 +1792,268 @@ VertexPtr GenTree::get_enum(const PhpDocComment * phpdoc) { G->register_and_require_function(cur_function, parsed_os, true); // push the class down the pipeline } - // parse body - CE(cur->type() == tok_opbrc); - - VertexPtr body_vertex = get_statement(); - kphp_error(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); + auto cases = get_enum_body_and_cases(enum_type); - std::vector cases; + generate_enum_fields(enum_type); + generate_enum_construct(enum_type); + generate_pure_enum_methods(cases); - const auto body_seq = body_vertex.try_as(); + if (vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)) { + generate_backed_enum_methods(); + } - // generating constants - for (const auto &stmt : body_seq->args()) { - if (const auto case_vertex = stmt.try_as()) { - auto cve = case_vertex->expr(); - const auto case_name_vertex = cve.try_as(); + return {}; +} - assert(case_name_vertex); - const auto case_name = case_name_vertex->get_string(); - cases.push_back(case_name); +void GenTree::generate_backed_enum_methods() { + auto param_var = VertexAdaptor::create(); + param_var->str_val = "value"; + auto param_pampam = VertexAdaptor::create(param_var); + param_pampam->type_hint = TypeHintPipe::create(std::vector{TypeHintPrimitive::create(tp_int), TypeHintPrimitive::create(tp_string)}); + auto params = VertexAdaptor::create(std::vector{param_pampam}); - VertexAdaptor cns = VertexAdaptor::create(); - cns->str_val = case_name; - auto is_pure_case = is_pure_enum_case(case_vertex->cmd()); + VertexPtr foreach_vert; + // generating foreach + { + // generating foreach param + VertexAdaptor cases_call = VertexAdaptor::create(); + cases_call->str_val = "self::cases"; - kphp_error( - (is_pure_case && enum_type == EnumType::Pure) || - (!is_pure_case && vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)), - fmt_format("Not appropriate case \"{}\" for enum \"{}\"", case_name, cur_class->name)); + VertexAdaptor cur_case = VertexAdaptor::create(); + cur_case->str_val = "cur_case"; - std::vector args{cns}; - if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { - kphp_error(!case_vertex->cmd()->args().empty(), "You cannot use pure enum' case in backed enum"); - args.push_back(case_vertex->cmd()->args()[0]); - } + VertexAdaptor tmp_var = VertexAdaptor::create(); + tmp_var->str_val = "tmp_var"; + tmp_var->extra_type = op_ex_var_superlocal; -// printf("cur class name: %s\n", cur_class->name.data()); - const auto ctor_call = gen_constructor_call_with_args(cur_class->name, args, auto_location()); - ctor_call->args()[0].as()->allocated_class_name = "self"; - cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); + VertexAdaptor fe_param = VertexAdaptor::create(cases_call, cur_case, tmp_var); - } - kphp_error(stmt->type() != op_var, "Fields are no allowed in enums"); + // generating body + auto inst_prop = VertexAdaptor::create(cur_case.clone()); + inst_prop->str_val = "value"; + auto cmp_vert = VertexAdaptor::create(VertexAdaptor::create(inst_prop.clone(), param_var.clone())); + auto ret_vert = VertexUtil::embrace(VertexAdaptor::create(cur_case.clone())); + auto if_vert = VertexAdaptor::create(cmp_vert, ret_vert); + foreach_vert = VertexAdaptor::create(fe_param, VertexUtil::embrace(if_vert)); } - cur_class->add_class_constant(); + auto func_body = VertexUtil::embrace(foreach_vert); + auto tryFrom_func = VertexAdaptor::create(params, func_body); + [[maybe_unused]]auto from_func = tryFrom_func.clone(); - // add $name field - { - auto modifiers_field_public = FieldModifiers(); - modifiers_field_public.set_public(); - - auto name_vertex = VertexAdaptor::create(); - name_vertex->str_val = "name"; - name_vertex->is_const = true; - cur_class->members.add_instance_field(name_vertex, - VertexPtr{}, - modifiers_field_public, - nullptr, - TypeHintPrimitive::create(PrimitiveType::tp_string)); - } + auto tf_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$tryFrom", tryFrom_func, FunctionData::func_local); - if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { - auto modifiers_field_public = FieldModifiers(); - modifiers_field_public.set_public(); + VertexUtil::func_force_return(tryFrom_func, VertexAdaptor::create()); - auto name_vertex = VertexAdaptor::create(); - name_vertex->str_val = "value"; - name_vertex->is_const = true; + // auto f_alive2 = StackPushPop(functions_stack, cur_function, tf_fun); - cur_class->members.add_instance_field(name_vertex, - VertexPtr{}, - modifiers_field_public, - nullptr, - TypeHintPrimitive::create(enum_type == EnumType::BackedInt ? tp_int : tp_string)); - } + tf_fun->update_location_in_body(); + tf_fun->is_inline = true; + tf_fun->modifiers = FunctionModifiers::nonmember(); + tf_fun->modifiers.set_public(); + tf_fun->modifiers.set_final(); + cur_class->members.add_static_method(tf_fun); + G->register_and_require_function(tf_fun, parsed_os, true); - #if 1 - // generating constructor + // from func { - auto param_var = VertexAdaptor::create(); - param_var->str_val = "name"; - auto param = VertexAdaptor::create(param_var.clone()); + VertexPtr cmd = from_func->cmd(); + assert (cmd->type() == op_seq); - auto params_vec = std::vector{param}; + VertexAdaptor text = VertexAdaptor::create(); + text->str_val = fmt_format("Not a valid backing value for enum \"{}\"", cur_class->name); + auto code = VertexAdaptor::create(); + code->set_string("0"); - auto this_vertex = VertexAdaptor::create(); - this_vertex->str_val = "this"; - auto inst_prop = VertexAdaptor::create(this_vertex.clone()); - inst_prop->str_val = "name"; - auto ctor_body_seq = std::vector{ VertexAdaptor::create(inst_prop, param_var.clone())}; + VertexPtr node = gen_constructor_call_with_args("\\ValueError", std::vector{text, code}, auto_location()); + node = VertexAdaptor::create(node); - if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { - auto param_var2 = VertexAdaptor::create(); - param_var2->str_val = "value_"; - auto param2= VertexAdaptor::create(param_var2.clone()); + std::vector next = cmd->get_next(); + next.push_back(node); + from_func->cmd_ref() = VertexAdaptor::create(next); - params_vec.emplace_back(param2); + auto f_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$from", from_func, FunctionData::func_local); - auto this_vertex2 = VertexAdaptor::create(); - this_vertex2->str_val = "this"; - auto inst_prop2 = VertexAdaptor::create(this_vertex2.clone()); - inst_prop2->str_val = "value"; - ctor_body_seq.emplace_back(VertexAdaptor::create(inst_prop2, param_var2.clone())); - } + f_fun->update_location_in_body(); + f_fun->is_inline = true; + f_fun->modifiers = FunctionModifiers::nonmember(); + f_fun->modifiers.set_public(); + f_fun->modifiers.set_final(); + cur_class->members.add_static_method(f_fun); + G->register_and_require_function(f_fun, parsed_os, true); + } +} - ctor_body_seq.emplace_back(VertexAdaptor::create(this_vertex.clone())); +void GenTree::generate_pure_enum_methods(const std::vector &cases) { + std::vector> arr_args; - const auto body = VertexAdaptor::create(ctor_body_seq); + std::transform(cases.begin(), cases.end(), std::back_inserter(arr_args), [](const std::string &case_name) { + auto item = VertexAdaptor::create(); + item->str_val = "self::" + case_name; + return item; + }); - auto func = VertexAdaptor::create(VertexAdaptor::create(params_vec), body); + auto response = VertexAdaptor::create(std::move(arr_args)); - std::string func_name = replace_backslashes(cur_class->name) + "$$" + ClassData::NAME_OF_CONSTRUCT; - auto this_param = cur_class->gen_param_this(func->get_location()); - func->param_list_ref() = VertexAdaptor::create(this_param, func->param_list()->params()); - VertexUtil::func_force_return(func, cur_class->gen_vertex_this(func->location)); - auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); + const auto params = VertexAdaptor::create(); + auto func = VertexAdaptor::create(params, VertexAdaptor::create()); + VertexUtil::func_force_return(func, response); + const std::string func_name = replace_backslashes(cur_class->name) + "$$cases"; + auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); - ctor_function->update_location_in_body(); - ctor_function->is_inline = true; - ctor_function->modifiers = FunctionModifiers::instance_public(); - ctor_function->phpdoc = phpdoc; - cur_class->members.add_instance_method(ctor_function); - G->register_and_require_function(ctor_function, parsed_os, true); - } - #endif -#if 1 - // generating cases() - { - std::vector> arr_args; + cases_fun->update_location_in_body(); + cases_fun->is_inline = true; + cases_fun->modifiers = FunctionModifiers::nonmember(); + cases_fun->modifiers.set_public(); + cases_fun->modifiers.set_final(); + cases_fun->return_typehint = TypeHintArray::create(cur_class->type_hint); + cur_class->members.add_static_method(cases_fun); + G->register_and_require_function(cases_fun, parsed_os, true); +} - std::transform(cases.begin(), cases.end(), std::back_inserter(arr_args), [](const std::string &case_name) { - auto item = VertexAdaptor::create(); - item->str_val = "self::" + case_name; - return item; - }); +void GenTree::generate_enum_construct(EnumType enum_type) { + auto param_var = VertexAdaptor::create(); + param_var->str_val = "name"; + auto param = VertexAdaptor::create(param_var.clone()); - auto response = VertexAdaptor::create(std::move(arr_args)); + auto params_vec = std::vector{param}; - const auto params = VertexAdaptor::create(std::vector>{}); - const auto body = VertexAdaptor::create(std::vector{VertexAdaptor::create(response)}); - auto func = VertexAdaptor::create(params, body); - std::string func_name = replace_backslashes(cur_class->name) + "$$cases"; + auto this_vertex = VertexAdaptor::create(); + this_vertex->str_val = "this"; + auto inst_prop = VertexAdaptor::create(this_vertex.clone()); + inst_prop->str_val = "name"; + auto ctor_body_seq = std::vector{VertexAdaptor::create(inst_prop, param_var.clone())}; + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + auto param_var2 = VertexAdaptor::create(); + param_var2->str_val = "value_"; + auto param2 = VertexAdaptor::create(param_var2.clone()); - auto cases_fun = FunctionData::create_function(func_name, func, FunctionData::func_local); - // auto f_alive2 = StackPushPop(functions_stack, cur_function, cases_fun); + params_vec.emplace_back(param2); - cases_fun->update_location_in_body(); - cases_fun->is_inline = true; - cases_fun->modifiers = FunctionModifiers::nonmember(); - cases_fun->modifiers.set_public(); - cases_fun->modifiers.set_final(); - cases_fun->return_typehint = TypeHintArray::create(cur_class->type_hint); - cur_class->members.add_static_method(cases_fun); - G->register_and_require_function(cases_fun, parsed_os, true); + auto this_vertex2 = VertexAdaptor::create(); + this_vertex2->str_val = "this"; + auto inst_prop2 = VertexAdaptor::create(this_vertex2.clone()); + inst_prop2->str_val = "value"; + + ctor_body_seq.emplace_back(VertexAdaptor::create(inst_prop2, param_var2.clone())); } -#endif - #if 1 - if (vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)) { - auto param_var = VertexAdaptor::create(); - param_var->str_val = "value"; - auto param_pampam = VertexAdaptor::create(param_var); - param_pampam->type_hint = TypeHintPipe::create(std::vector{TypeHintPrimitive::create(tp_int), TypeHintPrimitive::create(tp_string)}); - auto params = VertexAdaptor::create(std::vector{param_pampam}); + ctor_body_seq.emplace_back(VertexAdaptor::create(this_vertex.clone())); + const auto body = VertexAdaptor::create(ctor_body_seq); - VertexPtr foreach_vert; - // generating foreach - { - // generating foreach param - VertexAdaptor cases_call = VertexAdaptor::create(); - cases_call->str_val = "self::cases"; + auto func = VertexAdaptor::create(VertexAdaptor::create(params_vec), body); - VertexAdaptor cur_case = VertexAdaptor::create(); - cur_case->str_val = "cur_case"; + std::string func_name = replace_backslashes(cur_class->name) + "$$" + ClassData::NAME_OF_CONSTRUCT; + auto this_param = cur_class->gen_param_this(func->get_location()); + func->param_list_ref() = VertexAdaptor::create(this_param, func->param_list()->params()); + VertexUtil::func_force_return(func, cur_class->gen_vertex_this(func->location)); + auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); - VertexAdaptor tmp_var = VertexAdaptor::create(); - tmp_var->str_val = "tmp_var"; - tmp_var->extra_type = op_ex_var_superlocal; - VertexAdaptor fe_param = VertexAdaptor::create(cases_call, cur_case, tmp_var); + ctor_function->update_location_in_body(); + ctor_function->is_inline = true; + ctor_function->modifiers = FunctionModifiers::instance_public(); + cur_class->members.add_instance_method(ctor_function); + G->register_and_require_function(ctor_function, parsed_os, true); +} - // generating body - auto inst_prop = VertexAdaptor::create(cur_case.clone()); - inst_prop->str_val = "value"; - auto cmp_vert = VertexAdaptor::create(VertexAdaptor::create(inst_prop.clone(), param_var.clone())); - auto ret_vert = VertexUtil::embrace(VertexAdaptor::create(cur_case.clone())); - auto if_vert = VertexAdaptor::create(cmp_vert, ret_vert); - foreach_vert = VertexAdaptor::create(fe_param, VertexUtil::embrace(if_vert)); - } - auto func_body = VertexUtil::embrace(foreach_vert); - auto tryFrom_func = VertexAdaptor::create(params, func_body); - [[maybe_unused]]auto from_func = tryFrom_func.clone(); +void GenTree::generate_enum_fields(EnumType enum_type) { + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + auto modifiers_field_public = FieldModifiers(); + modifiers_field_public.set_public(); + + auto value_vertex = VertexAdaptor::create(); + value_vertex->str_val = "value"; + value_vertex->is_const = true; - auto tf_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$tryFrom", tryFrom_func, FunctionData::func_local); + cur_class->members.add_instance_field(value_vertex, + VertexPtr{}, + modifiers_field_public, + nullptr, + TypeHintPrimitive::create(enum_type == EnumType::BackedInt ? tp_int : tp_string)); + } + auto modifiers_field_public = FieldModifiers(); + modifiers_field_public.set_public(); + + auto name_vertex = VertexAdaptor::create(); + name_vertex->str_val = "name"; + name_vertex->is_const = true; + cur_class->members.add_instance_field(name_vertex, + VertexPtr{}, + modifiers_field_public, + nullptr, + TypeHintPrimitive::create(PrimitiveType::tp_string)); - VertexUtil::func_force_return(tryFrom_func, VertexAdaptor::create()); +} - // auto f_alive2 = StackPushPop(functions_stack, cur_function, tf_fun); +std::vector GenTree::get_enum_body_and_cases(EnumType enum_type) { + CE(cur->type() == tok_opbrc); - tf_fun->update_location_in_body(); - tf_fun->is_inline = true; - tf_fun->modifiers = FunctionModifiers::nonmember(); - tf_fun->modifiers.set_public(); - tf_fun->modifiers.set_final(); - cur_class->members.add_static_method(tf_fun); - G->register_and_require_function(tf_fun, parsed_os, true); + VertexPtr body_vertex = get_statement(); + kphp_error(body_vertex && body_vertex->type() == op_seq, "Incorrect enum body"); + std::vector cases; + const auto body_seq = body_vertex.try_as(); - // from func - { - VertexPtr cmd = from_func->cmd(); - assert (cmd->type() == op_seq); + auto is_pure_enum_case = [](VertexAdaptor value) -> bool { + return value->args().empty(); + }; - VertexAdaptor text = VertexAdaptor::create(); - text->str_val = fmt_format("Not a valid backing value for enum \"{}\"", cur_class->name); - auto code = VertexAdaptor::create(); - code->set_string("0"); + for (const auto &stmt: body_seq->args()) { + if (const auto case_vertex = stmt.try_as()) { + const auto case_name_vertex = case_vertex->expr().try_as(); + const auto case_name = case_name_vertex->get_string(); + cases.push_back(case_name); - VertexPtr node = gen_constructor_call_with_args("\\ValueError", std::vector{text, code}, auto_location()); - node = VertexAdaptor::create(node); + VertexAdaptor name_arg = VertexAdaptor::create(); + name_arg->str_val = case_name; - std::vector next = cmd->get_next(); - next.push_back(node); - from_func->cmd_ref() = VertexAdaptor::create(next); + const bool is_pure_case = is_pure_enum_case(case_vertex->cmd()); - auto f_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$from", from_func, FunctionData::func_local); + kphp_error( + (is_pure_case && enum_type == EnumType::Pure) || + (!is_pure_case && vk::any_of_equal(enum_type, EnumType::BackedInt, EnumType::BackedString)), + fmt_format("Not appropriate case \"{}\" for enum \"{}\"", case_name, cur_class->name)); + std::vector args{name_arg}; + if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { + kphp_error(!case_vertex->cmd()->args().empty(), "Cases without values are not allowed in backed enum"); + args.push_back(case_vertex->cmd()->args()[0]); + } - f_fun->update_location_in_body(); - f_fun->is_inline = true; - f_fun->modifiers = FunctionModifiers::nonmember(); - f_fun->modifiers.set_public(); - f_fun->modifiers.set_final(); - cur_class->members.add_static_method(f_fun); - G->register_and_require_function(f_fun, parsed_os, true); + const auto ctor_call = gen_constructor_call_with_args(cur_class->name, args, auto_location()); + ctor_call->args()[0].as()->allocated_class_name = "self"; + cur_class->members.add_constant(case_name, ctor_call, AccessModifiers::public_); } + kphp_error(stmt->type() != op_var, "Fields are not allowed in enums"); } - #endif + return cases; +} - return {}; +GenTree::EnumType GenTree::get_enum_type() { + kphp_assert_msg(cur_class && cur_class->is_enum(), "Cannot calculate enum type for non-enum"); + if (test_expect(tok_colon)) { + next_cur(); + + if (cur->type() == tok_int) { + next_cur(); + return EnumType::BackedInt; + } else if (cur->type() == tok_string) { + next_cur(); + return EnumType::BackedString; + } + kphp_error(false, fmt_format("Invalid type of enum {}", cur_class->name)); + } + return EnumType::Pure; } VertexAdaptor GenTree::get_enum_case() { - /* enum { ... @@ -2091,15 +2078,12 @@ VertexAdaptor GenTree::get_enum_case() { CE (!kphp_error(test_expect(tok_eq1), "\"=\" must be after backed enum case")); response = VertexAdaptor::create(case_name, VertexAdaptor::create()); - next_cur(); // WTF why I need it? + next_cur(); auto expr = get_expression(); -// puts("Enum value:\n"); -// expr.debugPrint(); response = VertexAdaptor::create(case_name, VertexAdaptor::create(expr)); } - return response; } diff --git a/compiler/gentree.h b/compiler/gentree.h index 87873593b0..95e0ff7d44 100644 --- a/compiler/gentree.h +++ b/compiler/gentree.h @@ -18,6 +18,13 @@ class GenTree { + enum class EnumType { + Empty, + Pure, + BackedString, + BackedInt, + }; + template class StackPushPop { std::vector &stack; @@ -119,9 +126,19 @@ class GenTree { static VertexAdaptor auto_capture_this_in_lambda(FunctionPtr f_lambda); VertexPtr get_class(const PhpDocComment *phpdoc, ClassType class_type); + void parse_extends(); + void parse_implements(); + void parse_extends_implements(); + VertexPtr get_enum(const PhpDocComment *phpdoc); VertexAdaptor get_enum_case(); - void parse_extends_implements(); + EnumType get_enum_type(); + std::vector get_enum_body_and_cases(EnumType enum_type); + void generate_enum_fields(EnumType enum_type); + void generate_enum_construct(EnumType enum_type); + void generate_pure_enum_methods(const std::vector &cases); + void generate_backed_enum_methods(); + static VertexPtr process_arrow(VertexPtr lhs, VertexPtr rhs); diff --git a/compiler/vertex-util.cpp b/compiler/vertex-util.cpp index 7f6d1606c8..fb3558cb63 100644 --- a/compiler/vertex-util.cpp +++ b/compiler/vertex-util.cpp @@ -93,6 +93,12 @@ VertexAdaptor VertexUtil::create_switch_vertex(FunctionPtr cur_functi return VertexAdaptor::create(switch_condition, temp_var_condition_on_switch, temp_var_matched_with_one_case, std::move(cases)).set_location(switch_condition); } +VertexAdaptor VertexUtil::create_var(const std::string &name) { + auto var = VertexAdaptor::create(); + var->str_val = name; + return var; +} + VertexAdaptor VertexUtil::embrace(VertexPtr v) { if (auto seq = v.try_as()) { return seq; diff --git a/compiler/vertex-util.h b/compiler/vertex-util.h index 24bedc2a5f..4796058978 100644 --- a/compiler/vertex-util.h +++ b/compiler/vertex-util.h @@ -25,6 +25,8 @@ class VertexUtil { static VertexAdaptor create_string_const(const std::string &s); static VertexAdaptor create_superlocal_var(const std::string& name_prefix, FunctionPtr cur_function); static VertexAdaptor create_switch_vertex(FunctionPtr cur_function, VertexPtr switch_condition, std::vector &&cases); + static VertexAdaptor create_var(const std::string& name); + static VertexAdaptor create_func_param(const std::string& name); static VertexPtr unwrap_array_value(VertexPtr v); static VertexPtr unwrap_string_value(VertexPtr v); From ee32b319785dd9005a7974eab8f401b5cb6fa1e1 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 29 May 2023 15:47:51 +0300 Subject: [PATCH 37/41] Small fix --- compiler/gentree.cpp | 7 ++----- compiler/vertex-util.h | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index ca26874114..9228a93d46 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1916,17 +1916,14 @@ void GenTree::generate_pure_enum_methods(const std::vector &cases) } void GenTree::generate_enum_construct(EnumType enum_type) { - auto param_var = VertexAdaptor::create(); - param_var->str_val = "name"; - auto param = VertexAdaptor::create(param_var.clone()); - + auto param = VertexAdaptor::create(VertexUtil::create_var("name")); auto params_vec = std::vector{param}; auto this_vertex = VertexAdaptor::create(); this_vertex->str_val = "this"; auto inst_prop = VertexAdaptor::create(this_vertex.clone()); inst_prop->str_val = "name"; - auto ctor_body_seq = std::vector{VertexAdaptor::create(inst_prop, param_var.clone())}; + auto ctor_body_seq = std::vector{VertexAdaptor::create(inst_prop, VertexUtil::create_var("name"))}; if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { auto param_var2 = VertexAdaptor::create(); diff --git a/compiler/vertex-util.h b/compiler/vertex-util.h index 4796058978..af380105be 100644 --- a/compiler/vertex-util.h +++ b/compiler/vertex-util.h @@ -26,7 +26,6 @@ class VertexUtil { static VertexAdaptor create_superlocal_var(const std::string& name_prefix, FunctionPtr cur_function); static VertexAdaptor create_switch_vertex(FunctionPtr cur_function, VertexPtr switch_condition, std::vector &&cases); static VertexAdaptor create_var(const std::string& name); - static VertexAdaptor create_func_param(const std::string& name); static VertexPtr unwrap_array_value(VertexPtr v); static VertexPtr unwrap_string_value(VertexPtr v); From 0f22e0a1a31314453a652e003aa990ef65ee6ef1 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 29 May 2023 22:54:36 +0300 Subject: [PATCH 38/41] Refactor gentree.cpp --- compiler/gentree.cpp | 128 +++++++++++++++------------------------ compiler/vertex-util.cpp | 6 -- compiler/vertex-util.h | 9 ++- 3 files changed, 56 insertions(+), 87 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 9228a93d46..b25169737d 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1775,7 +1775,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { cur_class->modifiers.set_final(); cur_class->file_id = processing_file; - cur_class->set_name_and_src_name(full_class_name); // with full namespaces and slashes + cur_class->set_name_and_src_name(full_class_name); cur_class->phpdoc = phpdoc; cur_class->is_immutable = true; cur_class->location_line_num = line_num; @@ -1789,7 +1789,7 @@ VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { } if (registered) { - G->register_and_require_function(cur_function, parsed_os, true); // push the class down the pipeline + G->register_and_require_function(cur_function, parsed_os, true); } auto cases = get_enum_body_and_cases(enum_type); @@ -1807,83 +1807,57 @@ VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { } void GenTree::generate_backed_enum_methods() { - auto param_var = VertexAdaptor::create(); - param_var->str_val = "value"; - auto param_pampam = VertexAdaptor::create(param_var); - param_pampam->type_hint = TypeHintPipe::create(std::vector{TypeHintPrimitive::create(tp_int), TypeHintPrimitive::create(tp_string)}); - auto params = VertexAdaptor::create(std::vector{param_pampam}); - + auto value_param = VertexAdaptor::create(VertexUtil::create_with_str_val("value")); + value_param->type_hint = TypeHintPipe::create(std::vector{TypeHintPrimitive::create(tp_int), TypeHintPrimitive::create(tp_string)}); + auto params = VertexAdaptor::create(std::vector{value_param}); VertexPtr foreach_vert; - // generating foreach - { - // generating foreach param - VertexAdaptor cases_call = VertexAdaptor::create(); - cases_call->str_val = "self::cases"; - - VertexAdaptor cur_case = VertexAdaptor::create(); - cur_case->str_val = "cur_case"; - - VertexAdaptor tmp_var = VertexAdaptor::create(); - tmp_var->str_val = "tmp_var"; - tmp_var->extra_type = op_ex_var_superlocal; - - VertexAdaptor fe_param = VertexAdaptor::create(cases_call, cur_case, tmp_var); - - // generating body - auto inst_prop = VertexAdaptor::create(cur_case.clone()); - inst_prop->str_val = "value"; - auto cmp_vert = VertexAdaptor::create(VertexAdaptor::create(inst_prop.clone(), param_var.clone())); - auto ret_vert = VertexUtil::embrace(VertexAdaptor::create(cur_case.clone())); - auto if_vert = VertexAdaptor::create(cmp_vert, ret_vert); - foreach_vert = VertexAdaptor::create(fe_param, VertexUtil::embrace(if_vert)); - } - auto func_body = VertexUtil::embrace(foreach_vert); - auto tryFrom_func = VertexAdaptor::create(params, func_body); - [[maybe_unused]]auto from_func = tryFrom_func.clone(); + VertexAdaptor cases_call = VertexUtil::create_with_str_val("self::cases"); - auto tf_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$tryFrom", tryFrom_func, FunctionData::func_local); + VertexAdaptor cur_case = VertexUtil::create_with_str_val("cur_case"); - VertexUtil::func_force_return(tryFrom_func, VertexAdaptor::create()); + VertexAdaptor tmp_var = VertexUtil::create_with_str_val("tmp_var"); + tmp_var->extra_type = op_ex_var_superlocal; - // auto f_alive2 = StackPushPop(functions_stack, cur_function, tf_fun); + VertexAdaptor fe_param = VertexAdaptor::create(cases_call, cur_case, tmp_var); - tf_fun->update_location_in_body(); - tf_fun->is_inline = true; - tf_fun->modifiers = FunctionModifiers::nonmember(); - tf_fun->modifiers.set_public(); - tf_fun->modifiers.set_final(); - cur_class->members.add_static_method(tf_fun); - G->register_and_require_function(tf_fun, parsed_os, true); + auto inst_prop = VertexUtil::create_with_str_val("value", cur_case.clone()); + auto cmp_vert = VertexAdaptor::create(VertexAdaptor::create(inst_prop.clone(), VertexUtil::create_with_str_val("value"))); + auto ret_vert = VertexUtil::embrace(VertexAdaptor::create(cur_case.clone())); + auto if_vert = VertexAdaptor::create(cmp_vert, ret_vert); + foreach_vert = VertexAdaptor::create(fe_param, VertexUtil::embrace(if_vert)); + auto func_body = VertexUtil::embrace(foreach_vert); - // from func - { - VertexPtr cmd = from_func->cmd(); - assert (cmd->type() == op_seq); - - VertexAdaptor text = VertexAdaptor::create(); - text->str_val = fmt_format("Not a valid backing value for enum \"{}\"", cur_class->name); + std::vector required_func_names = {replace_backslashes(cur_class->name) + "$$tryFrom", replace_backslashes(cur_class->name) + "$$from"}; + std::vector last_statements = { + VertexAdaptor::create(VertexAdaptor::create()), + [&]() { + VertexAdaptor text = VertexUtil::create_string_const(fmt_format("Not a valid backed value for enum \"{}\"", cur_class->name)); auto code = VertexAdaptor::create(); code->set_string("0"); VertexPtr node = gen_constructor_call_with_args("\\ValueError", std::vector{text, code}, auto_location()); node = VertexAdaptor::create(node); + return node; + }() + }; - std::vector next = cmd->get_next(); - next.push_back(node); - from_func->cmd_ref() = VertexAdaptor::create(next); - - auto f_fun = FunctionData::create_function(replace_backslashes(cur_class->name) + "$$from", from_func, FunctionData::func_local); + for (size_t i = 0; i < required_func_names.size(); ++i) { + auto func = VertexAdaptor::create(params, func_body); + std::vector next = func->cmd()->get_next(); + next.push_back(last_statements[i]); + func->cmd_ref() = VertexAdaptor::create(next); - f_fun->update_location_in_body(); - f_fun->is_inline = true; - f_fun->modifiers = FunctionModifiers::nonmember(); - f_fun->modifiers.set_public(); - f_fun->modifiers.set_final(); - cur_class->members.add_static_method(f_fun); - G->register_and_require_function(f_fun, parsed_os, true); + auto func_data = FunctionData::create_function(std::move(required_func_names[i]), func, FunctionData::func_local); + func_data->update_location_in_body(); + func_data->is_inline = true; + func_data->modifiers = FunctionModifiers::nonmember(); + func_data->modifiers.set_public(); + func_data->modifiers.set_final(); + cur_class->members.add_static_method(func_data); + G->register_and_require_function(func_data, parsed_os, true); } } @@ -1916,28 +1890,23 @@ void GenTree::generate_pure_enum_methods(const std::vector &cases) } void GenTree::generate_enum_construct(EnumType enum_type) { - auto param = VertexAdaptor::create(VertexUtil::create_var("name")); - auto params_vec = std::vector{param}; + auto name_param = VertexAdaptor::create(VertexUtil::create_with_str_val("name")); + auto params_vec = std::vector{name_param}; auto this_vertex = VertexAdaptor::create(); this_vertex->str_val = "this"; - auto inst_prop = VertexAdaptor::create(this_vertex.clone()); - inst_prop->str_val = "name"; - auto ctor_body_seq = std::vector{VertexAdaptor::create(inst_prop, VertexUtil::create_var("name"))}; + auto name_property = VertexAdaptor::create(this_vertex.clone()); + name_property->str_val = "name"; + auto ctor_body_seq = std::vector{VertexAdaptor::create(name_property, VertexUtil::create_with_str_val("name"))}; if (vk::any_of_equal(enum_type, EnumType::BackedString, EnumType::BackedInt)) { - auto param_var2 = VertexAdaptor::create(); - param_var2->str_val = "value_"; - auto param2 = VertexAdaptor::create(param_var2.clone()); + auto value_param = VertexAdaptor::create(VertexUtil::create_with_str_val("value")); + params_vec.emplace_back(value_param); - params_vec.emplace_back(param2); + auto value_property = name_property.clone(); + value_property->str_val = "value"; - auto this_vertex2 = VertexAdaptor::create(); - this_vertex2->str_val = "this"; - auto inst_prop2 = VertexAdaptor::create(this_vertex2.clone()); - inst_prop2->str_val = "value"; - - ctor_body_seq.emplace_back(VertexAdaptor::create(inst_prop2, param_var2.clone())); + ctor_body_seq.emplace_back(VertexAdaptor::create(value_property, VertexUtil::create_with_str_val("value"))); } ctor_body_seq.emplace_back(VertexAdaptor::create(this_vertex.clone())); @@ -1950,9 +1919,8 @@ void GenTree::generate_enum_construct(EnumType enum_type) { auto this_param = cur_class->gen_param_this(func->get_location()); func->param_list_ref() = VertexAdaptor::create(this_param, func->param_list()->params()); VertexUtil::func_force_return(func, cur_class->gen_vertex_this(func->location)); - auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); - + auto ctor_function = FunctionData::create_function(func_name, func, FunctionData::func_local); ctor_function->update_location_in_body(); ctor_function->is_inline = true; ctor_function->modifiers = FunctionModifiers::instance_public(); diff --git a/compiler/vertex-util.cpp b/compiler/vertex-util.cpp index fb3558cb63..7f6d1606c8 100644 --- a/compiler/vertex-util.cpp +++ b/compiler/vertex-util.cpp @@ -93,12 +93,6 @@ VertexAdaptor VertexUtil::create_switch_vertex(FunctionPtr cur_functi return VertexAdaptor::create(switch_condition, temp_var_condition_on_switch, temp_var_matched_with_one_case, std::move(cases)).set_location(switch_condition); } -VertexAdaptor VertexUtil::create_var(const std::string &name) { - auto var = VertexAdaptor::create(); - var->str_val = name; - return var; -} - VertexAdaptor VertexUtil::embrace(VertexPtr v) { if (auto seq = v.try_as()) { return seq; diff --git a/compiler/vertex-util.h b/compiler/vertex-util.h index af380105be..5dca908cd8 100644 --- a/compiler/vertex-util.h +++ b/compiler/vertex-util.h @@ -25,7 +25,14 @@ class VertexUtil { static VertexAdaptor create_string_const(const std::string &s); static VertexAdaptor create_superlocal_var(const std::string& name_prefix, FunctionPtr cur_function); static VertexAdaptor create_switch_vertex(FunctionPtr cur_function, VertexPtr switch_condition, std::vector &&cases); - static VertexAdaptor create_var(const std::string& name); +// static VertexAdaptor create_var(const std::string& name); + + template + static VertexAdaptor create_with_str_val(const std::string& str_val, Args&&... args) { + auto resp = VertexAdaptor::create(std::forward(args)...); + resp->str_val = str_val; + return resp; + } static VertexPtr unwrap_array_value(VertexPtr v); static VertexPtr unwrap_string_value(VertexPtr v); From a08acea5d1713a352414f6af55eaf36a36ba34b5 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 29 May 2023 23:16:43 +0300 Subject: [PATCH 39/41] Small fixes --- compiler/data/lib-data.cpp | 1 - compiler/gentree.cpp | 2 -- compiler/vertex-util.h | 1 - runtime/class_instance.inl | 1 - tests/phpt/enums/001_basic.php | 2 -- tests/phpt/enums/003_cases.php | 1 - tests/phpt/enums/008_namespace_interface.php | 2 -- tests/phpt/enums/009_namespace_enum.php | 2 +- tests/phpt/enums/016_backed_enum_interface.php | 2 +- tests/phpt/enums/102_not_implementation.php | 2 -- tests/phpt/enums/interfaces/Printer.php | 2 +- 11 files changed, 3 insertions(+), 15 deletions(-) diff --git a/compiler/data/lib-data.cpp b/compiler/data/lib-data.cpp index 53e03db523..476df61f42 100644 --- a/compiler/data/lib-data.cpp +++ b/compiler/data/lib-data.cpp @@ -86,7 +86,6 @@ const char *LibData::headers_tmp_dir() { return "lib_headers/"; } - const char *LibData::functions_txt_tmp_file() { return "lib_functions.txt"; } diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index b25169737d..80ee0ab058 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -2581,8 +2581,6 @@ VertexPtr GenTree::get_const(AccessModifiers access) { CE (check_statement_end()); if (const_in_class) { -// printf("Add to class: %s\n", const_name.c_str()); -// fflush(stdout); cur_class->members.add_constant(const_name, v, access); return VertexAdaptor::create(); } diff --git a/compiler/vertex-util.h b/compiler/vertex-util.h index 5dca908cd8..e5e79f11ed 100644 --- a/compiler/vertex-util.h +++ b/compiler/vertex-util.h @@ -25,7 +25,6 @@ class VertexUtil { static VertexAdaptor create_string_const(const std::string &s); static VertexAdaptor create_superlocal_var(const std::string& name_prefix, FunctionPtr cur_function); static VertexAdaptor create_switch_vertex(FunctionPtr cur_function, VertexPtr switch_condition, std::vector &&cases); -// static VertexAdaptor create_var(const std::string& name); template static VertexAdaptor create_with_str_val(const std::string& str_val, Args&&... args) { diff --git a/runtime/class_instance.inl b/runtime/class_instance.inl index 53249ad927..bed1014eb1 100644 --- a/runtime/class_instance.inl +++ b/runtime/class_instance.inl @@ -4,7 +4,6 @@ #error "this file must be included only from kphp_core.h" #endif - template class_instance &class_instance::operator=(const Optional &null) noexcept { php_assert(null.value_state() == OptionalState::null_value); diff --git a/tests/phpt/enums/001_basic.php b/tests/phpt/enums/001_basic.php index 65af07fef7..a6ba582478 100644 --- a/tests/phpt/enums/001_basic.php +++ b/tests/phpt/enums/001_basic.php @@ -14,5 +14,3 @@ enum MyBool { echo MyBool::F->name; echo MyBool::T->name; - -// TODO add 002_comparison \ No newline at end of file diff --git a/tests/phpt/enums/003_cases.php b/tests/phpt/enums/003_cases.php index 29f96ceac9..77b31bd5bb 100644 --- a/tests/phpt/enums/003_cases.php +++ b/tests/phpt/enums/003_cases.php @@ -27,4 +27,3 @@ enum EmptyEnum { var_dump(count(Foo::cases())); var_dump(count(Single::cases())); var_dump(count(EmptyEnum::cases())); - diff --git a/tests/phpt/enums/008_namespace_interface.php b/tests/phpt/enums/008_namespace_interface.php index 9b47372637..f31fbc63eb 100644 --- a/tests/phpt/enums/008_namespace_interface.php +++ b/tests/phpt/enums/008_namespace_interface.php @@ -17,5 +17,3 @@ public function print() { MyBool::T->print(); MyBool::F->print(); - -// TODO tests with namespaces, different dirs etc diff --git a/tests/phpt/enums/009_namespace_enum.php b/tests/phpt/enums/009_namespace_enum.php index e1dc1cb1b1..54c6bbb937 100644 --- a/tests/phpt/enums/009_namespace_enum.php +++ b/tests/phpt/enums/009_namespace_enum.php @@ -6,4 +6,4 @@ use enums\MyBoolNS\MyBool as AnotherBool; echo AnotherBool::T->name; -echo AnotherBool::F->name; \ No newline at end of file +echo AnotherBool::F->name; diff --git a/tests/phpt/enums/016_backed_enum_interface.php b/tests/phpt/enums/016_backed_enum_interface.php index 02385d3ed8..6a86462dac 100644 --- a/tests/phpt/enums/016_backed_enum_interface.php +++ b/tests/phpt/enums/016_backed_enum_interface.php @@ -14,4 +14,4 @@ enum A { case B; } -echo A::B instanceof \BackedEnum; \ No newline at end of file +echo A::B instanceof \BackedEnum; diff --git a/tests/phpt/enums/102_not_implementation.php b/tests/phpt/enums/102_not_implementation.php index 7b53e84804..649fea869d 100644 --- a/tests/phpt/enums/102_not_implementation.php +++ b/tests/phpt/enums/102_not_implementation.php @@ -17,5 +17,3 @@ public function notPrint() { } } } - -// TODO tests with namespaces, different dirs etc diff --git a/tests/phpt/enums/interfaces/Printer.php b/tests/phpt/enums/interfaces/Printer.php index b170606afb..083c44b47a 100644 --- a/tests/phpt/enums/interfaces/Printer.php +++ b/tests/phpt/enums/interfaces/Printer.php @@ -4,4 +4,4 @@ interface Printer { function print(); -} \ No newline at end of file +} From d9bc01e36d17860a8428da10ab048190c48723cb Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Wed, 31 May 2023 16:19:19 +0300 Subject: [PATCH 40/41] Fix bug with sharing same nodes --- compiler/gentree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 80ee0ab058..bb9a7afaf7 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1844,7 +1844,7 @@ void GenTree::generate_backed_enum_methods() { }; for (size_t i = 0; i < required_func_names.size(); ++i) { - auto func = VertexAdaptor::create(params, func_body); + auto func = VertexAdaptor::create(params.clone(), func_body.clone()); std::vector next = func->cmd()->get_next(); next.push_back(last_statements[i]); From 7d3f6387cdbe8df99dc97109591ed2d5c04cf2f5 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Thu, 1 Jun 2023 17:26:28 +0300 Subject: [PATCH 41/41] Fix bug with undefined constants --- compiler/gentree.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index bb9a7afaf7..2985fd20cd 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -1783,14 +1783,6 @@ VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { cur_class->add_class_constant(); - bool registered = G->register_class(cur_class); - if (registered) { - ++G->stats.total_classes; - } - - if (registered) { - G->register_and_require_function(cur_function, parsed_os, true); - } auto cases = get_enum_body_and_cases(enum_type); @@ -1803,6 +1795,14 @@ VertexPtr GenTree::get_enum(const PhpDocComment *phpdoc) { generate_backed_enum_methods(); } + bool registered = G->register_class(cur_class); + if (registered) { + ++G->stats.total_classes; + } + if (registered) { + G->register_and_require_function(cur_function, parsed_os, true); + } + return {}; } @@ -1865,9 +1865,7 @@ void GenTree::generate_pure_enum_methods(const std::vector &cases) std::vector> arr_args; std::transform(cases.begin(), cases.end(), std::back_inserter(arr_args), [](const std::string &case_name) { - auto item = VertexAdaptor::create(); - item->str_val = "self::" + case_name; - return item; + return VertexUtil::create_with_str_val("self::" + case_name); }); auto response = VertexAdaptor::create(std::move(arr_args));