diff options
| -rw-r--r-- | doc/langref.md | 9 | ||||
| -rw-r--r-- | src/all_types.hpp | 22 | ||||
| -rw-r--r-- | src/analyze.cpp | 15 | ||||
| -rw-r--r-- | src/ast_render.cpp | 8 | ||||
| -rw-r--r-- | src/codegen.cpp | 11 | ||||
| -rw-r--r-- | src/ir.cpp | 124 | ||||
| -rw-r--r-- | src/ir_print.cpp | 7 | ||||
| -rw-r--r-- | src/parser.cpp | 51 | ||||
| -rw-r--r-- | src/zig_llvm.cpp | 5 | ||||
| -rw-r--r-- | src/zig_llvm.hpp | 2 | ||||
| -rw-r--r-- | test/cases/fn.zig | 7 |
11 files changed, 213 insertions, 48 deletions
diff --git a/doc/langref.md b/doc/langref.md index c53d3f8fc2..f579f8d50c 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -81,9 +81,9 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbo SwitchItem = Expression | (Expression "..." Expression) -WhileExpression(body) = option("inline") "while" "(" Expression option(";" Expression) ")" body +WhileExpression(body) = "while" "(" Expression option(";" Expression) ")" body -ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body +ForExpression(body) = "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression @@ -127,7 +127,9 @@ MultiplyOperator = "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression -SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = InlineExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) + +InlineExpression = option("inline") PrimaryExpression FieldAccessExpression = "." Symbol @@ -161,6 +163,7 @@ ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" ma ## Operator Precedence ``` +inline x x() x[] x.y !x -x -%x ~x *x &x ?x %x %%x ??x x{} diff --git a/src/all_types.hpp b/src/all_types.hpp index d349e5d2ad..839f1bc354 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -167,6 +167,7 @@ struct ConstErrValue { struct ConstBoundFnValue { FnTableEntry *fn; IrInstruction *first_arg; + bool is_inline; }; struct ConstArgTuple { @@ -192,6 +193,11 @@ enum RuntimeHintMaybe { RuntimeHintMaybeNonNull, }; +struct ConstFn { + FnTableEntry *fn_entry; + bool is_inline; +}; + struct ConstExprValue { TypeTableEntry *type; ConstValSpecial special; @@ -202,7 +208,7 @@ struct ConstExprValue { // populated if special == ConstValSpecialStatic BigNum x_bignum; bool x_bool; - FnTableEntry *x_fn; + ConstFn x_fn; ConstBoundFnValue x_bound_fn; TypeTableEntry *x_type; ConstExprValue *x_maybe; @@ -366,6 +372,7 @@ enum NodeType { NodeTypeTypeLiteral, NodeTypeVarLiteral, NodeTypeTryExpr, + NodeTypeInlineExpr, }; struct AstNodeRoot { @@ -789,6 +796,10 @@ struct AstNodeTypeLiteral { struct AstNodeVarLiteral { }; +struct AstNodeInlineExpr { + AstNode *body; +}; + struct AstNode { enum NodeType type; size_t line; @@ -847,6 +858,7 @@ struct AstNode { AstNodeErrorType error_type; AstNodeTypeLiteral type_literal; AstNodeVarLiteral var_literal; + AstNodeInlineExpr inline_expr; } data; }; @@ -1748,6 +1760,7 @@ enum IrInstructionId { IrInstructionIdDeclRef, IrInstructionIdPanic, IrInstructionIdEnumTagName, + IrInstructionIdSetFnRefInline, }; struct IrInstruction { @@ -1943,6 +1956,7 @@ struct IrInstructionCall { IrInstruction **args; bool is_comptime; LLVMValueRef tmp_ptr; + bool is_inline; }; struct IrInstructionConst { @@ -2493,6 +2507,12 @@ struct IrInstructionEnumTagName { IrInstruction *target; }; +struct IrInstructionSetFnRefInline { + IrInstruction base; + + IrInstruction *fn_ref; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index ac564362de..1f2d014393 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2129,6 +2129,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeTypeLiteral: case NodeTypeVarLiteral: case NodeTypeTryExpr: + case NodeTypeInlineExpr: zig_unreachable(); } } @@ -3251,7 +3252,8 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 31643936; case TypeTableEntryIdFn: - return hash_ptr(const_val->data.x_fn); + return hash_ptr(const_val->data.x_fn.fn_entry) + + (const_val->data.x_fn.is_inline ? 4133894920 : 3983484790); case TypeTableEntryIdTypeDecl: return hash_ptr(const_val->data.x_type); case TypeTableEntryIdNamespace: @@ -3697,7 +3699,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdPureError: return a->data.x_pure_err == b->data.x_pure_err; case TypeTableEntryIdFn: - return a->data.x_fn == b->data.x_fn; + return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry && + a->data.x_fn.is_inline == b->data.x_fn.is_inline; case TypeTableEntryIdBool: return a->data.x_bool == b->data.x_bool; case TypeTableEntryIdInt: @@ -3933,8 +3936,9 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) { zig_unreachable(); case TypeTableEntryIdFn: { - FnTableEntry *fn_entry = const_val->data.x_fn; - buf_appendf(buf, "%s", buf_ptr(&fn_entry->symbol_name)); + FnTableEntry *fn_entry = const_val->data.x_fn.fn_entry; + const char *inline_str = const_val->data.x_fn.is_inline ? "inline " : ""; + buf_appendf(buf, "%s%s", inline_str, buf_ptr(&fn_entry->symbol_name)); return; } case TypeTableEntryIdBlock: @@ -4011,7 +4015,8 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) { case TypeTableEntryIdBoundFn: { FnTableEntry *fn_entry = const_val->data.x_bound_fn.fn; - buf_appendf(buf, "(bound fn %s)", buf_ptr(&fn_entry->symbol_name)); + const char *inline_str = const_val->data.x_bound_fn.is_inline ? "inline " : ""; + buf_appendf(buf, "(%sbound fn %s)", inline_str, buf_ptr(&fn_entry->symbol_name)); return; } case TypeTableEntryIdStruct: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index dfa2f2632b..faebd8f7a3 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -240,6 +240,8 @@ static const char *node_type_str(NodeType node_type) { return "VarLiteral"; case NodeTypeTryExpr: return "TryExpr"; + case NodeTypeInlineExpr: + return "InlineExpr"; } zig_unreachable(); } @@ -921,6 +923,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_ungrouped(ar, node->data.unwrap_err_expr.op2); break; } + case NodeTypeInlineExpr: + { + fprintf(ar->f, "inline "); + render_node_grouped(ar, node->data.inline_expr.body); + break; + } case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeErrorValueDecl: diff --git a/src/codegen.cpp b/src/codegen.cpp index 938798f505..ea68e0c304 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -608,7 +608,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) { LLVMBuildLoad(g->builder, ptr_ptr, ""), LLVMBuildLoad(g->builder, len_ptr, ""), }; - ZigLLVMBuildCall(g->builder, fn_val, args, 2, panic_fn->type_entry->data.fn.calling_convention, ""); + ZigLLVMBuildCall(g->builder, fn_val, args, 2, panic_fn->type_entry->data.fn.calling_convention, false, ""); LLVMBuildUnreachable(g->builder); } @@ -1773,8 +1773,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } } + bool want_always_inline = (instruction->fn_entry != nullptr && + instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline; + LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, fn_type->data.fn.calling_convention, ""); + gen_param_values, (unsigned)gen_param_index, fn_type->data.fn.calling_convention, + want_always_inline, ""); for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; @@ -2749,6 +2753,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdSetGlobalLinkage: case IrInstructionIdDeclRef: case IrInstructionIdSwitchVar: + case IrInstructionIdSetFnRefInline: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -3183,7 +3188,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } } case TypeTableEntryIdFn: - return fn_llvm_value(g, const_val->data.x_fn); + return fn_llvm_value(g, const_val->data.x_fn.fn_entry); case TypeTableEntryIdPointer: { render_const_val_global(g, const_val, ""); diff --git a/src/ir.cpp b/src/ir.cpp index 81cdfe0242..53f324e372 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -545,6 +545,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) { return IrInstructionIdEnumTagName; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnRefInline *) { + return IrInstructionIdSetFnRefInline; +} + template<typename T> static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate<T>(1); @@ -701,7 +705,7 @@ static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode * IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node); const_instruction->base.value.type = fn_entry->type_entry; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_fn = fn_entry; + const_instruction->base.value.data.x_fn.fn_entry = fn_entry; return &const_instruction->base; } @@ -882,12 +886,13 @@ static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime) + bool is_comptime, bool is_inline) { IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node); call_instruction->fn_entry = fn_entry; call_instruction->fn_ref = fn_ref; call_instruction->is_comptime = is_comptime; + call_instruction->is_inline = is_inline; call_instruction->args = args; call_instruction->arg_count = arg_count; @@ -901,10 +906,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime) + bool is_comptime, bool is_inline) { IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope, - old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime); + old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, is_inline); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -2145,6 +2150,18 @@ static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } +static IrInstruction *ir_build_set_fn_ref_inline(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *fn_ref) +{ + IrInstructionSetFnRefInline *instruction = ir_build_instruction<IrInstructionSetFnRefInline>( + irb, scope, source_node); + instruction->fn_ref = fn_ref; + + ir_ref_instruction(fn_ref, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { return nullptr; } @@ -2809,6 +2826,14 @@ static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagNam } } +static IrInstruction *ir_instruction_setfnrefinline_get_dep(IrInstructionSetFnRefInline *instruction, size_t index) { + switch (index) { + case 0: return instruction->fn_ref; + default: return nullptr; + } +} + + static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -2999,6 +3024,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index); case IrInstructionIdEnumTagName: return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index); + case IrInstructionIdSetFnRefInline: + return ir_instruction_setfnrefinline_get_dep((IrInstructionSetFnRefInline *) instruction, index); } zig_unreachable(); } @@ -4297,7 +4324,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node return args[i]; } - return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false); + return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, false); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5490,6 +5517,19 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo return ir_build_fn_proto(irb, parent_scope, node, param_types, return_type); } +static IrInstruction *ir_gen_inline_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeInlineExpr); + + AstNode *body_node = node->data.inline_expr.body; + + IrInstruction *fn_ptr = ir_gen_node(irb, body_node, parent_scope); + if (fn_ptr == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_build_set_fn_ref_inline(irb, parent_scope, node, fn_ptr); +} + + static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) { @@ -5580,6 +5620,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); + case NodeTypeInlineExpr: + return ir_lval_wrap(irb, scope, ir_gen_inline_expr(irb, scope, node), lval); case NodeTypeFnDef: zig_panic("TODO IR gen NodeTypeFnDef"); case NodeTypeFnDecl: @@ -6435,7 +6477,7 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value return const_val->data.x_type; } -static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { +static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value, bool *is_inline) { if (fn_value == ira->codegen->invalid_instruction) return nullptr; @@ -6452,7 +6494,8 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { if (!const_val) return nullptr; - return const_val->data.x_fn; + *is_inline = const_val->data.x_fn.is_inline; + return const_val->data.x_fn.fn_entry; } static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { @@ -8245,7 +8288,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref, - IrInstruction *first_arg_ptr, bool inline_fn_call) + IrInstruction *first_arg_ptr, bool comptime_fn_call, bool inline_fn_call) { FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; @@ -8276,7 +8319,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } - if (inline_fn_call) { + if (comptime_fn_call) { // No special handling is needed for compile time evaluation of generic functions. if (!fn_entry || fn_entry->type_entry->data.fn.fn_type_id.is_extern) { ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); @@ -8472,8 +8515,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal inst_fn_type_id.return_type = return_type; if (type_requires_comptime(return_type)) { - // Throw out our work and call the function as if it were inline. - return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true); + // Throw out our work and call the function as if it were comptime. + return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, false); } } @@ -8498,7 +8541,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, - impl_fn, nullptr, impl_param_count, casted_args, false); + impl_fn, nullptr, impl_param_count, casted_args, false, inline_fn_call); TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; ir_add_alloca(ira, new_call_instruction, return_type); @@ -8557,7 +8600,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, - fn_entry, fn_ref, call_param_count, casted_args, false); + fn_entry, fn_ref, call_param_count, casted_args, false, inline_fn_call); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); @@ -8568,10 +8611,10 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction if (type_is_invalid(fn_ref->value.type)) return ira->codegen->builtin_types.entry_invalid; - bool is_inline = call_instruction->is_comptime || + bool is_comptime = call_instruction->is_comptime || ir_should_inline(ira->new_irb.exec, call_instruction->base.scope); - if (is_inline || instr_is_comptime(fn_ref)) { + if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value.type->id == TypeTableEntryIdMetaType) { TypeTableEntry *dest_type = ir_resolve_type(ira, fn_ref); if (type_is_invalid(dest_type)) @@ -8594,15 +8637,17 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction ir_link_new_instruction(cast_instruction, &call_instruction->base); return ir_finish_anal(ira, cast_instruction->value.type); } else if (fn_ref->value.type->id == TypeTableEntryIdFn) { - FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref); + bool is_inline; + FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref, &is_inline); return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - fn_ref, nullptr, is_inline); + fn_ref, nullptr, is_comptime, is_inline); } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) { assert(fn_ref->value.special == ConstValSpecialStatic); FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn; IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg; + bool is_inline = fn_ref->value.data.x_bound_fn.is_inline; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - nullptr, first_arg_ptr, is_inline); + nullptr, first_arg_ptr, is_comptime, is_inline); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); @@ -8612,7 +8657,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction if (fn_ref->value.type->id == TypeTableEntryIdFn) { return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type, - fn_ref, nullptr, false); + fn_ref, nullptr, false, false); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); @@ -9354,7 +9399,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source ConstExprValue *const_val = allocate<ConstExprValue>(1); const_val->special = ConstValSpecialStatic; const_val->type = fn_entry->type_entry; - const_val->data.x_fn = fn_entry; + const_val->data.x_fn.fn_entry = fn_entry; bool ptr_is_const = true; bool ptr_is_volatile = false; @@ -9891,7 +9936,7 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, safety_off_ptr = &block_scope->safety_off; safety_set_node_ptr = &block_scope->safety_set_node; } else if (target_type->id == TypeTableEntryIdFn) { - FnTableEntry *target_fn = target_val->data.x_fn; + FnTableEntry *target_fn = target_val->data.x_fn.fn_entry; assert(target_fn->def_scope); safety_off_ptr = &target_fn->def_scope->safety_off; safety_set_node_ptr = &target_fn->def_scope->safety_set_node; @@ -11164,6 +11209,38 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_set_fn_ref_inline(IrAnalyze *ira, + IrInstructionSetFnRefInline *instruction) +{ + IrInstruction *fn_ref = instruction->fn_ref->other; + if (type_is_invalid(fn_ref->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (fn_ref->value.type->id == TypeTableEntryIdFn) { + ConstExprValue *fn_ref_val = ir_resolve_const(ira, fn_ref, UndefBad); + if (!fn_ref_val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + *out_val = *fn_ref_val; + out_val->data.x_fn.is_inline = true; + return out_val->type; + } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) { + ConstExprValue *fn_ref_val = ir_resolve_const(ira, fn_ref, UndefBad); + if (!fn_ref_val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + *out_val = *fn_ref_val; + out_val->data.x_bound_fn.is_inline = true; + return out_val->type; + } else { + ir_add_error(ira, &instruction->base, + buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_ref->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { IrInstruction *type_value = instruction->type_value->other; TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); @@ -12710,6 +12787,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction); case IrInstructionIdEnumTagName: return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionEnumTagName *)instruction); + case IrInstructionIdSetFnRefInline: + return ir_analyze_instruction_set_fn_ref_inline(ira, (IrInstructionSetFnRefInline *)instruction); case IrInstructionIdMaybeWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: @@ -12892,6 +12971,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrName: case IrInstructionIdTypeName: case IrInstructionIdEnumTagName: + case IrInstructionIdSetFnRefInline: return false; case IrInstructionIdAsm: { @@ -12953,7 +13033,7 @@ FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableE } IrInstruction *call_instruction = ir_build_call(irb, scope, source_node, nullptr, fn_ref_instruction, - arg_count, args, false); + arg_count, args, false, false); ir_build_return(irb, scope, source_node, call_instruction); if (codegen->verbose) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index dbad27afb7..e16e795c4d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -870,6 +870,10 @@ static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) { fprintf(irp->f, ")"); } +static void ir_print_set_fn_ref_inline(IrPrint *irp, IrInstructionSetFnRefInline *instruction) { + fprintf(irp->f, "inline "); + ir_print_other_instruction(irp, instruction->fn_ref); +} static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); @@ -1155,6 +1159,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdPanic: ir_print_panic(irp, (IrInstructionPanic *)instruction); break; + case IrInstructionIdSetFnRefInline: + ir_print_set_fn_ref_inline(irp, (IrInstructionSetFnRefInline *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 18452aeb69..16bc23f741 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -927,7 +927,32 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde } /* -SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +InlineExpression = option("inline") PrimaryExpression +*/ +static AstNode *ast_parse_inline_expr(ParseContext *pc, size_t *token_index, bool mandatory) { + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdKeywordInline) { + *token_index += 1; + AstNode *primary_expr_node = ast_parse_primary_expr(pc, token_index, true); + if (primary_expr_node->type == NodeTypeWhileExpr) { + primary_expr_node->data.while_expr.is_inline = true; + return primary_expr_node; + } else if (primary_expr_node->type == NodeTypeForExpr) { + primary_expr_node->data.for_expr.is_inline = true; + return primary_expr_node; + } else { + AstNode *node = ast_create_node(pc, NodeTypeInlineExpr, token); + node->data.inline_expr.body = primary_expr_node; + return node; + } + } else { + return ast_parse_primary_expr(pc, token_index, mandatory); + } +} + +/* +SuffixOpExpression = InlineExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const)) @@ -935,8 +960,8 @@ FieldAccessExpression : token(Dot) token(Symbol) StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); - if (!primary_expr) + AstNode *inline_expr = ast_parse_inline_expr(pc, token_index, mandatory); + if (!inline_expr) return nullptr; while (true) { @@ -945,10 +970,10 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token); - node->data.fn_call_expr.fn_ref_expr = primary_expr; + node->data.fn_call_expr.fn_ref_expr = inline_expr; ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); - primary_expr = node; + inline_expr = node; } else if (first_token->id == TokenIdLBracket) { *token_index += 1; @@ -960,7 +985,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token); - node->data.slice_expr.array_ref_expr = primary_expr; + node->data.slice_expr.array_ref_expr = inline_expr; node->data.slice_expr.start = expr_node; node->data.slice_expr.end = ast_parse_expression(pc, token_index, false); @@ -972,15 +997,15 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, node->data.slice_expr.is_const = true; } - primary_expr = node; + inline_expr = node; } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) { *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token); - node->data.array_access_expr.array_ref_expr = primary_expr; + node->data.array_access_expr.array_ref_expr = inline_expr; node->data.array_access_expr.subscript = expr_node; - primary_expr = node; + inline_expr = node; } else { ast_invalid_token_error(pc, first_token); } @@ -990,12 +1015,12 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); - node->data.field_access_expr.struct_expr = primary_expr; + node->data.field_access_expr.struct_expr = inline_expr; node->data.field_access_expr.field_name = token_buf(name_token); - primary_expr = node; + inline_expr = node; } else { - return primary_expr; + return inline_expr; } } } @@ -2807,5 +2832,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeVarLiteral: // none break; + case NodeTypeInlineExpr: + visit_field(&node->data.inline_expr.body, visit, context); } } diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 1f9a82543f..e2641cd63e 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -176,10 +176,13 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, unsigned CC, const char *Name) + unsigned NumArgs, unsigned CC, bool always_inline, const char *Name) { CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name); call_inst->setCallingConv(CC); + if (always_inline) { + call_inst->addAttribute(AttributeSet::FunctionIndex, Attribute::AlwaysInline); + } return wrap(unwrap(B)->Insert(call_inst)); } diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index 39069c9cef..bec8f3e115 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -38,7 +38,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM const char *filename, LLVMCodeGenFileType file_type, char **error_message, bool is_debug); LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, - unsigned NumArgs, unsigned CC, const char *Name); + unsigned NumArgs, unsigned CC, bool always_inline, const char *Name); LLVMValueRef ZigLLVMConstInlineAsm(LLVMTypeRef Ty, const char *AsmString, const char *Constraints, bool HasSideEffects, bool IsAlignStack, bool is_x86); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 7dd1c1fc0a..5b3f995d42 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -87,3 +87,10 @@ fn fn1() -> u32 {5} fn fn2() -> u32 {6} fn fn3() -> u32 {7} fn fn4() -> u32 {8} + + +test "inline function call" { + assert((inline add(3, 9)) == 12); +} + +fn add(a: i32, b: i32) -> i32 { a + b } |
