From a1aed87219a60453fa2cfe2d21a89a43631cf0f7 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Sat, 11 Sep 2021 17:51:01 +0300 Subject: [PATCH 1/2] wip --- compiler/gentree.cpp | 17 +++++++- compiler/pipes/check-function-calls.cpp | 52 ++++++++++++++++++++++++- compiler/pipes/check-function-calls.h | 4 +- compiler/pipes/final-check.cpp | 2 +- compiler/vertex-desc.json | 21 ++++++++++ 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index aebdcd0ddc..deb244463f 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -229,6 +229,10 @@ VertexAdaptor GenTree::get_func_call() { string name{cur->str_val}; next_cur(); + if (name == "foo") { + printf("sss"); + } + CE (expect(tok_oppar, "'('")); skip_phpdoc_tokens(); vector next; @@ -775,6 +779,16 @@ VertexPtr GenTree::get_binary_op(int op_priority_cur, bool till_ternary) { return left; } + // in function named argument call: + // foo(name: ) + // | color here + if (left->type() == op_func_name && cur->type() == tok_colon) { + // skip colon + next_cur(); + const auto arg_expr = get_expression(); + return VertexAdaptor::create(arg_expr, left.as()); + } + while (cur != end) { const TokenType origin_token = cur->type(); const TokenType token = transform_compare_operation_token(origin_token); @@ -1502,7 +1516,8 @@ VertexAdaptor GenTree::parse_cur_function_param_list() { ClassData::patch_func_add_this(params_next, Location(line_num)); } - bool ok_params_next = gen_list(¶ms_next, &GenTree::get_func_param, tok_comma); + + bool ok_params_next = gen_list(¶ms_next, &GenTree::get_func_param, tok_comma); CE(!kphp_error(ok_params_next, "Failed to parse function params")); CE(expect(tok_clpar, "')'")); diff --git a/compiler/pipes/check-function-calls.cpp b/compiler/pipes/check-function-calls.cpp index 4f0d5338df..36194744c3 100644 --- a/compiler/pipes/check-function-calls.cpp +++ b/compiler/pipes/check-function-calls.cpp @@ -7,6 +7,55 @@ #include "compiler/data/src-file.h" #include "compiler/type-hint.h" +VertexRange CheckFunctionCallsPass::process_named_args(FunctionPtr func, VertexAdaptor call) { + const auto args = call->args(); + auto see_named = false; + + const auto without_named_args = std::none_of(args.begin(), args.end(), [&see_named](const VertexPtr arg) -> bool { + const auto named = arg->type() == op_named_arg; + if (named) { + see_named = true; + } + // if positional arg after named + if (!named && see_named) { + kphp_error(0, "Cannot use positional argument after named argument"); + } + return named; + }); + + if (without_named_args) { + return call->args(); + } + + const auto params = func->get_params(); + auto params_map = std::unordered_map(params.size()); + + for (int i = 0; i < params.size(); ++i) { + const auto ¶m = params[i].as(); + params_map[param->var()->str_val] = i; + } + + auto new_call_args = std::vector(params.size()); + + for (const auto &arg : args) { + if (const auto named = arg.try_as()) { + const auto name = named->name()->str_val; + if (params_map.find(name) == params_map.end()) { + kphp_error(0, fmt_format("Unknown named parameter {}", name)); + continue; + } + + const auto param_index = params_map[name]; + new_call_args[param_index] = named; + } + } + + auto end = Vertex::iterator{&new_call_args.front()}; + auto begin = Vertex::iterator{&new_call_args.back()}; + + return {begin, end + 1}; +} + void CheckFunctionCallsPass::check_func_call(VertexPtr call) { FunctionPtr f = call->type() == op_func_ptr ? call.as()->func_id : call.as()->func_id; kphp_assert(f); @@ -27,7 +76,7 @@ void CheckFunctionCallsPass::check_func_call(VertexPtr call) { } VertexRange func_params = f->get_params(); - VertexRange call_params = call.as()->args(); + VertexRange call_params = process_named_args(f, call.as()); auto actual_params_n = [f](int n) { // instance methods always have implicit $this param which we don't want to mention @@ -65,6 +114,7 @@ void CheckFunctionCallsPass::check_func_call(VertexPtr call) { } } } + VertexPtr CheckFunctionCallsPass::on_enter_vertex(VertexPtr v) { if (v->type() == op_func_ptr || v->type() == op_func_call) { check_func_call(v); diff --git a/compiler/pipes/check-function-calls.h b/compiler/pipes/check-function-calls.h index a0dd666311..bd008b0de8 100644 --- a/compiler/pipes/check-function-calls.h +++ b/compiler/pipes/check-function-calls.h @@ -8,7 +8,9 @@ class CheckFunctionCallsPass final : public FunctionPassBase { private: - void check_func_call(VertexPtr call); + static void check_func_call(VertexPtr call); + static VertexRange process_named_args(FunctionPtr func, VertexAdaptor call); + public: string get_description() override { return "Check function calls"; diff --git a/compiler/pipes/final-check.cpp b/compiler/pipes/final-check.cpp index 8455b1480c..33e373bf52 100644 --- a/compiler/pipes/final-check.cpp +++ b/compiler/pipes/final-check.cpp @@ -269,7 +269,7 @@ void FinalCheckPass::on_start() { VertexPtr FinalCheckPass::on_enter_vertex(VertexPtr vertex) { if (vertex->type() == op_func_name) { - kphp_error (0, fmt_format("Unexpected {} (maybe, it should be a define?)", vertex->get_string())); +// kphp_error (0, fmt_format("Unexpected {} (maybe, it should be a define?)", vertex->get_string())); } if (vertex->type() == op_addr) { kphp_error (0, "Getting references is unsupported"); diff --git a/compiler/vertex-desc.json b/compiler/vertex-desc.json index 1cfba930a4..0f82d12c26 100644 --- a/compiler/vertex-desc.json +++ b/compiler/vertex-desc.json @@ -272,6 +272,27 @@ ] } }, + + { + "comment": "named argument for function call", + "name": "op_named_arg", + "base_name": "meta_op_base", + "props": { + "rl": "rl_const", + "cnst": "cnst_const_func", + "type": "common_op" + }, + "sons": { + "expr": { + "id": 0 + }, + "name": { + "id": 1, + "type": "op_func_name" + } + } + }, + { "comment": "any function (or method) call", "extras": [ From 7d1ff2cd9de924a6a47c7f9a15489f1ae5376ce9 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Wed, 22 Sep 2021 14:11:55 +0300 Subject: [PATCH 2/2] wip --- compiler/gentree.cpp | 4 -- compiler/pipes/check-function-calls.cpp | 54 +++++++++++++++++++------ compiler/pipes/check-function-calls.h | 4 +- compiler/pipes/preprocess-function.cpp | 6 ++- compiler/vertex-desc.json | 2 - 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index deb244463f..98008dadd8 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -229,10 +229,6 @@ VertexAdaptor GenTree::get_func_call() { string name{cur->str_val}; next_cur(); - if (name == "foo") { - printf("sss"); - } - CE (expect(tok_oppar, "'('")); skip_phpdoc_tokens(); vector next; diff --git a/compiler/pipes/check-function-calls.cpp b/compiler/pipes/check-function-calls.cpp index 36194744c3..cec9e4983b 100644 --- a/compiler/pipes/check-function-calls.cpp +++ b/compiler/pipes/check-function-calls.cpp @@ -7,24 +7,26 @@ #include "compiler/data/src-file.h" #include "compiler/type-hint.h" -VertexRange CheckFunctionCallsPass::process_named_args(FunctionPtr func, VertexAdaptor call) { +VertexAdaptor CheckFunctionCallsPass::process_named_args(FunctionPtr func, VertexAdaptor call) const { const auto args = call->args(); auto see_named = false; + auto without_named_args = true; - const auto without_named_args = std::none_of(args.begin(), args.end(), [&see_named](const VertexPtr arg) -> bool { + for (const auto &arg : args) { const auto named = arg->type() == op_named_arg; if (named) { see_named = true; + without_named_args = false; } // if positional arg after named if (!named && see_named) { kphp_error(0, "Cannot use positional argument after named argument"); + return call; } - return named; - }); + } if (without_named_args) { - return call->args(); + return call; } const auto params = func->get_params(); @@ -37,26 +39,50 @@ VertexRange CheckFunctionCallsPass::process_named_args(FunctionPtr func, VertexA auto new_call_args = std::vector(params.size()); - for (const auto &arg : args) { + for (int i = 0; i < args.size(); ++i) { + const auto &arg = args[i]; + if (const auto named = arg.try_as()) { const auto name = named->name()->str_val; if (params_map.find(name) == params_map.end()) { kphp_error(0, fmt_format("Unknown named parameter {}", name)); - continue; + return call; } const auto param_index = params_map[name]; - new_call_args[param_index] = named; + + if (new_call_args[param_index]) { + kphp_error(0, fmt_format("Named parameter ${} overwrites previous argument", name)); + continue; + } + + new_call_args[param_index] = named->expr(); + } else { + new_call_args[i] = arg; } } - auto end = Vertex::iterator{&new_call_args.front()}; - auto begin = Vertex::iterator{&new_call_args.back()}; + for (int i = 0; i < params.size(); ++i) { + const auto ¶m = params[i].as(); - return {begin, end + 1}; + if (!new_call_args[i]) { + const auto &default_val = param->default_value(); + if (const auto &default_var = default_val.try_as()) { + current_function->explicit_header_const_var_ids.insert(default_var->var_id); + } + + new_call_args[i] = param->default_value().clone(); + } + } + + auto new_call = VertexAdaptor::create(new_call_args); + new_call->func_id = call->func_id; + new_call->str_val = call->str_val; + + return new_call; } -void CheckFunctionCallsPass::check_func_call(VertexPtr call) { +void CheckFunctionCallsPass::check_func_call(VertexPtr &call) { FunctionPtr f = call->type() == op_func_ptr ? call.as()->func_id : call.as()->func_id; kphp_assert(f); kphp_error_return(f->root, fmt_format("Function [{}] undeclared", f->get_human_readable_name())); @@ -75,8 +101,10 @@ void CheckFunctionCallsPass::check_func_call(VertexPtr call) { return; } + call = process_named_args(f, call.as()); + VertexRange func_params = f->get_params(); - VertexRange call_params = process_named_args(f, call.as()); + VertexRange call_params = call.as()->args(); auto actual_params_n = [f](int n) { // instance methods always have implicit $this param which we don't want to mention diff --git a/compiler/pipes/check-function-calls.h b/compiler/pipes/check-function-calls.h index bd008b0de8..6dce9162d5 100644 --- a/compiler/pipes/check-function-calls.h +++ b/compiler/pipes/check-function-calls.h @@ -8,8 +8,8 @@ class CheckFunctionCallsPass final : public FunctionPassBase { private: - static void check_func_call(VertexPtr call); - static VertexRange process_named_args(FunctionPtr func, VertexAdaptor call); + void check_func_call(VertexPtr &call); + VertexAdaptor process_named_args(FunctionPtr func, VertexAdaptor call) const; public: string get_description() override { diff --git a/compiler/pipes/preprocess-function.cpp b/compiler/pipes/preprocess-function.cpp index ea70c74417..485fd7f5f6 100644 --- a/compiler/pipes/preprocess-function.cpp +++ b/compiler/pipes/preprocess-function.cpp @@ -415,7 +415,11 @@ class PreprocessFunctionPass final : public FunctionPassBase { // conversions automatically (implicit casts), unless the file from where // we're calling this function is annotated with strict_types=1 if (param->is_cast_param && implicit_cast_allowed(current_function->file_id->is_strict_types, param->type_hint)) { - call_arg = GenTree::conv_to_cast_param(call_arg, param->type_hint, param->var()->ref_flag); + if (auto named_arg = call_arg.try_as()) { + named_arg->expr() = GenTree::conv_to_cast_param(named_arg->expr(), param->type_hint, param->var()->ref_flag); + } else { + call_arg = GenTree::conv_to_cast_param(call_arg, param->type_hint, param->var()->ref_flag); + } } if (!param->is_callable) { diff --git a/compiler/vertex-desc.json b/compiler/vertex-desc.json index 0f82d12c26..dfa106da55 100644 --- a/compiler/vertex-desc.json +++ b/compiler/vertex-desc.json @@ -272,7 +272,6 @@ ] } }, - { "comment": "named argument for function call", "name": "op_named_arg", @@ -292,7 +291,6 @@ } } }, - { "comment": "any function (or method) call", "extras": [