Commit 4e134f6dcb

Veikka Tuominen <git@vexu.eu>
2022-10-17 13:52:18
Sema: respect inline call semantics
If an argument is comptime-known but shouldn't be create an alloc to store it in to get a runtime-known value.
1 parent be9a4a1
src/Sema.zig
@@ -6413,9 +6413,9 @@ fn analyzeInlineCallArg(
                 };
             }
             const casted_arg = try sema.coerce(arg_block, param_ty, uncasted_arg, arg_src);
-            try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg);
 
             if (is_comptime_call) {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg);
                 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
                     if (err == error.AnalysisFail and sema.err != null) {
                         try sema.addComptimeReturnTypeNote(arg_block, func, func_src, ret_ty, sema.err.?, comptime_only_ret_ty);
@@ -6440,6 +6440,20 @@ fn analyzeInlineCallArg(
                     .ty = param_ty,
                     .val = arg_val,
                 };
+            } else if ((try sema.resolveMaybeUndefVal(arg_block, arg_src, casted_arg) == null) or
+                try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_comptime)
+            {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg);
+            } else {
+                // We have a comptime value but we need a runtime value to preserve inlining semantics,
+                const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
+                    .pointee_type = param_ty,
+                    .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+                });
+                const alloc = try arg_block.addTy(.alloc, ptr_type);
+                _ = try arg_block.addBinOp(.store, alloc, casted_arg);
+                const loaded = try arg_block.addTyOp(.load, param_ty, alloc);
+                try sema.inst_map.putNoClobber(sema.gpa, inst, loaded);
             }
 
             arg_i.* += 1;
@@ -6448,9 +6462,10 @@ fn analyzeInlineCallArg(
             // No coercion needed.
             const uncasted_arg = uncasted_args[arg_i.*];
             new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg);
-            try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg);
+            const param_ty = sema.typeOf(uncasted_arg);
 
             if (is_comptime_call) {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg);
                 const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| {
                     if (err == error.AnalysisFail and sema.err != null) {
                         try sema.addComptimeReturnTypeNote(arg_block, func, func_src, ret_ty, sema.err.?, comptime_only_ret_ty);
@@ -6475,6 +6490,20 @@ fn analyzeInlineCallArg(
                     .ty = sema.typeOf(uncasted_arg),
                     .val = arg_val,
                 };
+            } else if ((try sema.resolveMaybeUndefVal(arg_block, arg_src, uncasted_arg)) == null or
+                try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_anytype_comptime)
+            {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg);
+            } else {
+                // We have a comptime value but we need a runtime value to preserve inlining semantics,
+                const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
+                    .pointee_type = param_ty,
+                    .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+                });
+                const alloc = try arg_block.addTy(.alloc, ptr_type);
+                _ = try arg_block.addBinOp(.store, alloc, uncasted_arg);
+                const loaded = try arg_block.addTyOp(.load, param_ty, alloc);
+                try sema.inst_map.putNoClobber(sema.gpa, inst, loaded);
             }
 
             arg_i.* += 1;
test/behavior/bugs/13164.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+inline fn setLimits(min: ?u32, max: ?u32) !void {
+    if (min != null and max != null) {
+        try std.testing.expect(min.? <= max.?);
+    }
+}
+
+test {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    var x: u32 = 42;
+    try setLimits(x, null);
+}
test/cases/compile_error_in_inline_fn_call_fixed.0.zig
@@ -1,16 +0,0 @@
-pub fn main() void {
-    var x: usize = 3;
-    const y = add(10, 2, x);
-    if (y - 6 != 0) unreachable;
-}
-
-inline fn add(a: usize, b: usize, c: usize) usize {
-    if (a == 10) @compileError("bad");
-    return a + b + c;
-}
-
-// error
-// output_mode=Exe
-//
-// :8:18: error: bad
-// :3:18: note: called from here
test/cases/compile_error_in_inline_fn_call_fixed.1.zig
@@ -1,13 +0,0 @@
-pub fn main() void {
-    var x: usize = 3;
-    const y = add(1, 2, x);
-    if (y - 6 != 0) unreachable;
-}
-
-inline fn add(a: usize, b: usize, c: usize) usize {
-    if (a == 10) @compileError("bad");
-    return a + b + c;
-}
-
-// run
-//
test/behavior.zig
@@ -105,6 +105,7 @@ test {
     _ = @import("behavior/bugs/13068.zig");
     _ = @import("behavior/bugs/13112.zig");
     _ = @import("behavior/bugs/13128.zig");
+    _ = @import("behavior/bugs/13164.zig");
     _ = @import("behavior/byteswap.zig");
     _ = @import("behavior/byval_arg_var.zig");
     _ = @import("behavior/call.zig");