Commit d949180ab0

Stéphan Kochen <git@stephank.nl>
2021-10-19 08:28:06
translate-c: create `inline fn` for always_inline
1 parent ed2a508
src/translate_c/ast.zig
@@ -540,6 +540,7 @@ pub const Payload = struct {
             is_pub: bool,
             is_extern: bool,
             is_export: bool,
+            is_inline: bool,
             is_var_args: bool,
             name: ?[]const u8,
             linksection_string: ?[]const u8,
@@ -2614,6 +2615,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex {
     if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub");
     if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern");
     if (payload.is_export) _ = try c.addToken(.keyword_export, "export");
+    if (payload.is_inline) _ = try c.addToken(.keyword_inline, "inline");
     const fn_token = try c.addToken(.keyword_fn, "fn");
     if (payload.name) |some| _ = try c.addIdentifier(some);
 
src/clang.zig
@@ -536,6 +536,9 @@ pub const FunctionDecl = opaque {
     pub const isInlineSpecified = ZigClangFunctionDecl_isInlineSpecified;
     extern fn ZigClangFunctionDecl_isInlineSpecified(*const FunctionDecl) bool;
 
+    pub const hasAlwaysInlineAttr = ZigClangFunctionDecl_hasAlwaysInlineAttr;
+    extern fn ZigClangFunctionDecl_hasAlwaysInlineAttr(*const FunctionDecl) bool;
+
     pub const isDefined = ZigClangFunctionDecl_isDefined;
     extern fn ZigClangFunctionDecl_isDefined(*const FunctionDecl) bool;
 
src/translate_c.zig
@@ -575,12 +575,14 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
     const fn_decl_loc = fn_decl.getLocation();
     const has_body = fn_decl.hasBody();
     const storage_class = fn_decl.getStorageClass();
+    const is_always_inline = has_body and fn_decl.hasAlwaysInlineAttr();
     var decl_ctx = FnDeclContext{
         .fn_name = fn_name,
         .has_body = has_body,
         .storage_class = storage_class,
+        .is_always_inline = is_always_inline,
         .is_export = switch (storage_class) {
-            .None => has_body and !fn_decl.isInlineSpecified(),
+            .None => has_body and !is_always_inline and !fn_decl.isInlineSpecified(),
             .Extern, .Static => false,
             .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
             .Auto => unreachable, // Not legal on functions
@@ -615,6 +617,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
                 decl_ctx.has_body = false;
                 decl_ctx.storage_class = .Extern;
                 decl_ctx.is_export = false;
+                decl_ctx.is_always_inline = false;
                 try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{});
             }
             break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
@@ -653,6 +656,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
         const param_name = param.name orelse {
             proto_node.data.is_extern = true;
             proto_node.data.is_export = false;
+            proto_node.data.is_inline = false;
             try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
             return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
         };
@@ -685,6 +689,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
         => {
             proto_node.data.is_extern = true;
             proto_node.data.is_export = false;
+            proto_node.data.is_inline = false;
             try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
             return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
         },
@@ -704,6 +709,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
             => {
                 proto_node.data.is_extern = true;
                 proto_node.data.is_export = false;
+                proto_node.data.is_inline = false;
                 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{});
                 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
             },
@@ -974,6 +980,7 @@ fn buildFlexibleArrayFn(
             .is_pub = true,
             .is_extern = false,
             .is_export = false,
+            .is_inline = false,
             .is_var_args = false,
             .name = field_name,
             .linksection_string = null,
@@ -4821,6 +4828,7 @@ const FnDeclContext = struct {
     fn_name: []const u8,
     has_body: bool,
     storage_class: clang.StorageClass,
+    is_always_inline: bool,
     is_export: bool,
 };
 
@@ -4871,7 +4879,7 @@ fn transFnNoProto(
     is_pub: bool,
 ) !*ast.Payload.Func {
     const cc = try transCC(c, fn_ty, source_loc);
-    const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true;
+    const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static and !ctx.is_always_inline) else true;
     return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
 }
 
@@ -4888,9 +4896,9 @@ fn finishTransFnProto(
 ) !*ast.Payload.Func {
     const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
     const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
+    const is_inline = if (fn_decl_context) |ctx| ctx.is_always_inline else false;
     const scope = &c.global_scope.base;
 
-    // TODO check for always_inline attribute
     // TODO check for align attribute
 
     var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
@@ -4934,7 +4942,7 @@ fn finishTransFnProto(
 
     const alignment = if (fn_decl) |decl| zigAlignment(decl.getAlignedAttribute(c.clang_context)) else null;
 
-    const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc;
+    const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc;
 
     const return_type_node = blk: {
         if (fn_ty.getNoReturnAttr()) {
@@ -4963,6 +4971,7 @@ fn finishTransFnProto(
             .is_pub = is_pub,
             .is_extern = is_extern,
             .is_export = is_export,
+            .is_inline = is_inline,
             .is_var_args = is_var_args,
             .name = name,
             .linksection_string = linksection_string,
src/zig_clang.cpp
@@ -2120,6 +2120,11 @@ bool ZigClangFunctionDecl_isInlineSpecified(const struct ZigClangFunctionDecl *s
     return casted->isInlineSpecified();
 }
 
+bool ZigClangFunctionDecl_hasAlwaysInlineAttr(const struct ZigClangFunctionDecl *self) {
+    auto casted = reinterpret_cast<const clang::FunctionDecl *>(self);
+    return casted->hasAttr<clang::AlwaysInlineAttr>();
+}
+
 const char* ZigClangFunctionDecl_getSectionAttribute(const struct ZigClangFunctionDecl *self, size_t *len) {
     auto casted = reinterpret_cast<const clang::FunctionDecl *>(self);
     if (const clang::SectionAttr *SA = casted->getAttr<clang::SectionAttr>()) {
src/zig_clang.h
@@ -1111,6 +1111,7 @@ ZIG_EXTERN_C bool ZigClangFunctionDecl_doesDeclarationForceExternallyVisibleDefi
 ZIG_EXTERN_C bool ZigClangFunctionDecl_isThisDeclarationADefinition(const struct ZigClangFunctionDecl *);
 ZIG_EXTERN_C bool ZigClangFunctionDecl_doesThisDeclarationHaveABody(const struct ZigClangFunctionDecl *);
 ZIG_EXTERN_C bool ZigClangFunctionDecl_isInlineSpecified(const struct ZigClangFunctionDecl *);
+ZIG_EXTERN_C bool ZigClangFunctionDecl_hasAlwaysInlineAttr(const struct ZigClangFunctionDecl *);
 ZIG_EXTERN_C bool ZigClangFunctionDecl_isDefined(const struct ZigClangFunctionDecl *);
 ZIG_EXTERN_C const struct ZigClangFunctionDecl* ZigClangFunctionDecl_getDefinition(const struct ZigClangFunctionDecl *);
 ZIG_EXTERN_C const char* ZigClangFunctionDecl_getSectionAttribute(const struct ZigClangFunctionDecl *, size_t *);
test/translate_c.zig
@@ -849,6 +849,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\pub extern fn foo() noreturn;
     });
 
+    cases.add("always_inline attribute",
+        \\__attribute__((always_inline)) int foo() {
+        \\    return 5;
+        \\}
+    , &[_][]const u8{
+        \\pub inline fn foo() c_int {
+        \\    return 5;
+        \\}
+    });
+
     cases.add("add, sub, mul, div, rem",
         \\int s() {
         \\    int a, b, c;