diff options
| author | gingerBill <bill@gingerbill.org> | 2020-05-22 14:54:30 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2020-05-22 14:54:30 +0100 |
| commit | 7bd1039a49e5fd771569cd1b5bca5b076d464a5f (patch) | |
| tree | 06382e3a2cf5bb813981faac87086d4c736b1664 /src | |
| parent | 76a230372f59e873b09ce333fd466b433697b936 (diff) | |
Selector Call Expressions: `x->y(123) == x.y(x, 123)`
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_expr.cpp | 205 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 12 | ||||
| -rw-r--r-- | src/ir.cpp | 7 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 7 | ||||
| -rw-r--r-- | src/parser.cpp | 24 | ||||
| -rw-r--r-- | src/parser.hpp | 1 |
6 files changed, 217 insertions, 39 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8def4d6ed..b19976de7 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6525,7 +6525,7 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, A } -CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) { +CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Array<Ast *> const &args) { ast_node(ce, CallExpr, call); CallArgumentCheckerType *call_checker = check_call_arguments_internal; @@ -6537,13 +6537,13 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (is_call_expr_field_value(ce)) { call_checker = check_named_call_arguments; - operands = array_make<Operand>(heap_allocator(), ce->args.count); + operands = array_make<Operand>(heap_allocator(), args.count); // NOTE(bill): This is give type hints for the named parameters // in order to improve the type inference system StringMap<Type *> type_hint_map = {}; // Key: String - string_map_init(&type_hint_map, heap_allocator(), 2*ce->args.count); + string_map_init(&type_hint_map, heap_allocator(), 2*args.count); defer (string_map_destroy(&type_hint_map)); Type *ptype = nullptr; @@ -6620,8 +6620,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } - for_array(i, ce->args) { - Ast *arg = ce->args[i]; + for_array(i, args) { + Ast *arg = args[i]; ast_node(fv, FieldValue, arg); Ast *field = fv->field; @@ -6637,7 +6637,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type check_expr_or_type(c, &operands[i], fv->value, type_hint); } } else { - operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); + operands = array_make<Operand>(heap_allocator(), 0, 2*args.count); Entity **lhs = nullptr; isize lhs_count = -1; bool is_variadic = false; @@ -6645,7 +6645,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic); } if (operand->mode != Addressing_ProcGroup) { - check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic); } } @@ -6667,7 +6667,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type isize lhs_count = -1; bool is_variadic = false; lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic); - check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, is_variadic); + check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic); CallArgumentData data = {}; @@ -6740,7 +6740,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } - check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); + check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, false); if (lhs != nullptr) { gb_free(heap_allocator(), lhs); @@ -7269,33 +7269,36 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper -ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { - ast_node(ce, CallExpr, call); - if (ce->proc != nullptr && - ce->proc->kind == Ast_BasicDirective) { - ast_node(bd, BasicDirective, ce->proc); +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Array<Ast *> const &args, ProcInlining inlining, Type *type_hint) { + if (proc != nullptr && + proc->kind == Ast_BasicDirective) { + ast_node(bd, BasicDirective, proc); String name = bd->name; if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; - operand->expr = ce->proc; + operand->expr = proc; operand->type = t_invalid; - add_type_and_value(&c->checker->info, ce->proc, operand->mode, operand->type, operand->value); + add_type_and_value(&c->checker->info, proc, operand->mode, operand->type, operand->value); } else { GB_PANIC("Unhandled #%.*s", LIT(name)); } - if (ce->inlining != ProcInlining_none) { + if (inlining != ProcInlining_none) { error(call, "Inlining operators are not allowed on built-in procedures"); } } else { - check_expr_or_type(c, operand, ce->proc); + if (proc != nullptr) { + check_expr_or_type(c, operand, proc); + } else { + GB_ASSERT(operand->expr != nullptr); + } } - if (ce->args.count > 0) { + if (args.count > 0) { bool fail = false; - bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; + bool first_is_field_value = (args[0]->kind == Ast_FieldValue); + for_array(i, args) { + Ast *arg = args[i]; bool mix = false; if (first_is_field_value) { mix = arg->kind != Ast_FieldValue; @@ -7316,8 +7319,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t } if (operand->mode == Addressing_Invalid) { - for_array(i, ce->args) { - Ast *arg = ce->args[i]; + for_array(i, args) { + Ast *arg = args[i]; if (arg->kind == Ast_FieldValue) { arg = arg->FieldValue.value; } @@ -7352,12 +7355,12 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t defer (gb_string_free(str)); operand->mode = Addressing_Invalid; - isize arg_count = ce->args.count; + isize arg_count = args.count; switch (arg_count) { case 0: error(call, "Missing argument in conversion to '%s'", str); break; default: error(call, "Too many arguments in conversion to '%s'", str); break; case 1: { - Ast *arg = ce->args[0]; + Ast *arg = args[0]; if (arg->kind == Ast_FieldValue) { error(call, "'field = value' cannot be used in a type conversion"); arg = arg->FieldValue.value; @@ -7412,7 +7415,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t } } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call); + CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args); Type *result_type = data.result_type; gb_zero_item(operand); operand->expr = call; @@ -7460,15 +7463,17 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t } } - switch (ce->inlining) { + switch (inlining) { case ProcInlining_inline: { - Entity *e = entity_from_expr(ce->proc); - if (e != nullptr && e->kind == Entity_Procedure) { - DeclInfo *decl = e->decl_info; - if (decl->proc_lit) { - ast_node(pl, ProcLit, decl->proc_lit); - if (pl->inlining == ProcInlining_no_inline) { - error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); + if (proc != nullptr) { + Entity *e = entity_from_expr(proc); + if (e != nullptr && e->kind == Entity_Procedure) { + DeclInfo *decl = e->decl_info; + if (decl->proc_lit) { + ast_node(pl, ProcLit, decl->proc_lit); + if (pl->inlining == ProcInlining_no_inline) { + error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); + } } } } @@ -9097,6 +9102,117 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type node->viral_state_flags |= se->expr->viral_state_flags; case_end; + case_ast_node(se, SelectorCallExpr, node); + // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables + // COM APIs is a great example of where this kind of thing is extremely useful + // General idea: + // + // x->y(123) == x.y(x, 123) + // + // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes + // Just regenerating a new AST aids things + // + // TODO(bill): Is this a good hack or not? + // + // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I? + + Operand x = {}; + ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); + if (x.mode == Addressing_Invalid || x.type == t_invalid) { + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return kind; + } + if (!is_type_proc(x.type)) { + gbString type_str = type_to_string(x.type); + error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); + gb_string_free(type_str); + + o->mode = Addressing_Invalid; + o->type = t_invalid; + o->expr = node; + return Expr_Stmt; + } + + ast_node(ce, CallExpr, se->call); + + GB_ASSERT(x.expr->kind == Ast_SelectorExpr); + + Ast *first_arg = x.expr->SelectorExpr.expr; + GB_ASSERT(first_arg != nullptr); + + Type *pt = base_type(x.type); + GB_ASSERT(pt->kind == Type_Proc); + Type *first_type = nullptr; + String first_arg_name = {}; + if (pt->Proc.param_count > 0) { + Entity *f = pt->Proc.params->Tuple.variables[0]; + first_type = f->type; + first_arg_name = f->token.string; + } + if (first_arg_name.len == 0) { + first_arg_name = str_lit("_"); + } + + Operand y = {}; + y.mode = first_arg->tav.mode; + y.type = first_arg->tav.type; + y.value = first_arg->tav.value; + if (check_is_assignable_to(c, &y, first_type)) { + // Do nothing, it's valid + } else { + Operand z = y; + z.type = type_deref(y.type); + if (check_is_assignable_to(c, &z, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_Pointer}; + first_arg = ast_deref_expr(first_arg->file, first_arg, op); + } else if (y.mode == Addressing_Variable) { + Operand w = y; + w.type = alloc_type_pointer(y.type); + if (check_is_assignable_to(c, &w, first_type)) { + // NOTE(bill): AST GENERATION HACK! + Token op = {Token_And}; + first_arg = ast_unary_expr(first_arg->file, op, first_arg); + } + } + } + + if (ce->args.count > 0) { + bool fail = false; + bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + bool mix = false; + if (first_is_field_value) { + mix = arg->kind != Ast_FieldValue; + } else { + mix = arg->kind == Ast_FieldValue; + } + if (mix) { + fail = true; + break; + } + } + if (!fail && first_is_field_value) { + Token op = {Token_Eq}; + AstFile *f = first_arg->file; + first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op); + } + } + + + + auto modified_args = array_make<Ast *>(heap_allocator(), ce->args.count+1); + modified_args[0] = first_arg; + array_copy(&modified_args, ce->args, 1); + ce->args = modified_args; + se->modified_call = true; + + check_expr_with_type_hint(c, o, se->call, type_hint); + case_end; + case_ast_node(ise, ImplicitSelectorExpr, node); o->type = t_invalid; @@ -9487,7 +9603,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node, type_hint); + return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint); case_end; case_ast_node(de, DerefExpr, node); @@ -9758,7 +9874,7 @@ gbString write_expr_to_string(gbString str, Ast *node) { case_ast_node(se, SelectorExpr, node); str = write_expr_to_string(str, se->expr); - str = gb_string_append_rune(str, '.'); + str = string_append_token(str, se->token); str = write_expr_to_string(str, se->selector); case_end; @@ -9767,6 +9883,21 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = write_expr_to_string(str, se->selector); case_end; + case_ast_node(se, SelectorCallExpr, node); + str = write_expr_to_string(str, se->expr); + str = gb_string_appendc(str, "("); + ast_node(ce, CallExpr, se->call); + isize start = se->modified_call ? 1 : 0; + for (isize i = start; i < ce->args.count; i++) { + Ast *arg = ce->args[i]; + if (i > start) { + str = gb_string_appendc(str, ", "); + } + str = write_expr_to_string(str, arg); + } + str = gb_string_appendc(str, ")"); + case_end; + case_ast_node(ta, TypeAssertion, node); str = write_expr_to_string(str, ta->expr); str = gb_string_appendc(str, ".("); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 92155cc5e..6672ea204 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1272,6 +1272,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } return; + } else if (operand.expr->kind == Ast_SelectorCallExpr) { + AstSelectorCallExpr *se = &operand.expr->SelectorCallExpr; + ast_node(ce, CallExpr, se->call); + Type *t = type_of_expr(ce->proc); + if (is_type_proc(t)) { + if (t->Proc.require_results) { + gbString expr_str = expr_to_string(ce->proc); + error(node, "'%s' requires that its results must be handled", expr_str); + gb_string_free(expr_str); + } + } + return; } gbString expr_str = expr_to_string(operand.expr); error(node, "Expression is not used: '%s'", expr_str); diff --git a/src/ir.cpp b/src/ir.cpp index 2127de7b4..b8b2bd46e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7678,6 +7678,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_add_module_constant(proc->module, tv.type, tv.value); case_end; + case_ast_node(se, SelectorCallExpr, expr); + GB_ASSERT(se->modified_call); + TypeAndValue tav = type_and_value_of_expr(expr); + GB_ASSERT(tav.mode != Addressing_Invalid); + return ir_addr_load(proc, ir_build_addr(proc, se->call)); + case_end; + case_ast_node(te, TernaryExpr, expr); ir_emit_comment(proc, str_lit("TernaryExpr")); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c0d23019e..f3ec62c89 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9025,6 +9025,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { return lb_const_value(p->module, tv.type, tv.value); case_end; + case_ast_node(se, SelectorCallExpr, expr); + GB_ASSERT(se->modified_call); + TypeAndValue tav = type_and_value_of_expr(expr); + GB_ASSERT(tav.mode != Addressing_Invalid); + return lb_addr_load(p, lb_build_addr(p, se->call)); + case_end; + case_ast_node(te, TernaryExpr, expr); LLVMValueRef incoming_values[2] = {}; LLVMBasicBlockRef incoming_blocks[2] = {}; diff --git a/src/parser.cpp b/src/parser.cpp index 664aeb56d..8bde4a403 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -24,6 +24,11 @@ Token ast_token(Ast *node) { return ast_token(node->SelectorExpr.selector); } return node->SelectorExpr.token; + case Ast_SelectorCallExpr: + if (node->SelectorCallExpr.expr != nullptr) { + return ast_token(node->SelectorCallExpr.expr); + } + return node->SelectorCallExpr.token; case Ast_ImplicitSelectorExpr: if (node->ImplicitSelectorExpr.selector != nullptr) { return ast_token(node->ImplicitSelectorExpr.selector); @@ -174,6 +179,10 @@ Ast *clone_ast(Ast *node) { case Ast_ImplicitSelectorExpr: n->ImplicitSelectorExpr.selector = clone_ast(n->ImplicitSelectorExpr.selector); break; + case Ast_SelectorCallExpr: + n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr); + n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call); + break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); n->IndexExpr.index = clone_ast(n->IndexExpr.index); @@ -546,6 +555,14 @@ Ast *ast_implicit_selector_expr(AstFile *f, Token token, Ast *selector) { return result; } +Ast *ast_selector_call_expr(AstFile *f, Token token, Ast *expr, Ast *call) { + Ast *result = alloc_ast_node(f, Ast_SelectorCallExpr); + result->SelectorCallExpr.token = token; + result->SelectorCallExpr.expr = expr; + result->SelectorCallExpr.call = call; + return result; +} + Ast *ast_index_expr(AstFile *f, Ast *expr, Ast *index, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_IndexExpr); @@ -2417,8 +2434,11 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { case Token_ArrowRight: { Token token = advance_token(f); - syntax_error(token, "Selector expressions use '.' rather than '->'"); - operand = ast_selector_expr(f, token, operand, parse_ident(f)); + // syntax_error(token, "Selector expressions use '.' rather than '->'"); + + Ast *sel = ast_selector_expr(f, token, operand, parse_ident(f)); + Ast *call = parse_call_expr(f, sel); + operand = ast_selector_call_expr(f, token, sel, call); break; } diff --git a/src/parser.hpp b/src/parser.hpp index bfe290c8f..00e299ba5 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -271,6 +271,7 @@ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(ParenExpr, "parentheses expression", struct { Ast *expr; Token open, close; }) \ AST_KIND(SelectorExpr, "selector expression", struct { Token token; Ast *expr, *selector; }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ + AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \ AST_KIND(SliceExpr, "slice expression", struct { \ |