From efe73c787a89f71de37bf876c3ef7bcfddebebba Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Thu, 18 Jun 2026 20:33:35 -0400 Subject: [PATCH] Sema: also do comptime check on bound arg Currently, if there is a bound arg then `analyzeArg` will break out of the uncoerced arg block immediately without checking if the bound arg is not comptime known despite the block or parameter being comptime. This makes it so that we do the same checks on the bound and regular args but only do resolution on regular args. --- src/Sema.zig | 42 +++++++++---------- .../runtime_bound_arg_with_comptime_param.zig | 22 ++++++++++ 2 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 test/cases/compile_errors/runtime_bound_arg_with_comptime_param.zig diff --git a/src/Sema.zig b/src/Sema.zig index 536214aed23611a20e694c9e6d6ab2e0fcc5f35f..4736985e8b332a831a12b03156c9ed66e4207c8a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6468,22 +6468,6 @@ const CallArgsInfo = union(enum) { const uncoerced_arg: Air.Inst.Ref = switch (cai) { inline .resolved, .call_builtin => |resolved| resolved.args[arg_index], .zir_call => |zir_call| arg_val: { - const has_bound_arg = zir_call.bound_arg != .none; - if (arg_index == 0 and has_bound_arg) { - break :arg_val zir_call.bound_arg; - } - const real_arg_idx = arg_index - @intFromBool(has_bound_arg); - - const arg_body = if (real_arg_idx == 0) blk: { - const start = zir_call.num_args; - const end = @intFromEnum(zir_call.args_body[0]); - break :blk zir_call.args_body[start..end]; - } else blk: { - const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]); - const end = @intFromEnum(zir_call.args_body[real_arg_idx]); - break :blk zir_call.args_body[start..end]; - }; - // Generate args to comptime params in comptime block const parent_comptime = block.comptime_reason; defer block.comptime_reason = parent_comptime; @@ -6505,11 +6489,27 @@ const CallArgsInfo = union(enum) { }; } } - // Give the arg its result type - const provide_param_ty: Type = maybe_param_ty orelse .generic_poison; - sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern())); - // Resolve the arg! - const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst); + + const has_bound_arg = zir_call.bound_arg != .none; + const uncoerced_arg = if (arg_index == 0 and has_bound_arg) zir_call.bound_arg else arg: { + const real_arg_idx = arg_index - @intFromBool(has_bound_arg); + + const arg_body = if (real_arg_idx == 0) blk: { + const start = zir_call.num_args; + const end = @intFromEnum(zir_call.args_body[0]); + break :blk zir_call.args_body[start..end]; + } else blk: { + const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]); + const end = @intFromEnum(zir_call.args_body[real_arg_idx]); + break :blk zir_call.args_body[start..end]; + }; + + // Give the arg its result type + const provide_param_ty: Type = maybe_param_ty orelse .generic_poison; + sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern())); + // Resolve the arg! + break :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); diff --git a/test/cases/compile_errors/runtime_bound_arg_with_comptime_param.zig b/test/cases/compile_errors/runtime_bound_arg_with_comptime_param.zig new file mode 100644 index 0000000000000000000000000000000000000000..398d6c77c89b58f2a6106119999c2f922b23d3d8 --- /dev/null +++ b/test/cases/compile_errors/runtime_bound_arg_with_comptime_param.zig @@ -0,0 +1,22 @@ +pub const A = enum { + a1, + a2, + + pub fn x(comptime _: A) usize { + return 0; + } + + pub fn y(self: A) usize { + return self.x(); + } +}; + +pub fn main() void { + _ = A.y(.a1); +} + +// error +// +// :10:20: error: unable to resolve comptime value +// :10:20: note: argument to comptime parameter must be comptime-known +// :5:14: note: parameter declared comptime here -- 2.54.0