authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-08-01 22:42:01+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-08-10 10:00:26+01:00
logf2c8fa769a92eb61c13f2cc0f75c526c8fd729a9
tree37f611ef1bd700533987eec239667815a491df6d
parent93e53d1e00793d769d4ee39b3cbfd0c88257687d
signaturelock-open Commit is signed but in an unrecognized format.

Sema: refactor generic calls to interleave argument analysis and parameter type resolution

AstGen provides all function call arguments with a result location, referenced through the call instruction index. The idea is that this should be the parameter type, but for `anytype` parameters, we use generic poison, which is required to be handled correctly. Previously, generic instantiations and inline calls worked by evaluating all args in advance, before resolving generic parameter types. This means any generic parameter (not just `anytype` ones) had generic poison result types. This caused missing result locations in some cases. Additionally, the generic instantiation logic caused `zirParam` to analyze the argument types a second time before coercion. This meant that for nominal types (struct/enum/etc), a *new* type was created, distinct to the result type which was previously forwarded to the argument expression. This commit fixes both of these issues. Generic parameter type resolution is now interleaved with argument analysis, so that we don't have unnecessary generic poison types, and generic instantiation logic now handles parameters itself rather than falling through to the standard zirParam logic, so avoids duplicating the types. Resolves: #16566 Resolves: #16258 Resolves: #16753

4 files changed, 604 insertions(+), 447 deletions(-)

src/Air.zig+4-2
......@@ -1528,11 +1528,13 @@ pub fn refToInterned(ref: Inst.Ref) ?InternPool.Index {
15281528}
15291529
15301530pub fn internedToRef(ip_index: InternPool.Index) Inst.Ref {
1531 assert(@intFromEnum(ip_index) >> 31 == 0);
15321531 return switch (ip_index) {
15331532 .var_args_param_type => .var_args_param_type,
15341533 .none => .none,
1535 else => @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index)))),
1534 else => {
1535 assert(@intFromEnum(ip_index) >> 31 == 0);
1536 return @enumFromInt(@as(u31, @intCast(@intFromEnum(ip_index))));
1537 },
15361538 };
15371539}
15381540
src/Module.zig-29
......@@ -5938,35 +5938,6 @@ pub fn paramSrc(
59385938 unreachable;
59395939}
59405940
5941pub fn argSrc(
5942 mod: *Module,
5943 call_node_offset: i32,
5944 decl: *Decl,
5945 start_arg_i: usize,
5946 bound_arg_src: ?LazySrcLoc,
5947) LazySrcLoc {
5948 @setCold(true);
5949 const gpa = mod.gpa;
5950 if (start_arg_i == 0 and bound_arg_src != null) return bound_arg_src.?;
5951 const arg_i = start_arg_i - @intFromBool(bound_arg_src != null);
5952 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
5953 // In this case we emit a warning + a less precise source location.
5954 log.warn("unable to load {s}: {s}", .{
5955 decl.getFileScope(mod).sub_file_path, @errorName(err),
5956 });
5957 return LazySrcLoc.nodeOffset(0);
5958 };
5959 const node = decl.relativeToNodeIndex(call_node_offset);
5960 var args: [1]Ast.Node.Index = undefined;
5961 const call_full = tree.fullCall(&args, node) orelse {
5962 assert(tree.nodes.items(.tag)[node] == .builtin_call);
5963 const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1];
5964 const call_args_offset = decl.nodeIndexToRelative(call_args_node);
5965 return mod.initSrc(call_args_offset, decl, arg_i);
5966 };
5967 return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(call_full.ast.params[arg_i]));
5968}
5969
59705941pub fn initSrc(
59715942 mod: *Module,
59725943 init_node_offset: i32,
src/Sema.zig+539-416
......@@ -70,7 +70,6 @@ generic_owner: InternPool.Index = .none,
7070/// instantiation can point back to the instantiation site in addition to the
7171/// declaration site.
7272generic_call_src: LazySrcLoc = .unneeded,
73generic_bound_arg_src: ?LazySrcLoc = null,
7473/// Corresponds to `generic_call_src`.
7574generic_call_decl: Decl.OptionalIndex = .none,
7675/// The key is types that must be fully resolved prior to machine code
......@@ -1401,22 +1400,22 @@ fn analyzeBodyInner(
14011400 continue;
14021401 },
14031402 .param => {
1404 try sema.zirParam(block, inst, i, false);
1403 try sema.zirParam(block, inst, false);
14051404 i += 1;
14061405 continue;
14071406 },
14081407 .param_comptime => {
1409 try sema.zirParam(block, inst, i, true);
1408 try sema.zirParam(block, inst, true);
14101409 i += 1;
14111410 continue;
14121411 },
14131412 .param_anytype => {
1414 try sema.zirParamAnytype(block, inst, i, false);
1413 try sema.zirParamAnytype(block, inst, false);
14151414 i += 1;
14161415 continue;
14171416 },
14181417 .param_anytype_comptime => {
1419 try sema.zirParamAnytype(block, inst, i, true);
1418 try sema.zirParamAnytype(block, inst, true);
14201419 i += 1;
14211420 continue;
14221421 },
......@@ -6536,7 +6535,6 @@ fn zirCall(
65366535 defer tracy.end();
65376536
65386537 const mod = sema.mod;
6539 const ip = &mod.intern_pool;
65406538 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
65416539 const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
65426540 const call_src = inst_data.src();
......@@ -6560,96 +6558,62 @@ fn zirCall(
65606558 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
65616559 },
65626560 };
6563 var resolved_args: []Air.Inst.Ref = undefined;
6564 var bound_arg_src: ?LazySrcLoc = null;
6565 var func: Air.Inst.Ref = undefined;
6566 var arg_index: u32 = 0;
6567 switch (callee) {
6568 .direct => |func_inst| {
6569 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len);
6570 func = func_inst;
6571 },
6572 .method => |method| {
6573 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1);
6574 func = method.func_inst;
6575 resolved_args[0] = method.arg0_inst;
6576 arg_index += 1;
6577 bound_arg_src = callee_src;
6578 },
6579 }
6561 const func: Air.Inst.Ref = switch (callee) {
6562 .direct => |func_inst| func_inst,
6563 .method => |method| method.func_inst,
6564 };
65806565
65816566 const callee_ty = sema.typeOf(func);
6582 const total_args = args_len + @intFromBool(bound_arg_src != null);
6583 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null);
6584
6585 const args_body = sema.code.extra[extra.end..];
6567 const total_args = args_len + @intFromBool(callee == .method);
6568 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method);
65866569
6587 var input_is_error = false;
6570 // The block index before the call, so we can potentially insert an error trace save here later.
65886571 const block_index: Air.Inst.Index = @intCast(block.instructions.items.len);
65896572
6590 const func_ty_info = mod.typeToFunc(func_ty).?;
6591 const fn_params_len = func_ty_info.param_types.len;
6592 const parent_comptime = block.is_comptime;
6593 // `extra_index` and `arg_index` are separate since the bound function is passed as the first argument.
6594 var extra_index: usize = 0;
6595 var arg_start: u32 = args_len;
6596 while (extra_index < args_len) : ({
6597 extra_index += 1;
6598 arg_index += 1;
6599 }) {
6600 const arg_end = sema.code.extra[extra.end + extra_index];
6601 defer arg_start = arg_end;
6602
6603 // Generate args to comptime params in comptime block.
6604 defer block.is_comptime = parent_comptime;
6605 if (arg_index < @min(fn_params_len, 32) and func_ty_info.paramIsComptime(@intCast(arg_index))) {
6606 block.is_comptime = true;
6607 // TODO set comptime_reason
6608 }
6609
6610 sema.inst_map.putAssumeCapacity(inst, inst: {
6611 if (arg_index >= fn_params_len)
6612 break :inst Air.Inst.Ref.var_args_param_type;
6573 // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the
6574 // error trace potentially dirty).
6575 var input_is_error = false;
66136576
6614 if (func_ty_info.param_types.get(ip)[arg_index] == .generic_poison_type)
6615 break :inst Air.Inst.Ref.generic_poison_type;
6577 const args_info: CallArgsInfo = .{ .zir_call = .{
6578 .bound_arg = switch (callee) {
6579 .direct => .none,
6580 .method => |method| method.arg0_inst,
6581 },
6582 .bound_arg_src = callee_src,
6583 .call_inst = inst,
6584 .call_node_offset = inst_data.src_node,
6585 .num_args = args_len,
6586 .args_body = sema.code.extra[extra.end..],
6587 .any_arg_is_error = &input_is_error,
6588 } };
66166589
6617 break :inst try sema.addType(func_ty_info.param_types.get(ip)[arg_index].toType());
6618 });
6590 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
6591 const call_dbg_node = inst - 1;
6592 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
66196593
6620 const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst);
6621 const resolved_ty = sema.typeOf(resolved);
6622 if (resolved_ty.zigTypeTag(mod) == .NoReturn) {
6623 return resolved;
6624 }
6625 if (resolved_ty.isError(mod)) {
6626 input_is_error = true;
6627 }
6628 resolved_args[arg_index] = resolved;
6629 }
66306594 if (sema.owner_func_index == .none or
6631 !ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn)
6595 !mod.intern_pool.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn)
66326596 {
6633 input_is_error = false; // input was an error type, but no errorable fn's were actually called
6597 // No errorable fn actually called; we have no error return trace
6598 input_is_error = false;
66346599 }
66356600
6636 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
6637 const call_dbg_node = inst - 1;
6638
66396601 if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and
66406602 !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace))
66416603 {
6642 const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: {
6643 break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
6644 };
6645
66466604 const return_ty = sema.typeOf(call_inst);
66476605 if (modifier != .always_tail and return_ty.isNoReturn(mod))
66486606 return call_inst; // call to "fn(...) noreturn", don't pop
66496607
6608 // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it
6609 // *before* the recursive call. This will be a bit tricky to do and probably requires
6610 // moving this logic into analyzeCall. But that's probably a good idea anyway.
6611 if (modifier == .always_tail)
6612 return call_inst;
6613
66506614 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
66516615 // need to clean-up our own trace if we were passed to a non-error-handling expression.
6652 if (input_is_error or (pop_error_return_trace and modifier != .always_tail and return_ty.isError(mod))) {
6616 if (input_is_error or (pop_error_return_trace and return_ty.isError(mod))) {
66536617 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
66546618 const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
66556619 const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index");
......@@ -6669,12 +6633,9 @@ fn zirCall(
66696633 try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
66706634 }
66716635
6672 if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible.
6673 return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
6674
66756636 return call_inst;
66766637 } else {
6677 return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
6638 return call_inst;
66786639 }
66796640}
66806641
......@@ -6781,7 +6742,19 @@ fn callBuiltin(
67816742 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
67826743 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
67836744 }
6784 _ = try sema.analyzeCall(block, builtin_fn, func_ty, call_src, call_src, modifier, false, args, null, null, operation);
6745
6746 _ = try sema.analyzeCall(
6747 block,
6748 builtin_fn,
6749 func_ty,
6750 call_src,
6751 call_src,
6752 modifier,
6753 false,
6754 .{ .resolved = .{ .src = call_src, .args = args } },
6755 null,
6756 operation,
6757 );
67856758}
67866759
67876760const CallOperation = enum {
......@@ -6792,6 +6765,251 @@ const CallOperation = enum {
67926765 @"error return",
67936766};
67946767
6768const CallArgsInfo = union(enum) {
6769 /// The full list of resolved (but uncoerced) arguments is known ahead of time.
6770 resolved: struct {
6771 src: LazySrcLoc,
6772 args: []const Air.Inst.Ref,
6773 },
6774
6775 /// The list of resolved (but uncoerced) arguments is known ahead of time, but
6776 /// originated from a usage of the @call builtin at the given node offset.
6777 call_builtin: struct {
6778 call_node_offset: i32,
6779 args: []const Air.Inst.Ref,
6780 },
6781
6782 /// This call corresponds to a ZIR call instruction. The arguments have not yet been
6783 /// resolved. They must be resolved by `analyzeCall` so that argument resolution and
6784 /// generic instantiation may be interleaved. This is required for RLS to work on
6785 /// generic parameters.
6786 zir_call: struct {
6787 /// This may be `none`, in which case it is ignored. Otherwise, it is the
6788 /// already-resolved value of the first argument, from method call syntax.
6789 bound_arg: Air.Inst.Ref,
6790 /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`.
6791 bound_arg_src: LazySrcLoc,
6792 /// The ZIR call instruction. The parameter type is placed at this index while
6793 /// analyzing arguments.
6794 call_inst: Zir.Inst.Index,
6795 /// The node offset of `call_inst`.
6796 call_node_offset: i32,
6797 /// The number of arguments to this call, not including `bound_arg`.
6798 num_args: u32,
6799 /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it
6800 /// is not `none`). Format is precisely the same as trailing data of ZIR `call`.
6801 args_body: []const Zir.Inst.Index,
6802 /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type.
6803 /// This is used by the caller to restore the error return trace when necessary.
6804 any_arg_is_error: *bool,
6805 },
6806
6807 fn count(cai: CallArgsInfo) usize {
6808 return switch (cai) {
6809 inline .resolved, .call_builtin => |resolved| resolved.args.len,
6810 .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none),
6811 };
6812 }
6813
6814 fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc {
6815 return switch (cai) {
6816 .resolved => |resolved| resolved.src,
6817 .call_builtin => |call_builtin| .{ .call_arg = .{
6818 .decl = block.src_decl,
6819 .call_node_offset = call_builtin.call_node_offset,
6820 .arg_index = @intCast(arg_index),
6821 } },
6822 .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) {
6823 return zir_call.bound_arg_src;
6824 } else .{ .call_arg = .{
6825 .decl = block.src_decl,
6826 .call_node_offset = zir_call.call_node_offset,
6827 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)),
6828 } },
6829 };
6830 }
6831
6832 /// Analyzes the arg at `arg_index` and coerces it to `param_ty`.
6833 /// `param_ty` may be `generic_poison` or `var_args_param`.
6834 /// `func_ty_info` may be the type before instantiation, even if a generic
6835 /// instantiation has been partially completed.
6836 fn analyzeArg(
6837 cai: CallArgsInfo,
6838 sema: *Sema,
6839 block: *Block,
6840 arg_index: usize,
6841 param_ty: Type,
6842 func_ty_info: InternPool.Key.FuncType,
6843 func_inst: Air.Inst.Ref,
6844 ) CompileError!Air.Inst.Ref {
6845 const mod = sema.mod;
6846 const param_count = func_ty_info.param_types.len;
6847 switch (param_ty.toIntern()) {
6848 .generic_poison_type, .var_args_param_type => {},
6849 else => try sema.queueFullTypeResolution(param_ty),
6850 }
6851 const uncoerced_arg: Air.Inst.Ref = switch (cai) {
6852 inline .resolved, .call_builtin => |resolved| resolved.args[arg_index],
6853 .zir_call => |zir_call| arg_val: {
6854 const has_bound_arg = zir_call.bound_arg != .none;
6855 if (arg_index == 0 and has_bound_arg) {
6856 break :arg_val zir_call.bound_arg;
6857 }
6858 const real_arg_idx = arg_index - @intFromBool(has_bound_arg);
6859
6860 const arg_body = if (real_arg_idx == 0) blk: {
6861 const start = zir_call.num_args;
6862 const end = zir_call.args_body[0];
6863 break :blk zir_call.args_body[start..end];
6864 } else blk: {
6865 const start = zir_call.args_body[real_arg_idx - 1];
6866 const end = zir_call.args_body[real_arg_idx];
6867 break :blk zir_call.args_body[start..end];
6868 };
6869
6870 // Generate args to comptime params in comptime block
6871 const parent_comptime = block.is_comptime;
6872 defer block.is_comptime = parent_comptime;
6873 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx`
6874 if (arg_index < @min(param_count, 32) and func_ty_info.paramIsComptime(@intCast(arg_index))) {
6875 block.is_comptime = true;
6876 // TODO set comptime_reason
6877 }
6878 // Give the arg its result type
6879 sema.inst_map.putAssumeCapacity(zir_call.call_inst, try sema.addType(param_ty));
6880 // Resolve the arg!
6881 const uncoerced_arg = try sema.resolveBody(block, arg_body, zir_call.call_inst);
6882
6883 if (sema.typeOf(uncoerced_arg).zigTypeTag(mod) == .NoReturn) {
6884 // This terminates resolution of arguments. The caller should
6885 // propagate this.
6886 return uncoerced_arg;
6887 }
6888
6889 if (sema.typeOf(uncoerced_arg).isError(mod)) {
6890 zir_call.any_arg_is_error.* = true;
6891 }
6892
6893 break :arg_val uncoerced_arg;
6894 },
6895 };
6896 switch (param_ty.toIntern()) {
6897 .generic_poison_type => return uncoerced_arg,
6898 .var_args_param_type => return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)),
6899 else => return sema.coerceExtra(
6900 block,
6901 param_ty,
6902 uncoerced_arg,
6903 cai.argSrc(block, arg_index),
6904 .{ .param_src = .{
6905 .func_inst = func_inst,
6906 .param_i = @intCast(arg_index),
6907 } },
6908 ) catch |err| switch (err) {
6909 error.NotCoercible => unreachable,
6910 else => |e| return e,
6911 },
6912 }
6913 }
6914};
6915
6916/// While performing an inline call, we need to switch between two Sema states a few times: the
6917/// state for the caller (with the callee's `code`, `fn_ret_ty`, etc), and the state for the callee.
6918/// These cannot be two separate Sema instances as they must share AIR.
6919/// Therefore, this struct acts as a helper to switch between the two.
6920/// This switching is required during argument evaluation, where function argument analysis must be
6921/// interleaved with resolving generic parameter types.
6922const InlineCallSema = struct {
6923 sema: *Sema,
6924 cur: enum {
6925 caller,
6926 callee,
6927 },
6928
6929 other_code: Zir,
6930 other_func_index: InternPool.Index,
6931 other_fn_ret_ty: Type,
6932 other_fn_ret_ty_ies: ?*InferredErrorSet,
6933 other_inst_map: InstMap,
6934 other_error_return_trace_index_on_fn_entry: Air.Inst.Ref,
6935 other_generic_owner: InternPool.Index,
6936 other_generic_call_src: LazySrcLoc,
6937 other_generic_call_decl: Decl.OptionalIndex,
6938
6939 /// Sema should currently be set up for the caller (i.e. unchanged yet). This init will not
6940 /// change that. The other parameters contain data for the callee Sema. The other modified
6941 /// Sema fields are all initialized to default values for the callee.
6942 /// Must call deinit on the result.
6943 fn init(
6944 sema: *Sema,
6945 callee_code: Zir,
6946 callee_func_index: InternPool.Index,
6947 callee_error_return_trace_index_on_fn_entry: Air.Inst.Ref,
6948 ) InlineCallSema {
6949 return .{
6950 .sema = sema,
6951 .cur = .caller,
6952 .other_code = callee_code,
6953 .other_func_index = callee_func_index,
6954 .other_fn_ret_ty = Type.void,
6955 .other_fn_ret_ty_ies = null,
6956 .other_inst_map = .{},
6957 .other_error_return_trace_index_on_fn_entry = callee_error_return_trace_index_on_fn_entry,
6958 .other_generic_owner = .none,
6959 .other_generic_call_src = .unneeded,
6960 .other_generic_call_decl = .none,
6961 };
6962 }
6963
6964 /// Switch back to the caller Sema if necessary and free all temporary state of the callee Sema.
6965 fn deinit(ics: *InlineCallSema) void {
6966 switch (ics.cur) {
6967 .caller => {},
6968 .callee => ics.swap(),
6969 }
6970 // Callee Sema owns the inst_map memory
6971 ics.other_inst_map.deinit(ics.sema.gpa);
6972 ics.* = undefined;
6973 }
6974
6975 /// Returns a Sema instance suitable for usage from the caller context.
6976 fn caller(ics: *InlineCallSema) *Sema {
6977 switch (ics.cur) {
6978 .caller => {},
6979 .callee => ics.swap(),
6980 }
6981 return ics.sema;
6982 }
6983
6984 /// Returns a Sema instance suitable for usage from the callee context.
6985 fn callee(ics: *InlineCallSema) *Sema {
6986 switch (ics.cur) {
6987 .caller => ics.swap(),
6988 .callee => {},
6989 }
6990 return ics.sema;
6991 }
6992
6993 /// Internal use only. Swaps to the other Sema state.
6994 fn swap(ics: *InlineCallSema) void {
6995 ics.cur = switch (ics.cur) {
6996 .caller => .callee,
6997 .callee => .caller,
6998 };
6999 // zig fmt: off
7000 std.mem.swap(Zir, &ics.sema.code, &ics.other_code);
7001 std.mem.swap(InternPool.Index, &ics.sema.func_index, &ics.other_func_index);
7002 std.mem.swap(Type, &ics.sema.fn_ret_ty, &ics.other_fn_ret_ty);
7003 std.mem.swap(?*InferredErrorSet, &ics.sema.fn_ret_ty_ies, &ics.other_fn_ret_ty_ies);
7004 std.mem.swap(InstMap, &ics.sema.inst_map, &ics.other_inst_map);
7005 std.mem.swap(InternPool.Index, &ics.sema.generic_owner, &ics.other_generic_owner);
7006 std.mem.swap(LazySrcLoc, &ics.sema.generic_call_src, &ics.other_generic_call_src);
7007 std.mem.swap(Decl.OptionalIndex, &ics.sema.generic_call_decl, &ics.other_generic_call_decl);
7008 std.mem.swap(Air.Inst.Ref, &ics.sema.error_return_trace_index_on_fn_entry, &ics.other_error_return_trace_index_on_fn_entry);
7009 // zig fmt: on
7010 }
7011};
7012
67957013fn analyzeCall(
67967014 sema: *Sema,
67977015 block: *Block,
......@@ -6801,8 +7019,7 @@ fn analyzeCall(
68017019 call_src: LazySrcLoc,
68027020 modifier: std.builtin.CallModifier,
68037021 ensure_result_used: bool,
6804 uncasted_args: []const Air.Inst.Ref,
6805 bound_arg_src: ?LazySrcLoc,
7022 args_info: CallArgsInfo,
68067023 call_dbg_node: ?Zir.Inst.Index,
68077024 operation: CallOperation,
68087025) CompileError!Air.Inst.Ref {
......@@ -6811,7 +7028,6 @@ fn analyzeCall(
68117028
68127029 const callee_ty = sema.typeOf(func);
68137030 const func_ty_info = mod.typeToFunc(func_ty).?;
6814 const fn_params_len = func_ty_info.param_types.len;
68157031 const cc = func_ty_info.cc;
68167032 if (cc == .Naked) {
68177033 const maybe_decl = try sema.funcDeclSrc(func);
......@@ -6896,9 +7112,8 @@ fn analyzeCall(
68967112 func_src,
68977113 call_src,
68987114 ensure_result_used,
6899 uncasted_args,
7115 args_info,
69007116 call_tag,
6901 bound_arg_src,
69027117 call_dbg_node,
69037118 )) |some| {
69047119 return some;
......@@ -6973,31 +7188,23 @@ fn analyzeCall(
69737188 .block_inst = block_inst,
69747189 },
69757190 };
6976 // In order to save a bit of stack space, directly modify Sema rather
6977 // than create a child one.
6978 const parent_zir = sema.code;
7191
69797192 const module_fn = mod.funcInfo(module_fn_index);
69807193 const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
6981 sema.code = fn_owner_decl.getFileScope(mod).zir;
6982 defer sema.code = parent_zir;
6983
6984 try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body);
6985
6986 const parent_inst_map = sema.inst_map;
6987 sema.inst_map = .{};
6988 defer {
6989 sema.src = call_src;
6990 sema.inst_map.deinit(gpa);
6991 sema.inst_map = parent_inst_map;
6992 }
69937194
6994 const parent_func_index = sema.func_index;
6995 sema.func_index = module_fn_index;
6996 defer sema.func_index = parent_func_index;
7195 // We effectively want a child Sema here, but can't literally do that, because we need AIR
7196 // to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in
7197 // scope, we should use its `caller`/`callee` methods rather than using `sema` directly
7198 // whenever performing an operation where the difference matters.
7199 var ics = InlineCallSema.init(
7200 sema,
7201 fn_owner_decl.getFileScope(mod).zir,
7202 module_fn_index,
7203 block.error_return_trace_index,
7204 );
7205 defer ics.deinit();
69977206
6998 const parent_err_ret_index = sema.error_return_trace_index_on_fn_entry;
6999 sema.error_return_trace_index_on_fn_entry = block.error_return_trace_index;
7000 defer sema.error_return_trace_index_on_fn_entry = parent_err_ret_index;
7207 try mod.declareDeclDependencyType(ics.callee().owner_decl_index, module_fn.owner_decl, .function_body);
70017208
70027209 var wip_captures = try WipCaptureScope.init(gpa, fn_owner_decl.src_scope);
70037210 defer wip_captures.deinit();
......@@ -7053,37 +7260,37 @@ fn analyzeCall(
70537260 // the AIR instructions of the callsite. The callee could be a generic function
70547261 // which means its parameter type expressions must be resolved in order and used
70557262 // to successively coerce the arguments.
7056 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
7057 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, fn_info.param_body);
7263 const fn_info = ics.callee().code.getFnInfo(module_fn.zir_body_inst);
7264 try ics.callee().inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
70587265
70597266 var has_comptime_args = false;
70607267 var arg_i: u32 = 0;
70617268 for (fn_info.param_body) |inst| {
7062 const arg_src: LazySrcLoc = if (arg_i == 0 and bound_arg_src != null)
7063 bound_arg_src.?
7064 else
7065 .{ .call_arg = .{
7066 .decl = block.src_decl,
7067 .call_node_offset = call_src.node_offset.x,
7068 .arg_index = arg_i - @intFromBool(bound_arg_src != null),
7069 } };
7070 try sema.analyzeInlineCallArg(
7269 const opt_noreturn_ref = try analyzeInlineCallArg(
7270 &ics,
70717271 block,
70727272 &child_block,
7073 arg_src,
70747273 inst,
70757274 new_fn_info.param_types,
70767275 &arg_i,
7077 uncasted_args,
7276 args_info,
70787277 is_comptime_call,
70797278 &should_memoize,
70807279 memoized_arg_values,
7081 func_ty_info.param_types,
7280 func_ty_info,
70827281 func,
70837282 &has_comptime_args,
70847283 );
7284 if (opt_noreturn_ref) |ref| {
7285 // Analyzing this argument gave a ref of a noreturn type. Terminate argument analysis here.
7286 return ref;
7287 }
70857288 }
70867289
7290 // From here, we only really need to use the callee Sema. Make it the active one, then we
7291 // can just use `sema` directly.
7292 _ = ics.callee();
7293
70877294 if (!has_comptime_args and module_fn.analysis(ip).state == .sema_failure)
70887295 return error.AnalysisFail;
70897296
......@@ -7107,26 +7314,7 @@ fn analyzeCall(
71077314 else
71087315 try sema.resolveInst(fn_info.ret_ty_ref);
71097316 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
7110 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
7111 const parent_fn_ret_ty = sema.fn_ret_ty;
7112 const parent_fn_ret_ty_ies = sema.fn_ret_ty_ies;
7113 const parent_generic_owner = sema.generic_owner;
7114 const parent_generic_call_src = sema.generic_call_src;
7115 const parent_generic_bound_arg_src = sema.generic_bound_arg_src;
7116 const parent_generic_call_decl = sema.generic_call_decl;
7117 sema.fn_ret_ty = bare_return_type;
7118 sema.fn_ret_ty_ies = null;
7119 sema.generic_owner = .none;
7120 sema.generic_call_src = .unneeded;
7121 sema.generic_bound_arg_src = null;
7122 sema.generic_call_decl = .none;
7123 defer sema.fn_ret_ty = parent_fn_ret_ty;
7124 defer sema.fn_ret_ty_ies = parent_fn_ret_ty_ies;
7125 defer sema.generic_owner = parent_generic_owner;
7126 defer sema.generic_call_src = parent_generic_call_src;
7127 defer sema.generic_bound_arg_src = parent_generic_bound_arg_src;
7128 defer sema.generic_call_decl = parent_generic_call_decl;
7129
7317 sema.fn_ret_ty = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
71307318 if (module_fn.analysis(ip).inferred_error_set) {
71317319 // Create a fresh inferred error set type for inline/comptime calls.
71327320 const ies = try sema.arena.create(InferredErrorSet);
......@@ -7134,7 +7322,7 @@ fn analyzeCall(
71347322 sema.fn_ret_ty_ies = ies;
71357323 sema.fn_ret_ty = (try ip.get(gpa, .{ .error_union_type = .{
71367324 .error_set_type = .adhoc_inferred_error_set_type,
7137 .payload_type = bare_return_type.toIntern(),
7325 .payload_type = sema.fn_ret_ty.toIntern(),
71387326 } })).toType();
71397327 }
71407328
......@@ -7157,7 +7345,7 @@ fn analyzeCall(
71577345 new_fn_info.return_type = sema.fn_ret_ty.toIntern();
71587346 const new_func_resolved_ty = try mod.funcType(new_fn_info);
71597347 if (!is_comptime_call and !block.is_typeof) {
7160 try sema.emitDbgInline(block, parent_func_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
7348 try sema.emitDbgInline(block, sema.func_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
71617349
71627350 const zir_tags = sema.code.instructions.items(.tag);
71637351 for (fn_info.param_body) |param| switch (zir_tags[param]) {
......@@ -7191,7 +7379,7 @@ fn analyzeCall(
71917379 const err_msg = sema.err orelse return err;
71927380 if (mem.eql(u8, err_msg.msg, recursive_msg)) return err;
71937381 try sema.errNote(block, call_src, err_msg, "called from here", .{});
7194 err_msg.clearTrace(sema.gpa);
7382 err_msg.clearTrace(gpa);
71957383 return err;
71967384 },
71977385 else => |e| return e,
......@@ -7205,8 +7393,8 @@ fn analyzeCall(
72057393 try sema.emitDbgInline(
72067394 block,
72077395 module_fn_index,
7208 parent_func_index,
7209 mod.funcOwnerDeclPtr(parent_func_index).ty,
7396 sema.func_index,
7397 mod.funcOwnerDeclPtr(sema.func_index).ty,
72107398 .dbg_inline_end,
72117399 );
72127400 }
......@@ -7251,47 +7439,16 @@ fn analyzeCall(
72517439 } else res: {
72527440 assert(!func_ty_info.is_generic);
72537441
7254 const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len);
7255 for (uncasted_args, 0..) |uncasted_arg, i| {
7256 if (i < fn_params_len) {
7257 const opts: CoerceOpts = .{ .param_src = .{
7258 .func_inst = func,
7259 .param_i = @intCast(i),
7260 } };
7261 const param_ty = func_ty_info.param_types.get(ip)[i].toType();
7262 args[i] = sema.analyzeCallArg(
7263 block,
7264 .unneeded,
7265 param_ty,
7266 uncasted_arg,
7267 opts,
7268 ) catch |err| switch (err) {
7269 error.NeededSourceLocation => {
7270 const decl = mod.declPtr(block.src_decl);
7271 _ = try sema.analyzeCallArg(
7272 block,
7273 mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
7274 param_ty,
7275 uncasted_arg,
7276 opts,
7277 );
7278 unreachable;
7279 },
7280 else => |e| return e,
7281 };
7282 } else {
7283 args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) {
7284 error.NeededSourceLocation => {
7285 const decl = mod.declPtr(block.src_decl);
7286 _ = try sema.coerceVarArgParam(
7287 block,
7288 uncasted_arg,
7289 mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
7290 );
7291 unreachable;
7292 },
7293 else => |e| return e,
7294 };
7442 const args = try sema.arena.alloc(Air.Inst.Ref, args_info.count());
7443 for (args, 0..) |*arg_out, arg_idx| {
7444 // Non-generic, so param types are already resolved
7445 const param_ty = if (arg_idx < func_ty_info.param_types.len) ty: {
7446 break :ty func_ty_info.param_types.get(ip)[arg_idx].toType();
7447 } else InternPool.Index.var_args_param_type.toType();
7448 assert(!param_ty.isGenericPoison());
7449 arg_out.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, func);
7450 if (sema.typeOf(arg_out.*).zigTypeTag(mod) == .NoReturn) {
7451 return arg_out.*;
72957452 }
72967453 }
72977454
......@@ -7375,25 +7532,25 @@ fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Typ
73757532 return Air.Inst.Ref.unreachable_value;
73767533}
73777534
7535/// Usually, returns null. If an argument was noreturn, returns that ref (which should become the call result).
73787536fn analyzeInlineCallArg(
7379 sema: *Sema,
7537 ics: *InlineCallSema,
73807538 arg_block: *Block,
73817539 param_block: *Block,
7382 arg_src: LazySrcLoc,
73837540 inst: Zir.Inst.Index,
73847541 new_param_types: []InternPool.Index,
73857542 arg_i: *u32,
7386 uncasted_args: []const Air.Inst.Ref,
7543 args_info: CallArgsInfo,
73877544 is_comptime_call: bool,
73887545 should_memoize: *bool,
73897546 memoized_arg_values: []InternPool.Index,
7390 raw_param_types: InternPool.Index.Slice,
7547 func_ty_info: InternPool.Key.FuncType,
73917548 func_inst: Air.Inst.Ref,
73927549 has_comptime_args: *bool,
7393) !void {
7394 const mod = sema.mod;
7550) !?Air.Inst.Ref {
7551 const mod = ics.sema.mod;
73957552 const ip = &mod.intern_pool;
7396 const zir_tags = sema.code.instructions.items(.tag);
7553 const zir_tags = ics.callee().code.instructions.items(.tag);
73977554 switch (zir_tags[inst]) {
73987555 .param_comptime, .param_anytype_comptime => has_comptime_args.* = true,
73997556 else => {},
......@@ -7402,39 +7559,36 @@ fn analyzeInlineCallArg(
74027559 .param, .param_comptime => {
74037560 // Evaluate the parameter type expression now that previous ones have
74047561 // been mapped, and coerce the corresponding argument to it.
7405 const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok;
7562 const pl_tok = ics.callee().code.instructions.items(.data)[inst].pl_tok;
74067563 const param_src = pl_tok.src();
7407 const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index);
7408 const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
7564 const extra = ics.callee().code.extraData(Zir.Inst.Param, pl_tok.payload_index);
7565 const param_body = ics.callee().code.extra[extra.end..][0..extra.data.body_len];
74097566 const param_ty = param_ty: {
7410 const raw_param_ty = raw_param_types.get(ip)[arg_i.*];
7567 const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*];
74117568 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty;
7412 const param_ty_inst = try sema.resolveBody(param_block, param_body, inst);
7413 const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst);
7569 const param_ty_inst = try ics.callee().resolveBody(param_block, param_body, inst);
7570 const param_ty = try ics.callee().analyzeAsType(param_block, param_src, param_ty_inst);
74147571 break :param_ty param_ty.toIntern();
74157572 };
74167573 new_param_types[arg_i.*] = param_ty;
7417 const uncasted_arg = uncasted_args[arg_i.*];
7418 if (try sema.typeRequiresComptime(param_ty.toType())) {
7419 _ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime-only type must be comptime-known") catch |err| {
7420 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
7574 const casted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, param_ty.toType(), func_ty_info, func_inst);
7575 if (ics.caller().typeOf(casted_arg).zigTypeTag(mod) == .NoReturn) {
7576 return casted_arg;
7577 }
7578 const arg_src = args_info.argSrc(arg_block, arg_i.*);
7579 if (try ics.callee().typeRequiresComptime(param_ty.toType())) {
7580 _ = ics.caller().resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to parameter with comptime-only type must be comptime-known") catch |err| {
7581 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(ics.caller(), ics.caller().err);
74217582 return err;
74227583 };
74237584 } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) {
7424 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
7585 _ = try ics.caller().resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "parameter is comptime");
74257586 }
7426 const casted_arg = sema.coerceExtra(arg_block, param_ty.toType(), uncasted_arg, arg_src, .{ .param_src = .{
7427 .func_inst = func_inst,
7428 .param_i = @intCast(arg_i.*),
7429 } }) catch |err| switch (err) {
7430 error.NotCoercible => unreachable,
7431 else => |e| return e,
7432 };
74337587
74347588 if (is_comptime_call) {
7435 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
7436 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
7437 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
7589 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
7590 const arg_val = ics.caller().resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
7591 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(ics.caller(), ics.caller().err);
74387592 return err;
74397593 };
74407594 switch (arg_val.toIntern()) {
......@@ -7448,14 +7602,14 @@ fn analyzeInlineCallArg(
74487602 // Needed so that lazy values do not trigger
74497603 // assertion due to type not being resolved
74507604 // when the hash function is called.
7451 const resolved_arg_val = try sema.resolveLazyValue(arg_val);
7605 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val);
74527606 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
74537607 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(param_ty.toType(), mod);
74547608 } else {
7455 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
7609 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
74567610 }
74577611
7458 if (try sema.resolveMaybeUndefVal(casted_arg)) |_| {
7612 if (try ics.caller().resolveMaybeUndefVal(casted_arg)) |_| {
74597613 has_comptime_args.* = true;
74607614 }
74617615
......@@ -7463,13 +7617,17 @@ fn analyzeInlineCallArg(
74637617 },
74647618 .param_anytype, .param_anytype_comptime => {
74657619 // No coercion needed.
7466 const uncasted_arg = uncasted_args[arg_i.*];
7467 new_param_types[arg_i.*] = sema.typeOf(uncasted_arg).toIntern();
7620 const uncasted_arg = try args_info.analyzeArg(ics.caller(), arg_block, arg_i.*, Type.generic_poison, func_ty_info, func_inst);
7621 if (ics.caller().typeOf(uncasted_arg).zigTypeTag(mod) == .NoReturn) {
7622 return uncasted_arg;
7623 }
7624 const arg_src = args_info.argSrc(arg_block, arg_i.*);
7625 new_param_types[arg_i.*] = ics.caller().typeOf(uncasted_arg).toIntern();
74687626
74697627 if (is_comptime_call) {
7470 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
7471 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
7472 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
7628 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
7629 const arg_val = ics.caller().resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
7630 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(ics.caller(), ics.caller().err);
74737631 return err;
74747632 };
74757633 switch (arg_val.toIntern()) {
......@@ -7483,17 +7641,17 @@ fn analyzeInlineCallArg(
74837641 // Needed so that lazy values do not trigger
74847642 // assertion due to type not being resolved
74857643 // when the hash function is called.
7486 const resolved_arg_val = try sema.resolveLazyValue(arg_val);
7644 const resolved_arg_val = try ics.caller().resolveLazyValue(arg_val);
74877645 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
7488 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(sema.typeOf(uncasted_arg), mod);
7646 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(ics.caller().typeOf(uncasted_arg), mod);
74897647 } else {
74907648 if (zir_tags[inst] == .param_anytype_comptime) {
7491 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
7649 _ = try ics.caller().resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
74927650 }
7493 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
7651 ics.callee().inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
74947652 }
74957653
7496 if (try sema.resolveMaybeUndefVal(uncasted_arg)) |_| {
7654 if (try ics.caller().resolveMaybeUndefVal(uncasted_arg)) |_| {
74977655 has_comptime_args.* = true;
74987656 }
74997657
......@@ -7501,6 +7659,8 @@ fn analyzeInlineCallArg(
75017659 },
75027660 else => {},
75037661 }
7662
7663 return null;
75047664}
75057665
75067666fn analyzeCallArg(
......@@ -7525,9 +7685,8 @@ fn instantiateGenericCall(
75257685 func_src: LazySrcLoc,
75267686 call_src: LazySrcLoc,
75277687 ensure_result_used: bool,
7528 uncasted_args: []const Air.Inst.Ref,
7688 args_info: CallArgsInfo,
75297689 call_tag: Air.Inst.Tag,
7530 bound_arg_src: ?LazySrcLoc,
75317690 call_dbg_node: ?Zir.Inst.Index,
75327691) CompileError!Air.Inst.Ref {
75337692 const mod = sema.mod;
......@@ -7541,6 +7700,7 @@ fn instantiateGenericCall(
75417700 else => unreachable,
75427701 };
75437702 const generic_owner_func = mod.intern_pool.indexToKey(generic_owner).func;
7703 const generic_owner_ty_info = mod.typeToFunc(generic_owner_func.ty.toType()).?;
75447704
75457705 // Even though there may already be a generic instantiation corresponding
75467706 // to this callsite, we must evaluate the expressions of the generic
......@@ -7556,9 +7716,13 @@ fn instantiateGenericCall(
75567716 const fn_zir = namespace.file_scope.zir;
75577717 const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst);
75587718
7559 const comptime_args = try sema.arena.alloc(InternPool.Index, uncasted_args.len);
7719 const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
75607720 @memset(comptime_args, .none);
75617721
7722 // We may overestimate the number of runtime args, but this will definitely be sufficient.
7723 const max_runtime_args = args_info.count() - @popCount(generic_owner_ty_info.comptime_bits);
7724 var runtime_args = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(sema.arena, max_runtime_args);
7725
75627726 // Re-run the block that creates the function, with the comptime parameters
75637727 // pre-populated inside `inst_map`. This causes `param_comptime` and
75647728 // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
......@@ -7583,7 +7747,6 @@ fn instantiateGenericCall(
75837747 .comptime_args = comptime_args,
75847748 .generic_owner = generic_owner,
75857749 .generic_call_src = call_src,
7586 .generic_bound_arg_src = bound_arg_src,
75877750 .generic_call_decl = block.src_decl.toOptional(),
75887751 .branch_quota = sema.branch_quota,
75897752 .branch_count = sema.branch_count,
......@@ -7608,25 +7771,138 @@ fn instantiateGenericCall(
76087771
76097772 try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
76107773
7611 for (fn_info.param_body[0..uncasted_args.len], uncasted_args, 0..) |inst, arg, i| {
7612 // `child_sema` will use a different `inst_map` which means we have to
7613 // convert from parent-relative `Air.Inst.Ref` to child-relative here.
7614 // Constants are simple; runtime-known values need a new instruction.
7615 child_sema.inst_map.putAssumeCapacityNoClobber(inst, if (try sema.resolveMaybeUndefVal(arg)) |val|
7616 Air.internedToRef(val.toIntern())
7617 else
7618 // We insert into the map an instruction which is runtime-known
7619 // but has the type of the argument.
7620 try child_block.addInst(.{
7774 for (fn_info.param_body[0..args_info.count()], 0..) |param_inst, arg_index| {
7775 const param_tag = fn_zir.instructions.items(.tag)[param_inst];
7776
7777 const param_ty = switch (generic_owner_ty_info.param_types.get(ip)[arg_index]) {
7778 else => |ty| ty.toType(), // parameter is not generic, so type is already resolved
7779 .generic_poison_type => param_ty: {
7780 // We have every parameter before this one, so can resolve this parameter's type now.
7781 // However, first check the param type, since it may be anytype.
7782 switch (param_tag) {
7783 .param_anytype, .param_anytype_comptime => {
7784 // The parameter doesn't have a type.
7785 break :param_ty Type.generic_poison;
7786 },
7787 .param, .param_comptime => {
7788 // We now know every prior parameter, so can resolve this
7789 // parameter's type. The child sema has these types.
7790 const param_data = fn_zir.instructions.items(.data)[param_inst].pl_tok;
7791 const param_extra = fn_zir.extraData(Zir.Inst.Param, param_data.payload_index);
7792 const param_ty_body = fn_zir.extra[param_extra.end..][0..param_extra.data.body_len];
7793
7794 // Make sure any nested instructions don't clobber our work.
7795 const prev_params = child_block.params;
7796 const prev_no_partial_func_ty = child_sema.no_partial_func_ty;
7797 const prev_generic_owner = child_sema.generic_owner;
7798 const prev_generic_call_src = child_sema.generic_call_src;
7799 const prev_generic_call_decl = child_sema.generic_call_decl;
7800 child_block.params = .{};
7801 child_sema.no_partial_func_ty = true;
7802 child_sema.generic_owner = .none;
7803 child_sema.generic_call_src = .unneeded;
7804 child_sema.generic_call_decl = .none;
7805 defer {
7806 child_block.params = prev_params;
7807 child_sema.no_partial_func_ty = prev_no_partial_func_ty;
7808 child_sema.generic_owner = prev_generic_owner;
7809 child_sema.generic_call_src = prev_generic_call_src;
7810 child_sema.generic_call_decl = prev_generic_call_decl;
7811 }
7812
7813 const param_ty_inst = try child_sema.resolveBody(&child_block, param_ty_body, param_inst);
7814 break :param_ty try child_sema.analyzeAsType(&child_block, param_data.src(), param_ty_inst);
7815 },
7816 else => unreachable,
7817 }
7818 },
7819 };
7820 const arg_ref = try args_info.analyzeArg(sema, block, arg_index, param_ty, generic_owner_ty_info, func);
7821 const arg_ty = sema.typeOf(arg_ref);
7822 if (arg_ty.zigTypeTag(mod) == .NoReturn) {
7823 // This terminates argument analysis.
7824 return arg_ref;
7825 }
7826
7827 const arg_is_comptime = switch (param_tag) {
7828 .param_comptime, .param_anytype_comptime => true,
7829 .param, .param_anytype => try sema.typeRequiresComptime(arg_ty),
7830 else => unreachable,
7831 };
7832
7833 if (arg_is_comptime) {
7834 if (try sema.resolveMaybeUndefVal(arg_ref)) |arg_val| {
7835 comptime_args[arg_index] = arg_val.toIntern();
7836 child_sema.inst_map.putAssumeCapacityNoClobber(
7837 param_inst,
7838 Air.internedToRef(arg_val.toIntern()),
7839 );
7840 } else switch (param_tag) {
7841 .param_comptime,
7842 .param_anytype_comptime,
7843 => return sema.failWithOwnedErrorMsg(msg: {
7844 const arg_src = args_info.argSrc(block, arg_index);
7845 const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to comptime parameter", .{});
7846 errdefer msg.destroy(sema.gpa);
7847 const param_src = switch (param_tag) {
7848 .param_comptime => fn_zir.instructions.items(.data)[param_inst].pl_tok.src(),
7849 .param_anytype_comptime => fn_zir.instructions.items(.data)[param_inst].str_tok.src(),
7850 else => unreachable,
7851 };
7852 try child_sema.errNote(&child_block, param_src, msg, "declared comptime here", .{});
7853 break :msg msg;
7854 }),
7855
7856 .param,
7857 .param_anytype,
7858 => return sema.failWithOwnedErrorMsg(msg: {
7859 const arg_src = args_info.argSrc(block, arg_index);
7860 const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to parameter of comptime-only type", .{});
7861 errdefer msg.destroy(sema.gpa);
7862 const param_src = switch (param_tag) {
7863 .param => fn_zir.instructions.items(.data)[param_inst].pl_tok.src(),
7864 .param_anytype => fn_zir.instructions.items(.data)[param_inst].str_tok.src(),
7865 else => unreachable,
7866 };
7867 try child_sema.errNote(&child_block, param_src, msg, "declared here", .{});
7868 const src_decl = mod.declPtr(block.src_decl);
7869 try sema.explainWhyTypeIsComptime(msg, arg_src.toSrcLoc(src_decl, mod), arg_ty);
7870 break :msg msg;
7871 }),
7872
7873 else => unreachable,
7874 }
7875 } else {
7876 // The parameter is runtime-known.
7877 try sema.queueFullTypeResolution(arg_ty);
7878 child_sema.inst_map.putAssumeCapacityNoClobber(param_inst, try child_block.addInst(.{
76217879 .tag = .arg,
76227880 .data = .{ .arg = .{
7623 .ty = Air.internedToRef(sema.typeOf(arg).toIntern()),
7624 .src_index = @intCast(i),
7881 .ty = Air.internedToRef(arg_ty.toIntern()),
7882 .src_index = @intCast(arg_index),
76257883 } },
76267884 }));
7885 const param_name: Zir.NullTerminatedString = switch (param_tag) {
7886 .param_anytype => @enumFromInt(fn_zir.instructions.items(.data)[param_inst].str_tok.start),
7887 .param => name: {
7888 const inst_data = fn_zir.instructions.items(.data)[param_inst].pl_tok;
7889 const extra = fn_zir.extraData(Zir.Inst.Param, inst_data.payload_index);
7890 break :name @enumFromInt(extra.data.name);
7891 },
7892 else => unreachable,
7893 };
7894 try child_block.params.append(sema.arena, .{
7895 .ty = arg_ty.toIntern(), // This is the type after coercion
7896 .is_comptime = false, // We're adding only runtime args to the instantiation
7897 .name = param_name,
7898 });
7899 runtime_args.appendAssumeCapacity(arg_ref);
7900 }
76277901 }
76287902
7629 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst);
7903 // We've already handled parameters, so don't resolve the whole body. Instead, just
7904 // do the instructions after the params (i.e. the func itself).
7905 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body[args_info.count()..], fn_info.param_body_inst);
76307906 const callee_index = (child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable).toIntern();
76317907
76327908 const callee = mod.funcInfo(callee_index);
......@@ -7649,33 +7925,7 @@ fn instantiateGenericCall(
76497925 return error.GenericPoison;
76507926 }
76517927
7652 const runtime_args_len: u32 = func_ty_info.param_types.len;
7653 const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
7654 {
7655 var runtime_i: u32 = 0;
7656 for (uncasted_args, 0..) |uncasted_arg, total_i| {
7657 // In the case of a function call generated by the language, the LazySrcLoc
7658 // provided for `call_src` may not point to anything interesting.
7659 const arg_src: LazySrcLoc = if (total_i == 0 and bound_arg_src != null)
7660 bound_arg_src.?
7661 else if (call_src == .node_offset) .{ .call_arg = .{
7662 .decl = block.src_decl,
7663 .call_node_offset = call_src.node_offset.x,
7664 .arg_index = @intCast(total_i - @intFromBool(bound_arg_src != null)),
7665 } } else .unneeded;
7666
7667 const comptime_arg = callee.comptime_args.get(ip)[total_i];
7668 if (comptime_arg == .none) {
7669 const param_ty = func_ty_info.param_types.get(ip)[runtime_i].toType();
7670 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
7671 try sema.queueFullTypeResolution(param_ty);
7672 runtime_args[runtime_i] = casted_arg;
7673 runtime_i += 1;
7674 }
7675 }
7676
7677 try sema.queueFullTypeResolution(func_ty_info.return_type.toType());
7678 }
7928 try sema.queueFullTypeResolution(func_ty_info.return_type.toType());
76797929
76807930 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
76817931
......@@ -7687,18 +7937,17 @@ fn instantiateGenericCall(
76877937
76887938 try mod.ensureFuncBodyAnalysisQueued(callee_index);
76897939
7690 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
7691 runtime_args_len);
7940 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + runtime_args.items.len);
76927941 const result = try block.addInst(.{
76937942 .tag = call_tag,
76947943 .data = .{ .pl_op = .{
76957944 .operand = Air.internedToRef(callee_index),
76967945 .payload = sema.addExtraAssumeCapacity(Air.Call{
7697 .args_len = runtime_args_len,
7946 .args_len = @intCast(runtime_args.items.len),
76987947 }),
76997948 } },
77007949 });
7701 sema.appendRefsAssumeCapacity(runtime_args);
7950 sema.appendRefsAssumeCapacity(runtime_args.items);
77027951
77037952 if (ensure_result_used) {
77047953 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
......@@ -8647,20 +8896,17 @@ fn resolveGenericBody(
86478896 const prev_no_partial_func_type = sema.no_partial_func_ty;
86488897 const prev_generic_owner = sema.generic_owner;
86498898 const prev_generic_call_src = sema.generic_call_src;
8650 const prev_generic_bound_arg_src = sema.generic_bound_arg_src;
86518899 const prev_generic_call_decl = sema.generic_call_decl;
86528900 block.params = .{};
86538901 sema.no_partial_func_ty = true;
86548902 sema.generic_owner = .none;
86558903 sema.generic_call_src = .unneeded;
8656 sema.generic_bound_arg_src = null;
86578904 sema.generic_call_decl = .none;
86588905 defer {
86598906 block.params = prev_params;
86608907 sema.no_partial_func_ty = prev_no_partial_func_type;
86618908 sema.generic_owner = prev_generic_owner;
86628909 sema.generic_call_src = prev_generic_call_src;
8663 sema.generic_bound_arg_src = prev_generic_bound_arg_src;
86648910 sema.generic_call_decl = prev_generic_call_decl;
86658911 }
86668912
......@@ -9278,37 +9524,18 @@ fn finishFunc(
92789524 return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty);
92799525}
92809526
9281fn genericArgSrcLoc(sema: *Sema, block: *Block, param_index: u32, param_src: LazySrcLoc) Module.SrcLoc {
9282 const mod = sema.mod;
9283 if (sema.generic_owner == .none) return param_src.toSrcLoc(mod.declPtr(block.src_decl), mod);
9284 const arg_decl = sema.generic_call_decl.unwrap().?;
9285 const arg_src: LazySrcLoc = if (param_index == 0 and sema.generic_bound_arg_src != null)
9286 sema.generic_bound_arg_src.?
9287 else
9288 .{ .call_arg = .{
9289 .decl = arg_decl,
9290 .call_node_offset = sema.generic_call_src.node_offset.x,
9291 .arg_index = param_index - @intFromBool(sema.generic_bound_arg_src != null),
9292 } };
9293 return arg_src.toSrcLoc(mod.declPtr(arg_decl), mod);
9294}
9295
92969527fn zirParam(
92979528 sema: *Sema,
92989529 block: *Block,
92999530 inst: Zir.Inst.Index,
9300 param_index: u32,
93019531 comptime_syntax: bool,
93029532) CompileError!void {
9303 const gpa = sema.gpa;
93049533 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
93059534 const src = inst_data.src();
93069535 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
93079536 const param_name: Zir.NullTerminatedString = @enumFromInt(extra.data.name);
93089537 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
93099538
9310 // We could be in a generic function instantiation, or we could be evaluating a generic
9311 // function without any comptime args provided.
93129539 const param_ty = param_ty: {
93139540 const err = err: {
93149541 // Make sure any nested param instructions don't clobber our work.
......@@ -9316,20 +9543,17 @@ fn zirParam(
93169543 const prev_no_partial_func_type = sema.no_partial_func_ty;
93179544 const prev_generic_owner = sema.generic_owner;
93189545 const prev_generic_call_src = sema.generic_call_src;
9319 const prev_generic_bound_arg_src = sema.generic_bound_arg_src;
93209546 const prev_generic_call_decl = sema.generic_call_decl;
93219547 block.params = .{};
93229548 sema.no_partial_func_ty = true;
93239549 sema.generic_owner = .none;
93249550 sema.generic_call_src = .unneeded;
9325 sema.generic_bound_arg_src = null;
93269551 sema.generic_call_decl = .none;
93279552 defer {
93289553 block.params = prev_params;
93299554 sema.no_partial_func_ty = prev_no_partial_func_type;
93309555 sema.generic_owner = prev_generic_owner;
93319556 sema.generic_call_src = prev_generic_call_src;
9332 sema.generic_bound_arg_src = prev_generic_bound_arg_src;
93339557 sema.generic_call_decl = prev_generic_call_decl;
93349558 }
93359559
......@@ -9341,11 +9565,6 @@ fn zirParam(
93419565 };
93429566 switch (err) {
93439567 error.GenericPoison => {
9344 if (sema.inst_map.contains(inst)) {
9345 // A generic function is about to evaluate to another generic function.
9346 // Return an error instead.
9347 return error.GenericPoison;
9348 }
93499568 // The type is not available until the generic instantiation.
93509569 // We result the param instruction with a poison value and
93519570 // insert an anytype parameter.
......@@ -9363,11 +9582,6 @@ fn zirParam(
93639582
93649583 const is_comptime = sema.typeRequiresComptime(param_ty) catch |err| switch (err) {
93659584 error.GenericPoison => {
9366 if (sema.inst_map.contains(inst)) {
9367 // A generic function is about to evaluate to another generic function.
9368 // Return an error instead.
9369 return error.GenericPoison;
9370 }
93719585 // The type is not available until the generic instantiation.
93729586 // We result the param instruction with a poison value and
93739587 // insert an anytype parameter.
......@@ -9382,46 +9596,6 @@ fn zirParam(
93829596 else => |e| return e,
93839597 } or comptime_syntax;
93849598
9385 if (sema.inst_map.get(inst)) |arg| {
9386 if (is_comptime and sema.generic_owner != .none) {
9387 // We have a comptime value for this parameter so it should be elided from the
9388 // function type of the function instruction in this block.
9389 const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) {
9390 error.NeededSourceLocation => {
9391 // We are instantiating a generic function and a comptime arg
9392 // cannot be coerced to the param type, but since we don't
9393 // have the callee source location return `GenericPoison`
9394 // so that the instantiation is failed and the coercion
9395 // is handled by comptime call logic instead.
9396 assert(sema.generic_owner != .none);
9397 return error.GenericPoison;
9398 },
9399 else => |e| return e,
9400 };
9401 sema.inst_map.putAssumeCapacity(inst, coerced_arg);
9402 if (try sema.resolveMaybeUndefVal(coerced_arg)) |val| {
9403 sema.comptime_args[param_index] = val.toIntern();
9404 return;
9405 }
9406 const msg = msg: {
9407 const src_loc = sema.genericArgSrcLoc(block, param_index, src);
9408 const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{
9409 @as([]const u8, "runtime-known argument passed to comptime parameter"),
9410 });
9411 errdefer msg.destroy(gpa);
9412
9413 if (sema.generic_call_decl != .none) {
9414 try sema.errNote(block, src, msg, "{s}", .{@as([]const u8, "declared comptime here")});
9415 }
9416 break :msg msg;
9417 };
9418 return sema.failWithOwnedErrorMsg(msg);
9419 }
9420 // Even though a comptime argument is provided, the generic function wants to treat
9421 // this as a runtime parameter.
9422 assert(sema.inst_map.remove(inst));
9423 }
9424
94259599 try block.params.append(sema.arena, .{
94269600 .ty = param_ty.toIntern(),
94279601 .is_comptime = comptime_syntax,
......@@ -9447,75 +9621,10 @@ fn zirParamAnytype(
94479621 sema: *Sema,
94489622 block: *Block,
94499623 inst: Zir.Inst.Index,
9450 param_index: u32,
94519624 comptime_syntax: bool,
94529625) CompileError!void {
9453 const gpa = sema.gpa;
94549626 const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
94559627 const param_name: Zir.NullTerminatedString = @enumFromInt(inst_data.start);
9456 const src = inst_data.src();
9457
9458 if (sema.inst_map.get(inst)) |air_ref| {
9459 const param_ty = sema.typeOf(air_ref);
9460 // If we have a comptime value for this parameter, it should be elided
9461 // from the function type of the function instruction in this block.
9462 if (try sema.typeHasOnePossibleValue(param_ty)) |opv| {
9463 sema.comptime_args[param_index] = opv.toIntern();
9464 return;
9465 }
9466
9467 if (comptime_syntax) {
9468 if (try sema.resolveMaybeUndefVal(air_ref)) |val| {
9469 sema.comptime_args[param_index] = val.toIntern();
9470 return;
9471 }
9472 const msg = msg: {
9473 const src_loc = sema.genericArgSrcLoc(block, param_index, src);
9474 const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{
9475 @as([]const u8, "runtime-known argument passed to comptime parameter"),
9476 });
9477 errdefer msg.destroy(gpa);
9478
9479 if (sema.generic_call_decl != .none) {
9480 try sema.errNote(block, src, msg, "{s}", .{@as([]const u8, "declared comptime here")});
9481 }
9482 break :msg msg;
9483 };
9484 return sema.failWithOwnedErrorMsg(msg);
9485 }
9486
9487 if (try sema.typeRequiresComptime(param_ty)) {
9488 if (try sema.resolveMaybeUndefVal(air_ref)) |val| {
9489 sema.comptime_args[param_index] = val.toIntern();
9490 return;
9491 }
9492 const msg = msg: {
9493 const src_loc = sema.genericArgSrcLoc(block, param_index, src);
9494 const msg = try Module.ErrorMsg.create(gpa, src_loc, "{s}", .{
9495 @as([]const u8, "runtime-known argument passed to comptime-only type parameter"),
9496 });
9497 errdefer msg.destroy(gpa);
9498
9499 if (sema.generic_call_decl != .none) {
9500 try sema.errNote(block, src, msg, "{s}", .{@as([]const u8, "declared here")});
9501 }
9502
9503 try sema.explainWhyTypeIsComptime(msg, src_loc, param_ty);
9504
9505 break :msg msg;
9506 };
9507 return sema.failWithOwnedErrorMsg(msg);
9508 }
9509
9510 // The parameter is runtime-known.
9511 // The map is already populated but we do need to add a runtime parameter.
9512 try block.params.append(sema.arena, .{
9513 .ty = param_ty.toIntern(),
9514 .is_comptime = false,
9515 .name = param_name,
9516 });
9517 return;
9518 }
95199628
95209629 // We are evaluating a generic function without any comptime args provided.
95219630
......@@ -23152,7 +23261,21 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
2315223261 const callee_ty = sema.typeOf(func);
2315323262 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
2315423263 const ensure_result_used = extra.flags.ensure_result_used;
23155 return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null, .@"@call");
23264 return sema.analyzeCall(
23265 block,
23266 func,
23267 func_ty,
23268 func_src,
23269 call_src,
23270 modifier,
23271 ensure_result_used,
23272 .{ .call_builtin = .{
23273 .call_node_offset = inst_data.src_node,
23274 .args = resolved_args,
23275 } },
23276 null,
23277 .@"@call",
23278 );
2315623279}
2315723280
2315823281fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
test/behavior/call.zig+61
......@@ -430,3 +430,64 @@ test "method call as parameter type" {
430430 try expectEqual(@as(u64, 123), S.foo(S{}, 123));
431431 try expectEqual(@as(u64, 500), S.foo(S{}, 500));
432432}
433
434test "non-anytype generic parameters provide result type" {
435 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
436 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
437 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
438 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
439 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
440 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
441
442 const S = struct {
443 fn f(comptime T: type, y: T) !void {
444 try expectEqual(@as(T, 123), y);
445 }
446
447 fn g(x: anytype, y: @TypeOf(x)) !void {
448 try expectEqual(@as(@TypeOf(x), 0x222), y);
449 }
450 };
451
452 var rt_u16: u16 = 123;
453 var rt_u32: u32 = 0x10000222;
454
455 try S.f(u8, @intCast(rt_u16));
456 try S.f(u8, @intCast(123));
457
458 try S.g(rt_u16, @truncate(rt_u32));
459 try S.g(rt_u16, @truncate(0x10000222));
460
461 try comptime S.f(u8, @intCast(123));
462 try comptime S.g(@as(u16, undefined), @truncate(0x99990222));
463}
464
465test "argument to generic function has correct result type" {
466 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
467 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
468 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
469 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
470 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
471 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
472
473 const S = struct {
474 fn foo(_: anytype, e: enum { a, b }) bool {
475 return e == .b;
476 }
477
478 fn doTheTest() !void {
479 var t = true;
480
481 // Since the enum literal passes through a runtime conditional here, these can only
482 // compile if RLS provides the correct result type to the argument
483 try expect(foo({}, if (!t) .a else .b));
484 try expect(!foo("dummy", if (t) .a else .b));
485 try expect(foo({}, if (t) .b else .a));
486 try expect(!foo(123, if (t) .a else .a));
487 try expect(foo(123, if (t) .b else .b));
488 }
489 };
490
491 try S.doTheTest();
492 try comptime S.doTheTest();
493}