Commit 03b356e34a

Veikka Tuominen <git@vexu.eu>
2022-06-28 15:43:09
Sema: improve `@call` errors
1 parent 3204d00
src/Sema.zig
@@ -5231,6 +5231,10 @@ fn analyzeCall(
         .async_kw => return sema.fail(block, call_src, "TODO implement async call", .{}),
     };
 
+    if (modifier == .never_inline and func_ty_info.cc == .Inline) {
+        return sema.fail(block, call_src, "no-inline call of inline function", .{});
+    }
+
     const gpa = sema.gpa;
 
     var is_generic_call = func_ty_info.is_generic;
@@ -5270,6 +5274,10 @@ fn analyzeCall(
         }
     }
 
+    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 func_val = try sema.resolveConstValue(block, func_src, func);
         const module_fn = switch (func_val.tag()) {
@@ -11612,8 +11620,13 @@ fn analyzeCmpUnionTag(
 ) CompileError!Air.Inst.Ref {
     const union_ty = try sema.resolveTypeFields(block, un_src, sema.typeOf(un));
     const union_tag_ty = union_ty.unionTagType() orelse {
-        // TODO note at declaration site that says "union foo is not tagged"
-        return sema.fail(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
+        const msg = msg: {
+            const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
+            errdefer msg.destroy(sema.gpa);
+            try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)});
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(block, msg);
     };
     // Coerce both the union and the tag to the union's tag type, and then execute the
     // enum comparison codepath.
@@ -16878,10 +16891,15 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier);
     };
 
+    const is_comptime = extra.flags.is_comptime or block.is_comptime;
+
     const modifier: std.builtin.CallOptions.Modifier = switch (wanted_modifier) {
         // These can be upgraded to comptime or nosuspend calls.
         .auto, .never_tail, .no_async => m: {
-            if (extra.flags.is_comptime) {
+            if (is_comptime) {
+                if (wanted_modifier == .never_tail) {
+                    return sema.fail(block, options_src, "unable to perform 'never_tail' call at compile-time", .{});
+                }
                 break :m .compile_time;
             }
             if (extra.flags.is_nosuspend) {
@@ -16891,7 +16909,11 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         },
         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
         .always_tail, .always_inline, .compile_time => m: {
-            if (extra.flags.is_comptime) {
+            _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
+                return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)});
+            };
+
+            if (is_comptime) {
                 break :m .compile_time;
             }
             break :m wanted_modifier;
@@ -16900,14 +16922,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
             if (extra.flags.is_nosuspend) {
                 return sema.fail(block, options_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
             }
-            if (extra.flags.is_comptime) {
+            if (is_comptime) {
                 return sema.fail(block, options_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
             }
             break :m wanted_modifier;
         },
         .never_inline => m: {
-            if (extra.flags.is_comptime) {
-                return sema.fail(block, options_src, "modifier 'never_inline' cannot be used in combination with comptime function call", .{});
+            if (is_comptime) {
+                return sema.fail(block, options_src, "unable to perform 'never_inline' call at compile-time", .{});
             }
             break :m wanted_modifier;
         },
test/cases/compile_errors/stage1/obj/bad_usage_of_call.zig
@@ -1,30 +0,0 @@
-export fn entry1() void {
-    @call(.{}, foo, {});
-}
-export fn entry2() void {
-    comptime @call(.{ .modifier = .never_inline }, foo, .{});
-}
-export fn entry3() void {
-    comptime @call(.{ .modifier = .never_tail }, foo, .{});
-}
-export fn entry4() void {
-    @call(.{ .modifier = .never_inline }, bar, .{});
-}
-export fn entry5(c: bool) void {
-    var baz = if (c) baz1 else baz2;
-    @call(.{ .modifier = .compile_time }, baz, .{});
-}
-fn foo() void {}
-fn bar() callconv(.Inline) void {}
-fn baz1() void {}
-fn baz2() void {}
-
-// error
-// backend=stage1
-// target=native
-//
-// tmp.zig:2:21: error: expected tuple or struct, found 'void'
-// tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time
-// tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time
-// tmp.zig:11:5: error: no-inline call of inline function
-// tmp.zig:15:5: error: the specified modifier requires a comptime-known function
test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-always_inline.zig
@@ -1,11 +0,0 @@
-pub export fn entry() void {
-    var call_me: fn () void = undefined;
-    @call(.{ .modifier = .always_inline }, call_me, .{});
-}
-
-// error
-// backend=stage1
-// target=native
-// is_test=1
-//
-// tmp.zig:3:5: error: the specified modifier requires a comptime-known function
test/cases/compile_errors/stage1/test/call_rejects_non_comptime-known_fn-compile_time.zig
@@ -1,11 +0,0 @@
-pub export fn entry() void {
-    var call_me: fn () void = undefined;
-    @call(.{ .modifier = .compile_time }, call_me, .{});
-}
-
-// error
-// backend=stage1
-// target=native
-// is_test=1
-//
-// tmp.zig:3:5: error: the specified modifier requires a comptime-known function
test/cases/compile_errors/stage1/test/comparison_of_non-tagged_union_and_enum_literal.zig
@@ -1,14 +0,0 @@
-export fn entry() void {
-    const U = union { A: u32, B: u64 };
-    var u = U{ .A = 42 };
-    var ok = u == .A;
-    _ = ok;
-}
-
-// error
-// backend=stage1
-// target=native
-// is_test=1
-//
-// tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types
-// tmp.zig:2:15: note: type U is not a tagged union
test/cases/compile_errors/stage1/test/alignCast_of_zero_sized_types.zig → test/cases/compile_errors/stage1/alignCast_of_zero_sized_types.zig
@@ -20,7 +20,6 @@ export fn qux() void {
 // error
 // backend=stage1
 // target=native
-// is_test=1
 //
 // tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void'
 // tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void'
test/cases/compile_errors/stage1/test/ptrToInt_with_pointer_to_zero-sized_type.zig → test/cases/compile_errors/stage1/ptrToInt_with_pointer_to_zero-sized_type.zig
@@ -7,6 +7,5 @@ export fn entry() void {
 // error
 // backend=stage1
 // target=native
-// is_test=1
 //
 // tmp.zig:3:23: error: pointer to size 0 type has no address
test/cases/compile_errors/bad_usage_of_call.zig
@@ -0,0 +1,35 @@
+export fn entry1() void {
+    @call(.{}, foo, {});
+}
+export fn entry2() void {
+    comptime @call(.{ .modifier = .never_inline }, foo, .{});
+}
+export fn entry3() void {
+    comptime @call(.{ .modifier = .never_tail }, foo, .{});
+}
+export fn entry4() void {
+    @call(.{ .modifier = .never_inline }, bar, .{});
+}
+export fn entry5(c: bool) void {
+    var baz = if (c) &baz1 else &baz2;
+    @call(.{ .modifier = .compile_time }, baz, .{});
+}
+pub export fn entry() void {
+    var call_me: *const fn () void = undefined;
+    @call(.{ .modifier = .always_inline }, call_me, .{});
+}
+fn foo() void {}
+fn bar() callconv(.Inline) void {}
+fn baz1() void {}
+fn baz2() void {}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:21: 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: no-inline call of inline function
+// :15:43: error: modifier 'compile_time' requires a comptime-known function
+// :19:44: error: modifier 'always_inline' requires a comptime-known function
test/cases/comparison_of_non-tagged_union_and_enum_literal.zig
@@ -0,0 +1,13 @@
+export fn entry() void {
+    const U = union { A: u32, B: u64 };
+    var u = U{ .A = 42 };
+    var ok = u == .A;
+    _ = ok;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :4:14: error: comparison of union and enum literal is only valid for tagged union types
+// :2:15: note: union 'tmp.entry.U' is not a tagged union