Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion compiler/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,16 @@ VertexPtr GenTree::get_binary_op(int op_priority_cur, bool till_ternary) {
return left;
}

// in function named argument call:
// foo(name: <expr>)
// | color here
if (left->type() == op_func_name && cur->type() == tok_colon) {
// skip colon
next_cur();
const auto arg_expr = get_expression();
return VertexAdaptor<op_named_arg>::create(arg_expr, left.as<op_func_name>());
}

while (cur != end) {
const TokenType origin_token = cur->type();
const TokenType token = transform_compare_operation_token(origin_token);
Expand Down Expand Up @@ -1502,7 +1512,8 @@ VertexAdaptor<op_func_param_list> GenTree::parse_cur_function_param_list() {
ClassData::patch_func_add_this(params_next, Location(line_num));
}

bool ok_params_next = gen_list<op_err>(&params_next, &GenTree::get_func_param, tok_comma);

bool ok_params_next = gen_list<op_none>(&params_next, &GenTree::get_func_param, tok_comma);
CE(!kphp_error(ok_params_next, "Failed to parse function params"));
CE(expect(tok_clpar, "')'"));

Expand Down
80 changes: 79 additions & 1 deletion compiler/pipes/check-function-calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,82 @@
#include "compiler/data/src-file.h"
#include "compiler/type-hint.h"

void CheckFunctionCallsPass::check_func_call(VertexPtr call) {
VertexAdaptor<op_func_call> CheckFunctionCallsPass::process_named_args(FunctionPtr func, VertexAdaptor<op_func_call> call) const {
const auto args = call->args();
auto see_named = false;
auto without_named_args = true;

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;
}
}

if (without_named_args) {
return call;
}

const auto params = func->get_params();
auto params_map = std::unordered_map<std::string, size_t>(params.size());

for (int i = 0; i < params.size(); ++i) {
const auto &param = params[i].as<op_func_param>();
params_map[param->var()->str_val] = i;
}

auto new_call_args = std::vector<VertexPtr>(params.size());

for (int i = 0; i < args.size(); ++i) {
const auto &arg = args[i];

if (const auto named = arg.try_as<op_named_arg>()) {
const auto name = named->name()->str_val;
if (params_map.find(name) == params_map.end()) {
kphp_error(0, fmt_format("Unknown named parameter {}", name));
return call;
}

const auto param_index = params_map[name];

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;
}
}

for (int i = 0; i < params.size(); ++i) {
const auto &param = params[i].as<op_func_param>();

if (!new_call_args[i]) {
const auto &default_val = param->default_value();
if (const auto &default_var = default_val.try_as<op_var>()) {
current_function->explicit_header_const_var_ids.insert(default_var->var_id);
}

new_call_args[i] = param->default_value().clone();
}
}

auto new_call = VertexAdaptor<op_func_call>::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) {
FunctionPtr f = call->type() == op_func_ptr ? call.as<op_func_ptr>()->func_id : call.as<op_func_call>()->func_id;
kphp_assert(f);
kphp_error_return(f->root, fmt_format("Function [{}] undeclared", f->get_human_readable_name()));
Expand All @@ -26,6 +101,8 @@ void CheckFunctionCallsPass::check_func_call(VertexPtr call) {
return;
}

call = process_named_args(f, call.as<op_func_call>());

VertexRange func_params = f->get_params();
VertexRange call_params = call.as<op_func_call>()->args();

Expand Down Expand Up @@ -65,6 +142,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);
Expand Down
4 changes: 3 additions & 1 deletion compiler/pipes/check-function-calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

class CheckFunctionCallsPass final : public FunctionPassBase {
private:
void check_func_call(VertexPtr call);
void check_func_call(VertexPtr &call);
VertexAdaptor<op_func_call> process_named_args(FunctionPtr func, VertexAdaptor<op_func_call> call) const;

public:
string get_description() override {
return "Check function calls";
Expand Down
2 changes: 1 addition & 1 deletion compiler/pipes/final-check.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
6 changes: 5 additions & 1 deletion compiler/pipes/preprocess-function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<op_named_arg>()) {
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) {
Expand Down
19 changes: 19 additions & 0 deletions compiler/vertex-desc.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,25 @@
]
}
},
{
"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": [
Expand Down