Commit bd8b5c25ec

r00ster91 <r00ster91@proton.me>
2023-04-29 04:19:58
Sema: emit error for always_inline call of noinline function
Fixes #15489 This also lays the groundwork for exposing the whether or not a function is noinline in std.builtin.Fn as an `is_noinline: bool` field if we ever want to do that.
1 parent 0c9c911
Changed files (3)
src
test
cases
compile_errors
src/Sema.zig
@@ -6565,7 +6565,10 @@ fn analyzeCall(
     };
 
     if (modifier == .never_inline and func_ty_info.cc == .Inline) {
-        return sema.fail(block, call_src, "no-inline call of inline function", .{});
+        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;
@@ -8784,7 +8787,8 @@ fn funcCommon(
         if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and
             alignment.? == 0 and
             address_space.? == target_util.defaultAddressSpace(target, .function) and
-            section == .default)
+            section == .default and
+            !is_noinline)
         {
             if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) {
                 break :fn_ty Type.initTag(.fn_noreturn_no_args);
@@ -9002,6 +9006,7 @@ fn funcCommon(
             .addrspace_is_generic = address_space == null,
             .is_var_args = var_args,
             .is_generic = is_generic,
+            .is_noinline = is_noinline,
             .noalias_bits = noalias_bits,
         });
     };
@@ -19217,6 +19222,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
                 .cc = cc,
                 .is_var_args = is_var_args,
                 .is_generic = false,
+                .is_noinline = false,
                 .align_is_generic = false,
                 .cc_is_generic = false,
                 .section_is_generic = false,
src/type.zig
@@ -666,6 +666,9 @@ pub const Type = extern union {
                 if (a_info.is_generic != b_info.is_generic)
                     return false;
 
+                if (a_info.is_noinline != b_info.is_noinline)
+                    return false;
+
                 if (a_info.noalias_bits != b_info.noalias_bits)
                     return false;
 
@@ -1074,6 +1077,7 @@ pub const Type = extern union {
                 }
                 std.hash.autoHash(hasher, fn_info.is_var_args);
                 std.hash.autoHash(hasher, fn_info.is_generic);
+                std.hash.autoHash(hasher, fn_info.is_noinline);
                 std.hash.autoHash(hasher, fn_info.noalias_bits);
 
                 std.hash.autoHash(hasher, fn_info.param_types.len);
@@ -1454,6 +1458,7 @@ pub const Type = extern union {
                     .alignment = payload.alignment,
                     .is_var_args = payload.is_var_args,
                     .is_generic = payload.is_generic,
+                    .is_noinline = payload.is_noinline,
                     .comptime_params = comptime_params.ptr,
                     .align_is_generic = payload.align_is_generic,
                     .cc_is_generic = payload.cc_is_generic,
@@ -2069,6 +2074,9 @@ pub const Type = extern union {
 
             .function => {
                 const fn_info = ty.fnInfo();
+                if (fn_info.is_noinline) {
+                    try writer.writeAll("noinline ");
+                }
                 try writer.writeAll("fn(");
                 for (fn_info.param_types, 0..) |param_ty, i| {
                     if (i != 0) try writer.writeAll(", ");
@@ -4863,6 +4871,7 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .is_noinline = false,
                 .align_is_generic = false,
                 .cc_is_generic = false,
                 .section_is_generic = false,
@@ -4877,6 +4886,7 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .is_noinline = false,
                 .align_is_generic = false,
                 .cc_is_generic = false,
                 .section_is_generic = false,
@@ -4891,6 +4901,7 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .is_noinline = false,
                 .align_is_generic = false,
                 .cc_is_generic = false,
                 .section_is_generic = false,
@@ -4905,6 +4916,7 @@ pub const Type = extern union {
                 .alignment = 0,
                 .is_var_args = false,
                 .is_generic = false,
+                .is_noinline = false,
                 .align_is_generic = false,
                 .cc_is_generic = false,
                 .section_is_generic = false,
@@ -6367,6 +6379,7 @@ pub const Type = extern union {
                 cc: std.builtin.CallingConvention,
                 is_var_args: bool,
                 is_generic: bool,
+                is_noinline: bool,
                 align_is_generic: bool,
                 cc_is_generic: bool,
                 section_is_generic: bool,
test/cases/compile_errors/bad_usage_of_call.zig
@@ -14,14 +14,25 @@ export fn entry5(c: bool) void {
     var baz = if (c) &baz1 else &baz2;
     @call(.compile_time, baz, .{});
 }
+export fn entry6() void {
+    _ = @call(.always_inline, dummy, .{});
+}
+export fn entry7() void {
+    _ = @call(.always_inline, dummy2, .{});
+}
 pub export fn entry() void {
     var call_me: *const fn () void = undefined;
     @call(.always_inline, call_me, .{});
 }
+
 fn foo() void {}
-fn bar() callconv(.Inline) void {}
+inline fn bar() void {}
 fn baz1() void {}
 fn baz2() void {}
+noinline fn dummy() u32 {
+    return 0;
+}
+noinline fn dummy2() void {}
 
 // error
 // backend=stage2
@@ -30,7 +41,8 @@ fn baz2() 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: no-inline call of inline function
+// :11:5: error: 'never_inline' call of inline function
 // :15:26: error: modifier 'compile_time' requires a comptime-known function
-// :19:27: error: modifier 'always_inline' requires a comptime-known function
-
+// :18:9: error: 'always_inline' call of noinline function
+// :21:9: error: 'always_inline' call of noinline function
+// :25:27: error: modifier 'always_inline' requires a comptime-known function