From a6f7a9ce2b008ff66ab32c8162d64dd77ffe952f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 10 May 2019 16:03:54 -0400 Subject: [PATCH] translate-c: we have our first test of self-hosted See #1964 --- src-self-hosted/clang.zig | 15 +- src-self-hosted/translate_c.zig | 316 +++++++++++++++++++++----------- src/translate_c.cpp | 18 +- src/zig_clang.cpp | 43 +++++ src/zig_clang.h | 17 +- test/tests.zig | 28 ++- test/translate_c.zig | 6 + 7 files changed, 320 insertions(+), 123 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 3e2346f681..dd9e49b05b 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -56,7 +56,6 @@ pub const struct_ZigClangSkipFunctionBodiesScope = @OpaqueType(); pub const struct_ZigClangSourceManager = @OpaqueType(); pub const struct_ZigClangSourceRange = @OpaqueType(); pub const struct_ZigClangStmt = @OpaqueType(); -pub const struct_ZigClangStorageClass = @OpaqueType(); pub const struct_ZigClangStringLiteral = @OpaqueType(); pub const struct_ZigClangStringRef = @OpaqueType(); pub const struct_ZigClangSwitchStmt = @OpaqueType(); @@ -513,6 +512,10 @@ pub extern fn ZigClangASTUnit_delete(arg0: ?*struct_ZigClangASTUnit) void; pub extern fn ZigClangFunctionDecl_getType(self: *const struct_ZigClangFunctionDecl) struct_ZigClangQualType; pub extern fn ZigClangFunctionDecl_getLocation(self: *const struct_ZigClangFunctionDecl) struct_ZigClangSourceLocation; +pub extern fn ZigClangFunctionDecl_hasBody(self: *const struct_ZigClangFunctionDecl) bool; +pub extern fn ZigClangFunctionDecl_getStorageClass(self: *const struct_ZigClangFunctionDecl) ZigClangStorageClass; +pub extern fn ZigClangFunctionDecl_getParamDecl(self: *const struct_ZigClangFunctionDecl, i: c_uint) *const struct_ZigClangParmVarDecl; + pub extern fn ZigClangBuiltinType_getKind(self: *const struct_ZigClangBuiltinType) ZigClangBuiltinTypeKind; pub extern fn ZigClangFunctionType_getNoReturnAttr(self: *const ZigClangFunctionType) bool; @@ -584,7 +587,6 @@ pub const ZigClangSkipFunctionBodiesScope = struct_ZigClangSkipFunctionBodiesSco pub const ZigClangSourceManager = struct_ZigClangSourceManager; pub const ZigClangSourceRange = struct_ZigClangSourceRange; pub const ZigClangStmt = struct_ZigClangStmt; -pub const ZigClangStorageClass = struct_ZigClangStorageClass; pub const ZigClangStringLiteral = struct_ZigClangStringLiteral; pub const ZigClangStringRef = struct_ZigClangStringRef; pub const ZigClangSwitchStmt = struct_ZigClangSwitchStmt; @@ -849,3 +851,12 @@ pub const ZigClangCallingConv = extern enum { PreserveAll, AArch64VectorCall, }; + +pub const ZigClangStorageClass = extern enum { + None, + Extern, + Static, + PrivateExtern, + Auto, + Register, +}; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 84e45b9dc4..9642212de1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -2,6 +2,7 @@ // and stage2. Currently the only way it is used is with `zig translate-c-2`. const std = @import("std"); +const builtin = @import("builtin"); const ast = std.zig.ast; const Token = std.zig.Token; use @import("clang.zig"); @@ -12,13 +13,7 @@ pub const Mode = enum { }; // TODO merge with Type.Fn.CallingConvention -pub const CallingConvention = enum { - auto, - c, - cold, - naked, - stdcall, -}; +const CallingConvention = builtin.TypeInfo.CallingConvention; pub const ClangErrMsg = Stage2ErrorMsg; @@ -27,11 +22,64 @@ pub const Error = error{ UnsupportedType, }; +const DeclTable = std.HashMap(usize, void, addrHash, addrEql); + +fn addrHash(x: usize) u32 { + switch (@typeInfo(usize).Int.bits) { + 32 => return x, + // pointers are usually aligned so we ignore the bits that are probably all 0 anyway + // usually the larger bits of addr space are unused so we just chop em off + 64 => return @truncate(u32, x >> 4), + else => @compileError("unreachable"), + } +} + +fn addrEql(a: usize, b: usize) bool { + return a == b; +} + +const Scope = struct { + id: Id, + parent: ?*Scope, + + const Id = enum { + Switch, + Var, + Block, + Root, + While, + }; + const Switch = struct { + base: Scope, + }; + + const Var = struct { + base: Scope, + c_name: []const u8, + zig_name: []const u8, + }; + + const Block = struct { + base: Scope, + }; + + const Root = struct { + base: Scope, + }; + + const While = struct { + base: Scope, + }; +}; + const Context = struct { tree: *ast.Tree, source_buffer: *std.Buffer, err: Error, source_manager: *ZigClangSourceManager, + decl_table: DeclTable, + global_scope: *Scope.Root, + mode: Mode, fn a(c: *Context) *std.mem.Allocator { return &c.tree.arena_allocator.allocator; @@ -76,7 +124,7 @@ pub fn translate( var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); errdefer tree_arena.deinit(); - const arena = &tree_arena.allocator; + var arena = &tree_arena.allocator; const root_node = try arena.create(ast.Node.Root); root_node.* = ast.Node.Root{ @@ -96,14 +144,24 @@ pub fn translate( .errors = ast.Tree.ErrorList.init(arena), }; tree.arena_allocator = tree_arena; + arena = &tree.arena_allocator.allocator; - var source_buffer = try std.Buffer.initSize(&tree.arena_allocator.allocator, 0); + var source_buffer = try std.Buffer.initSize(arena, 0); var context = Context{ .tree = tree, .source_buffer = &source_buffer, .source_manager = ZigClangASTUnit_getSourceManager(ast_unit), .err = undefined, + .decl_table = DeclTable.init(arena), + .global_scope = try arena.create(Scope.Root), + .mode = mode, + }; + context.global_scope.* = Scope.Root{ + .base = Scope{ + .id = Scope.Id.Root, + .parent = null, + }, }; if (!ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, &context, declVisitorC)) { @@ -149,27 +207,39 @@ 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 fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); - - // TODO The C++ code has this: - //if (get_global(c, fn_name)) { - // // we already saw this function - // return; - //} - const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl); - const proto_node = transQualType(c, ZigClangFunctionDecl_getType(fn_decl), fn_decl_loc) catch |e| switch (e) { - error.UnsupportedType => { - try failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); - return; + 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, + @ptrCast(*const ZigClangFunctionProtoType, fn_type), + fn_decl_loc, + fn_decl, + fn_name, + ) catch |err| switch (err) { + error.UnsupportedType => { + return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); + }, + else => return err, }, - else => return e, + .FunctionNoProto => return failDecl(c, fn_decl_loc, fn_name, "TODO support functions with no prototype"), + else => unreachable, }; - const semi_tok = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, fn_decl_loc, "TODO implement more translate-c for function decls"); + if (!ZigClangFunctionDecl_hasBody(fn_decl)) { + const semi_tok = try appendToken(c, .Semicolon, ";"); + return addTopLevelDecl(c, fn_name, &proto_node.base); + } - try c.tree.root_node.decls.push(proto_node); + try emitWarning(c, fn_decl_loc, "TODO implement function body translation"); +} + +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 { @@ -205,91 +275,7 @@ fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLoc else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"), } }, - .FunctionProto => { - const fn_ty = @ptrCast(*const ZigClangFunctionType, ty); - const cc = switch (ZigClangFunctionType_getCallConv(fn_ty)) { - .C => CallingConvention.c, - .X86StdCall => CallingConvention.stdcall, - .X86FastCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 fastcall"), - .X86ThisCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 thiscall"), - .X86VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 vectorcall"), - .X86Pascal => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 pascal"), - .Win64 => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: win64"), - .X86_64SysV => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 64sysv"), - .X86RegCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 reg"), - .AAPCS => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs"), - .AAPCS_VFP => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs-vfp"), - .IntelOclBicc => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: intel_ocl_bicc"), - .SpirFunction => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: SPIR function"), - .OpenCLKernel => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: OpenCLKernel"), - .Swift => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: Swift"), - .PreserveMost => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveMost"), - .PreserveAll => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveAll"), - .AArch64VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: AArch64VectorCall"), - }; - - const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty); - const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty); - const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty); - var i: usize = 0; - while (i < param_count) : (i += 1) { - return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType"); - } - // TODO check for always_inline attribute - // TODO check for align attribute - - // extern fn (...) T - const cc_tok = if (cc == .stdcall) try appendToken(c, .Keyword_stdcallcc, "stdcallcc") else null; - const extern_tok = if (cc == .c) try appendToken(c, .Keyword_extern, "extern") else null; - const fn_tok = try appendToken(c, .Keyword_fn, "fn"); - 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 return_type_node = blk: { - if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) { - break :blk try appendIdentifier(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 fn_proto = try 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 = null, - .params = ast.Node.FnProto.ParamList.init(c.a()), - .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node }, - .var_args_token = var_args_tok, - .extern_export_inline_token = extern_tok, - .cc_token = cc_tok, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - .section_expr = null, - }; - return &fn_proto.base; - }, - + .FunctionProto => return transFnProto(c, @ptrCast(*const ZigClangFunctionType, ty), source_loc, null, false), else => { const type_name = c.str(ZigClangType_getTypeClassName(ty)); return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name); @@ -297,6 +283,118 @@ fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLoc } } +fn transFnProto( + c: *Context, + 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, + .X86FastCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 fastcall"), + .X86ThisCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 thiscall"), + .X86VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 vectorcall"), + .X86Pascal => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 pascal"), + .Win64 => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: win64"), + .X86_64SysV => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 64sysv"), + .X86RegCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 reg"), + .AAPCS => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs"), + .AAPCS_VFP => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs-vfp"), + .IntelOclBicc => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: intel_ocl_bicc"), + .SpirFunction => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: SPIR function"), + .OpenCLKernel => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: OpenCLKernel"), + .Swift => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: Swift"), + .PreserveMost => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveMost"), + .PreserveAll => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveAll"), + .AArch64VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: AArch64VectorCall"), + }; + + const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty); + const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty); + var i: usize = 0; + while (i < param_count) : (i += 1) { + return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType"); + } + // TODO check for always_inline attribute + // TODO check for align attribute + + // extern fn name(...) T + const cc_tok = if (cc == .Stdcall) try appendToken(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) { + .import => false, + .translate => has_body, + }, + .Extern, .Static => false, + .PrivateExtern => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported storage class: private extern"), + .Auto => unreachable, // Not legal on functions + .Register => unreachable, // Not legal on functions + }; + }; + const extern_export_inline_tok = if (is_export) + try appendToken(c, .Keyword_export, "export") + else if (cc == .C) + try appendToken(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 return_type_node = blk: { + if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) { + break :blk try appendIdentifier(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 fn_proto = try 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()), + .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node }, + .var_args_token = var_args_tok, + .extern_export_inline_token = extern_export_inline_tok, + .cc_token = cc_tok, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + .section_expr = null, + }; + return fn_proto; +} + fn revertAndWarn( restore_point: RestorePoint, err: var, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 64bf995cdd..71c0376130 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4038,15 +4038,15 @@ static void visit_fn_decl(Context *c, const ZigClangFunctionDecl *fn_decl) { } proto_node->data.fn_proto.name = fn_name; - proto_node->data.fn_proto.is_extern = !((const clang::FunctionDecl*)fn_decl)->hasBody(); + proto_node->data.fn_proto.is_extern = !ZigClangFunctionDecl_hasBody(fn_decl); - clang::StorageClass sc = ((const clang::FunctionDecl*)fn_decl)->getStorageClass(); - if (sc == clang::SC_None) { + ZigClangStorageClass sc = ZigClangFunctionDecl_getStorageClass(fn_decl); + if (sc == ZigClangStorageClass_None) { proto_node->data.fn_proto.visib_mod = c->visib_mod; - proto_node->data.fn_proto.is_export = ((const clang::FunctionDecl*)fn_decl)->hasBody() ? c->want_export : false; - } else if (sc == clang::SC_Extern || sc == clang::SC_Static) { + proto_node->data.fn_proto.is_export = ZigClangFunctionDecl_hasBody(fn_decl) ? c->want_export : false; + } else if (sc == ZigClangStorageClass_Extern || sc == ZigClangStorageClass_Static) { proto_node->data.fn_proto.visib_mod = c->visib_mod; - } else if (sc == clang::SC_PrivateExtern) { + } else if (sc == ZigClangStorageClass_PrivateExtern) { emit_warning(c, ZigClangFunctionDecl_getLocation(fn_decl), "unsupported storage class: private extern"); return; } else { @@ -4058,7 +4058,7 @@ static void visit_fn_decl(Context *c, const ZigClangFunctionDecl *fn_decl) { for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(i); - const clang::ParmVarDecl *param = ((const clang::FunctionDecl*)fn_decl)->getParamDecl(i); + const ZigClangParmVarDecl *param = ZigClangFunctionDecl_getParamDecl(fn_decl, i); const char *name = ZigClangDecl_getName_bytes_begin((const ZigClangDecl *)param); Buf *proto_param_name; @@ -4077,7 +4077,7 @@ static void visit_fn_decl(Context *c, const ZigClangFunctionDecl *fn_decl) { param_node->data.param_decl.name = scope_var->zig_name; } - if (!((const clang::FunctionDecl*)fn_decl)->hasBody()) { + if (!ZigClangFunctionDecl_hasBody(fn_decl)) { // just a prototype add_top_level_decl(c, proto_node->data.fn_proto.name, proto_node); return; @@ -4085,7 +4085,7 @@ static void visit_fn_decl(Context *c, const ZigClangFunctionDecl *fn_decl) { // actual function definition with body c->ptr_params.clear(); - const ZigClangStmt *body = bitcast(((const clang::FunctionDecl*)fn_decl)->getBody()); + const ZigClangStmt *body = ZigClangFunctionDecl_getBody(fn_decl); AstNode *actual_body_node; TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node); if (result_scope == nullptr) { diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index b1457fd817..25e309a63e 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -1236,6 +1236,25 @@ static_assert((clang::CallingConv)ZigClangCallingConv_PreserveMost == clang::CC_ static_assert((clang::CallingConv)ZigClangCallingConv_PreserveAll == clang::CC_PreserveAll, ""); static_assert((clang::CallingConv)ZigClangCallingConv_AArch64VectorCall == clang::CC_AArch64VectorCall, ""); +void ZigClang_detect_enum_StorageClass(clang::StorageClass x) { + switch (x) { + case clang::SC_None: + case clang::SC_Extern: + case clang::SC_Static: + case clang::SC_PrivateExtern: + case clang::SC_Auto: + case clang::SC_Register: + break; + } +} + +static_assert((clang::StorageClass)ZigClangStorageClass_None == clang::SC_None, ""); +static_assert((clang::StorageClass)ZigClangStorageClass_Extern == clang::SC_Extern, ""); +static_assert((clang::StorageClass)ZigClangStorageClass_Static == clang::SC_Static, ""); +static_assert((clang::StorageClass)ZigClangStorageClass_PrivateExtern == clang::SC_PrivateExtern, ""); +static_assert((clang::StorageClass)ZigClangStorageClass_Auto == clang::SC_Auto, ""); +static_assert((clang::StorageClass)ZigClangStorageClass_Register == clang::SC_Register, ""); + static_assert(sizeof(ZigClangSourceLocation) == sizeof(clang::SourceLocation), ""); static ZigClangSourceLocation bitcast(clang::SourceLocation src) { @@ -1434,6 +1453,30 @@ struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigC return bitcast(casted->getLocation()); } +bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return casted->hasBody(); +} + +enum ZigClangStorageClass ZigClangFunctionDecl_getStorageClass(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + return (ZigClangStorageClass)casted->getStorageClass(); +} + +const struct ZigClangParmVarDecl *ZigClangFunctionDecl_getParamDecl(const struct ZigClangFunctionDecl *self, + unsigned i) +{ + auto casted = reinterpret_cast(self); + const clang::ParmVarDecl *parm_var_decl = casted->getParamDecl(i); + return reinterpret_cast(parm_var_decl); +} + +const struct ZigClangStmt *ZigClangFunctionDecl_getBody(const struct ZigClangFunctionDecl *self) { + auto casted = reinterpret_cast(self); + const clang::Stmt *stmt = casted->getBody(); + return reinterpret_cast(stmt); +} + const ZigClangTypedefNameDecl *ZigClangTypedefType_getDecl(const ZigClangTypedefType *self) { auto casted = reinterpret_cast(self); const clang::TypedefNameDecl *name_decl = casted->getDecl(); diff --git a/src/zig_clang.h b/src/zig_clang.h index 6a67ec85c1..8e0d2f9f98 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -88,7 +88,6 @@ struct ZigClangSkipFunctionBodiesScope; struct ZigClangSourceManager; struct ZigClangSourceRange; struct ZigClangStmt; -struct ZigClangStorageClass; struct ZigClangStringLiteral; struct ZigClangStringRef; struct ZigClangSwitchStmt; @@ -700,6 +699,18 @@ enum ZigClangCallingConv { ZigClangCallingConv_AArch64VectorCall, // __attribute__((aarch64_vector_pcs)) }; +enum ZigClangStorageClass { + // These are legal on both functions and variables. + ZigClangStorageClass_None, + ZigClangStorageClass_Extern, + ZigClangStorageClass_Static, + ZigClangStorageClass_PrivateExtern, + + // These are only legal on variables. + ZigClangStorageClass_Auto, + ZigClangStorageClass_Register, +}; + ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *, struct ZigClangSourceLocation Loc); ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *, @@ -742,6 +753,10 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangDecl_getLocation(const struct ZIG_EXTERN_C struct ZigClangQualType ZigClangFunctionDecl_getType(const struct ZigClangFunctionDecl *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFunctionDecl_getLocation(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C bool ZigClangFunctionDecl_hasBody(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C enum ZigClangStorageClass ZigClangFunctionDecl_getStorageClass(const struct ZigClangFunctionDecl *); +ZIG_EXTERN_C const struct ZigClangParmVarDecl *ZigClangFunctionDecl_getParamDecl(const struct ZigClangFunctionDecl *, unsigned i); +ZIG_EXTERN_C const struct ZigClangStmt *ZigClangFunctionDecl_getBody(const struct ZigClangFunctionDecl *); ZIG_EXTERN_C bool ZigClangRecordDecl_isUnion(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C bool ZigClangRecordDecl_isStruct(const struct ZigClangRecordDecl *record_decl); diff --git a/test/tests.zig b/test/tests.zig index 61fd0426f1..fec6a294ff 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -903,6 +903,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, + stage2: bool, const SourceFile = struct { filename: []const u8, @@ -955,7 +956,8 @@ pub const TranslateCContext = struct { var zig_args = ArrayList([]const u8).init(b.allocator); zig_args.append(b.zig_exe) catch unreachable; - zig_args.append("translate-c") catch unreachable; + const translate_c_cmd = if (self.case.stage2) "translate-c-2" else "translate-c"; + zig_args.append(translate_c_cmd) catch unreachable; zig_args.append(b.pathFromRoot(root_src)) catch unreachable; warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); @@ -1052,6 +1054,7 @@ pub const TranslateCContext = struct { .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), .allow_warnings = allow_warnings, + .stage2 = false, }; tc.addSourceFile(filename, source); @@ -1072,6 +1075,26 @@ pub const TranslateCContext = struct { self.addCase(tc); } + pub fn add_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + for ([]bool{ false, true }) |stage2| { + const tc = self.create(false, "source.c", name, source, expected_lines); + tc.stage2 = stage2; + self.addCase(tc); + } + } + + pub fn add_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + const tc = self.create(false, "source.c", name, source, expected_lines); + tc.stage2 = true; + self.addCase(tc); + } + + pub fn addC_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + const tc = self.create(false, "source.c", name, source, expected_lines); + tc.stage2 = true; + self.addCase(tc); + } + pub fn addAllowWarnings(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(true, "source.h", name, source, expected_lines); self.addCase(tc); @@ -1080,7 +1103,8 @@ pub const TranslateCContext = struct { pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "translate-c {}", case.name) catch unreachable; + const translate_c_cmd = if (case.stage2) "translate-c-2" else "translate-c"; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", translate_c_cmd, case.name) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/translate_c.zig b/test/translate_c.zig index 8ad1583396..762b28e3aa 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2,6 +2,12 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add_both("simple noreturn fn", + \\void __attribute__((noreturn)) foo(void); + , + \\extern fn foo() noreturn; + ); + cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) ,