From e9bd2d45d4bbaf7eff7e95bc3ef7a0123b66a103 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 5 Jan 2025 05:27:48 +0000 Subject: [PATCH] Sema: rewrite semantic analysis of function calls This rewrite improves some error messages, hugely simplifies the logic, and fixes several bugs. One of these bugs is technically a new rule which Andrew and I agreed on: if a parameter has a comptime-only type but is not declared `comptime`, then the corresponding call argument should not be *evaluated* at comptime; only resolved. Implementing this required changing how function types work a little, which in turn required allowing a new kind of function coercion for some generic use cases: function coercions are now allowed to implicitly *remove* `comptime` annotations from parameters with comptime-only types. This is okay because removing the annotation affects only the call site. Resolves: #22262 --- lib/compiler/aro_translate_c.zig | 1 + lib/std/math/log_int.zig | 1 + lib/std/os/windows.zig | 1 + lib/std/zig.zig | 6 +- lib/std/zig/AstGen.zig | 3 - lib/std/zig/Zir.zig | 11 +- src/Sema.zig | 1822 +++++++---------- src/Zcu.zig | 24 + src/translate_c.zig | 1 + test/behavior/eval.zig | 2 +- test/behavior/generics.zig | 2 +- test/behavior/struct.zig | 2 +- test/behavior/typename.zig | 2 +- ...only_type_is_not_evaluated_at_comptime.zig | 36 + .../compile_errors/bad_usage_of_call.zig | 6 +- .../comptime_call_of_function_pointer.zig | 5 +- .../condition_comptime_reason_explained.zig | 6 +- .../compile_errors/dereference_anyopaque.zig | 53 +- .../explain_why_fn_is_called_at_comptime.zig | 3 +- ...n_why_generic_fn_is_called_at_comptime.zig | 3 +- ..._instance_with_non-constant_expression.zig | 7 +- ...antiation_inherits_parent_branch_quota.zig | 3 +- ...ailure_in_generic_function_return_type.zig | 1 + ...generic_method_call_with_invalid_param.zig | 7 +- ...nitializer_must_be_constant_expression.zig | 2 - ...e_call_runtime_value_to_comptime_param.zig | 3 +- .../invalid_extern_function_call.zig | 6 +- .../invalid_pointer_for_var_type.zig | 2 - ...d_generic_function_param_type_mismatch.zig | 4 +- ..._call_of_inline_fn_with_comptime_param.zig | 4 +- ...ion_in_struct_literal_outside_function.zig | 2 - ...on_comptime_param_in_comptime_function.zig | 5 +- .../compile_errors/recursive_inline_fn.zig | 6 +- .../runtime_operation_in_comptime_scope.zig | 5 +- test/compile_errors.zig | 15 +- .../standalone/simple/std_enums_big_enums.zig | 1 + 36 files changed, 842 insertions(+), 1221 deletions(-) create mode 100644 test/cases/compile_errors/arg_to_non_comptime_param_with_comptime_only_type_is_not_evaluated_at_comptime.zig diff --git a/lib/compiler/aro_translate_c.zig b/lib/compiler/aro_translate_c.zig index 6be33a196f..ffb133dad5 100644 --- a/lib/compiler/aro_translate_c.zig +++ b/lib/compiler/aro_translate_c.zig @@ -168,6 +168,7 @@ pub fn translate( context.pattern_list.deinit(gpa); } + @setEvalBranchQuota(2000); inline for (@typeInfo(std.zig.c_builtins).@"struct".decls) |decl| { const builtin_fn = try ZigTag.pub_var_simple.create(arena, .{ .name = decl.name, diff --git a/lib/std/math/log_int.zig b/lib/std/math/log_int.zig index 376aa69a4d..96227639d0 100644 --- a/lib/std/math/log_int.zig +++ b/lib/std/math/log_int.zig @@ -61,6 +61,7 @@ pub fn log_int(comptime T: type, base: T, x: T) Log2Int(T) { } test "log_int" { + @setEvalBranchQuota(2000); // Test all unsigned integers with 2, 3, ..., 64 bits. // We cannot test 0 or 1 bits since base must be > 1. inline for (2..64 + 1) |bits| { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index c56e27d8ec..ceed0618d1 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1468,6 +1468,7 @@ fn mountmgrIsVolumeName(name: []const u16) bool { } test mountmgrIsVolumeName { + @setEvalBranchQuota(2000); const L = std.unicode.utf8ToUtf16LeStringLiteral; try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}"))); try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}"))); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 7af8bacb34..6fec585326 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -749,6 +749,8 @@ pub const SimpleComptimeReason = enum(u32) { array_mul_factor, slice_cat_operand, comptime_call_target, + inline_call_target, + generic_call_target, wasm_memory_index, work_group_dim_index, @@ -791,7 +793,6 @@ pub const SimpleComptimeReason = enum(u32) { struct_field_default_value, enum_field_tag_value, slice_single_item_ptr_bounds, - comptime_param_arg, stored_to_comptime_field, stored_to_comptime_var, casted_to_comptime_enum, @@ -828,6 +829,8 @@ pub const SimpleComptimeReason = enum(u32) { .array_mul_factor => "array multiplication factor must be comptime-known", .slice_cat_operand => "slice being concatenated must be comptime-known", .comptime_call_target => "function being called at comptime must be comptime-known", + .inline_call_target => "function being called inline must be comptime-known", + .generic_call_target => "generic function being called must be comptime-known", .wasm_memory_index => "wasm memory index must be comptime-known", .work_group_dim_index => "work group dimension index must be comptime-known", @@ -865,7 +868,6 @@ pub const SimpleComptimeReason = enum(u32) { .struct_field_default_value => "struct field default value must be comptime-known", .enum_field_tag_value => "enum field tag value must be comptime-known", .slice_single_item_ptr_bounds => "slice of single-item pointer must have comptime-known bounds", - .comptime_param_arg => "argument to comptime parameter must be comptime-known", .stored_to_comptime_field => "value stored to a comptime field must be comptime-known", .stored_to_comptime_var => "value stored to a comptime variable must be comptime-known", .casted_to_comptime_enum => "value casted to enum with 'comptime_int' tag type must be comptime-known", diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index d318a9f3e5..1355c7cc77 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -10209,9 +10209,6 @@ fn callExpr( const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr); const modifier: std.builtin.CallModifier = blk: { - if (gz.is_comptime) { - break :blk .compile_time; - } if (call.async_token != null) { break :blk .async_kw; } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 21f59f8e83..3b27f1cf5b 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -4735,6 +4735,7 @@ pub const FnInfo = struct { body: []const Inst.Index, ret_ty_ref: Zir.Inst.Ref, total_params_len: u32, + inferred_error_set: bool, }; pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index { @@ -4774,8 +4775,9 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { body: []const Inst.Index, ret_ty_ref: Inst.Ref, ret_ty_body: []const Inst.Index, + ies: bool, } = switch (tags[@intFromEnum(fn_inst)]) { - .func, .func_inferred => blk: { + .func, .func_inferred => |tag| blk: { const inst_data = datas[@intFromEnum(fn_inst)].pl_node; const extra = zir.extraData(Inst.Func, inst_data.payload_index); @@ -4805,6 +4807,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .ret_ty_ref = ret_ty_ref, .ret_ty_body = ret_ty_body, .body = body, + .ies = tag == .func_inferred, }; }, .func_fancy => blk: { @@ -4812,7 +4815,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index); var extra_index: usize = extra.end; - var ret_ty_ref: Inst.Ref = .void_type; + var ret_ty_ref: Inst.Ref = .none; var ret_ty_body: []const Inst.Index = &.{}; if (extra.data.bits.has_cc_body) { @@ -4828,6 +4831,8 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { } else if (extra.data.bits.has_ret_ty_ref) { ret_ty_ref = @enumFromInt(zir.extra[extra_index]); extra_index += 1; + } else { + ret_ty_ref = .void_type; } extra_index += @intFromBool(extra.data.bits.has_any_noalias); @@ -4839,6 +4844,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .ret_ty_ref = ret_ty_ref, .ret_ty_body = ret_ty_body, .body = body, + .ies = extra.data.bits.is_inferred_error, }; }, else => unreachable, @@ -4860,6 +4866,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { .ret_ty_ref = info.ret_ty_ref, .body = info.body, .total_params_len = total_params_len, + .inferred_error_set = info.ies, }; } diff --git a/src/Sema.zig b/src/Sema.zig index 081e51af36..c27fd7321e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -46,21 +46,6 @@ branch_count: u32 = 0, /// Populated when returning `error.ComptimeBreak`. Used to communicate the /// break instruction up the stack to find the corresponding Block. comptime_break_inst: Zir.Inst.Index = undefined, -/// When doing a generic function instantiation, this array collects a value -/// for each parameter of the generic owner. `none` for non-comptime parameters. -/// This is a separate array from `block.params` so that it can be passed -/// directly to `comptime_args` when calling `InternPool.getFuncInstance`. -/// This memory is allocated by a parent `Sema` in the temporary arena, and is -/// used only to add a `func_instance` into the `InternPool`. -comptime_args: []InternPool.Index = &.{}, -/// Used to communicate from a generic function instantiation to the logic that -/// creates a generic function instantiation value in `funcCommon`. -generic_owner: InternPool.Index = .none, -/// When `generic_owner` is not none, this contains the generic function -/// instantiation callsite so that compile errors on the parameter types of the -/// instantiation can point back to the instantiation site in addition to the -/// declaration site. -generic_call_src: LazySrcLoc = LazySrcLoc.unneeded, /// These are lazily created runtime blocks from block_inline instructions. /// They are created when an break_inline passes through a runtime condition, because /// Sema must convert comptime control flow to runtime control flow, which means @@ -862,12 +847,29 @@ const ComptimeReason = union(enum) { union_init, struct_init, tuple_init, - param_ty_arg, - ret_ty_call, - ret_ty_generic_call, }, }, + /// Like `comptime_only`, but for a parameter type. + /// Includes a "parameter type declared here" note. + comptime_only_param_ty: struct { + ty: Type, + param_ty_src: LazySrcLoc, + }, + + /// Like `comptime_only`, but for a return type. + /// Includes a "return type declared here" note. + comptime_only_ret_ty: struct { + ty: Type, + is_generic_inst: bool, + ret_ty_src: LazySrcLoc, + }, + + /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`. + comptime_param: struct { + comptime_src: LazySrcLoc, + }, + fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void { switch (reason) { .simple => |simple| { @@ -878,13 +880,25 @@ const ComptimeReason = union(enum) { .union_init => .{ "initializer of comptime-only union", "must be comptime-known" }, .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" }, .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" }, - .param_ty_arg => .{ "argument to parameter with comptime-only type", "must be comptime-known" }, - .ret_ty_call => .{ "function with comptime-only return type", "is evaluated at comptime" }, - .ret_ty_generic_call => .{ "generic function instantiated with comptime-only return type", "is evaluated at comptime" }, }; try sema.errNote(src, err_msg, "{s} '{}' {s}", .{ pre, co.ty.fmt(sema.pt), post }); try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); }, + .comptime_only_param_ty => |co| { + try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{}' must be comptime-known", .{co.ty.fmt(sema.pt)}); + try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{}); + try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); + }, + .comptime_only_ret_ty => |co| { + const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with"; + try sema.errNote(src, err_msg, "call to {s} comptime-only return type '{}' is evaluated at comptime", .{ function_with, co.ty.fmt(sema.pt) }); + try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{}); + try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); + }, + .comptime_param => |cp| { + try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{}); + try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{}); + }, } } }; @@ -7423,8 +7437,9 @@ const CallArgsInfo = union(enum) { /// Analyzes the arg at `arg_index` and coerces it to `param_ty`. /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter. - /// `func_ty_info` may be the type before instantiation, even if a generic - /// instantiation has been partially completed. + /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress. + /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or + /// the parameter being marked `comptime`. fn analyzeArg( cai: CallArgsInfo, sema: *Sema, @@ -7433,6 +7448,7 @@ const CallArgsInfo = union(enum) { maybe_param_ty: ?Type, func_ty_info: InternPool.Key.FuncType, func_inst: Air.Inst.Ref, + maybe_func_src_inst: ?InternPool.TrackedInst.Index, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; @@ -7460,11 +7476,22 @@ const CallArgsInfo = union(enum) { const parent_comptime = block.comptime_reason; defer block.comptime_reason = parent_comptime; // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx` - if (arg_index < @min(param_count, 32) and func_ty_info.paramIsComptime(@intCast(arg_index))) { - block.comptime_reason = .{ .reason = .{ - .src = cai.argSrc(block, arg_index), - .r = .{ .simple = .comptime_param_arg }, - } }; + if (std.math.cast(u5, arg_index)) |i| { + if (i < param_count and func_ty_info.paramIsComptime(i)) { + block.comptime_reason = .{ + .reason = .{ + .src = cai.argSrc(block, arg_index), + .r = .{ + .comptime_param = .{ + .comptime_src = if (maybe_func_src_inst) |src_inst| .{ + .base_node_inst = src_inst, + .offset = .{ .func_decl_param_comptime = @intCast(arg_index) }, + } else unreachable, // should be non-null because the function is generic + }, + }, + }, + }; + } } // Give the arg its result type const provide_param_ty = if (maybe_param_ty) |t| t else Type.generic_poison; @@ -7472,6 +7499,10 @@ const CallArgsInfo = union(enum) { // Resolve the arg! const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst); + if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) { + return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null); + } + if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) { // This terminates resolution of arguments. The caller should // propagate this. @@ -7507,104 +7538,10 @@ const CallArgsInfo = union(enum) { } }; -/// While performing an inline call, we need to switch between two Sema states a few times: the -/// state for the caller (with the callee's `code`, `fn_ret_ty`, etc), and the state for the callee. -/// These cannot be two separate Sema instances as they must share AIR. -/// Therefore, this struct acts as a helper to switch between the two. -/// This switching is required during argument evaluation, where function argument analysis must be -/// interleaved with resolving generic parameter types. -const InlineCallSema = struct { - sema: *Sema, - cur: enum { - caller, - callee, - }, - - other_code: Zir, - other_func_index: InternPool.Index, - other_fn_ret_ty: Type, - other_fn_ret_ty_ies: ?*InferredErrorSet, - other_inst_map: InstMap, - other_error_return_trace_index_on_fn_entry: Air.Inst.Ref, - other_generic_owner: InternPool.Index, - other_generic_call_src: LazySrcLoc, - - /// Sema should currently be set up for the caller (i.e. unchanged yet). This init will not - /// change that. The other parameters contain data for the callee Sema. The other modified - /// Sema fields are all initialized to default values for the callee. - /// Must call deinit on the result. - fn init( - sema: *Sema, - callee_code: Zir, - callee_func_index: InternPool.Index, - callee_error_return_trace_index_on_fn_entry: Air.Inst.Ref, - ) InlineCallSema { - return .{ - .sema = sema, - .cur = .caller, - .other_code = callee_code, - .other_func_index = callee_func_index, - .other_fn_ret_ty = Type.void, - .other_fn_ret_ty_ies = null, - .other_inst_map = .{}, - .other_error_return_trace_index_on_fn_entry = callee_error_return_trace_index_on_fn_entry, - .other_generic_owner = .none, - .other_generic_call_src = LazySrcLoc.unneeded, - }; - } - - /// Switch back to the caller Sema if necessary and free all temporary state of the callee Sema. - fn deinit(ics: *InlineCallSema) void { - switch (ics.cur) { - .caller => {}, - .callee => ics.swap(), - } - // Callee Sema owns the inst_map memory - ics.other_inst_map.deinit(ics.sema.gpa); - ics.* = undefined; - } - - /// Returns a Sema instance suitable for usage from the caller context. - fn caller(ics: *InlineCallSema) *Sema { - switch (ics.cur) { - .caller => {}, - .callee => ics.swap(), - } - return ics.sema; - } - - /// Returns a Sema instance suitable for usage from the callee context. - fn callee(ics: *InlineCallSema) *Sema { - switch (ics.cur) { - .caller => ics.swap(), - .callee => {}, - } - return ics.sema; - } - - /// Internal use only. Swaps to the other Sema state. - fn swap(ics: *InlineCallSema) void { - ics.cur = switch (ics.cur) { - .caller => .callee, - .callee => .caller, - }; - // zig fmt: off - std.mem.swap(Zir, &ics.sema.code, &ics.other_code); - std.mem.swap(InternPool.Index, &ics.sema.func_index, &ics.other_func_index); - std.mem.swap(Type, &ics.sema.fn_ret_ty, &ics.other_fn_ret_ty); - std.mem.swap(?*InferredErrorSet, &ics.sema.fn_ret_ty_ies, &ics.other_fn_ret_ty_ies); - std.mem.swap(InstMap, &ics.sema.inst_map, &ics.other_inst_map); - std.mem.swap(InternPool.Index, &ics.sema.generic_owner, &ics.other_generic_owner); - std.mem.swap(LazySrcLoc, &ics.sema.generic_call_src, &ics.other_generic_call_src); - std.mem.swap(Air.Inst.Ref, &ics.sema.error_return_trace_index_on_fn_entry, &ics.other_error_return_trace_index_on_fn_entry); - // zig fmt: on - } -}; - fn analyzeCall( sema: *Sema, block: *Block, - func: Air.Inst.Ref, + callee: Air.Inst.Ref, func_ty: Type, func_src: LazySrcLoc, call_src: LazySrcLoc, @@ -7616,543 +7553,675 @@ fn analyzeCall( ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; + const gpa = zcu.gpa; const ip = &zcu.intern_pool; + const arena = sema.arena; - const callee_ty = sema.typeOf(func); - const func_ty_info = zcu.typeToFunc(func_ty).?; - const cc = func_ty_info.cc; - if (try sema.resolveValue(func)) |func_val| - if (func_val.isUndef(zcu)) - return sema.failWithUseOfUndef(block, call_src); - if (!callConvIsCallable(cc)) { - const maybe_func_inst = try sema.funcDeclSrcInst(func); - const msg = msg: { - const msg = try sema.errMsg( - func_src, - "unable to call function with calling convention '{s}'", - .{@tagName(cc)}, - ); - errdefer msg.destroy(sema.gpa); - - if (maybe_func_inst) |func_inst| try sema.errNote(.{ - .base_node_inst = func_inst, - .offset = LazySrcLoc.Offset.nodeOffset(0), - }, msg, "function declared here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + if (modifier == .async_kw) { + return sema.failWithUseOfAsync(block, call_src); } - const call_tag: Air.Inst.Tag = switch (modifier) { - .auto, - .always_inline, - .compile_time, - .no_async, - => Air.Inst.Tag.call, - - .never_tail => Air.Inst.Tag.call_never_tail, - .never_inline => Air.Inst.Tag.call_never_inline, - .always_tail => Air.Inst.Tag.call_always_tail, - - .async_kw => return sema.failWithUseOfAsync(block, call_src), - }; - - if (modifier == .never_inline and func_ty_info.cc == .@"inline") { - return sema.fail(block, call_src, "'never_inline' call of inline function", .{}); - } - if (modifier == .always_inline and func_ty_info.is_noinline) { - return sema.fail(block, call_src, "'always_inline' call of noinline function", .{}); - } - - const gpa = sema.gpa; - - const func_ret_ty_src: LazySrcLoc = if (try sema.funcDeclSrcInst(func)) |fn_decl_inst| .{ + const maybe_func_inst = try sema.funcDeclSrcInst(callee); + const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ .base_node_inst = fn_decl_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 }, } else func_src; - // If this is not `null`, the call is comptime. - var comptime_call_reason: ?BlockComptimeReason = cr: { - if (block.comptime_reason) |r| break :cr r; - if (modifier == .compile_time) break :cr .{ .reason = .{ - .src = call_src, - .r = .{ .simple = .comptime_call_modifier }, - } }; - break :cr null; - }; + const func_ty_info = zcu.typeToFunc(func_ty).?; + if (!callConvIsCallable(func_ty_info.cc)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg( + func_src, + "unable to call function with calling convention '{s}'", + .{@tagName(func_ty_info.cc)}, + ); + errdefer msg.destroy(gpa); + if (maybe_func_inst) |func_inst| try sema.errNote(.{ + .base_node_inst = func_inst, + .offset = .nodeOffset(0), + }, msg, "function declared here", .{}); + break :msg msg; + }); + } - const is_generic_call = func_ty_info.is_generic; - var is_inline_call = comptime_call_reason != null or modifier == .always_inline or func_ty_info.cc == .@"inline"; - if (!is_inline_call) { - if (try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { - is_inline_call = true; - comptime_call_reason = .{ .reason = .{ - .src = func_ret_ty_src, - .r = .{ .comptime_only = .{ - .ty = .fromInterned(func_ty_info.return_type), - .msg = .ret_ty_call, - } }, + // We need this value in a few code paths. + const callee_val = try sema.resolveDefinedValue(block, call_src, callee); + // If the callee is a comptime-known *non-extern* function, `func_val` is populated. + // If it is a comptime-known extern function, `func_is_extern` is set instead. + // If it is not comptime-known, neither is set. + const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) { + .func => .{ c, false }, + .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) { + .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false }, + .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) { + .func => .{ pointee, false }, + .@"extern" => .{ null, true }, + else => unreachable, + }, + }, + .@"extern" => .{ null, true }, + else => unreachable, + } else .{ null, false }; + + if (func_ty_info.is_generic and func_val == null) { + return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target }); + } + + const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline; + + // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only, + // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd. + const old_block_comptime_reason = block.comptime_reason; + defer block.comptime_reason = old_block_comptime_reason; + if (!block.isComptime()) { + if (modifier == .compile_time) { + block.comptime_reason = .{ .reason = .{ + .src = call_src, + .r = .{ .simple = .comptime_call_modifier }, } }; + } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { + block.comptime_reason = .{ + .reason = .{ + .src = call_src, + .r = .{ + .comptime_only_ret_ty = .{ + .ty = .fromInterned(func_ty_info.return_type), + .is_generic_inst = false, + .ret_ty_src = func_ret_ty_src, + }, + }, + }, + }; } } - if (sema.func_is_naked and !is_inline_call) { - const msg = msg: { - const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); - errdefer msg.destroy(sema.gpa); + // These values are undefined if `func_val == null`. + const fn_nav: InternPool.Nav, const fn_zir: Zir, const fn_tracked_inst: InternPool.TrackedInst.Index, const fn_zir_inst: Zir.Inst.Index, const fn_zir_info: Zir.FnInfo = if (func_val) |f| b: { + const info = ip.indexToKey(f.toIntern()).func; + const nav = ip.getNav(info.owner_nav); + const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; + const file = zcu.fileByIndex(resolved_func_inst.file); + assert(file.zir_loaded); + const zir_info = file.zir.getFnInfo(resolved_func_inst.inst); + break :b .{ nav, file.zir, info.zir_body_inst, resolved_func_inst.inst, zir_info }; + } else .{ undefined, undefined, undefined, undefined, undefined }; + // This is the `inst_map` used when evaluating generic parameters and return types. + var generic_inst_map: InstMap = .{}; + defer generic_inst_map.deinit(gpa); + if (func_ty_info.is_generic) { + try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); + } + + // This exists so that `generic_block` below can include a "called from here" note back to this + // call site when analyzing generic parameter/return types. + var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{ + .call_block = block, + .call_src = call_src, + .has_comptime_args = false, // unused by error reporting + .func = .none, // unused by error reporting + .comptime_result = .none, // unused by error reporting + .merges = undefined, // unused because we'll never `return` + } else undefined; + + // This is the block in which we evaluate generic function components: that is, generic parameter + // types and the generic return type. This must not be used if the function is not generic. + // `comptime_reason` is set as needed. + var generic_block: Block = if (func_ty_info.is_generic) .{ + .parent = null, + .sema = sema, + .namespace = fn_nav.analysis.?.namespace, + .instructions = .{}, + .inlining = &generic_inlining, + .src_base_inst = fn_nav.analysis.?.zir_index, + .type_name_ctx = fn_nav.fqn, + } else undefined; + defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa); + + if (func_ty_info.is_generic) { + // We certainly depend on the generic owner's signature! + try sema.declareDependency(.{ .src_hash = fn_tracked_inst }); + } + + const args = try arena.alloc(Air.Inst.Ref, args_info.count()); + for (args, 0..) |*arg, arg_idx| { + const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: { + const raw = func_ty_info.param_types.get(ip)[arg_idx]; + if (raw != .generic_poison_type) break :ty .fromInterned(raw); + + // We must discover the generic parameter type. + assert(func_ty_info.is_generic); + const param_inst_idx = fn_zir_info.param_body[arg_idx]; + const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx)); + switch (param_inst.tag) { + .param_anytype, .param_anytype_comptime => break :ty .generic_poison, + .param, .param_comptime => {}, + else => unreachable, + } + + // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because + // the function definition may be in a different file to the call site. + const old_code = sema.code; + const old_inst_map = sema.inst_map; + defer { + generic_inst_map = sema.inst_map; + sema.code = old_code; + sema.inst_map = old_inst_map; + } + sema.code = fn_zir; + sema.inst_map = generic_inst_map; + + const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index); + const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok); + const body = sema.code.bodySlice(extra.end, extra.data.body_len); + + generic_block.comptime_reason = .{ .reason = .{ + .r = .{ .simple = .function_parameters }, + .src = param_src, + } }; + + const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx); + const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref); + + if (!param_ty.isValidParamType(zcu)) { + const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; + return sema.fail(block, param_src, "parameter of {s}type '{}' not allowed", .{ + opaque_str, param_ty.fmt(pt), + }); + } + + break :ty param_ty; + } else null; // vararg + + arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst); + const arg_ty = sema.typeOf(arg.*); + if (arg_ty.zigTypeTag(zcu) == .noreturn) { + return arg.*; // terminate analysis here + } + + if (func_ty_info.is_generic) { + // We need to put the argument into `generic_inst_map` so that other parameters can refer to it. + const param_inst_idx = fn_zir_info.param_body[arg_idx]; + const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false; + const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt); + if (param_is_comptime) { + if (!try sema.isComptimeKnown(arg.*)) { + assert(!declared_comptime); // `analyzeArg` handles this + const arg_src = args_info.argSrc(block, arg_idx); + const param_ty_src: LazySrcLoc = .{ + .base_node_inst = maybe_func_inst.?, // the function is generic + .offset = .{ .func_decl_param_ty = @intCast(arg_idx) }, + }; + return sema.failWithNeededComptime( + block, + arg_src, + .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } }, + ); + } + generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*); + } else { + // We need a dummy instruction with this type. It doesn't actually need to be in any block, + // since it will never be referenced at runtime! + const dummy: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); + try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } }); + generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef()); + } + } + } + + // This return type is never generic poison. + // However, if it has an IES, it is always associated with the callee value. + // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic + // calls (where it should be the IES of the instantiation). However, it's how we print this + // in error messages. + const resolved_ret_ty: Type = ret_ty: { + if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type); + + const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: { + break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type); + } else func_ty_info.return_type; + + if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type); + + // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`. + + assert(func_ty_info.is_generic); + + const old_code = sema.code; + const old_inst_map = sema.inst_map; + defer { + generic_inst_map = sema.inst_map; + sema.code = old_code; + sema.inst_map = old_inst_map; + } + sema.code = fn_zir; + sema.inst_map = generic_inst_map; + + generic_block.comptime_reason = .{ .reason = .{ + .r = .{ .simple = .function_ret_ty }, + .src = func_ret_ty_src, + } }; + + const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: { + assert(fn_zir_info.ret_ty_body.len == 0); + break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref); + } else bare: { + assert(fn_zir_info.ret_ty_body.len != 0); + const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst); + break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref); + }; + assert(bare_ty.toIntern() != .generic_poison_type); + + const full_ty = if (fn_zir_info.inferred_error_set) full: { + try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src); + const set = ip.errorUnionSet(func_ty_info.return_type); + break :full try pt.errorUnionType(.fromInterned(set), bare_ty); + } else bare_ty; + + if (!full_ty.isValidReturnType(zcu)) { + const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; + return sema.fail(block, func_ret_ty_src, "{s}return type '{}' not allowed", .{ + opaque_str, full_ty.fmt(pt), + }); + } + + break :ret_ty full_ty; + }; + + // If we've discovered after evaluating arguments that a generic function instantiation is + // comptime-only, then we can mark the block as comptime *now*. + if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) { + block.comptime_reason = .{ + .reason = .{ + .src = call_src, + .r = .{ + .comptime_only_ret_ty = .{ + .ty = resolved_ret_ty, + .is_generic_inst = true, + .ret_ty_src = func_ret_ty_src, + }, + }, + }, + }; + } + + if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); + + const is_inline_call = block.isComptime() or inline_requested; + + if (!is_inline_call) { + if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); + errdefer msg.destroy(gpa); switch (operation) { .call, .@"@call", .@"@panic", .@"error return" => {}, .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}), } break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - - if (!is_inline_call and is_generic_call) { - var comptime_ret_ty: Type = undefined; - if (sema.instantiateGenericCall( - block, - func, - func_src, - call_src, - ensure_result_used, - args_info, - call_tag, - call_dbg_node, - &comptime_ret_ty, - )) |some| { - return some; - } else |err| switch (err) { - error.GenericPoison => { - is_inline_call = true; - }, - error.ComptimeReturn => { - is_inline_call = true; - comptime_call_reason = .{ .reason = .{ - .src = func_ret_ty_src, - .r = .{ - .comptime_only = .{ - .ty = comptime_ret_ty, - .msg = .ret_ty_generic_call, - }, - }, - } }; - }, - else => |e| return e, - } - } - - const is_comptime_call = comptime_call_reason != null; - // `comptime_call_reason` shouldn't be mutated again - defer assert(is_comptime_call == (comptime_call_reason != null)); - - if (is_comptime_call and modifier == .never_inline) { - return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{}); - } - - const result: Air.Inst.Ref = if (is_inline_call) res: { - const old_comptime_reason = block.comptime_reason; - block.comptime_reason = comptime_call_reason; - defer block.comptime_reason = old_comptime_reason; - - const func_val = try sema.resolveConstDefinedValue(block, func_src, func, .{ .simple = .comptime_call_target }); - const module_fn_index = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) { - .@"extern" => return sema.fail(block, call_src, "{s} call of extern function", .{ - @as([]const u8, if (is_comptime_call) "comptime" else "inline"), - }), - .func => func_val.toIntern(), - .ptr => |ptr| blk: { - switch (ptr.base_addr) { - .nav => |nav_index| if (ptr.byte_offset == 0) { - try sema.ensureNavResolved(call_src, nav_index, .fully); - const nav = ip.getNav(nav_index); - if (nav.getExtern(ip) != null) - return sema.fail(block, call_src, "{s} call of extern function pointer", .{ - if (is_comptime_call) "comptime" else "inline", - }); - break :blk nav.status.fully_resolved.val; - }, - else => {}, - } - assert(callee_ty.isPtrAtRuntime(zcu)); - return sema.fail(block, call_src, "{s} call of function pointer", .{ - if (is_comptime_call) "comptime" else "inline", - }); - }, - else => unreachable, - }; - if (func_ty_info.is_var_args) { - return sema.fail(block, call_src, "{s} call of variadic function", .{ - if (is_comptime_call) "comptime" else "inline", - }); - } - - // Analyze the ZIR. The same ZIR gets analyzed into a runtime function - // or an inlined call depending on what union tag the `label` field is - // set to in the `Block`. - // This block instruction will be used to capture the return value from the - // inlined function. - const need_debug_scope = !is_comptime_call and !block.is_typeof and !block.ownerModule().strip; - const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); - try sema.air_instructions.append(gpa, .{ - .tag = if (need_debug_scope) .dbg_inline_block else .block, - .data = undefined, }); - // This one is shared among sub-blocks within the same callee, but not - // shared among the entire inline/comptime call stack. - var inlining: Block.Inlining = .{ - .call_block = block, - .call_src = call_src, - .has_comptime_args = false, - .func = module_fn_index, - .comptime_result = undefined, - .merges = .{ - .src_locs = .{}, - .results = .{}, - .br_list = .{}, - .block_inst = block_inst, - }, - }; - - const module_fn = zcu.funcInfo(module_fn_index); - - // The call site definitely depends on the function's signature. - try sema.declareDependency(.{ .src_hash = module_fn.zir_body_inst }); - - // This is not a function instance, so the function's `Nav` has analysis - // state -- we don't need to check `generic_owner`. - const fn_nav = ip.getNav(module_fn.owner_nav); - - // We effectively want a child Sema here, but can't literally do that, because we need AIR - // to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in - // scope, we should use its `caller`/`callee` methods rather than using `sema` directly - // whenever performing an operation where the difference matters. - var ics = InlineCallSema.init( - sema, - zcu.navFileScope(module_fn.owner_nav).zir, - module_fn_index, - block.error_return_trace_index, - ); - defer ics.deinit(); - - var child_block: Block = .{ - .parent = null, - .sema = sema, - // The function body exists in the same namespace as the corresponding function declaration. - .namespace = fn_nav.analysis.?.namespace, - .instructions = .{}, - .label = null, - .inlining = &inlining, - .is_typeof = block.is_typeof, - .comptime_reason = if (is_comptime_call) .inlining_parent else null, - .error_return_trace_index = block.error_return_trace_index, - .runtime_cond = block.runtime_cond, - .runtime_loop = block.runtime_loop, - .runtime_index = block.runtime_index, - .src_base_inst = fn_nav.analysis.?.zir_index, - .type_name_ctx = fn_nav.fqn, - }; - - const merges = &child_block.inlining.?.merges; - - defer child_block.instructions.deinit(gpa); - defer merges.deinit(gpa); - - try sema.emitBackwardBranch(block, call_src); - - // Whether this call should be memoized, set to false if the call can - // mutate comptime state. - // TODO: comptime call memoization is currently not supported under incremental compilation - // since dependencies are not marked on callers. If we want to keep this around (we should - // check that it's worthwhile first!), each memoized call needs an `AnalUnit`. - var should_memoize = !zcu.comp.incremental; - - // If it's a comptime function call, we need to memoize it as long as no external - // comptime memory is mutated. - const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); - - const owner_info = zcu.typeToFunc(Type.fromInterned(module_fn.ty)).?; - const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len); - var new_fn_info: InternPool.GetFuncTypeKey = .{ - .param_types = new_param_types, - .return_type = owner_info.return_type, - .noalias_bits = owner_info.noalias_bits, - .cc = owner_info.cc, - .is_var_args = owner_info.is_var_args, - .is_noinline = owner_info.is_noinline, - .is_generic = owner_info.is_generic, - }; - - // This will have return instructions analyzed as break instructions to - // the block_inst above. Here we are performing "comptime/inline semantic analysis" - // for a function body, which means we must map the parameter ZIR instructions to - // the AIR instructions of the callsite. The callee could be a generic function - // which means its parameter type expressions must be resolved in order and used - // to successively coerce the arguments. - const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail); - try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); - - var arg_i: u32 = 0; - for (fn_info.param_body) |inst| { - const opt_noreturn_ref = try analyzeInlineCallArg( - &ics, - block, - &child_block, - inst, - new_param_types, - &arg_i, - args_info, - is_comptime_call, - &should_memoize, - memoized_arg_values, - func_ty_info, - func, - ); - if (opt_noreturn_ref) |ref| { - // Analyzing this argument gave a ref of a noreturn type. Terminate argument analysis here. - return ref; - } + for (args, 0..) |arg, arg_idx| { + try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg); } + const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { + if (!func_ty_info.is_generic) break :func .{ callee, args }; - // From here, we only really need to use the callee Sema. Make it the active one, then we - // can just use `sema` directly. - _ = ics.callee(); + // Instantiate the generic function! - if (!inlining.has_comptime_args) { - var block_it = block; - while (block_it.inlining) |parent_inlining| { - if (!parent_inlining.has_comptime_args and parent_inlining.func == module_fn_index) { - const err_msg = try sema.errMsg(call_src, "inline call is recursive", .{}); - return sema.failWithOwnedErrorMsg(null, err_msg); + // This may be an overestimate, but it's definitely sufficient. + const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits); + var runtime_args: std.ArrayListUnmanaged(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args); + var runtime_param_tys: std.ArrayListUnmanaged(InternPool.Index) = try .initCapacity(arena, max_runtime_args); + + const comptime_args = try arena.alloc(InternPool.Index, args_info.count()); + + var noalias_bits: u32 = 0; + + for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| { + const arg_ty = sema.typeOf(arg); + + const is_comptime = c: { + if (std.math.cast(u5, arg_idx)) |i| { + if (func_ty_info.paramIsComptime(i)) { + break :c true; + } + } + break :c try arg_ty.comptimeOnlySema(pt); + }; + const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false; + + if (is_comptime) { + // We already emitted an error if the argument isn't comptime-known. + comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern(); + } else { + comptime_arg.* = .none; + if (is_noalias) { + const runtime_idx = runtime_args.items.len; + noalias_bits |= @as(u32, 1) << @intCast(runtime_idx); + } + runtime_args.appendAssumeCapacity(arg); + runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern()); } - block_it = parent_inlining.call_block; } - } - // In case it is a generic function with an expression for the return type that depends - // on parameters, we must now do the same for the return type as we just did with - // each of the parameters, resolving the return type and providing it to the child - // `Sema` so that it can be used for the `ret_ptr` instruction. - const ret_ty_src: LazySrcLoc = .{ .base_node_inst = module_fn.zir_body_inst, .offset = .{ .node_offset_fn_type_ret_ty = 0 } }; - const ret_ty_inst = if (fn_info.ret_ty_body.len != 0) r: { - const old_child_comptime_reason = child_block.comptime_reason; - defer child_block.comptime_reason = old_child_comptime_reason; - child_block.comptime_reason = .{ .reason = .{ - .src = ret_ty_src, - .r = .{ .simple = .function_ret_ty }, - } }; - break :r try sema.resolveInlineBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst.resolve(ip) orelse return error.AnalysisFail); - } else try sema.resolveInst(fn_info.ret_ty_ref); - sema.fn_ret_ty = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst); - if (module_fn.analysisUnordered(ip).inferred_error_set) { - // Create a fresh inferred error set type for inline/comptime calls. - const ies = try sema.arena.create(InferredErrorSet); - ies.* = .{ .func = .none }; - sema.fn_ret_ty_ies = ies; - sema.fn_ret_ty = Type.fromInterned(try pt.intern(.{ .error_union_type = .{ - .error_set_type = .adhoc_inferred_error_set_type, - .payload_type = sema.fn_ret_ty.toIntern(), - } })); - } + const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: { + break :t resolved_ret_ty.errorUnionPayload(zcu); + } else resolved_ret_ty; - memoize: { - if (!should_memoize) break :memoize; - if (!is_comptime_call) break :memoize; - const memoized_call_index = ip.getIfExists(.{ - .memoized_call = .{ - .func = module_fn_index, - .arg_values = memoized_arg_values, - .result = undefined, // ignored by hash+eql - .branch_count = undefined, // ignored by hash+eql - }, - }) orelse break :memoize; - const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; - if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { - // Let the call play out se we get the correct source location for the - // "evaluation exceeded X backwards branches" error. - break :memoize; - } - sema.branch_count += memoized_call.branch_count; - break :res Air.internedToRef(memoized_call.result); - } + // We now need to actually create the function instance. + const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{ + .param_types = runtime_param_tys.items, + .noalias_bits = noalias_bits, + .bare_return_type = bare_ret_ty.toIntern(), + .is_noinline = func_ty_info.is_noinline, + .inferred_error_set = fn_zir_info.inferred_error_set, + .generic_owner = func_val.?.toIntern(), + .comptime_args = comptime_args, + }); - // Since we're doing an inline call, we depend on the source code of the whole - // function declaration. - try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); + // This call is problematic as it breaks guarantees about order-independency of semantic analysis. + // These guarantees are necessary for incremental compilation and parallel semantic analysis. + // See: #22410 + zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota); - new_fn_info.return_type = sema.fn_ret_ty.toIntern(); - if (!is_comptime_call and !block.is_typeof) { - const zir_tags = sema.code.instructions.items(.tag); - for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { - .param, .param_comptime => { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok; - const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); - const param_name = sema.code.nullTerminatedString(extra.data.name); - const inst = sema.inst_map.get(param).?; - - try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name); - }, - .param_anytype, .param_anytype_comptime => { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok; - const param_name = inst_data.get(sema.code); - const inst = sema.inst_map.get(param).?; - - try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name); - }, - else => continue, - }; - } - - if (is_comptime_call and ensure_result_used) { - try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src); - } - - if (is_comptime_call or block.is_typeof) { - // Save the error trace as our first action in the function - // to match the behavior of runtime function calls. - const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); - sema.error_return_trace_index_on_fn_entry = error_return_trace_index; - child_block.error_return_trace_index = error_return_trace_index; - } - - // We temporarily set `allow_memoize` to `true` to track this comptime call. - // It is restored after this call finishes analysis, so that a caller may - // know whether an in-progress call (containing this call) may be memoized. - const old_allow_memoize = sema.allow_memoize; - defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; - sema.allow_memoize = true; - - // Store the current eval branch count so we can find out how many eval branches - // the comptime call caused. - const old_branch_count = sema.branch_count; - - const result = result: { - sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) { - error.ComptimeReturn => break :result inlining.comptime_result, - else => |e| return e, - }; - break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope); + break :func .{ Air.internedToRef(func_instance), runtime_args.items }; }; - if (is_comptime_call) { - const result_val = try sema.resolveConstValue(block, LazySrcLoc.unneeded, result, undefined); - const result_interned = result_val.toIntern(); - - // Transform ad-hoc inferred error set types into concrete error sets. - const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); - - // If the result can mutate comptime vars, we must not memoize it, as it contains - // a reference to `comptime_allocs` so is not stable across instances of `Sema`. - // TODO: check whether any external comptime memory was mutated by the - // comptime function call. If so, then do not memoize the call here. - if (should_memoize and sema.allow_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) { - _ = try pt.intern(.{ .memoized_call = .{ - .func = module_fn_index, - .arg_values = memoized_arg_values, - .result = result_transformed, - .branch_count = sema.branch_count - old_branch_count, - } }); - } - - break :res Air.internedToRef(result_transformed); + ref_func: { + const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func; + if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; + try sema.addReferenceEntry(call_src, .wrap(.{ .func = runtime_func_val.toIntern() })); + try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern()); } - if (try sema.resolveValue(result)) |result_val| { - const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); - break :res Air.internedToRef(result_transformed); - } - - const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern()); - if (new_ty != .none) { - // TODO: mutate in place the previous instruction if possible - // rather than adding a bitcast instruction. - break :res try block.addBitCast(Type.fromInterned(new_ty), result); - } - - break :res result; - } else res: { - assert(!func_ty_info.is_generic); - - const args = try sema.arena.alloc(Air.Inst.Ref, args_info.count()); - for (args, 0..) |*arg_out, arg_idx| { - // Non-generic, so param types are already resolved - const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: { - break :ty Type.fromInterned(func_ty_info.param_types.get(ip)[arg_idx]); - } else null; - if (param_ty) |t| assert(!t.isGenericPoison()); - arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func); - try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg_out.*); - if (sema.typeOf(arg_out.*).zigTypeTag(zcu) == .noreturn) { - return arg_out.*; - } - } - - if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); - switch (sema.owner.unwrap()) { .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, - .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { + .func => |owner_func| if (resolved_ret_ty.isError(zcu)) { ip.funcSetCallsOrAwaitsErrorableFn(owner_func); }, } - if (try sema.resolveValue(func)) |func_val| { - if (zcu.intern_pool.isFuncBody(func_val.toIntern())) { - try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = func_val.toIntern() })); - try zcu.ensureFuncBodyAnalysisQueued(func_val.toIntern()); - } - } + const call_tag: Air.Inst.Tag = switch (modifier) { + .auto, .no_async => .call, + .never_tail => .call_never_tail, + .never_inline => .call_never_inline, + .always_tail => .call_always_tail, - try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + - args.len); - const func_inst = try block.addInst(.{ + .always_inline, + .compile_time, + .async_kw, + => unreachable, + }; + + try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len); + const result = try block.addInst(.{ .tag = call_tag, .data = .{ .pl_op = .{ - .operand = func, + .operand = runtime_func, .payload = sema.addExtraAssumeCapacity(Air.Call{ - .args_len = @intCast(args.len), + .args_len = @intCast(runtime_args.len), }), } }, }); - sema.appendRefsAssumeCapacity(args); + sema.appendRefsAssumeCapacity(runtime_args); + + if (ensure_result_used) { + try sema.ensureResultUsed(block, sema.typeOf(result), call_src); + } if (call_tag == .call_always_tail) { - if (ensure_result_used) { - try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src); - } - return sema.handleTailCall(block, call_src, func_ty, func_inst); + return sema.handleTailCall(block, call_src, sema.typeOf(runtime_func), result); } - if (block.wantSafety() and func_ty_info.return_type == .noreturn_type) skip_safety: { - // Function pointers and extern functions aren't guaranteed to - // actually be noreturn so we add a safety check for them. - if (try sema.resolveValue(func)) |func_val| { - switch (zcu.intern_pool.indexToKey(func_val.toIntern())) { - .func => break :skip_safety, - .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { - .nav => |nav| { - try sema.ensureNavResolved(call_src, nav, .fully); - if (ip.getNav(nav).getExtern(ip) == null) break :skip_safety; - }, - else => {}, - }, - else => {}, - } + + if (resolved_ret_ty.toIntern() == .noreturn_type) { + const want_check = c: { + if (!block.wantSafety()) break :c false; + if (func_val != null) break :c false; + break :c true; + }; + if (want_check) { + try sema.safetyPanic(block, call_src, .noreturn_returned); + } else { + _ = try block.addNoOp(.unreach); } - try sema.safetyPanic(block, call_src, .noreturn_returned); return .unreachable_value; } - if (func_ty_info.return_type == .noreturn_type) { - _ = try block.addNoOp(.unreach); - return .unreachable_value; + + return result; + } + + // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. + + const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline"; + + if (modifier == .never_inline) { + return sema.fail(block, call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); + } + if (func_ty_info.is_noinline and !block.isComptime()) { + return sema.fail(block, call_src, "{s} call of noinline function", .{call_type}); + } + if (func_ty_info.is_var_args) { + return sema.fail(block, call_src, "{s} call of variadic function", .{call_type}); + } + + if (func_val == null) { + if (func_is_extern) { + return sema.fail(block, call_src, "{s} call of extern function", .{call_type}); } - break :res func_inst; + return sema.failWithNeededComptime( + block, + func_src, + .{ .simple = if (block.isComptime()) .comptime_call_target else .inline_call_target }, + ); + } + + if (block.isComptime()) { + for (args, 0..) |arg, arg_idx| { + if (!try sema.isComptimeKnown(arg)) { + const arg_src = args_info.argSrc(block, arg_idx); + return sema.failWithNeededComptime(block, arg_src, null); + } + } + } + + // For an inline call, we depend on the source code of the whole function definition. + try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); + + try sema.emitBackwardBranch(block, call_src); + + const want_memoize = m: { + // TODO: comptime call memoization is currently not supported under incremental compilation + // since dependencies are not marked on callers. If we want to keep this around (we should + // check that it's worthwhile first!), each memoized call needs an `AnalUnit`. + if (zcu.comp.incremental) break :m false; + if (!block.isComptime()) break :m false; + for (args) |a| { + const val = (try sema.resolveValue(a)).?; + if (val.canMutateComptimeVarState(zcu)) break :m false; + } + break :m true; }; + const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: { + const vals = try sema.arena.alloc(InternPool.Index, args.len); + for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern(); + break :arg_vals vals; + } else undefined; + if (want_memoize) memoize: { + const memoized_call_index = ip.getIfExists(.{ + .memoized_call = .{ + .func = func_val.?.toIntern(), + .arg_values = memoized_arg_values, + .result = undefined, // ignored by hash+eql + .branch_count = undefined, // ignored by hash+eql + }, + }) orelse break :memoize; + const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; + if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { + // Let the call play out se we get the correct source location for the + // "evaluation exceeded X backwards branches" error. + break :memoize; + } + sema.branch_count += memoized_call.branch_count; + const result = Air.internedToRef(memoized_call.result); + if (ensure_result_used) { + try sema.ensureResultUsed(block, sema.typeOf(result), call_src); + } + return result; + } + + var new_ies: InferredErrorSet = .{ .func = .none }; + + const old_inst_map = sema.inst_map; + const old_code = sema.code; + const old_func_index = sema.func_index; + const old_fn_ret_ty = sema.fn_ret_ty; + const old_fn_ret_ty_ies = sema.fn_ret_ty_ies; + const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry; + defer { + sema.inst_map.deinit(gpa); + sema.inst_map = old_inst_map; + sema.code = old_code; + sema.func_index = old_func_index; + sema.fn_ret_ty = old_fn_ret_ty; + sema.fn_ret_ty_ies = old_fn_ret_ty_ies; + sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry; + } + sema.inst_map = .{}; + sema.code = fn_zir; + sema.func_index = func_val.?.toIntern(); + sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType( + .fromInterned(.adhoc_inferred_error_set_type), + resolved_ret_ty.errorUnionPayload(zcu), + ) else resolved_ret_ty; + sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null; + + try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); + for (args, 0..) |arg, arg_idx| { + sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg); + } + + const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip; + const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); + try sema.air_instructions.append(gpa, .{ + .tag = if (need_debug_scope) .dbg_inline_block else .block, + .data = undefined, + }); + + var inlining: Block.Inlining = .{ + .call_block = block, + .call_src = call_src, + .has_comptime_args = for (args) |a| { + if (try sema.isComptimeKnown(a)) break true; + } else false, + .func = func_val.?.toIntern(), + .comptime_result = undefined, + .merges = .{ + .block_inst = block_inst, + .results = .empty, + .br_list = .empty, + .src_locs = .empty, + }, + }; + var child_block: Block = .{ + .parent = null, + .sema = sema, + .namespace = fn_nav.analysis.?.namespace, + .instructions = .{}, + .inlining = &inlining, + .is_typeof = block.is_typeof, + .comptime_reason = if (block.isComptime()) .inlining_parent else null, + .error_return_trace_index = block.error_return_trace_index, + .runtime_cond = block.runtime_cond, + .runtime_loop = block.runtime_loop, + .runtime_index = block.runtime_index, + .src_base_inst = fn_nav.analysis.?.zir_index, + .type_name_ctx = fn_nav.fqn, + }; + + defer child_block.instructions.deinit(gpa); + defer inlining.merges.deinit(gpa); + + if (!inlining.has_comptime_args) { + var block_it = block; + while (block_it.inlining) |parent_inlining| { + if (!parent_inlining.has_comptime_args and parent_inlining.func == func_val.?.toIntern()) { + return sema.fail(block, call_src, "inline call is recursive", .{}); + } + block_it = parent_inlining.call_block; + } + } + + if (!block.isComptime() and !block.is_typeof) { + const zir_tags = sema.code.instructions.items(.tag); + const zir_datas = sema.code.instructions.items(.data); + for (fn_zir_info.param_body) |inst| switch (zir_tags[@intFromEnum(inst)]) { + .param, .param_comptime => { + const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@intFromEnum(inst)].pl_tok.payload_index); + const param_name = sema.code.nullTerminatedString(extra.data.name); + const air_inst = sema.inst_map.get(inst).?; + try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); + }, + .param_anytype, .param_anytype_comptime => { + const param_name = zir_datas[@intFromEnum(inst)].str_tok.get(sema.code); + const air_inst = sema.inst_map.get(inst).?; + try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); + }, + else => {}, + }; + } + + child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); + // Save the error trace as our first action in the function + // to match the behavior of runtime function calls. + const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry; + sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index; + defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry; + + // We temporarily set `allow_memoize` to `true` to track this comptime call. + // It is restored after the call finishes analysis, so that a caller may + // know whether an in-progress call (containing this call) may be memoized. + const old_allow_memoize = sema.allow_memoize; + defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; + sema.allow_memoize = true; + + // Store the current eval branch count so we can find out how many eval branches + // the comptime call caused. + const old_branch_count = sema.branch_count; + + const result_raw: Air.Inst.Ref = result: { + sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) { + error.ComptimeReturn => break :result inlining.comptime_result, + else => |e| return e, + }; + break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope); + }; + + const result: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: { + const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); + break :r Air.internedToRef(val_resolved); + } else r: { + const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern()); + if (resolved_ty == .none) break :r result_raw; + // TODO: mutate in place the previous instruction if possible + // rather than adding a bitcast instruction. + break :r try block.addBitCast(.fromInterned(resolved_ty), result_raw); + }; + + if (block.isComptime()) { + const result_val = (try sema.resolveValue(result)).?; + if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) { + _ = try pt.intern(.{ .memoized_call = .{ + .func = func_val.?.toIntern(), + .arg_values = memoized_arg_values, + .result = result_val.toIntern(), + .branch_count = sema.branch_count - old_branch_count, + } }); + } + } if (ensure_result_used) { try sema.ensureResultUsed(block, sema.typeOf(result), call_src); } + return result; } @@ -8176,425 +8245,6 @@ fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Typ return .unreachable_value; } -/// Usually, returns null. If an argument was noreturn, returns that ref (which should become the call result). -fn analyzeInlineCallArg( - ics: *InlineCallSema, - arg_block: *Block, - param_block: *Block, - inst: Zir.Inst.Index, - new_param_types: []InternPool.Index, - arg_i: *u32, - args_info: CallArgsInfo, - is_comptime_call: bool, - should_memoize: *bool, - memoized_arg_values: []InternPool.Index, - func_ty_info: InternPool.Key.FuncType, - func_inst: Air.Inst.Ref, -) !?Air.Inst.Ref { - const zcu = ics.sema.pt.zcu; - const ip = &zcu.intern_pool; - const zir_tags = ics.callee().code.instructions.items(.tag); - switch (zir_tags[@intFromEnum(inst)]) { - .param_comptime, .param_anytype_comptime => param_block.inlining.?.has_comptime_args = true, - else => {}, - } - switch (zir_tags[@intFromEnum(inst)]) { - .param, .param_comptime => { - // Evaluate the parameter type expression now that previous ones have - // been mapped, and coerce the corresponding argument to it. - const pl_tok = ics.callee().code.instructions.items(.data)[@intFromEnum(inst)].pl_tok; - const param_src = param_block.tokenOffset(pl_tok.src_tok); - const extra = ics.callee().code.extraData(Zir.Inst.Param, pl_tok.payload_index); - const param_body = ics.callee().code.bodySlice(extra.end, extra.data.body_len); - const param_ty = param_ty: { - const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*]; - if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty; - const param_ty_inst = try ics.callee().resolveInlineBody(param_block, param_body, inst); - const param_ty = try ics.callee().analyzeAsType(param_block, param_src, param_ty_inst); - break :param_ty param_ty.toIntern(); - }; - new_param_types[arg_i.*] = param_ty; - const casted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.fromInterned(param_ty), func_ty_info, func_inst); - if (ics.caller().typeOf(casted_arg).zigTypeTag(zcu) == .noreturn) { - return casted_arg; - } - const arg_src = args_info.argSrc(arg_block, arg_i.*); - if (zir_tags[@intFromEnum(inst)] == .param_comptime) { - _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{ .simple = .comptime_param_arg }); - } else if (!is_comptime_call and try Type.fromInterned(param_ty).comptimeOnlySema(ics.callee().pt)) { - _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{ .comptime_only = .{ - .ty = .fromInterned(param_ty), - .msg = .param_ty_arg, - } }); - } - - if (is_comptime_call) { - ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg); - const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, null); - switch (arg_val.toIntern()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => {}, - } - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val); - should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(zcu); - memoized_arg_values[arg_i.*] = resolved_arg_val.toIntern(); - } else { - ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg); - } - - if (try ics.caller().resolveValue(casted_arg)) |_| { - param_block.inlining.?.has_comptime_args = true; - } - - arg_i.* += 1; - }, - .param_anytype, .param_anytype_comptime => { - // No coercion needed. - const uncasted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.generic_poison, func_ty_info, func_inst); - if (ics.caller().typeOf(uncasted_arg).zigTypeTag(zcu) == .noreturn) { - return uncasted_arg; - } - const arg_src = args_info.argSrc(arg_block, arg_i.*); - new_param_types[arg_i.*] = ics.caller().typeOf(uncasted_arg).toIntern(); - - if (is_comptime_call) { - ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); - const arg_val = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, null); - switch (arg_val.toIntern()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => {}, - } - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val); - should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(zcu); - memoized_arg_values[arg_i.*] = resolved_arg_val.toIntern(); - } else { - if (zir_tags[@intFromEnum(inst)] == .param_anytype_comptime) { - _ = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{ .simple = .comptime_param_arg }); - } - ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); - } - - if (try ics.caller().resolveValue(uncasted_arg)) |_| { - param_block.inlining.?.has_comptime_args = true; - } - - arg_i.* += 1; - }, - else => {}, - } - - return null; -} - -fn instantiateGenericCall( - sema: *Sema, - block: *Block, - func: Air.Inst.Ref, - func_src: LazySrcLoc, - call_src: LazySrcLoc, - ensure_result_used: bool, - args_info: CallArgsInfo, - call_tag: Air.Inst.Tag, - call_dbg_node: ?Zir.Inst.Index, - /// Populated when `error.ComptimeReturn` is returned. - comptime_ret_ty: *Type, -) CompileError!Air.Inst.Ref { - const pt = sema.pt; - const zcu = pt.zcu; - const gpa = sema.gpa; - const ip = &zcu.intern_pool; - - // Generic function pointers are comptime-only types, so `func` is definitely comptime-known. - const func_val = (sema.resolveValue(func) catch unreachable).?; - if (func_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, func_src); - - const generic_owner = switch (zcu.intern_pool.indexToKey(func_val.toIntern())) { - .func => func_val.toIntern(), - .ptr => |ptr| ip.getNav(ptr.base_addr.nav).status.fully_resolved.val, - else => unreachable, - }; - const generic_owner_func = zcu.intern_pool.indexToKey(generic_owner).func; - const generic_owner_ty_info = zcu.typeToFunc(Type.fromInterned(generic_owner_func.ty)).?; - - try sema.declareDependency(.{ .src_hash = generic_owner_func.zir_body_inst }); - - // Even though there may already be a generic instantiation corresponding - // to this callsite, we must evaluate the expressions of the generic - // function signature with the values of the callsite plugged in. - // Importantly, this may include type coercions that determine whether the - // instantiation is a match of a previous instantiation. - // The actual monomorphization happens via adding `func_instance` to - // `InternPool`. - - // Since we are looking at the generic owner here, it has analysis state. - const fn_nav = ip.getNav(generic_owner_func.owner_nav); - const fn_zir = zcu.navFileScope(generic_owner_func.owner_nav).zir; - const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip) orelse return error.AnalysisFail); - - const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count()); - @memset(comptime_args, .none); - - // We may overestimate the number of runtime args, but this will definitely be sufficient. - const max_runtime_args = args_info.count() - @popCount(generic_owner_ty_info.comptime_bits); - var runtime_args = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(sema.arena, max_runtime_args); - - // Re-run the block that creates the function, with the comptime parameters - // pre-populated inside `inst_map`. This causes `param_comptime` and - // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a - // new, monomorphized function, with the comptime parameters elided. - var child_sema: Sema = .{ - .pt = pt, - .gpa = gpa, - .arena = sema.arena, - .code = fn_zir, - // We pass the generic callsite's owner decl here because whatever `Decl` - // dependencies are chased at this point should be attached to the - // callsite, not the `Decl` associated with the `func_instance`. - .owner = sema.owner, - .func_index = sema.func_index, - // This may not be known yet, since the calling convention could be generic, but there - // should be no illegal instructions encountered while creating the function anyway. - .func_is_naked = false, - .fn_ret_ty = Type.void, - .fn_ret_ty_ies = null, - .comptime_args = comptime_args, - .generic_owner = generic_owner, - .generic_call_src = call_src, - .branch_quota = sema.branch_quota, - .branch_count = sema.branch_count, - .comptime_err_ret_trace = sema.comptime_err_ret_trace, - }; - defer child_sema.deinit(); - - var child_block: Block = .{ - .parent = null, - .sema = &child_sema, - .namespace = fn_nav.analysis.?.namespace, - .instructions = .{}, - .inlining = null, - .comptime_reason = undefined, // set as needed - .src_base_inst = fn_nav.analysis.?.zir_index, - .type_name_ctx = fn_nav.fqn, - }; - defer child_block.instructions.deinit(gpa); - - try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); - - for (fn_info.param_body[0..args_info.count()], 0..) |param_inst, arg_index| { - const param_tag = fn_zir.instructions.items(.tag)[@intFromEnum(param_inst)]; - - const param_ty = switch (generic_owner_ty_info.param_types.get(ip)[arg_index]) { - else => |ty| Type.fromInterned(ty), // parameter is not generic, so type is already resolved - .generic_poison_type => param_ty: { - // We have every parameter before this one, so can resolve this parameter's type now. - // However, first check the param type, since it may be anytype. - switch (param_tag) { - .param_anytype, .param_anytype_comptime => { - // The parameter doesn't have a type. - break :param_ty Type.generic_poison; - }, - .param, .param_comptime => { - // We now know every prior parameter, so can resolve this - // parameter's type. The child sema has these types. - const param_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok; - const param_extra = fn_zir.extraData(Zir.Inst.Param, param_data.payload_index); - const param_ty_body = fn_zir.bodySlice(param_extra.end, param_extra.data.body_len); - - // Make sure any nested instructions don't clobber our work. - const prev_params = child_block.params; - const prev_no_partial_func_ty = child_sema.no_partial_func_ty; - const prev_generic_owner = child_sema.generic_owner; - const prev_generic_call_src = child_sema.generic_call_src; - child_block.params = .{}; - child_sema.no_partial_func_ty = true; - child_sema.generic_owner = .none; - child_sema.generic_call_src = LazySrcLoc.unneeded; - defer { - child_block.params = prev_params; - child_sema.no_partial_func_ty = prev_no_partial_func_ty; - child_sema.generic_owner = prev_generic_owner; - child_sema.generic_call_src = prev_generic_call_src; - } - - const param_ty_src = child_block.tokenOffset(param_data.src_tok); - child_block.comptime_reason = .{ .reason = .{ - .src = param_ty_src, - .r = .{ .simple = .type }, - } }; - const param_ty_inst = try child_sema.resolveInlineBody(&child_block, param_ty_body, param_inst); - break :param_ty try child_sema.analyzeAsType(&child_block, param_ty_src, param_ty_inst); - }, - else => unreachable, - } - }, - }; - const arg_ref = try args_info.analyzeArg(sema, block, arg_index, param_ty, generic_owner_ty_info, func); - try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_index), arg_ref); - const arg_ty = sema.typeOf(arg_ref); - if (arg_ty.zigTypeTag(zcu) == .noreturn) { - // This terminates argument analysis. - return arg_ref; - } - - const arg_is_comptime = switch (param_tag) { - .param_comptime, .param_anytype_comptime => true, - .param, .param_anytype => try arg_ty.comptimeOnlySema(pt), - else => unreachable, - }; - - if (arg_is_comptime) { - if (try sema.resolveValue(arg_ref)) |arg_val| { - comptime_args[arg_index] = arg_val.toIntern(); - child_sema.inst_map.putAssumeCapacityNoClobber( - param_inst, - Air.internedToRef(arg_val.toIntern()), - ); - } else switch (param_tag) { - .param_comptime, - .param_anytype_comptime, - => return sema.failWithOwnedErrorMsg(block, msg: { - const arg_src = args_info.argSrc(block, arg_index); - const msg = try sema.errMsg(arg_src, "runtime-known argument passed to comptime parameter", .{}); - errdefer msg.destroy(sema.gpa); - const param_src = child_block.tokenOffset(switch (param_tag) { - .param_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src_tok, - .param_anytype_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src_tok, - else => unreachable, - }); - try child_sema.errNote(param_src, msg, "declared comptime here", .{}); - break :msg msg; - }), - - .param, - .param_anytype, - => return sema.failWithOwnedErrorMsg(block, msg: { - const arg_src = args_info.argSrc(block, arg_index); - const msg = try sema.errMsg(arg_src, "runtime-known argument passed to parameter of comptime-only type", .{}); - errdefer msg.destroy(sema.gpa); - const param_src = child_block.tokenOffset(switch (param_tag) { - .param => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src_tok, - .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src_tok, - else => unreachable, - }); - try child_sema.errNote(param_src, msg, "declared here", .{}); - try sema.explainWhyTypeIsComptime(msg, arg_src, arg_ty); - break :msg msg; - }), - - else => unreachable, - } - } else { - // The parameter is runtime-known. - const param_name: Zir.NullTerminatedString = switch (param_tag) { - .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.start, - .param => name: { - const inst_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok; - const extra = fn_zir.extraData(Zir.Inst.Param, inst_data.payload_index); - break :name extra.data.name; - }, - else => unreachable, - }; - child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{ - .tag = .arg, - .data = .{ .arg = .{ - .ty = Air.internedToRef(arg_ty.toIntern()), - .name = if (child_block.ownerModule().strip) - .none - else - try sema.appendAirString(fn_zir.nullTerminatedString(param_name)), - } }, - })); - try child_block.params.append(sema.arena, .{ - .ty = arg_ty.toIntern(), // This is the type after coercion - .is_comptime = false, // We're adding only runtime args to the instantiation - .name = param_name, - }); - runtime_args.appendAssumeCapacity(arg_ref); - } - } - - // We've already handled parameters, so don't resolve the whole body. Instead, just - // do the instructions after the params (i.e. the func itself). - child_block.comptime_reason = .{ .reason = .{ - .src = call_src, - .r = .{ .simple = .type }, - } }; - const new_func_inst = try child_sema.resolveInlineBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst); - const callee_index = (child_sema.resolveConstDefinedValue(&child_block, LazySrcLoc.unneeded, new_func_inst, undefined) catch unreachable).toIntern(); - - const callee = zcu.funcInfo(callee_index); - callee.maxBranchQuota(ip, sema.branch_quota); - - // Make a runtime call to the new function, making sure to omit the comptime args. - const func_ty = Type.fromInterned(callee.ty); - const func_ty_info = zcu.typeToFunc(func_ty).?; - - // If the call evaluated to a return type that requires comptime, never mind - // our generic instantiation. Instead we need to perform a comptime call. - if (try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) { - comptime_ret_ty.* = .fromInterned(func_ty_info.return_type); - return error.ComptimeReturn; - } - // Similarly, if the call evaluated to a generic type we need to instead - // call it inline. - if (func_ty_info.is_generic or func_ty_info.cc == .@"inline") { - return error.GenericPoison; - } - - if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); - - switch (sema.owner.unwrap()) { - .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {}, - .func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) { - ip.funcSetCallsOrAwaitsErrorableFn(owner_func); - }, - } - - try sema.addReferenceEntry(call_src, AnalUnit.wrap(.{ .func = callee_index })); - try zcu.ensureFuncBodyAnalysisQueued(callee_index); - - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.items.len); - const result = try block.addInst(.{ - .tag = call_tag, - .data = .{ .pl_op = .{ - .operand = Air.internedToRef(callee_index), - .payload = sema.addExtraAssumeCapacity(Air.Call{ - .args_len = @intCast(runtime_args.items.len), - }), - } }, - }); - sema.appendRefsAssumeCapacity(runtime_args.items); - - // `child_sema` is owned by us, so just take its exports. - try sema.exports.appendSlice(sema.gpa, child_sema.exports.items); - - if (ensure_result_used) { - try sema.ensureResultUsed(block, sema.typeOf(result), call_src); - } - if (call_tag == .call_always_tail) { - return sema.handleTailCall(block, call_src, func_ty, result); - } - if (func_ty.fnReturnType(zcu).isNoReturn(zcu)) { - _ = try block.addNoOp(.unreach); - return .unreachable_value; - } - return result; -} - fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type; const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count); @@ -9576,9 +9226,7 @@ fn zirFunc( // the callconv based on whether it is exported. Otherwise, the callconv defaults // to `.auto`. const cc: std.builtin.CallingConvention = if (has_body) cc: { - const func_decl_nav = if (sema.generic_owner != .none) nav: { - break :nav zcu.funcInfo(sema.generic_owner).owner_nav; - } else sema.owner.unwrap().nav_val; + const func_decl_nav = sema.owner.unwrap().nav_val; const fn_is_exported = exported: { const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail; const zir_decl = sema.code.getDeclaration(decl_inst); @@ -9635,17 +9283,11 @@ fn resolveGenericBody( // Make sure any nested param instructions don't clobber our work. const prev_params = block.params; const prev_no_partial_func_type = sema.no_partial_func_ty; - const prev_generic_owner = sema.generic_owner; - const prev_generic_call_src = sema.generic_call_src; block.params = .{}; sema.no_partial_func_ty = true; - sema.generic_owner = .none; - sema.generic_call_src = LazySrcLoc.unneeded; defer { block.params = prev_params; sema.no_partial_func_ty = prev_no_partial_func_type; - sema.generic_owner = prev_generic_owner; - sema.generic_call_src = prev_generic_call_src; } const uncasted = sema.resolveInlineBody(block, body, func_inst) catch |err| break :err err; @@ -9911,16 +9553,10 @@ fn funcCommon( const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); const func_src = block.nodeOffset(src_node_offset); - var is_generic = bare_return_type.isGenericPoison(); + if (bare_return_type.isGenericPoison() and sema.no_partial_func_ty) return error.GenericPoison; - if (var_args) { - if (is_generic) { - return sema.fail(block, func_src, "generic function cannot be variadic", .{}); - } - try sema.checkCallConvSupportsVarArgs(block, cc_src, cc); - } - - const is_source_decl = sema.generic_owner == .none; + const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt); + var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime; var comptime_bits: u32 = 0; for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { @@ -9933,16 +9569,21 @@ fn funcCommon( .fn_proto_node_offset = src_node_offset, .param_index = @intCast(i), } }); - const requires_comptime = try param_ty.comptimeOnlySema(pt); - if (param_is_comptime or requires_comptime) { + const param_ty_comptime = try param_ty.comptimeOnlySema(pt); + const param_ty_generic = param_ty.isGenericPoison(); + if (param_ty_generic and sema.no_partial_func_ty) { + return error.GenericPoison; + } + if (param_is_comptime or param_ty_comptime or param_ty_generic) { + is_generic = true; + } + if (param_is_comptime) { comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error } - const this_generic = param_ty.isGenericPoison(); - is_generic = is_generic or this_generic; if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) { return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); } - if (this_generic and !sema.no_partial_func_ty and !target_util.fnCallConvAllowsZigTypes(cc)) { + if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) { return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); } if (!param_ty.isValidParamType(zcu)) { @@ -9951,7 +9592,7 @@ fn funcCommon( opaque_str, param_ty.fmt(pt), }); } - if (!this_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) { + if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) { const msg = msg: { const msg = try sema.errMsg(param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ param_ty.fmt(pt), @tagName(cc), @@ -9965,7 +9606,7 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (is_source_decl and requires_comptime and !param_is_comptime and has_body and !block.isComptime()) { + if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) { const msg = msg: { const msg = try sema.errMsg(param_src, "parameter of type '{}' must be declared comptime", .{ param_ty.fmt(pt), @@ -9979,7 +9620,7 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (is_source_decl and !this_generic and is_noalias and + if (!param_ty_generic and is_noalias and !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu))) { return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); @@ -10007,48 +9648,17 @@ fn funcCommon( } } - const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt); + if (var_args) { + if (is_generic) { + return sema.fail(block, func_src, "generic function cannot be variadic", .{}); + } + try sema.checkCallConvSupportsVarArgs(block, cc_src, cc); + } + const ret_poison = bare_return_type.isGenericPoison(); - const final_is_generic = is_generic or comptime_bits != 0 or ret_ty_requires_comptime; const param_types = block.params.items(.ty); - if (!is_source_decl) { - assert(has_body); - assert(!is_generic); - assert(comptime_bits == 0); - assert(!var_args); - if (inferred_error_set) { - try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); - } - const func_index = try ip.getFuncInstance(gpa, pt.tid, .{ - .param_types = param_types, - .noalias_bits = noalias_bits, - .bare_return_type = bare_return_type.toIntern(), - .is_noinline = is_noinline, - .inferred_error_set = inferred_error_set, - .generic_owner = sema.generic_owner, - .comptime_args = sema.comptime_args, - }); - return finishFunc( - sema, - block, - func_index, - .none, - ret_poison, - bare_return_type, - ret_ty_src, - cc, - is_source_decl, - ret_ty_requires_comptime, - func_inst, - cc_src, - is_noinline, - is_generic, - final_is_generic, - ); - } - if (inferred_error_set) { assert(has_body); if (!ret_poison) @@ -10062,7 +9672,7 @@ fn funcCommon( .bare_return_type = bare_return_type.toIntern(), .cc = cc, .is_var_args = var_args, - .is_generic = final_is_generic, + .is_generic = is_generic, .is_noinline = is_noinline, .zir_body_inst = try block.trackZir(func_inst), @@ -10080,13 +9690,11 @@ fn funcCommon( bare_return_type, ret_ty_src, cc, - is_source_decl, ret_ty_requires_comptime, func_inst, cc_src, is_noinline, is_generic, - final_is_generic, ); } @@ -10097,7 +9705,7 @@ fn funcCommon( .return_type = bare_return_type.toIntern(), .cc = cc, .is_var_args = var_args, - .is_generic = final_is_generic, + .is_generic = is_generic, .is_noinline = is_noinline, }); @@ -10122,13 +9730,11 @@ fn funcCommon( bare_return_type, ret_ty_src, cc, - is_source_decl, ret_ty_requires_comptime, func_inst, cc_src, is_noinline, is_generic, - final_is_generic, ); } @@ -10141,13 +9747,11 @@ fn funcCommon( bare_return_type, ret_ty_src, cc, - is_source_decl, ret_ty_requires_comptime, func_inst, cc_src, is_noinline, is_generic, - final_is_generic, ); } @@ -10160,13 +9764,11 @@ fn finishFunc( bare_return_type: Type, ret_ty_src: LazySrcLoc, cc_resolved: std.builtin.CallingConvention, - is_source_decl: bool, ret_ty_requires_comptime: bool, func_inst: Zir.Inst.Index, cc_src: LazySrcLoc, is_noinline: bool, is_generic: bool, - final_is_generic: bool, ) CompileError!Air.Inst.Ref { const pt = sema.pt; const zcu = pt.zcu; @@ -10203,7 +9805,7 @@ fn finishFunc( // If the return type is comptime-only but not dependent on parameters then // all parameter types also need to be comptime. - if (is_source_decl and opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { + if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: { for (block.params.items(.is_comptime)) |is_comptime| { if (!is_comptime) break; } else break :comptime_check; @@ -10300,8 +9902,7 @@ fn finishFunc( }), } - if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; - if (!final_is_generic and sema.wantErrorReturnTracing(return_type)) { + if (!is_generic and sema.wantErrorReturnTracing(return_type)) { // Make sure that StackTrace's fields are resolved so that the backend can // lower this fn type. const unresolved_stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(0), .StackTrace); @@ -10328,17 +9929,11 @@ fn zirParam( // Make sure any nested param instructions don't clobber our work. const prev_params = block.params; const prev_no_partial_func_type = sema.no_partial_func_ty; - const prev_generic_owner = sema.generic_owner; - const prev_generic_call_src = sema.generic_call_src; block.params = .{}; sema.no_partial_func_ty = true; - sema.generic_owner = .none; - sema.generic_call_src = LazySrcLoc.unneeded; defer { block.params = prev_params; sema.no_partial_func_ty = prev_no_partial_func_type; - sema.generic_owner = prev_generic_owner; - sema.generic_call_src = prev_generic_call_src; } if (sema.resolveInlineBody(block, body, inst)) |param_ty_inst| { @@ -26646,11 +26241,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val); } else cc: { if (has_body) { - const func_decl_nav = if (sema.generic_owner != .none) nav: { - // Generic instance -- use the original function declaration to - // look for the `export` syntax. - break :nav zcu.funcInfo(sema.generic_owner).owner_nav; - } else sema.owner.unwrap().nav_val; + const func_decl_nav = sema.owner.unwrap().nav_val; const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail; const zir_decl = sema.code.getDeclaration(func_decl_inst); if (zir_decl.linkage == .@"export") { @@ -31068,9 +30659,16 @@ fn coerceInMemoryAllowedFns( const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]); const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]); - const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); - const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); - if (src_is_comptime != dest_is_comptime) { + comptime_param: { + const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); + const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); + if (src_is_comptime == dest_is_comptime) break :comptime_param; + if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) { + // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. + // The function remains generic, and the parameter is going to be comptime-resolved either way, + // so this just affects whether or not the argument is comptime-evaluated at the call site. + break :comptime_param; + } return .{ .fn_param_comptime = .{ .index = param_i, .wanted = dest_is_comptime, diff --git a/src/Zcu.zig b/src/Zcu.zig index 60aeffdf44..338c4c5528 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -1928,6 +1928,24 @@ pub const SrcLoc = struct { }, } }, + .func_decl_param_comptime => |param_idx| { + const tree = try src_loc.file_scope.getTree(gpa); + var buf: [1]Ast.Node.Index = undefined; + const full = tree.fullFnProto(&buf, src_loc.base_node).?; + var param_it = full.iterate(tree); + for (0..param_idx) |_| assert(param_it.next() != null); + const param = param_it.next().?; + return tree.tokenToSpan(param.comptime_noalias.?); + }, + .func_decl_param_ty => |param_idx| { + const tree = try src_loc.file_scope.getTree(gpa); + var buf: [1]Ast.Node.Index = undefined; + const full = tree.fullFnProto(&buf, src_loc.base_node).?; + var param_it = full.iterate(tree); + for (0..param_idx) |_| assert(param_it.next() != null); + const param = param_it.next().?; + return tree.nodeToSpan(param.type_expr); + }, } } }; @@ -2235,6 +2253,12 @@ pub const LazySrcLoc = struct { /// The source location points to the "tag" capture (second capture) of /// a specific case of a `switch`. switch_tag_capture: SwitchCapture, + /// The source location points to the `comptime` token on the given comptime parameter, + /// where the base node is a function declaration. The value is the parameter index. + func_decl_param_comptime: u32, + /// The source location points to the type annotation on the given function parameter, + /// where the base node is a function declaration. The value is the parameter index. + func_decl_param_ty: u32, pub const FnProtoParam = struct { /// The offset of the function prototype AST node. diff --git a/src/translate_c.zig b/src/translate_c.zig index 64a673456a..c28d2c46b2 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -160,6 +160,7 @@ pub fn translate( context.pattern_list.deinit(gpa); } + @setEvalBranchQuota(2000); inline for (@typeInfo(std.zig.c_builtins).@"struct".decls) |decl| { const builtin = try Tag.pub_var_simple.create(arena, .{ .name = decl.name, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index f53e05feda..dd3de9bb9f 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -363,7 +363,7 @@ test "comptime modification of const struct field" { } test "refer to the type of a generic function" { - const Func = fn (type) void; + const Func = fn (comptime type) void; const f: Func = doNothingWithType; f(i32); } diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index f7cc179f5e..d49c534709 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -427,7 +427,7 @@ test "generic function passed as comptime argument" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const S = struct { - fn doMath(comptime f: fn (type, i32, i32) error{Overflow}!i32, a: i32, b: i32) !void { + fn doMath(comptime f: fn (comptime type, i32, i32) error{Overflow}!i32, a: i32, b: i32) !void { const result = try f(i32, a, b); try expect(result == 11); } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index b45fa171cb..1ba81695b8 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1511,7 +1511,7 @@ test "if inside struct init inside if" { test "optional generic function label struct field" { const Options = struct { - isFoo: ?fn (type) u8 = defaultIsFoo, + isFoo: ?fn (comptime type) u8 = defaultIsFoo, fn defaultIsFoo(comptime _: type) u8 { return 123; } diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index 6682b99f4d..79de5fd442 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -238,7 +238,7 @@ test "comptime parameters not converted to anytype in function type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - const T = fn (fn (type) void, void) void; + const T = fn (comptime fn (comptime type) void, void) void; try expectEqualStrings("fn (comptime fn (comptime type) void, void) void", @typeName(T)); } diff --git a/test/cases/compile_errors/arg_to_non_comptime_param_with_comptime_only_type_is_not_evaluated_at_comptime.zig b/test/cases/compile_errors/arg_to_non_comptime_param_with_comptime_only_type_is_not_evaluated_at_comptime.zig new file mode 100644 index 0000000000..02a6464339 --- /dev/null +++ b/test/cases/compile_errors/arg_to_non_comptime_param_with_comptime_only_type_is_not_evaluated_at_comptime.zig @@ -0,0 +1,36 @@ +//! Whew, that filename is a bit of a mouthful! +//! To maximise consistency with other parts of the language, function arguments expressions are +//! only *evaluated* at comptime if the parameter is declared `comptime`. If the parameter type is +//! comptime-only, but the parameter is not declared `comptime`, the evaluation happens at runtime, +//! and the value is just comptime-resolved. + +export fn foo() void { + // This function is itself generic, with the comptime-only parameter being generic. + simpleGeneric(type, if (cond()) u8 else u16); +} + +export fn bar() void { + // This function is not generic; once `Wrapper` is called, its parameter type is immediately known. + Wrapper(type).inner(if (cond()) u8 else u16); +} + +fn simpleGeneric(comptime T: type, _: T) void {} + +fn Wrapper(comptime T: type) type { + return struct { + fn inner(_: T) void {} + }; +} + +fn cond() bool { + return true; +} + +// error +// +// :9:25: error: value with comptime-only type 'type' depends on runtime control flow +// :9:33: note: runtime control flow here +// :9:25: note: types are not available at runtime +// :14:25: error: value with comptime-only type 'type' depends on runtime control flow +// :14:33: note: runtime control flow here +// :14:25: note: types are not available at runtime diff --git a/test/cases/compile_errors/bad_usage_of_call.zig b/test/cases/compile_errors/bad_usage_of_call.zig index 3b7abe53f6..6eca5d7e3d 100644 --- a/test/cases/compile_errors/bad_usage_of_call.zig +++ b/test/cases/compile_errors/bad_usage_of_call.zig @@ -42,8 +42,8 @@ noinline fn dummy2() void {} // :2:23: error: expected a tuple, found 'void' // :5:21: error: unable to perform 'never_inline' call at compile-time // :8:21: error: unable to perform 'never_tail' call at compile-time -// :11:5: error: 'never_inline' call of inline function +// :11:5: error: cannot perform inline call with 'never_inline' modifier // :15:26: error: modifier 'compile_time' requires a comptime-known function -// :18:9: error: 'always_inline' call of noinline function -// :21:9: error: 'always_inline' call of noinline function +// :18:9: error: inline call of noinline function +// :21:9: error: inline call of noinline function // :26:27: error: modifier 'always_inline' requires a comptime-known function diff --git a/test/cases/compile_errors/comptime_call_of_function_pointer.zig b/test/cases/compile_errors/comptime_call_of_function_pointer.zig index 574f55e9f3..a73f541b5e 100644 --- a/test/cases/compile_errors/comptime_call_of_function_pointer.zig +++ b/test/cases/compile_errors/comptime_call_of_function_pointer.zig @@ -4,7 +4,6 @@ export fn entry() void { } // error -// backend=stage2 -// target=native // -// :3:20: error: comptime call of function pointer +// :3:14: error: unable to resolve comptime value +// :3:14: note: function being called at comptime must be comptime-known diff --git a/test/cases/compile_errors/condition_comptime_reason_explained.zig b/test/cases/compile_errors/condition_comptime_reason_explained.zig index d641b6176b..4cd12d039b 100644 --- a/test/cases/compile_errors/condition_comptime_reason_explained.zig +++ b/test/cases/compile_errors/condition_comptime_reason_explained.zig @@ -36,11 +36,13 @@ pub export fn entry2() void { // // :8:9: error: unable to resolve comptime value // :19:15: note: called at comptime from here -// :7:13: note: function with comptime-only return type 'tmp.S' is evaluated at comptime +// :19:15: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime +// :7:13: note: return type declared here // :2:12: note: struct requires comptime because of this field // :2:12: note: use '*const fn () void' for a function pointer type // :22:13: error: unable to resolve comptime value // :32:19: note: called at comptime from here -// :21:17: note: function with comptime-only return type 'tmp.S' is evaluated at comptime +// :32:19: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime +// :21:17: note: return type declared here // :2:12: note: struct requires comptime because of this field // :2:12: note: use '*const fn () void' for a function pointer type diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index c4e4649892..f4ce9efdd8 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -1,54 +1,7 @@ -const std = @import("std"); - -const Error = error{Something}; - -fn next() Error!void { - return; -} - -fn parse(comptime T: type, allocator: std.mem.Allocator) !void { - parseFree(T, undefined, allocator); - _ = (try next()) != null; -} - -fn parseFree(comptime T: type, value: T, allocator: std.mem.Allocator) void { - switch (@typeInfo(T)) { - .@"struct" => |structInfo| { - inline for (structInfo.fields) |field| { - if (!field.is_comptime) - parseFree(field.type, undefined, allocator); - } - }, - .pointer => |ptrInfo| { - switch (ptrInfo.size) { - .One => { - parseFree(ptrInfo.child, value.*, allocator); - }, - .Slice => { - for (value) |v| - parseFree(ptrInfo.child, v, allocator); - }, - else => unreachable, - } - }, - else => unreachable, - } -} - -pub export fn entry() void { - const allocator = std.testing.failing_allocator; - _ = parse(std.StringArrayHashMap(bool), allocator) catch return; +export fn foo(ptr: *anyopaque) void { + _ = ptr.*; } // error -// target=native -// backend=llvm // -// :11:22: error: comparison of 'void' with null -// :25:51: error: cannot load opaque type 'anyopaque' -// :25:51: error: values of type 'fn (*anyopaque, usize, u8, usize) ?[*]u8' must be comptime-known, but operand value is runtime-known -// :25:51: note: use '*const fn (*anyopaque, usize, u8, usize) ?[*]u8' for a function pointer type -// :25:51: error: values of type 'fn (*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known -// :25:51: note: use '*const fn (*anyopaque, []u8, u8, usize, usize) bool' for a function pointer type -// :25:51: error: values of type 'fn (*anyopaque, []u8, u8, usize) void' must be comptime-known, but operand value is runtime-known -// :25:51: note: use '*const fn (*anyopaque, []u8, u8, usize) void' for a function pointer type +// :2:12: error: cannot load opaque type 'anyopaque' diff --git a/test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig b/test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig index 7901808f3c..d1f6599f36 100644 --- a/test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig +++ b/test/cases/compile_errors/explain_why_fn_is_called_at_comptime.zig @@ -15,6 +15,7 @@ pub export fn entry() void { // error // // :12:13: error: unable to resolve comptime value -// :7:16: note: function with comptime-only return type 'tmp.S' is evaluated at comptime +// :12:12: note: call to function with comptime-only return type 'tmp.S' is evaluated at comptime +// :7:16: note: return type declared here // :2:12: note: struct requires comptime because of this field // :2:12: note: use '*const fn () void' for a function pointer type diff --git a/test/cases/compile_errors/explain_why_generic_fn_is_called_at_comptime.zig b/test/cases/compile_errors/explain_why_generic_fn_is_called_at_comptime.zig index cfec89d5a7..f57bd44f88 100644 --- a/test/cases/compile_errors/explain_why_generic_fn_is_called_at_comptime.zig +++ b/test/cases/compile_errors/explain_why_generic_fn_is_called_at_comptime.zig @@ -17,6 +17,7 @@ pub export fn entry() void { // error // // :15:13: error: unable to resolve comptime value -// :9:38: note: generic function instantiated with comptime-only return type 'tmp.S(fn () void)' is evaluated at comptime +// :15:12: note: call to generic function instantiated with comptime-only return type 'tmp.S(fn () void)' is evaluated at comptime +// :9:38: note: return type declared here // :3:16: note: struct requires comptime because of this field // :3:16: note: use '*const fn () void' for a function pointer type diff --git a/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig b/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig index 829db19d87..ebef24d293 100644 --- a/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig +++ b/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig @@ -10,8 +10,7 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // -// :5:16: error: runtime-known argument passed to comptime parameter -// :1:17: note: declared comptime here +// :5:16: error: unable to resolve comptime value +// :5:16: note: argument to comptime parameter must be comptime-known +// :1:8: note: parameter declared comptime here diff --git a/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig b/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig index 1d45ce86db..4db27f0620 100644 --- a/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig +++ b/test/cases/compile_errors/generic_function_instantiation_inherits_parent_branch_quota.zig @@ -22,9 +22,8 @@ fn Type(comptime n: usize) type { } // error -// backend=stage2 -// target=native // // :21:16: error: evaluation exceeded 1001 backwards branches // :21:16: note: use @setEvalBranchQuota() to raise the branch limit from 1001 // :16:34: note: called from here +// :8:15: note: called from here diff --git a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig index a4aaf8eb91..1be4360317 100644 --- a/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig +++ b/test/cases/compile_errors/generic_instantiation_failure_in_generic_function_return_type.zig @@ -40,3 +40,4 @@ pub fn is(comptime id: std.builtin.TypeId) TraitFn { // target=native // // :8:48: error: expected type 'type', found 'bool' +// :5:21: note: called from here diff --git a/test/cases/compile_errors/generic_method_call_with_invalid_param.zig b/test/cases/compile_errors/generic_method_call_with_invalid_param.zig index 6ce431461b..04eba6e99e 100644 --- a/test/cases/compile_errors/generic_method_call_with_invalid_param.zig +++ b/test/cases/compile_errors/generic_method_call_with_invalid_param.zig @@ -22,12 +22,11 @@ const S = struct { }; // error -// backend=stage2 -// target=native // // :3:18: error: expected type 'bool', found 'void' // :19:43: note: parameter type declared here // :8:18: error: expected type 'void', found 'bool' // :20:43: note: parameter type declared here -// :15:26: error: runtime-known argument passed to comptime parameter -// :21:57: note: declared comptime here +// :15:26: error: unable to resolve comptime value +// :15:26: note: argument to comptime parameter must be comptime-known +// :21:48: note: parameter declared comptime here diff --git a/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig index a87f628396..b06767111d 100644 --- a/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig +++ b/test/cases/compile_errors/global_variable_initializer_must_be_constant_expression.zig @@ -5,7 +5,5 @@ export fn entry() i32 { } // error -// backend=stage2 -// target=native // // :2:14: error: comptime call of extern function diff --git a/test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig b/test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig index 67079c8a2d..abeb4bcaae 100644 --- a/test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig +++ b/test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig @@ -10,8 +10,7 @@ pub export fn entry() void { } // error -// backend=stage2 -// target=native // // :5:18: error: unable to resolve comptime value // :5:18: note: argument to comptime parameter must be comptime-known +// :1:24: note: parameter declared comptime here diff --git a/test/cases/compile_errors/invalid_extern_function_call.zig b/test/cases/compile_errors/invalid_extern_function_call.zig index c270c3bf48..36a8ac3fd7 100644 --- a/test/cases/compile_errors/invalid_extern_function_call.zig +++ b/test/cases/compile_errors/invalid_extern_function_call.zig @@ -9,8 +9,6 @@ export fn entry1() void { } // error -// backend=stage2 -// target=native // -// :4:15: error: comptime call of extern function pointer -// :8:5: error: inline call of extern function pointer +// :4:15: error: comptime call of extern function +// :8:5: error: inline call of extern function diff --git a/test/cases/compile_errors/invalid_pointer_for_var_type.zig b/test/cases/compile_errors/invalid_pointer_for_var_type.zig index d94c7bd3b8..2d80718b4f 100644 --- a/test/cases/compile_errors/invalid_pointer_for_var_type.zig +++ b/test/cases/compile_errors/invalid_pointer_for_var_type.zig @@ -7,7 +7,5 @@ export fn f() void { } // error -// backend=stage2 -// target=native // // :2:16: error: comptime call of extern function diff --git a/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig b/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig index 99f4b7a193..93718771b4 100644 --- a/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig +++ b/test/cases/compile_errors/nested_generic_function_param_type_mismatch.zig @@ -19,6 +19,6 @@ pub export fn entry() void { // backend=llvm // target=native // -// :15:28: error: expected type '*const fn (comptime type, u8, u8) u32', found '*const fn (void, u8, u8) u32' -// :15:28: note: pointer type child 'fn (void, u8, u8) u32' cannot cast into pointer type child 'fn (comptime type, u8, u8) u32' +// :15:28: error: expected type '*const fn (type, u8, u8) u32', found '*const fn (void, u8, u8) u32' +// :15:28: note: pointer type child 'fn (void, u8, u8) u32' cannot cast into pointer type child 'fn (type, u8, u8) u32' // :15:28: note: non-generic function cannot cast into a generic function diff --git a/test/cases/compile_errors/never_inline_call_of_inline_fn_with_comptime_param.zig b/test/cases/compile_errors/never_inline_call_of_inline_fn_with_comptime_param.zig index f1eadb7625..20556f1cf5 100644 --- a/test/cases/compile_errors/never_inline_call_of_inline_fn_with_comptime_param.zig +++ b/test/cases/compile_errors/never_inline_call_of_inline_fn_with_comptime_param.zig @@ -19,5 +19,5 @@ export fn entry2() void { // error // -// :14:5: error: 'never_inline' call of inline function -// :17:5: error: 'never_inline' call of inline function +// :14:5: error: cannot perform inline call with 'never_inline' modifier +// :17:5: error: cannot perform inline call with 'never_inline' modifier diff --git a/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig index c6d4e04fc1..808f94d9bf 100644 --- a/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig +++ b/test/cases/compile_errors/non-const_expression_in_struct_literal_outside_function.zig @@ -9,7 +9,5 @@ export fn entry() usize { } // error -// backend=stage2 -// target=native // // :4:27: error: comptime call of extern function diff --git a/test/cases/compile_errors/non_comptime_param_in_comptime_function.zig b/test/cases/compile_errors/non_comptime_param_in_comptime_function.zig index cfb68bbcf6..3f913991a6 100644 --- a/test/cases/compile_errors/non_comptime_param_in_comptime_function.zig +++ b/test/cases/compile_errors/non_comptime_param_in_comptime_function.zig @@ -11,5 +11,6 @@ export fn entry() void { // error // // :8:11: error: unable to resolve comptime value -// :1:20: note: function with comptime-only return type 'type' is evaluated at comptime -// :1:20: note: types are not available at runtime +// :8:10: note: call to function with comptime-only return type 'type' is evaluated at comptime +// :1:20: note: return type declared here +// :8:10: note: types are not available at runtime diff --git a/test/cases/compile_errors/recursive_inline_fn.zig b/test/cases/compile_errors/recursive_inline_fn.zig index 1cecfdbada..7f96ad6709 100644 --- a/test/cases/compile_errors/recursive_inline_fn.zig +++ b/test/cases/compile_errors/recursive_inline_fn.zig @@ -29,8 +29,10 @@ pub export fn entry2() void { } // error -// backend=stage2 -// target=native // // :5:27: error: inline call is recursive +// :12:12: note: called from here // :24:10: error: inline call is recursive +// :20:10: note: called from here +// :16:11: note: called from here +// :28:10: note: called from here diff --git a/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig b/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig index 8c1d31adf2..e3576c179b 100644 --- a/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig +++ b/test/cases/compile_errors/runtime_operation_in_comptime_scope.zig @@ -27,8 +27,9 @@ var rt: u32 = undefined; // :19:5: note: operation is runtime due to this operand // :14:8: note: called at comptime from here // :10:12: note: called at comptime from here -// :13:10: note: function with comptime-only return type 'type' is evaluated at comptime -// :13:10: note: types are not available at runtime +// :10:12: note: call to function with comptime-only return type 'type' is evaluated at comptime +// :13:10: note: return type declared here +// :10:12: note: types are not available at runtime // :2:8: note: called from here // :19:8: error: unable to evaluate comptime expression // :19:5: note: operation is runtime due to this operand diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f566464bac..13d97ba256 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -57,8 +57,9 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void { \\} , &[_][]const u8{ ":3:12: error: unable to resolve comptime value", - ":2:55: note: generic function instantiated with comptime-only return type '?fn () void' is evaluated at comptime", - ":2:55: note: use '*const fn () void' for a function pointer type", + ":3:19: note: call to generic function instantiated with comptime-only return type '?fn () void' is evaluated at comptime", + ":2:55: note: return type declared here", + ":3:19: note: use '*const fn () void' for a function pointer type", }); case.addSourceFile("b.zig", \\pub const ElfDynLib = struct { @@ -193,10 +194,12 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void { \\ import.anytypeFunction(S{ .x = x, .y = u32 }); \\} , &[_][]const u8{ - ":4:33: error: runtime-known argument passed to comptime parameter", - ":1:38: note: declared comptime here", - ":8:36: error: runtime-known argument passed to comptime parameter", - ":2:41: note: declared comptime here", + ":4:33: error: unable to resolve comptime value", + ":4:33: note: argument to comptime parameter must be comptime-known", + ":1:29: note: parameter declared comptime here", + ":8:36: error: unable to resolve comptime value", + ":8:36: note: argument to comptime parameter must be comptime-known", + ":2:32: note: parameter declared comptime here", ":13:32: error: unable to resolve comptime value", ":13:32: note: initializer of comptime-only struct 'tmp.callAnytypeFunctionWithRuntimeComptimeOnlyType.S' must be comptime-known", ":12:35: note: struct requires comptime because of this field", diff --git a/test/standalone/simple/std_enums_big_enums.zig b/test/standalone/simple/std_enums_big_enums.zig index c2358bafc0..1ad24a4147 100644 --- a/test/standalone/simple/std_enums_big_enums.zig +++ b/test/standalone/simple/std_enums_big_enums.zig @@ -31,6 +31,7 @@ pub fn main() void { var bounded_multiset = std.enums.BoundedEnumMultiset(big.Big, u8).init(.{}); _ = &bounded_multiset; + @setEvalBranchQuota(3000); var array = std.enums.EnumArray(big.Big, u8).init(undefined); array = std.enums.EnumArray(big.Big, u8).initDefault(123, .{}); }