Commit 82219b1fd5

Andrew Kelley <andrew@ziglang.org>
2019-05-10 23:44:47
translate-c: better handling of restore points
1 parent 2933d6b
Changed files (2)
src-self-hosted
test
src-self-hosted/translate_c.zig
@@ -208,14 +208,14 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void {
 
 fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
     if (try c.decl_table.put(@ptrToInt(fn_decl), {})) |_| return; // Avoid processing this decl twice
-
+    const rp = makeRestorePoint(c);
     const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
     const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
     const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
     const fn_type = ZigClangQualType_getTypePtr(fn_qt);
     const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
         .FunctionProto => transFnProto(
-            c,
+            rp,
             @ptrCast(*const ZigClangFunctionProtoType, fn_type),
             fn_decl_loc,
             fn_decl,
@@ -242,32 +242,35 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void {
     try c.tree.root_node.decls.push(decl_node);
 }
 
-fn transQualType(c: *Context, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) !*ast.Node {
-    return transType(c, ZigClangQualType_getTypePtr(qt), source_loc);
+fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
+    return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
+}
+
+fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
+    const canon = ZigClangQualType_getCanonicalType(qt);
+    return ZigClangQualType_getTypePtr(canon);
 }
 
 const RestorePoint = struct {
-    context: *Context,
+    c: *Context,
     token_index: ast.TokenIndex,
     src_buf_index: usize,
 
     fn activate(self: RestorePoint) void {
-        self.context.tree.tokens.shrink(self.token_index);
-        self.context.source_buffer.shrink(self.src_buf_index);
+        self.c.tree.tokens.shrink(self.token_index);
+        self.c.source_buffer.shrink(self.src_buf_index);
     }
 };
 
 fn makeRestorePoint(c: *Context) RestorePoint {
     return RestorePoint{
-        .context = c,
+        .c = c,
         .token_index = c.tree.tokens.len,
         .src_buf_index = c.source_buffer.len(),
     };
 }
 
-fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) !*ast.Node {
-    const rp = makeRestorePoint(c);
-
+fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
     switch (ZigClangType_getTypeClass(ty)) {
         .Builtin => {
             const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
@@ -275,23 +278,26 @@ fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLoc
                 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"),
             }
         },
-        .FunctionProto => return transFnProto(c, @ptrCast(*const ZigClangFunctionType, ty), source_loc, null, false),
+        .FunctionProto => {
+            const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
+            const fn_proto = try transFnProto(rp, fn_proto_ty, source_loc, null, null);
+            return &fn_proto.base;
+        },
         else => {
-            const type_name = c.str(ZigClangType_getTypeClassName(ty));
+            const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
             return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name);
         },
     }
 }
 
 fn transFnProto(
-    c: *Context,
+    rp: RestorePoint,
     fn_proto_ty: *const ZigClangFunctionProtoType,
     source_loc: ZigClangSourceLocation,
     opt_fn_decl: ?*const ZigClangFunctionDecl,
     fn_name: ?[]const u8,
 ) !*ast.Node.FnProto {
     const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
-    const rp = makeRestorePoint(c);
     const cc = switch (ZigClangFunctionType_getCallConv(fn_ty)) {
         .C => CallingConvention.C,
         .X86StdCall => CallingConvention.Stdcall,
@@ -323,13 +329,13 @@ fn transFnProto(
     // TODO check for align attribute
 
     // extern fn name(...) T
-    const cc_tok = if (cc == .Stdcall) try appendToken(c, .Keyword_stdcallcc, "stdcallcc") else null;
+    const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null;
     const is_export = exp: {
         const fn_decl = opt_fn_decl orelse break :exp false;
         const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
         const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
         break :exp switch (storage_class) {
-            .None => switch (c.mode) {
+            .None => switch (rp.c.mode) {
                 .import => false,
                 .translate => has_body,
             },
@@ -340,48 +346,44 @@ fn transFnProto(
         };
     };
     const extern_export_inline_tok = if (is_export)
-        try appendToken(c, .Keyword_export, "export")
+        try appendToken(rp.c, .Keyword_export, "export")
     else if (cc == .C)
-        try appendToken(c, .Keyword_extern, "extern")
+        try appendToken(rp.c, .Keyword_extern, "extern")
     else
         null;
-    const fn_tok = try appendToken(c, .Keyword_fn, "fn");
-    const name_tok = if (fn_name) |n| try appendToken(c, .Identifier, "{}", n) else null;
-    const lparen_tok = try appendToken(c, .LParen, "(");
-    const var_args_tok = if (is_var_args) try appendToken(c, .Ellipsis3, "...") else null;
-    const rparen_tok = try appendToken(c, .RParen, ")");
+    const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
+    const name_tok = if (fn_name) |n| try appendToken(rp.c, .Identifier, "{}", n) else null;
+    const lparen_tok = try appendToken(rp.c, .LParen, "(");
+    const var_args_tok = if (is_var_args) try appendToken(rp.c, .Ellipsis3, "...") else null;
+    const rparen_tok = try appendToken(rp.c, .RParen, ")");
 
     const return_type_node = blk: {
         if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
-            break :blk try appendIdentifier(c, "noreturn");
+            break :blk try appendIdentifier(rp.c, "noreturn");
         } else {
-            return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: non-noreturn FunctionProto return type");
-            //proto_node->data.fn_proto.return_type = trans_qual_type(c,
-            //        ZigClangFunctionType_getReturnType(fn_ty), source_loc);
-            //if (proto_node->data.fn_proto.return_type == nullptr) {
-            //    emit_warning(c, source_loc, "unsupported function proto return type");
-            //    return nullptr;
-            //}
-            //// convert c_void to actual void (only for return type)
-            //// we do want to look at the AstNode instead of ZigClangQualType, because
-            //// if they do something like:
-            ////     typedef Foo void;
-            ////     void foo(void) -> Foo;
-            //// we want to keep the return type AST node.
-            //if (is_c_void_type(proto_node->data.fn_proto.return_type)) {
-            //    proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "void");
-            //}
+            const return_qt = ZigClangFunctionType_getReturnType(fn_ty);
+            if (ZigClangType_isVoidType(qualTypeCanon(return_qt))) {
+                break :blk try appendIdentifier(rp.c, "void");
+            } else {
+                break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
+                    error.UnsupportedType => {
+                        try emitWarning(rp.c, source_loc, "unsupported function proto return type");
+                        return err;
+                    },
+                    else => return err,
+                };
+            }
         }
     };
 
-    const fn_proto = try c.a().create(ast.Node.FnProto);
+    const fn_proto = try rp.c.a().create(ast.Node.FnProto);
     fn_proto.* = ast.Node.FnProto{
         .base = ast.Node{ .id = ast.Node.Id.FnProto },
         .doc_comments = null,
         .visib_token = null,
         .fn_token = fn_tok,
         .name_token = name_tok,
-        .params = ast.Node.FnProto.ParamList.init(c.a()),
+        .params = ast.Node.FnProto.ParamList.init(rp.c.a()),
         .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
         .var_args_token = var_args_tok,
         .extern_export_inline_token = extern_export_inline_tok,
@@ -396,14 +398,14 @@ fn transFnProto(
 }
 
 fn revertAndWarn(
-    restore_point: RestorePoint,
+    rp: RestorePoint,
     err: var,
     source_loc: ZigClangSourceLocation,
     comptime format: []const u8,
     args: ...,
 ) (@typeOf(err) || error{OutOfMemory}) {
-    restore_point.activate();
-    try emitWarning(restore_point.context, source_loc, format, args);
+    rp.activate();
+    try emitWarning(rp.c, source_loc, format, args);
     return err;
 }
 
test/translate_c.zig
@@ -2,12 +2,26 @@ const tests = @import("tests.zig");
 const builtin = @import("builtin");
 
 pub fn addCases(cases: *tests.TranslateCContext) void {
+    /////////////// Cases that pass for both stage1/stage2 ////////////////
     cases.add_both("simple noreturn fn",
         \\void __attribute__((noreturn)) foo(void);
     ,
         \\extern fn foo() noreturn;
     );
 
+    /////////////// Cases that pass for only stage2 ////////////////
+    // (none)
+
+    /////////////// Cases that pass for only stage1 ////////////////
+
+    cases.addC("Parameterless function prototypes",
+        \\void foo() {}
+        \\void bar(void) {}
+    ,
+        \\pub export fn foo() void {}
+        \\pub export fn bar() void {}
+    );
+
     cases.add("macro with left shift",
         \\#define REDISMODULE_READ (1<<0)
     ,
@@ -1523,14 +1537,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     );
 
-    cases.addC("Parameterless function prototypes",
-        \\void foo() {}
-        \\void bar(void) {}
-    ,
-        \\pub export fn foo() void {}
-        \\pub export fn bar() void {}
-    );
-
     cases.addC(
         "u integer suffix after 0 (zero) in macro definition",
         "#define ZERO 0U",
@@ -1667,34 +1673,4 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    }
         \\}
     );
-
-    // cases.add("empty array with initializer",
-    //     "int a[4] = {};"
-    // ,
-    //     "pub var a: [4]c_int = [1]c_int{0} ** 4;"
-    // );
-
-    // cases.add("array with initialization",
-    //     "int a[4] = {1, 2, 3, 4};"
-    // ,
-    //     "pub var a: [4]c_int = [4]c_int{1, 2, 3, 4};"
-    // );
-
-    // cases.add("array with incomplete initialization",
-    //     "int a[4] = {3, 4};"
-    // ,
-    //     "pub var a: [4]c_int = [2]c_int{3, 4} ++ ([1]c_int{0} ** 2);"
-    // );
-
-    // cases.add("2D array with initialization",
-    //     "int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };"
-    // ,
-    //     "pub var a: [3][3]c_int = [3][3]c_int{[3]c_int{1, 2, 3}, [3]c_int{4, 5, 6}, [3]c_int{7, 8, 9}};"
-    // );
-
-    // cases.add("2D array with incomplete initialization",
-    //     "int a[3][3] = { {1, 2}, {4, 5, 6} };"
-    // ,
-    //     "pub var a: [3][3]c_int = [2][3]c_int{[2]c_int{1, 2} ++ [1]c_int{0}, [3]c_int{4, 5, 6}} ++ [1][3]c_int{[1]c_int{0} ** 3};"
-    // );
 }