diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f608f1aa1..1225aec0a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -85,6 +85,17 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * +Entity *entity_from_expr(Ast *expr) { + expr = unparen_expr(expr); + switch (expr->kind) { + case Ast_Ident: + return expr->Ident.entity; + case Ast_SelectorExpr: + return entity_from_expr(expr->SelectorExpr.selector); + } + return nullptr; +} + void error_operand_not_expression(Operand *o) { if (o->mode == Addressing_Type) { gbString err = expr_to_string(o->expr); @@ -2862,6 +2873,10 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) { ast_node(ce, CallExpr, call); + if (ce->inlining != ProcInlining_none) { + error(call, "Inlining operators are not allowed on built-in procedures"); + } + BuiltinProc *bp = &builtin_procs[id]; { char *err = nullptr; @@ -4807,6 +4822,9 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { } else { GB_PANIC("Unhandled #%.*s", LIT(name)); } + if (ce->inlining != ProcInlining_none) { + error(call, "Inlining operators are not allowed on built-in procedures"); + } } else { check_expr_or_type(c, operand, ce->proc); } @@ -4964,6 +4982,25 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { } } + switch (ce->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'"); + } + } + } + break; + } + + case ProcInlining_no_inline: + break; + } + operand->expr = call; return Expr_Expr; } @@ -6511,6 +6548,15 @@ gbString write_expr_to_string(gbString str, Ast *node) { case_end; case_ast_node(ce, CallExpr, node); + switch (ce->inlining) { + case ProcInlining_inline: + str = gb_string_appendc(str, "inline "); + break; + case ProcInlining_no_inline: + str = gb_string_appendc(str, "no_inline "); + break; + } + str = write_expr_to_string(str, ce->proc); str = gb_string_appendc(str, "("); diff --git a/src/ir.cpp b/src/ir.cpp index a46c375ae..ebeab9869 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -250,6 +250,7 @@ gbAllocator ir_allocator(void) { irValue * return_ptr; \ Array args; \ irValue * context_ptr; \ + ProcInlining inlining; \ }) \ IR_INSTR_KIND(StartupRuntime, i32) \ IR_INSTR_KIND(DebugDeclare, struct { \ @@ -1075,13 +1076,14 @@ irValue *ir_instr_select(irProcedure *p, irValue *cond, irValue *t, irValue *f) return v; } -irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, Array args, Type *result_type, irValue *context_ptr) { +irValue *ir_instr_call(irProcedure *p, irValue *value, irValue *return_ptr, Array args, Type *result_type, irValue *context_ptr, ProcInlining inlining) { irValue *v = ir_alloc_instr(p, irInstr_Call); v->Instr.Call.value = value; v->Instr.Call.return_ptr = return_ptr; v->Instr.Call.args = args; v->Instr.Call.type = result_type; v->Instr.Call.context_ptr = context_ptr; + v->Instr.Call.inlining = inlining; return v; } @@ -1684,7 +1686,7 @@ irValue *ir_find_or_generate_context_ptr(irProcedure *proc) { } -irValue *ir_emit_call(irProcedure *p, irValue *value, Array args) { +irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, ProcInlining inlining = ProcInlining_none) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; @@ -1726,16 +1728,20 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args) { } } + if (inlining == ProcInlining_none) { + inlining = p->inlining; + } + Type *abi_rt = pt->Proc.abi_compat_result_type; Type *rt = reduce_tuple_to_single_type(results); if (pt->Proc.return_by_pointer) { irValue *return_ptr = ir_add_local_generated(p, rt); GB_ASSERT(is_type_pointer(ir_type(return_ptr))); - ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr)); + ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining)); return ir_emit_load(p, return_ptr); } - irValue *result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr)); + irValue *result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); if (abi_rt != results) { result = ir_emit_transmute(p, result, rt); } @@ -5150,7 +5156,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { } } } - return ir_emit_call(proc, value, args); + return ir_emit_call(proc, value, args, ce->inlining); } isize arg_index = 0; @@ -5344,7 +5350,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { } auto call_args = array_slice(args, 0, final_count); - return ir_emit_call(proc, value, call_args); + return ir_emit_call(proc, value, call_args, ce->inlining); case_end; case_ast_node(se, SliceExpr, expr); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 8de79fd07..6d3084de9 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1478,6 +1478,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (proc_type->Proc.diverging) { ir_write_str_lit(f, " noreturn"); } + switch (call->inlining) { + case ProcInlining_inline: ir_write_str_lit(f, " alwaysinline"); break; + case ProcInlining_no_inline: ir_write_str_lit(f, " noinline"); break; + } ir_print_debug_location(f, m, value, instr->block->proc); break; diff --git a/src/parser.cpp b/src/parser.cpp index 8476d4332..a4bc16116 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1553,6 +1553,7 @@ Ast * parse_type (AstFile *f); Ast * parse_call_expr (AstFile *f, Ast *operand); Ast * parse_struct_field_list(AstFile *f, isize *name_count_); Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token); +Ast *parse_unary_expr(AstFile *f, bool lhs); Ast *convert_stmt_to_expr(AstFile *f, Ast *statement, String kind) { @@ -1673,9 +1674,10 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_no_inline: { Token token = advance_token(f); - Ast *expr = parse_operand(f, false); - if (expr->kind != Ast_ProcLit) { - syntax_error(expr, "%.*s must be followed by a procedure literal, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); + Ast *expr = parse_unary_expr(f, false); + Ast *e = unparen_expr(expr); + if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { + syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); return ast_bad_expr(f, token, f->curr_token); } ProcInlining pi = ProcInlining_none; @@ -1685,11 +1687,19 @@ Ast *parse_operand(AstFile *f, bool lhs) { pi = ProcInlining_no_inline; } if (pi != ProcInlining_none) { - if (expr->ProcLit.inlining != ProcInlining_none && - expr->ProcLit.inlining != pi) { - syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal"); + if (e->kind == Ast_ProcLit) { + if (expr->ProcLit.inlining != ProcInlining_none && + expr->ProcLit.inlining != pi) { + syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal"); + } + expr->ProcLit.inlining = pi; + } else if (e->kind == Ast_CallExpr) { + if (expr->CallExpr.inlining != ProcInlining_none && + expr->CallExpr.inlining != pi) { + syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal"); + } + expr->CallExpr.inlining = pi; } - expr->ProcLit.inlining = pi; } return expr; @@ -3657,7 +3667,9 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context <-` + case Token_context: // Also allows for `context =` + case Token_inline: + case Token_no_inline: case Token_Ident: case Token_Integer: case Token_Float: diff --git a/src/parser.hpp b/src/parser.hpp index 1d76045d4..4409f0e74 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -252,6 +252,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token open; \ Token close; \ Token ellipsis; \ + ProcInlining inlining; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \