From cbc05aedf54932ee96d456ad97041d20c31d2cdb Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Tue, 6 Jun 2023 18:28:50 +0300 Subject: [PATCH 01/12] Add syntax; order still matter --- compiler/gentree.cpp | 16 +++++++++++++++- compiler/gentree.h | 1 + .../pipes/deduce-implicit-types-and-casts.cpp | 5 +++++ compiler/vertex-desc.json | 9 +++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 8dc0fa7afe..95afba1c84 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -221,7 +221,7 @@ VertexAdaptor GenTree::get_func_call() { CE (expect(tok_oppar, "'('")); skip_phpdoc_tokens(); std::vector next; - bool ok_next = gen_list(&next, &GenTree::get_expression, tok_comma); + bool ok_next = gen_list(&next, &GenTree::get_func_call_arg, tok_comma); CE (!kphp_error(ok_next, "get argument list failed")); CE (expect(tok_clpar, "')'")); @@ -856,6 +856,20 @@ VertexPtr GenTree::get_expression() { return get_expression_impl(false); } +VertexPtr GenTree::get_func_call_arg() { + skip_phpdoc_tokens(); + VertexPtr name = get_expression(); +// name.debugPrint(); +// assert(name->type() == op_func_name); + if (cur->type() == tok_colon) { + puts("!!!!!!!"); + next_cur(); + VertexPtr value = get_expression(); + return VertexAdaptor::create(VertexUtil::create_string_const(name->get_string()), value); + } + return name; +} + VertexPtr GenTree::get_def_value() { VertexPtr val; diff --git a/compiler/gentree.h b/compiler/gentree.h index 31e5fe6314..bd1afc4db0 100644 --- a/compiler/gentree.h +++ b/compiler/gentree.h @@ -64,6 +64,7 @@ class GenTree { VertexPtr get_binary_op(int op_priority_cur, bool till_ternary); VertexPtr get_expression_impl(bool till_ternary); VertexPtr get_expression(); + VertexPtr get_func_call_arg(); VertexPtr get_statement(const PhpDocComment *phpdoc = nullptr); VertexAdaptor get_catch(); void get_instance_var_list(const PhpDocComment *phpdoc, FieldModifiers modifiers, const TypeHint *type_hint); diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 5396b8d9e3..5542a6b205 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -605,6 +605,11 @@ void DeduceImplicitTypesAndCastsPass::on_func_call(VertexAdaptor c FunctionPtr f_called = call->func_id; auto call_args = call->args(); + for (auto & arg : call_args) { + if (arg->type() == op_named_arg) { + arg = arg.as()->expr(); + } + } auto f_called_params = f_called->get_params(); // if we are calling `f`, then `f` has not been instantiated yet at this point, so we have a generic func call diff --git a/compiler/vertex-desc.json b/compiler/vertex-desc.json index 493011e3c0..44a9bee38e 100644 --- a/compiler/vertex-desc.json +++ b/compiler/vertex-desc.json @@ -580,6 +580,15 @@ ] } }, + { + "comment": "proxy vertex for named argument", + "name": "op_named_arg", + "base_name": "meta_op_base", + "sons": { + "name" : 0, + "expr" : 1 + } + }, { "comment": "variadic argument: array()...", "name": "op_varg", From 5f7d0552c379e8586c4a86a328857bad9839121f Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 9 Jun 2023 13:02:38 +0300 Subject: [PATCH 02/12] Tmp --- .../pipes/deduce-implicit-types-and-casts.cpp | 58 +++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 5542a6b205..53feba7b20 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -605,12 +605,62 @@ void DeduceImplicitTypesAndCastsPass::on_func_call(VertexAdaptor c FunctionPtr f_called = call->func_id; auto call_args = call->args(); - for (auto & arg : call_args) { - if (arg->type() == op_named_arg) { - arg = arg.as()->expr(); +// for (auto & arg : call_args) { +// if (arg->type() == op_named_arg) { +// arg = arg.as()->expr(); +// } +// } + + auto f_called_params = f_called->get_params(); + + +// map call_args to params + const int stub = -1; + int fst_named = stub; + int lst_positional = stub; + for (int i = 0; i < call_args.size(); ++i) { + if (fst_named == stub && call_args[i]->type() == op_named_arg) { + fst_named = i; + } + if (call_args[i]->type() != op_named_arg) { + lst_positional = i; } } - auto f_called_params = f_called->get_params(); + + kphp_error(fst_named == stub || fst_named > lst_positional, "Cannot use positional argument after named argument"); + + std::vector mapping(call_args.size(), VertexPtr{}); + + // trivial + for (int i = 0; i <= lst_positional; ++i) { + mapping[i] = call_args[i]; + } + + // TODO think about default arguments + + if (fst_named != stub) { + for (int pos = fst_named; pos < call_args.size(); ++pos) { + auto cur_call_arg = call_args[pos].as(); + printf("cur_call_arg.name = %s\n", cur_call_arg->name()->get_string().c_str()); + bool found = false; + for (int i = 0; i < f_called_params.size(); ++i) { + assert(f_called_params[i]->type() == op_func_param); + auto param_name = f_called_params[i].as()->var()->str_val; + printf("\tparam_name = %s\n", param_name.c_str()); + if (cur_call_arg->name()->get_string() == param_name) { // TODO(mkornaukhov03) think about case? + found = true; + mapping[i] = cur_call_arg->expr(); // assert i < mapping.size() + break; + } + } + kphp_error(found, "Appropriate parameter for named argument is not found"); + } + } + + for (int i = 0; i < call_args.size(); ++i) { +// kphp_error(!mapping[i], "Not enough arguments for function call"); + call_args[i] = mapping[i]; + } // if we are calling `f`, then `f` has not been instantiated yet at this point, so we have a generic func call // at first, we need to know all generic types (call->reifiedTs) From 7b9c9a1d1b06a900eba6a02d31087dea6fae7608 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 16 Jun 2023 15:03:22 +0300 Subject: [PATCH 03/12] Change DeduceImplicitTypes --- .../pipes/deduce-implicit-types-and-casts.cpp | 139 +++++++++--------- .../pipes/deduce-implicit-types-and-casts.h | 1 + 2 files changed, 73 insertions(+), 67 deletions(-) diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 53feba7b20..3c547af8cd 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -559,6 +559,77 @@ void DeduceImplicitTypesAndCastsPass::on_phpdoc_for_var(VertexAdaptor &call, VertexRange f_called_params) { + auto call_args = call->args(); + + auto find_corresponding_param = [&f_called_params](const std::string &call_arg_name) -> std::optional> { + for (auto param: f_called_params) { + if (param.as()->var()->get_string() == call_arg_name) { + return param.as(); + } + } + return std::nullopt; + }; + + std::vector> call_arg_to_func_param(call_args.size()); + int call_arg_idx = 0; + + // positional args + while (call_arg_idx < call_args.size() && call_arg_idx < f_called_params.size() && call_args[call_arg_idx]->type() != op_named_arg) { + call_arg_to_func_param[call_arg_idx] = f_called_params[call_arg_idx].as(); + + if (call_arg_idx < f_called_params.size() && f_called_params[call_arg_idx]->extra_type == op_ex_param_variadic) { + int vararg_idx = call_arg_idx; + while (call_arg_idx < call_args.size()) { + call_arg_to_func_param[call_arg_idx] = f_called_params[vararg_idx].as(); + call_arg_idx++; + } + break; + } + call_arg_idx++; + } + + std::vector mismatched_named_arg; + std::unordered_set unique_names; + unique_names.reserve(30); + + // named args + while (call_arg_idx < call_args.size() && call_arg_idx < f_called_params.size()) { + kphp_error(call_args[call_arg_idx]->type() == op_named_arg, "Positional arguments after named ones are prohibited"); + auto as_named = call_args[call_arg_idx].as(); + + if (auto [_, absent] = unique_names.insert(as_named->name()->get_string()); !absent) { + kphp_error(false, fmt_format("Named arguments with the same name: \"{}\"", as_named->name()->get_string())); + } + + std::optional> corresp_func_param = find_corresponding_param(as_named->name()->get_string()); + if (corresp_func_param.has_value()) { + call_arg_to_func_param[call_arg_idx] = corresp_func_param.value(); + } else { + mismatched_named_arg.push_back(call_arg_idx); + } + call_arg_idx++; + } + + // if function declaration has variadic, then we should patch each extra named arg with it + if (!f_called_params.empty() && f_called_params.back()->extra_type == op_ex_param_variadic) { + for (auto idx: mismatched_named_arg) { + call_arg_to_func_param[idx] = f_called_params.back().as(); + } + } + + for (call_arg_idx = 0; call_arg_idx < call_args.size(); ++call_arg_idx) { + if (call_arg_to_func_param[call_arg_idx] && call_arg_to_func_param[call_arg_idx]->type_hint) { + if (auto as_named = call_args[call_arg_idx].try_as()) { + patch_call_arg_on_func_call(call_arg_to_func_param[call_arg_idx], as_named->expr(), call); + } else { + patch_call_arg_on_func_call(call_arg_to_func_param[call_arg_idx], call_args[call_arg_idx], call); + } + } + } + +} + // for every `f(...)`, bind func_id // for every call argument, patch it to fit @param of f() // if f() is a generic function `f(...)`, deduce instantiation Ts (save call->reifiedTs) @@ -605,63 +676,8 @@ void DeduceImplicitTypesAndCastsPass::on_func_call(VertexAdaptor c FunctionPtr f_called = call->func_id; auto call_args = call->args(); -// for (auto & arg : call_args) { -// if (arg->type() == op_named_arg) { -// arg = arg.as()->expr(); -// } -// } - auto f_called_params = f_called->get_params(); - -// map call_args to params - const int stub = -1; - int fst_named = stub; - int lst_positional = stub; - for (int i = 0; i < call_args.size(); ++i) { - if (fst_named == stub && call_args[i]->type() == op_named_arg) { - fst_named = i; - } - if (call_args[i]->type() != op_named_arg) { - lst_positional = i; - } - } - - kphp_error(fst_named == stub || fst_named > lst_positional, "Cannot use positional argument after named argument"); - - std::vector mapping(call_args.size(), VertexPtr{}); - - // trivial - for (int i = 0; i <= lst_positional; ++i) { - mapping[i] = call_args[i]; - } - - // TODO think about default arguments - - if (fst_named != stub) { - for (int pos = fst_named; pos < call_args.size(); ++pos) { - auto cur_call_arg = call_args[pos].as(); - printf("cur_call_arg.name = %s\n", cur_call_arg->name()->get_string().c_str()); - bool found = false; - for (int i = 0; i < f_called_params.size(); ++i) { - assert(f_called_params[i]->type() == op_func_param); - auto param_name = f_called_params[i].as()->var()->str_val; - printf("\tparam_name = %s\n", param_name.c_str()); - if (cur_call_arg->name()->get_string() == param_name) { // TODO(mkornaukhov03) think about case? - found = true; - mapping[i] = cur_call_arg->expr(); // assert i < mapping.size() - break; - } - } - kphp_error(found, "Appropriate parameter for named argument is not found"); - } - } - - for (int i = 0; i < call_args.size(); ++i) { -// kphp_error(!mapping[i], "Not enough arguments for function call"); - call_args[i] = mapping[i]; - } - // if we are calling `f`, then `f` has not been instantiated yet at this point, so we have a generic func call // at first, we need to know all generic types (call->reifiedTs) // 1) they could be explicitly set using syntax `f/**/(...)` @@ -694,18 +710,7 @@ void DeduceImplicitTypesAndCastsPass::on_func_call(VertexAdaptor c } // now, loop through every argument and potentially patch it - for (int i = 0; i < f_called_params.size() && i < call_args.size(); ++i) { - auto param = f_called_params[i].as(); - - if (param->type_hint) { - patch_call_arg_on_func_call(param, call_args[i], call); - if (param->extra_type == op_ex_param_variadic) { // all the rest arguments are meant to be passed to this param - for (++i; i < call_args.size(); ++i) { // here, they are not replaced with an array: see CheckFuncCallsAndVarargPass - patch_call_arg_on_func_call(param, call_args[i], call); - } - } - } - } + patch_call_args(call, f_called_params); } // a helper to print a human-readable error for `f()` when f not found diff --git a/compiler/pipes/deduce-implicit-types-and-casts.h b/compiler/pipes/deduce-implicit-types-and-casts.h index fe6fd6ef6c..5a84a12fec 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.h +++ b/compiler/pipes/deduce-implicit-types-and-casts.h @@ -35,6 +35,7 @@ class DeduceImplicitTypesAndCastsPass final : public FunctionPassBase { int print_error_unexisting_function(const std::string &call_string); + void patch_call_args(VertexAdaptor &call, VertexRange f_called_params); void patch_call_arg_on_func_call(VertexAdaptor param, VertexPtr &call_arg, VertexAdaptor call); void on_set_to_var(VertexAdaptor lhs, VertexPtr &rhs); From 7b9b75af1dbcf01166ae4e6aaa0f55a829545bd4 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 16 Jun 2023 17:48:54 +0300 Subject: [PATCH 04/12] Got how it works --- compiler/pipes/check-func-calls-and-vararg.cpp | 3 +-- compiler/pipes/deduce-implicit-types-and-casts.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 450f3b8298..1d7bfb9a25 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -41,7 +41,6 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA std::vector flattened_call_args; flattened_call_args.reserve(call->args().size()); VertexRange f_params = f_called->get_params(); - VertexRange call_args = call->args(); // 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) { @@ -95,7 +94,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA } if (auto unpack_as_varg = ith_call_arg.try_as()) { - if (i_call_arg == call_args.size() - 1 && i_func_param == f_params.size() - 1 && variadic_args_passed.empty()) { + if (i_call_arg == flattened_call_args.size() - 1 && i_func_param == f_params.size() - 1 && variadic_args_passed.empty()) { // variadic just have been forwarded, e.g. f(...$args) transformed to f($args) without any array_merge variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); is_just_single_arg_forwarded = true; diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 3c547af8cd..d10d8a0014 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -5,6 +5,9 @@ #include "compiler/pipes/deduce-implicit-types-and-casts.h" #include "compiler/pipes/transform-to-smart-instanceof.h" +#include +#include + #include "compiler/compiler-core.h" #include "compiler/data/src-file.h" #include "compiler/data/generics-mixins.h" @@ -591,7 +594,10 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptor mismatched_named_arg; std::unordered_set unique_names; - unique_names.reserve(30); + unique_names.reserve(10); // TODO check that no one overwrites + mismatched_named_arg.reserve(10); + + // named args while (call_arg_idx < call_args.size() && call_arg_idx < f_called_params.size()) { From fa32aa61e9811f545dcd32b523726be95632229e Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 16 Jun 2023 19:43:32 +0300 Subject: [PATCH 05/12] refactoring in CheckFuncCallAndVararg pass --- .../pipes/check-func-calls-and-vararg.cpp | 118 +++++++++++------- .../pipes/deduce-implicit-types-and-casts.cpp | 2 - 2 files changed, 74 insertions(+), 46 deletions(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 1d7bfb9a25..29c4fed04a 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -4,6 +4,9 @@ #include "compiler/pipes/check-func-calls-and-vararg.h" +#include +#include + #include "compiler/modulite-check-rules.h" #include "compiler/data/src-file.h" #include "compiler/rewrite-rules/replace-extern-func-calls.h" @@ -26,48 +29,41 @@ VertexPtr CheckFuncCallsAndVarargPass::on_enter_vertex(VertexPtr root) { return root; } -// calling a function with variadic arguments involves some transformations: -// function fun($x, ...$args) -// transformations will be: -// fun(1, 2, 3) -> fun(1, [2, 3]) -// fun(1, ...$arr) -> fun(1, $arr) -// fun(1, 2, 3, ...$arr1, ...$arr2) -> fun(1, array_merge([2, 3], $arr1, $arr2)) -// here we also deal with unpacking fixed-size arrays into positional arguments: -// fun(...[1]) -> fun(1) -// fun(...[1,2]) -> fun(1, [2]) -// fun(...[1, ...[2, ...[3, ...$rest]]]) => fun(1, array_merge([2,3], $rest)) -// this is done here (not in deducing types), as $f(...$variadic) — in invoke, not func call — are applicable only here -VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexAdaptor call, FunctionPtr f_called) { - std::vector flattened_call_args; - flattened_call_args.reserve(call->args().size()); - VertexRange f_params = f_called->get_params(); +namespace { +struct VarargAppendOptions { + int variadic_func_param_idx; + bool is_just_single_arg_forwarded; + bool needs_wrap_array_merge; +}; - // at first, convert f(1, ...[2, ...[3]], ...$all, ...[5]) to f(1,2,3,...$all,5) +std::vector flatten_call_args(VertexRange args, VertexRange func_params) { + std::vector flattened_call_args; + flattened_call_args.reserve(args.size()); 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"); - kphp_assert(item->type() != op_varg); - flattened_call_args.emplace_back(item); - } - } else if (auto as_merge = inner.try_as(); as_merge && as_merge->str_val == "array_merge_spread") { - for (VertexPtr item : as_merge->args()) { - kphp_assert(item->type() == op_conv_array); - flatten_call_varg(item.as()->expr()); - } - } else { - auto wrap_varg = VertexAdaptor::create(inner).set_location(inner); - flattened_call_args.emplace_back(wrap_varg); + 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"); + kphp_assert(item->type() != op_varg); + flattened_call_args.emplace_back(item); } + } else if (auto as_merge = inner.try_as(); as_merge && as_merge->str_val == "array_merge_spread") { + for (VertexPtr item: as_merge->args()) { + kphp_assert(item->type() == op_conv_array); + flatten_call_varg(item.as()->expr()); + } + } else { + auto wrap_varg = VertexAdaptor::create(inner).set_location(inner); + flattened_call_args.emplace_back(wrap_varg); + } }; - for (VertexPtr call_arg : call->args()) { + for (VertexPtr call_arg: args) { if (auto as_varg = call_arg.try_as()) { size_t n_before = flattened_call_args.size(); flatten_call_varg(as_varg->array()); - for (size_t i = n_before; i <= flattened_call_args.size() && i < f_params.size(); ++i) { - auto ith_param = f_params[i].as(); + for (size_t i = n_before; i <= flattened_call_args.size() && i < func_params.size(); ++i) { + auto ith_param = func_params[i].as(); kphp_error(!ith_param->is_cast_param, fmt_format("Invalid place for unpack, because param ${} is @kphp-infer cast", ith_param->var()->str_val)); } } else { @@ -75,16 +71,20 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA } } + return flattened_call_args; +} + +std::tuple, std::vector, VarargAppendOptions> detect_variadic_args(const std::vector &flattened_call_args, FunctionPtr f_called) { std::vector new_call_args; bool is_just_single_arg_forwarded = false; bool needs_wrap_array_merge = false; std::vector variadic_args_passed; +// variadic_args_passed.reserve(flattened_call_args.size()); TODO uncomment and measure memory and time consumption int i_func_param = 0; - // then, having f($x,$y,...$rest) and a call f(1,2,3,...$all,5), detect that variadic_args_passed = [3,...$all,5] for (int i_call_arg = 0; i_call_arg < flattened_call_args.size(); ++i_call_arg) { VertexPtr ith_call_arg = flattened_call_args[i_call_arg]; - bool is_variadic_param = f_called->has_variadic_param && i_func_param == f_params.size() - 1; + bool is_variadic_param = f_called->has_variadic_param && i_func_param == f_called->get_params().size() - 1; if (!is_variadic_param) { i_func_param++; @@ -94,7 +94,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA } if (auto unpack_as_varg = ith_call_arg.try_as()) { - if (i_call_arg == flattened_call_args.size() - 1 && i_func_param == f_params.size() - 1 && variadic_args_passed.empty()) { + if (i_call_arg == flattened_call_args.size() - 1 && i_func_param == f_called->get_params().size() - 1 && variadic_args_passed.empty()) { // variadic just have been forwarded, e.g. f(...$args) transformed to f($args) without any array_merge variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); is_just_single_arg_forwarded = true; @@ -108,16 +108,19 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA } } - // append $rest = (from variadic_args_passed) as the last new_call_args - if (f_called->has_variadic_param && i_func_param == f_params.size() - 1) { - if (is_just_single_arg_forwarded) { + return {std::move(new_call_args), std::move(variadic_args_passed), VarargAppendOptions{i_func_param, is_just_single_arg_forwarded, needs_wrap_array_merge}}; +} + +void append_variadic(std::vector &new_call_args, const std::vector & variadic_args_passed, VarargAppendOptions opts, FunctionPtr f_called, Location loc) { + if (f_called->has_variadic_param && opts.variadic_func_param_idx == f_called->get_params().size() - 1) { + if (opts.is_just_single_arg_forwarded) { // optimization: f(...$args) transformed to f($args) without any array_merge kphp_assert(variadic_args_passed.size() == 1); new_call_args.emplace_back(variadic_args_passed.front()); - } else if (!needs_wrap_array_merge) { + } else if (!opts.needs_wrap_array_merge) { // f(1, 2, 3) transformed into f([1,2,3]) - auto rest_array = VertexAdaptor::create(variadic_args_passed).set_location(call); + auto rest_array = VertexAdaptor::create(variadic_args_passed).set_location(loc); new_call_args.emplace_back(rest_array); } else { @@ -138,16 +141,43 @@ VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexA } seq_items.emplace_back(variadic_args_passed[i]); } - auto seq_array = VertexAdaptor::create(seq_items).set_location(call); + auto seq_array = VertexAdaptor::create(seq_items).set_location(loc); variadic_args_conv_array.emplace_back(seq_array); } - auto merge_arrays = VertexAdaptor::create(variadic_args_conv_array).set_location(call); + auto merge_arrays = VertexAdaptor::create(variadic_args_conv_array).set_location(loc); merge_arrays->str_val = "array_merge"; merge_arrays->func_id = G->get_function(merge_arrays->str_val); new_call_args.emplace_back(merge_arrays); } } +} +} + + + +// calling a function with variadic arguments involves some transformations: +// function fun($x, ...$args) +// transformations will be: +// fun(1, 2, 3) -> fun(1, [2, 3]) +// fun(1, ...$arr) -> fun(1, $arr) +// fun(1, 2, 3, ...$arr1, ...$arr2) -> fun(1, array_merge([2, 3], $arr1, $arr2)) +// here we also deal with unpacking fixed-size arrays into positional arguments: +// fun(...[1]) -> fun(1) +// fun(...[1,2]) -> fun(1, [2]) +// fun(...[1, ...[2, ...[3, ...$rest]]]) => fun(1, array_merge([2,3], $rest)) +// this is done here (not in deducing types), as $f(...$variadic) — in invoke, not func call — are applicable only here +VertexAdaptor CheckFuncCallsAndVarargPass::process_varargs(VertexAdaptor call, FunctionPtr f_called) { + VertexRange f_params = f_called->get_params(); + + // at first, convert f(1, ...[2, ...[3]], ...$all, ...[5]) to f(1,2,3,...$all,5) + std::vector flattened_call_args = flatten_call_args(call->args(), f_params); + + // then, having f($x,$y,...$rest) and a call f(1,2,3,...$all,5), detect that variadic_args_passed = [3,...$all,5] + auto [new_call_args, variadic_args_passed, opts] = detect_variadic_args(flattened_call_args, f_called); + + // append $rest = (from variadic_args_passed) as the last new_call_args + append_variadic(new_call_args, variadic_args_passed, opts, f_called, call->location); auto new_call = VertexAdaptor::create(new_call_args).set_location(call); new_call->extra_type = call->extra_type; diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index d10d8a0014..b595239321 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -597,8 +597,6 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptortype() == op_named_arg, "Positional arguments after named ones are prohibited"); From 2c50bd8ea99d8728f26f6f816ebce5b972a9567f Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 19 Jun 2023 19:31:44 +0300 Subject: [PATCH 06/12] Add error check(unknown argument name); implement detect_variadic_args --- .../pipes/check-func-calls-and-vararg.cpp | 25 +++++++++++++++++-- .../pipes/deduce-implicit-types-and-casts.cpp | 5 +++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 29c4fed04a..bda13da9e2 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "compiler/modulite-check-rules.h" #include "compiler/data/src-file.h" @@ -80,6 +81,17 @@ std::tuple, std::vector, VarargAppendOptions> bool needs_wrap_array_merge = false; std::vector variadic_args_passed; // variadic_args_passed.reserve(flattened_call_args.size()); TODO uncomment and measure memory and time consumption + + std::unordered_set func_arg_names; +// for (auto param : f_called->get_params()) { +// if (param.as()->var()->get_string()) +// } + std::for_each(f_called->get_params().begin(), f_called->get_params().end(), [&](VertexPtr param) { + if (param.as()->extra_type != op_ex_param_variadic) { + func_arg_names.insert(param.as()->var()->get_string()); + } + }); + int i_func_param = 0; for (int i_call_arg = 0; i_call_arg < flattened_call_args.size(); ++i_call_arg) { @@ -88,8 +100,15 @@ std::tuple, std::vector, VarargAppendOptions> if (!is_variadic_param) { i_func_param++; - new_call_args.emplace_back(ith_call_arg); - kphp_error(ith_call_arg->type() != op_varg, "It's prohibited to unpack non-fixed arrays where positional arguments expected"); + + if (auto as_named = ith_call_arg.try_as(); as_named && func_arg_names.count(as_named->name()->get_string()) == 0) { + // foo($a, ...$args); foo(name : val, a: 1) --> foo(a : 1, ["name" : val]) + auto as_double_arrow = VertexAdaptor::create(as_named->name(), as_named->expr()); + variadic_args_passed.emplace_back(as_double_arrow); + } else { + new_call_args.emplace_back(ith_call_arg); + kphp_error(ith_call_arg->type() != op_varg, "It's prohibited to unpack non-fixed arrays where positional arguments expected"); + } continue; } @@ -108,6 +127,8 @@ std::tuple, std::vector, VarargAppendOptions> } } + + return {std::move(new_call_args), std::move(variadic_args_passed), VarargAppendOptions{i_func_param, is_just_single_arg_forwarded, needs_wrap_array_merge}}; } diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index b595239321..5b1a22ca01 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -594,7 +594,7 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptor mismatched_named_arg; std::unordered_set unique_names; - unique_names.reserve(10); // TODO check that no one overwrites + unique_names.reserve(10); mismatched_named_arg.reserve(10); // named args @@ -622,6 +622,9 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptorextra_type == op_ex_param_variadic && !mismatched_named_arg.empty()), fmt_format("Unknown parameter name: %s", call_args[mismatched_named_arg.front()].as()->name()->get_string())); + + for (call_arg_idx = 0; call_arg_idx < call_args.size(); ++call_arg_idx) { if (call_arg_to_func_param[call_arg_idx] && call_arg_to_func_param[call_arg_idx]->type_hint) { if (auto as_named = call_args[call_arg_idx].try_as()) { From cdcbb9f550080edf0211813cc19ca8a7572a3879 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 26 Jun 2023 19:17:49 +0300 Subject: [PATCH 07/12] Implement --- compiler/code-gen/vertex-compiler.cpp | 20 ++-- .../pipes/check-func-calls-and-vararg.cpp | 102 +++++++++++++++--- compiler/pipes/check-func-calls-and-vararg.h | 1 + .../pipes/deduce-implicit-types-and-casts.cpp | 7 +- tests/phpt/named_args/001_basic.phpt | 37 +++++++ tests/phpt/named_args/002_vararg.phpt | 13 +++ tests/phpt/named_args/003_defaults.phpt | 15 +++ tests/phpt/named_args/100_duplicate.phpt | 9 ++ 8 files changed, 178 insertions(+), 26 deletions(-) create mode 100644 tests/phpt/named_args/001_basic.phpt create mode 100644 tests/phpt/named_args/002_vararg.phpt create mode 100644 tests/phpt/named_args/003_defaults.phpt create mode 100644 tests/phpt/named_args/100_duplicate.phpt diff --git a/compiler/code-gen/vertex-compiler.cpp b/compiler/code-gen/vertex-compiler.cpp index df8678ac3e..e4dc6667ec 100644 --- a/compiler/code-gen/vertex-compiler.cpp +++ b/compiler/code-gen/vertex-compiler.cpp @@ -1447,16 +1447,16 @@ void compile_function(VertexAdaptor func_root, CodeGenerator &W) { } } - if (func->has_variadic_param) { - auto params = func->get_params(); - kphp_assert(!params.empty()); - auto variadic_arg = std::prev(params.end()); - auto name_of_variadic_param = VarName(variadic_arg->as()->var()->var_id); - W << "if (!" << name_of_variadic_param << ".is_vector())" << BEGIN; - W << "php_warning(\"pass associative array(" << name_of_variadic_param << ") to variadic function: " << FunctionName(func) << "\");" << NL; - W << name_of_variadic_param << " = f$array_values(" << name_of_variadic_param << ");" << NL; - W << END << NL; - } +// if (func->has_variadic_param) { +// auto params = func->get_params(); +// kphp_assert(!params.empty()); +// auto variadic_arg = std::prev(params.end()); +// auto name_of_variadic_param = VarName(variadic_arg->as()->var()->var_id); +// W << "if (!" << name_of_variadic_param << ".is_vector())" << BEGIN; +// W << "php_warning(\"pass associative array(" << name_of_variadic_param << ") to variadic function: " << FunctionName(func) << "\");" << NL; +// W << name_of_variadic_param << " = f$array_values(" << name_of_variadic_param << ");" << NL; +// W << END << NL; +// } W << AsSeq{func_root->cmd()} << END << NL; } diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index bda13da9e2..93a622a19a 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -83,9 +83,6 @@ std::tuple, std::vector, VarargAppendOptions> // variadic_args_passed.reserve(flattened_call_args.size()); TODO uncomment and measure memory and time consumption std::unordered_set func_arg_names; -// for (auto param : f_called->get_params()) { -// if (param.as()->var()->get_string()) -// } std::for_each(f_called->get_params().begin(), f_called->get_params().end(), [&](VertexPtr param) { if (param.as()->extra_type != op_ex_param_variadic) { func_arg_names.insert(param.as()->var()->get_string()); @@ -122,13 +119,16 @@ std::tuple, std::vector, VarargAppendOptions> variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); needs_wrap_array_merge = true; } + } else if (auto as_named = ith_call_arg.try_as(); as_named && func_arg_names.count(as_named->name()->get_string()) == 0) { + // foo($a, ...$args); foo(name : val, a: 1) --> foo(a : 1, ["name" : val]) + auto as_double_arrow = VertexAdaptor::create(as_named->name(), as_named->expr()); + variadic_args_passed.emplace_back(as_double_arrow); } else { variadic_args_passed.emplace_back(ith_call_arg); } } - return {std::move(new_call_args), std::move(variadic_args_passed), VarargAppendOptions{i_func_param, is_just_single_arg_forwarded, needs_wrap_array_merge}}; } @@ -175,6 +175,74 @@ void append_variadic(std::vector &new_call_args, const std::vector CheckFuncCallsAndVarargPass::reorder_with_defaults(VertexAdaptor call, FunctionPtr f) { + + + auto call_params = call->args(); + auto func_params = f->get_params(); + auto find_corresponding_param = [&](const std::string &call_arg_name) -> int { + for (int i = 0; i < func_params.size(); ++i) { + auto param = func_params[i]; + if (param.as()->var()->get_string() == call_arg_name) { + return i; + } + } + return -1; + }; + + std::vector call_arg_to_func_param(func_params.size()); + + int call_arg_idx = 0; + // positional args + while (call_arg_idx < call_params.size() /*&& call_arg_idx < func_params.size()*/ && call_params[call_arg_idx]->type() != op_named_arg) { + call_arg_to_func_param[call_arg_idx] = call_params[call_arg_idx]; + call_arg_idx++; + } + while (call_arg_idx < call_params.size() && call_params[call_arg_idx]->type() == op_named_arg) { + auto as_named = call_params[call_arg_idx].as(); + auto corresp_param_idx = find_corresponding_param(as_named->name()->get_string()); + kphp_assert_msg(corresp_param_idx != -1, fmt_format("Unknown named argument {}", as_named->name()->get_string())); + call_arg_to_func_param[corresp_param_idx] = as_named->expr(); + call_arg_idx++; + } + + if (call_arg_idx < call_params.size()) { + kphp_assert_msg(call_arg_idx + 1 == call_params.size(), "Unexpected parameters"); + kphp_assert_msg(call_params[call_arg_idx]->type() == op_array, "op_array as vararg is expected"); + call_arg_to_func_param[call_arg_idx] = call_params[call_arg_idx]; + call_arg_idx++; + } else { + int lst_idx = std::distance(std::find_if(call_arg_to_func_param.rbegin(), call_arg_to_func_param.rend(), [](VertexPtr v) { return static_cast(v); }), call_arg_to_func_param.rend()); + kphp_assert_msg(lst_idx != 0 || func_params.size() == 0, "Cannot reorder params correctly"); + call_arg_to_func_param.resize(lst_idx); + for (int i = 0; i < lst_idx; ++i) { + if (!call_arg_to_func_param[i]) { + kphp_error(func_params[i].as()->has_default_value(), "Not enough arguments for function call"); + call_arg_to_func_param[i] = func_params[i].as()->default_value(); + } + } + } + + auto new_call = VertexAdaptor::create(call_arg_to_func_param).set_location_recursively(call); + new_call->str_val = call->str_val; + new_call->func_id = call->func_id; + new_call->extra_type = call->extra_type; + new_call->auto_inserted = call->auto_inserted; + + if (call_params.size() < func_params.size()) { + for (int missing_i = call_params.size(); missing_i < func_params.size(); ++missing_i) { + if (VertexPtr auto_added = maybe_autofill_missing_call_arg(new_call, f, func_params[missing_i].as())) { + for (int def_i = call_params.size(); def_i < missing_i; ++def_i) { + new_call = VertexUtil::add_call_arg(func_params[def_i].as()->default_value(), new_call, false); + } + new_call = VertexUtil::add_call_arg(auto_added, new_call, false); + call_params = new_call->args(); + } + } + } + + return new_call; +} // calling a function with variadic arguments involves some transformations: @@ -303,16 +371,22 @@ VertexPtr CheckFuncCallsAndVarargPass::on_func_call(VertexAdaptor VertexRange func_params = f->get_params(); VertexRange call_params = call->args(); - if (call_params.size() < func_params.size()) { - for (int missing_i = call_params.size(); missing_i < func_params.size(); ++missing_i) { - if (VertexPtr auto_added = maybe_autofill_missing_call_arg(call, f, func_params[missing_i].as())) { - for (int def_i = call_params.size(); def_i < missing_i; ++def_i) { - call = VertexUtil::add_call_arg(func_params[def_i].as()->default_value(), call, false); - } - call = VertexUtil::add_call_arg(auto_added, call, false); - call_params = call->args(); - } - } +// if (call_params.size() < func_params.size()) { +// for (int missing_i = call_params.size(); missing_i < func_params.size(); ++missing_i) { +// if (VertexPtr auto_added = maybe_autofill_missing_call_arg(call, f, func_params[missing_i].as())) { +// for (int def_i = call_params.size(); def_i < missing_i; ++def_i) { +// call = VertexUtil::add_call_arg(func_params[def_i].as()->default_value(), call, false); +// } +// call = VertexUtil::add_call_arg(auto_added, call, false); +// call_params = call->args(); +// } +// } +// } + call = reorder_with_defaults(call, f); + + if (f->name == "foo") { + puts("DEBUG: "); + call.debugPrint(); } int call_n_params = call_params.size(); diff --git a/compiler/pipes/check-func-calls-and-vararg.h b/compiler/pipes/check-func-calls-and-vararg.h index 30f8e65ede..ae763e807f 100644 --- a/compiler/pipes/check-func-calls-and-vararg.h +++ b/compiler/pipes/check-func-calls-and-vararg.h @@ -12,6 +12,7 @@ class CheckFuncCallsAndVarargPass final : public FunctionPassBase { VertexPtr maybe_autofill_missing_call_arg(VertexAdaptor call, FunctionPtr f_called, VertexAdaptor param); VertexPtr create_CompileTimeLocation_call_arg(const Location &call_location); + VertexAdaptor reorder_with_defaults(VertexAdaptor call, FunctionPtr f); public: std::string get_description() override { diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 5b1a22ca01..7f8191258c 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -603,7 +603,7 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptor(); if (auto [_, absent] = unique_names.insert(as_named->name()->get_string()); !absent) { - kphp_error(false, fmt_format("Named arguments with the same name: \"{}\"", as_named->name()->get_string())); + kphp_error(false, fmt_format("Named arguments with duplicated name: \'{}\'", as_named->name()->get_string())); } std::optional> corresp_func_param = find_corresponding_param(as_named->name()->get_string()); @@ -622,7 +622,10 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptorextra_type == op_ex_param_variadic && !mismatched_named_arg.empty()), fmt_format("Unknown parameter name: %s", call_args[mismatched_named_arg.front()].as()->name()->get_string())); + + const bool ok = mismatched_named_arg.size() == 0 || f_called_params.back()->extra_type == op_ex_param_variadic; + + kphp_error(ok, fmt_format("Unknown parameter name: {}", call_args[mismatched_named_arg.front()].as()->name()->get_string())); for (call_arg_idx = 0; call_arg_idx < call_args.size(); ++call_arg_idx) { diff --git a/tests/phpt/named_args/001_basic.phpt b/tests/phpt/named_args/001_basic.phpt new file mode 100644 index 0000000000..4a318a3c03 --- /dev/null +++ b/tests/phpt/named_args/001_basic.phpt @@ -0,0 +1,37 @@ +@ok php8 + Date: Wed, 28 Jun 2023 17:26:48 +0300 Subject: [PATCH 08/12] Fix --- compiler/pipes/check-func-calls-and-vararg.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 93a622a19a..dd729f195a 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -213,7 +213,6 @@ VertexAdaptor CheckFuncCallsAndVarargPass::reorder_with_defaults(V call_arg_idx++; } else { int lst_idx = std::distance(std::find_if(call_arg_to_func_param.rbegin(), call_arg_to_func_param.rend(), [](VertexPtr v) { return static_cast(v); }), call_arg_to_func_param.rend()); - kphp_assert_msg(lst_idx != 0 || func_params.size() == 0, "Cannot reorder params correctly"); call_arg_to_func_param.resize(lst_idx); for (int i = 0; i < lst_idx; ++i) { if (!call_arg_to_func_param[i]) { From 583ec06a77902bc8510e87ab9e1db2d8de5b568e Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 3 Jul 2023 14:12:28 +0300 Subject: [PATCH 09/12] Fix --- compiler/pipes/check-func-calls-and-vararg.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index dd729f195a..7ec3e6f0d7 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -190,7 +190,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::reorder_with_defaults(V return -1; }; - std::vector call_arg_to_func_param(func_params.size()); + std::vector call_arg_to_func_param(std::max(func_params.size(), call_params.size())); int call_arg_idx = 0; // positional args @@ -383,11 +383,6 @@ VertexPtr CheckFuncCallsAndVarargPass::on_func_call(VertexAdaptor // } call = reorder_with_defaults(call, f); - if (f->name == "foo") { - puts("DEBUG: "); - call.debugPrint(); - } - int call_n_params = call_params.size(); int delta_this = f->has_implicit_this_arg() ? 1 : 0; // not to count implicit $this on error output From 487fdec7229cc315f9719e53a92eff777290e9f6 Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Mon, 3 Jul 2023 15:12:52 +0300 Subject: [PATCH 10/12] Fix tests --- tests/phpt/named_args/{001_basic.phpt => 001_basic.php} | 2 +- tests/phpt/named_args/{002_vararg.phpt => 002_vararg.php} | 0 tests/phpt/named_args/{003_defaults.phpt => 003_defaults.php} | 2 +- tests/phpt/named_args/{100_duplicate.phpt => 100_duplicate.php} | 1 - 4 files changed, 2 insertions(+), 3 deletions(-) rename tests/phpt/named_args/{001_basic.phpt => 001_basic.php} (99%) rename tests/phpt/named_args/{002_vararg.phpt => 002_vararg.php} (100%) rename tests/phpt/named_args/{003_defaults.phpt => 003_defaults.php} (93%) rename tests/phpt/named_args/{100_duplicate.phpt => 100_duplicate.php} (74%) diff --git a/tests/phpt/named_args/001_basic.phpt b/tests/phpt/named_args/001_basic.php similarity index 99% rename from tests/phpt/named_args/001_basic.phpt rename to tests/phpt/named_args/001_basic.php index 4a318a3c03..dc197db514 100644 --- a/tests/phpt/named_args/001_basic.phpt +++ b/tests/phpt/named_args/001_basic.php @@ -1,4 +1,4 @@ -@ok php8 +@php8 ok Date: Tue, 4 Jul 2023 19:42:50 +0300 Subject: [PATCH 11/12] Fix error with clone --- compiler/pipes/check-func-calls-and-vararg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index 7ec3e6f0d7..a8cda559c4 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -217,7 +217,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::reorder_with_defaults(V for (int i = 0; i < lst_idx; ++i) { if (!call_arg_to_func_param[i]) { kphp_error(func_params[i].as()->has_default_value(), "Not enough arguments for function call"); - call_arg_to_func_param[i] = func_params[i].as()->default_value(); + call_arg_to_func_param[i] = func_params[i].as()->default_value().clone(); } } } @@ -232,7 +232,7 @@ VertexAdaptor CheckFuncCallsAndVarargPass::reorder_with_defaults(V for (int missing_i = call_params.size(); missing_i < func_params.size(); ++missing_i) { if (VertexPtr auto_added = maybe_autofill_missing_call_arg(new_call, f, func_params[missing_i].as())) { for (int def_i = call_params.size(); def_i < missing_i; ++def_i) { - new_call = VertexUtil::add_call_arg(func_params[def_i].as()->default_value(), new_call, false); + new_call = VertexUtil::add_call_arg(func_params[def_i].as()->default_value().clone(), new_call, false); } new_call = VertexUtil::add_call_arg(auto_added, new_call, false); call_params = new_call->args(); From edb76356b00d209217dcd46b96d3ed2e1b73bb0b Mon Sep 17 00:00:00 2001 From: mkornaukhov03 Date: Fri, 7 Jul 2023 20:22:31 +0300 Subject: [PATCH 12/12] Fix review notes --- compiler/code-gen/vertex-compiler.cpp | 12 +----------- compiler/gentree.cpp | 14 ++++++++------ compiler/pipes/check-func-calls-and-vararg.cpp | 5 +---- compiler/pipes/deduce-implicit-types-and-casts.cpp | 3 +-- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/compiler/code-gen/vertex-compiler.cpp b/compiler/code-gen/vertex-compiler.cpp index e4dc6667ec..efbe515e19 100644 --- a/compiler/code-gen/vertex-compiler.cpp +++ b/compiler/code-gen/vertex-compiler.cpp @@ -1446,17 +1446,7 @@ void compile_function(VertexAdaptor func_root, CodeGenerator &W) { W << VarDeclaration(var); } } - -// if (func->has_variadic_param) { -// auto params = func->get_params(); -// kphp_assert(!params.empty()); -// auto variadic_arg = std::prev(params.end()); -// auto name_of_variadic_param = VarName(variadic_arg->as()->var()->var_id); -// W << "if (!" << name_of_variadic_param << ".is_vector())" << BEGIN; -// W << "php_warning(\"pass associative array(" << name_of_variadic_param << ") to variadic function: " << FunctionName(func) << "\");" << NL; -// W << name_of_variadic_param << " = f$array_values(" << name_of_variadic_param << ");" << NL; -// W << END << NL; -// } + W << AsSeq{func_root->cmd()} << END << NL; } diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index 95afba1c84..1f02b0aca9 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -858,16 +858,18 @@ VertexPtr GenTree::get_expression() { VertexPtr GenTree::get_func_call_arg() { skip_phpdoc_tokens(); - VertexPtr name = get_expression(); -// name.debugPrint(); -// assert(name->type() == op_func_name); + VertexPtr name_or_val = get_expression(); + + // in case of named argument 'name_or_val' is a name if (cur->type() == tok_colon) { - puts("!!!!!!!"); next_cur(); VertexPtr value = get_expression(); - return VertexAdaptor::create(VertexUtil::create_string_const(name->get_string()), value); + CE (!kphp_error(name_or_val, "Bad value of named argument")); + return VertexAdaptor::create(VertexUtil::create_string_const(name_or_val->get_string()), value); } - return name; + + // in case of positional argument 'name_or_val' is a value + return name_or_val; } VertexPtr GenTree::get_def_value() { diff --git a/compiler/pipes/check-func-calls-and-vararg.cpp b/compiler/pipes/check-func-calls-and-vararg.cpp index a8cda559c4..cfcd008f09 100644 --- a/compiler/pipes/check-func-calls-and-vararg.cpp +++ b/compiler/pipes/check-func-calls-and-vararg.cpp @@ -112,13 +112,12 @@ std::tuple, std::vector, VarargAppendOptions> if (auto unpack_as_varg = ith_call_arg.try_as()) { if (i_call_arg == flattened_call_args.size() - 1 && i_func_param == f_called->get_params().size() - 1 && variadic_args_passed.empty()) { // variadic just have been forwarded, e.g. f(...$args) transformed to f($args) without any array_merge - variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); is_just_single_arg_forwarded = true; } else { // f(...$args, ...$another) transformed to f(array_merge($args, $another)) - variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); needs_wrap_array_merge = true; } + variadic_args_passed.emplace_back(VertexUtil::create_conv_to(tp_array, unpack_as_varg->array())); } else if (auto as_named = ith_call_arg.try_as(); as_named && func_arg_names.count(as_named->name()->get_string()) == 0) { // foo($a, ...$args); foo(name : val, a: 1) --> foo(a : 1, ["name" : val]) auto as_double_arrow = VertexAdaptor::create(as_named->name(), as_named->expr()); @@ -176,8 +175,6 @@ void append_variadic(std::vector &new_call_args, const std::vector CheckFuncCallsAndVarargPass::reorder_with_defaults(VertexAdaptor call, FunctionPtr f) { - - auto call_params = call->args(); auto func_params = f->get_params(); auto find_corresponding_param = [&](const std::string &call_arg_name) -> int { diff --git a/compiler/pipes/deduce-implicit-types-and-casts.cpp b/compiler/pipes/deduce-implicit-types-and-casts.cpp index 7f8191258c..40f3b5c382 100644 --- a/compiler/pipes/deduce-implicit-types-and-casts.cpp +++ b/compiler/pipes/deduce-implicit-types-and-casts.cpp @@ -584,8 +584,7 @@ void DeduceImplicitTypesAndCastsPass::patch_call_args(VertexAdaptorextra_type == op_ex_param_variadic) { int vararg_idx = call_arg_idx; while (call_arg_idx < call_args.size()) { - call_arg_to_func_param[call_arg_idx] = f_called_params[vararg_idx].as(); - call_arg_idx++; + call_arg_to_func_param[call_arg_idx++] = f_called_params[vararg_idx].as(); } break; }