diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-05-11 12:05:33 -0400 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2019-05-11 12:09:11 -0400 |
| commit | 10e9d47b499752f5c40e8ded1bf50385d84912ff (patch) | |
| tree | ba14e8208f292d60ea6d834beca3385e8f4fc545 | |
| parent | 2ef2f9d71f851823966ae16b45a189657051b8df (diff) | |
| download | zig-10e9d47b499752f5c40e8ded1bf50385d84912ff.tar.gz zig-10e9d47b499752f5c40e8ded1bf50385d84912ff.zip | |
stage2 translate-c: implement functions with no prototype
stage1 translate-c actually has this wrong. When exporting a function,
it's ok to use empty parameters. But for prototypes, "no prototype"
means that it has to be emitted as a function that accepts anything,
e.g. extern fn foo(...) void;
See #1964
| -rw-r--r-- | src-self-hosted/translate_c.zig | 112 | ||||
| -rw-r--r-- | test/tests.zig | 10 | ||||
| -rw-r--r-- | test/translate_c.zig | 44 |
3 files changed, 115 insertions, 51 deletions
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index e7d6bfd09c..703f907907 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -254,11 +254,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { const fn_qt = ZigClangFunctionDecl_getType(fn_decl); const fn_type = ZigClangQualType_getTypePtr(fn_qt); var scope = &c.global_scope.base; + const has_body = ZigClangFunctionDecl_hasBody(fn_decl); + const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl); const decl_ctx = FnDeclContext{ .fn_name = fn_name, - .has_body = ZigClangFunctionDecl_hasBody(fn_decl), - .storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl), + .has_body = has_body, + .storage_class = storage_class, .scope = &scope, + .is_export = switch (storage_class) { + .None => has_body, + .Extern, .Static => false, + .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"), + .Auto => unreachable, // Not legal on functions + .Register => unreachable, // Not legal on functions + }, }; const proto_node = switch (ZigClangType_getTypeClass(fn_type)) { .FunctionProto => blk: { @@ -270,7 +279,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { error.OutOfMemory => return error.OutOfMemory, }; }, - .FunctionNoProto => return failDecl(c, fn_decl_loc, fn_name, "TODO support functions with no prototype"), + .FunctionNoProto => blk: { + const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type); + break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) { + error.UnsupportedType => { + return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function"); + }, + error.OutOfMemory => return error.OutOfMemory, + }; + }, else => unreachable, }; @@ -432,8 +449,22 @@ const FnDeclContext = struct { has_body: bool, storage_class: ZigClangStorageClass, scope: **Scope, + is_export: bool, }; +fn transCC( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, +) !CallingConvention { + const clang_cc = ZigClangFunctionType_getCallConv(fn_ty); + switch (clang_cc) { + .C => return CallingConvention.C, + .X86StdCall => return CallingConvention.Stdcall, + else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)), + } +} + fn transFnProto( rp: RestorePoint, fn_proto_ty: *const ZigClangFunctionProtoType, @@ -441,52 +472,44 @@ fn transFnProto( fn_decl_context: ?FnDeclContext, ) !*ast.Node.FnProto { const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_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 cc = try transCC(rp, fn_ty, source_loc); 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"); } + + return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc); +} + +fn transFnNoProto( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, + fn_decl_context: ?FnDeclContext, +) !*ast.Node.FnProto { + const cc = try transCC(rp, fn_ty, source_loc); + const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true; + return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc); +} + +fn finishTransFnProto( + rp: RestorePoint, + fn_ty: *const ZigClangFunctionType, + source_loc: ZigClangSourceLocation, + fn_decl_context: ?FnDeclContext, + is_var_args: bool, + cc: CallingConvention, +) !*ast.Node.FnProto { + const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; + // TODO check for always_inline attribute // TODO check for align attribute // pub extern fn name(...) T const pub_tok = try appendToken(rp.c, .Keyword_pub, "pub"); const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null; - const is_export = exp: { - const decl_ctx = fn_decl_context orelse break :exp false; - break :exp switch (decl_ctx.storage_class) { - .None => switch (rp.c.mode) { - .import => false, - .translate => decl_ctx.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(rp.c, .Keyword_export, "export") else if (cc == .C) @@ -527,7 +550,7 @@ fn transFnProto( .name_token = name_tok, .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, + .var_args_token = null, // TODO this field is broken in the AST data model .extern_export_inline_token = extern_export_inline_tok, .cc_token = cc_tok, .async_attr = null, @@ -536,6 +559,19 @@ fn transFnProto( .align_expr = null, .section_expr = null, }; + if (is_var_args) { + const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl); + var_arg_node.* = ast.Node.ParamDecl{ + .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, + .doc_comments = null, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = var_args_tok, + }; + try fn_proto.params.push(&var_arg_node.base); + } return fn_proto; } diff --git a/test/tests.zig b/test/tests.zig index fec6a294ff..4b67cf0f6c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1077,6 +1077,14 @@ pub const TranslateCContext = struct { 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.h", name, source, expected_lines); + tc.stage2 = stage2; + self.addCase(tc); + } + } + + pub fn addC_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); @@ -1084,7 +1092,7 @@ pub const TranslateCContext = struct { } 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); + const tc = self.create(false, "source.h", name, source, expected_lines); tc.stage2 = true; self.addCase(tc); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 6a2d2ef1d9..930442f293 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); +// add_both - test for stage1 and stage2, in #include mode +// add - test stage1 only, in #include mode +// add_2 - test stage2 only, in #include mode +// addC_both - test for stage1 and stage2, in -c mode +// addC - test stage1 only, in -c mode +// addC_2 - test stage2 only, in -c mode + pub fn addCases(cases: *tests.TranslateCContext) void { /////////////// Cases that pass for both stage1/stage2 //////////////// cases.add_both("simple function prototypes", @@ -11,25 +18,29 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn bar() c_int; ); - cases.add_both("simple function definition", - \\void foo(void) {}; + /////////////// Cases that pass for only stage2 //////////////// + cases.add_2("Parameterless function prototypes", + \\void a() {} + \\void b(void) {} + \\void c(); + \\void d(void); , - \\pub export fn foo() void {} + \\pub export fn a() void {} + \\pub export fn b() void {} + \\pub extern fn c(...) void; + \\pub extern fn d() void; ); - /////////////// Cases that pass for only stage2 //////////////// - // (none) - - /////////////// Cases that pass for only stage1 //////////////// - - cases.addC("Parameterless function prototypes", - \\void foo() {} - \\void bar(void) {} + cases.add_2("simple function definition", + \\void foo(void) {} + \\static void bar(void) {} , \\pub export fn foo() void {} - \\pub export fn bar() void {} + \\pub extern fn bar() void {} ); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// + cases.add("macro with left shift", \\#define REDISMODULE_READ (1<<0) , @@ -1681,4 +1692,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} ); + + /////////////// Cases for only stage1 because stage2 behavior is better //////////////// + cases.addC("Parameterless function prototypes", + \\void foo() {} + \\void bar(void) {} + , + \\pub export fn foo() void {} + \\pub export fn bar() void {} + ); } |
