Commit 3fd8ac092e

Isaac Freund <ifreund@ifreund.xyz>
2021-05-20 14:08:57
stage2: support inline keyword on function decls
This is an alternative to callconv(.Inline). Using an inline keyword as well as an explicit callconv() is a compile error.
1 parent 569525f
Changed files (3)
lib
std
src
lib/std/zig/ast.zig
@@ -1833,7 +1833,7 @@ pub const Tree = struct {
         var result: full.FnProto = .{
             .ast = info,
             .visib_token = null,
-            .extern_export_token = null,
+            .extern_export_inline_token = null,
             .lib_name = null,
             .name_token = null,
             .lparen = undefined,
@@ -1842,7 +1842,11 @@ pub const Tree = struct {
         while (i > 0) {
             i -= 1;
             switch (token_tags[i]) {
-                .keyword_extern, .keyword_export => result.extern_export_token = i,
+                .keyword_extern,
+                .keyword_export,
+                .keyword_inline,
+                .keyword_noinline,
+                => result.extern_export_inline_token = i,
                 .keyword_pub => result.visib_token = i,
                 .string_literal => result.lib_name = i,
                 else => break,
@@ -2123,7 +2127,7 @@ pub const full = struct {
 
     pub const FnProto = struct {
         visib_token: ?TokenIndex,
-        extern_export_token: ?TokenIndex,
+        extern_export_inline_token: ?TokenIndex,
         lib_name: ?TokenIndex,
         name_token: ?TokenIndex,
         lparen: TokenIndex,
src/AstGen.zig
@@ -995,7 +995,7 @@ fn fnProtoExpr(
     const token_tags = tree.tokens.items(.tag);
 
     const is_extern = blk: {
-        const maybe_extern_token = fn_proto.extern_export_token orelse break :blk false;
+        const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
         break :blk token_tags[maybe_extern_token] == .keyword_extern;
     };
     assert(!is_extern);
@@ -2735,15 +2735,20 @@ fn fnDecl(
     };
     defer decl_gz.instructions.deinit(gpa);
 
+    // TODO: support noinline
     const is_pub = fn_proto.visib_token != null;
     const is_export = blk: {
-        const maybe_export_token = fn_proto.extern_export_token orelse break :blk false;
+        const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
         break :blk token_tags[maybe_export_token] == .keyword_export;
     };
     const is_extern = blk: {
-        const maybe_extern_token = fn_proto.extern_export_token orelse break :blk false;
+        const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
         break :blk token_tags[maybe_extern_token] == .keyword_extern;
     };
+    const has_inline_keyword = blk: {
+        const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
+        break :blk token_tags[maybe_inline_token] == .keyword_inline;
+    };
     const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
         break :inst try expr(&decl_gz, &decl_gz.base, align_rl, fn_proto.ast.align_expr);
     };
@@ -2812,17 +2817,30 @@ fn fnDecl(
         fn_proto.ast.return_type,
     );
 
-    const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
-        try AstGen.expr(
-            &decl_gz,
-            &decl_gz.base,
-            .{ .ty = .calling_convention_type },
-            fn_proto.ast.callconv_expr,
-        )
-    else if (is_extern) // note: https://github.com/ziglang/zig/issues/5269
-        Zir.Inst.Ref.calling_convention_c
-    else
-        Zir.Inst.Ref.none;
+    const cc: Zir.Inst.Ref = blk: {
+        if (fn_proto.ast.callconv_expr != 0) {
+            if (has_inline_keyword) {
+                return astgen.failNode(
+                    fn_proto.ast.callconv_expr,
+                    "explicit callconv incompatible with inline keyword",
+                    .{},
+                );
+            }
+            break :blk try AstGen.expr(
+                &decl_gz,
+                &decl_gz.base,
+                .{ .ty = .calling_convention_type },
+                fn_proto.ast.callconv_expr,
+            );
+        } else if (is_extern) {
+            // note: https://github.com/ziglang/zig/issues/5269
+            break :blk .calling_convention_c;
+        } else if (has_inline_keyword) {
+            break :blk .calling_convention_inline;
+        } else {
+            break :blk .none;
+        }
+    };
 
     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
         if (!is_extern) {
src/Zir.zig
@@ -1687,6 +1687,8 @@ pub const Inst = struct {
         one_usize,
         /// `std.builtin.CallingConvention.C`
         calling_convention_c,
+        /// `std.builtin.CallingConvention.Inline`
+        calling_convention_inline,
 
         _,
 
@@ -1954,6 +1956,10 @@ pub const Inst = struct {
                 .ty = Type.initTag(.calling_convention),
                 .val = .{ .ptr_otherwise = &calling_convention_c_payload.base },
             },
+            .calling_convention_inline = .{
+                .ty = Type.initTag(.calling_convention),
+                .val = .{ .ptr_otherwise = &calling_convention_inline_payload.base },
+            },
         });
     };
 
@@ -1964,6 +1970,13 @@ pub const Inst = struct {
         .data = @enumToInt(std.builtin.CallingConvention.C),
     };
 
+    /// We would like this to be const but `Value` wants a mutable pointer for
+    /// its payload field. Nothing should mutate this though.
+    var calling_convention_inline_payload: Value.Payload.U32 = .{
+        .base = .{ .tag = .enum_field_index },
+        .data = @enumToInt(std.builtin.CallingConvention.Inline),
+    };
+
     /// All instructions have an 8-byte payload, which is contained within
     /// this union. `Tag` determines which union field is active, as well as
     /// how to interpret the data within.