authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-11 12:05:33-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-11 12:09:11-04:00
log10e9d47b499752f5c40e8ded1bf50385d84912ff
treeba14e8208f292d60ea6d834beca3385e8f4fc545
parent2ef2f9d71f851823966ae16b45a189657051b8df
signaturelock-open Commit is signed but in an unrecognized format.

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

3 files changed, 115 insertions(+), 51 deletions(-)

src-self-hosted/translate_c.zig+74-38
......@@ -254,11 +254,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
254254 const fn_qt = ZigClangFunctionDecl_getType(fn_decl);
255255 const fn_type = ZigClangQualType_getTypePtr(fn_qt);
256256 var scope = &c.global_scope.base;
257 const has_body = ZigClangFunctionDecl_hasBody(fn_decl);
258 const storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl);
257259 const decl_ctx = FnDeclContext{
258260 .fn_name = fn_name,
259 .has_body = ZigClangFunctionDecl_hasBody(fn_decl),
260 .storage_class = ZigClangFunctionDecl_getStorageClass(fn_decl),
261 .has_body = has_body,
262 .storage_class = storage_class,
261263 .scope = &scope,
264 .is_export = switch (storage_class) {
265 .None => has_body,
266 .Extern, .Static => false,
267 .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern"),
268 .Auto => unreachable, // Not legal on functions
269 .Register => unreachable, // Not legal on functions
270 },
262271 };
263272 const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
264273 .FunctionProto => blk: {
......@@ -270,7 +279,15 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
270279 error.OutOfMemory => return error.OutOfMemory,
271280 };
272281 },
273 .FunctionNoProto => return failDecl(c, fn_decl_loc, fn_name, "TODO support functions with no prototype"),
282 .FunctionNoProto => blk: {
283 const fn_no_proto_type = @ptrCast(*const ZigClangFunctionType, fn_type);
284 break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx) catch |err| switch (err) {
285 error.UnsupportedType => {
286 return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function");
287 },
288 error.OutOfMemory => return error.OutOfMemory,
289 };
290 },
274291 else => unreachable,
275292 };
276293
......@@ -432,8 +449,22 @@ const FnDeclContext = struct {
432449 has_body: bool,
433450 storage_class: ZigClangStorageClass,
434451 scope: **Scope,
452 is_export: bool,
435453};
436454
455fn transCC(
456 rp: RestorePoint,
457 fn_ty: *const ZigClangFunctionType,
458 source_loc: ZigClangSourceLocation,
459) !CallingConvention {
460 const clang_cc = ZigClangFunctionType_getCallConv(fn_ty);
461 switch (clang_cc) {
462 .C => return CallingConvention.C,
463 .X86StdCall => return CallingConvention.Stdcall,
464 else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: {}", @tagName(clang_cc)),
465 }
466}
467
437468fn transFnProto(
438469 rp: RestorePoint,
439470 fn_proto_ty: *const ZigClangFunctionProtoType,
......@@ -441,52 +472,44 @@ fn transFnProto(
441472 fn_decl_context: ?FnDeclContext,
442473) !*ast.Node.FnProto {
443474 const fn_ty = @ptrCast(*const ZigClangFunctionType, fn_proto_ty);
444 const cc = switch (ZigClangFunctionType_getCallConv(fn_ty)) {
445 .C => CallingConvention.C,
446 .X86StdCall => CallingConvention.Stdcall,
447 .X86FastCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 fastcall"),
448 .X86ThisCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 thiscall"),
449 .X86VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 vectorcall"),
450 .X86Pascal => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 pascal"),
451 .Win64 => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: win64"),
452 .X86_64SysV => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 64sysv"),
453 .X86RegCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: x86 reg"),
454 .AAPCS => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs"),
455 .AAPCS_VFP => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: aapcs-vfp"),
456 .IntelOclBicc => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: intel_ocl_bicc"),
457 .SpirFunction => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: SPIR function"),
458 .OpenCLKernel => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: OpenCLKernel"),
459 .Swift => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: Swift"),
460 .PreserveMost => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveMost"),
461 .PreserveAll => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: PreserveAll"),
462 .AArch64VectorCall => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported calling convention: AArch64VectorCall"),
463 };
464
475 const cc = try transCC(rp, fn_ty, source_loc);
465476 const is_var_args = ZigClangFunctionProtoType_isVariadic(fn_proto_ty);
466477 const param_count: usize = ZigClangFunctionProtoType_getNumParams(fn_proto_ty);
467478 var i: usize = 0;
468479 while (i < param_count) : (i += 1) {
469480 return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO: implement parameters for FunctionProto in transType");
470481 }
482
483 return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
484}
485
486fn transFnNoProto(
487 rp: RestorePoint,
488 fn_ty: *const ZigClangFunctionType,
489 source_loc: ZigClangSourceLocation,
490 fn_decl_context: ?FnDeclContext,
491) !*ast.Node.FnProto {
492 const cc = try transCC(rp, fn_ty, source_loc);
493 const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true;
494 return finishTransFnProto(rp, fn_ty, source_loc, fn_decl_context, is_var_args, cc);
495}
496
497fn finishTransFnProto(
498 rp: RestorePoint,
499 fn_ty: *const ZigClangFunctionType,
500 source_loc: ZigClangSourceLocation,
501 fn_decl_context: ?FnDeclContext,
502 is_var_args: bool,
503 cc: CallingConvention,
504) !*ast.Node.FnProto {
505 const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
506
471507 // TODO check for always_inline attribute
472508 // TODO check for align attribute
473509
474510 // pub extern fn name(...) T
475511 const pub_tok = try appendToken(rp.c, .Keyword_pub, "pub");
476512 const cc_tok = if (cc == .Stdcall) try appendToken(rp.c, .Keyword_stdcallcc, "stdcallcc") else null;
477 const is_export = exp: {
478 const decl_ctx = fn_decl_context orelse break :exp false;
479 break :exp switch (decl_ctx.storage_class) {
480 .None => switch (rp.c.mode) {
481 .import => false,
482 .translate => decl_ctx.has_body,
483 },
484 .Extern, .Static => false,
485 .PrivateExtern => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported storage class: private extern"),
486 .Auto => unreachable, // Not legal on functions
487 .Register => unreachable, // Not legal on functions
488 };
489 };
490513 const extern_export_inline_tok = if (is_export)
491514 try appendToken(rp.c, .Keyword_export, "export")
492515 else if (cc == .C)
......@@ -527,7 +550,7 @@ fn transFnProto(
527550 .name_token = name_tok,
528551 .params = ast.Node.FnProto.ParamList.init(rp.c.a()),
529552 .return_type = ast.Node.FnProto.ReturnType{ .Explicit = return_type_node },
530 .var_args_token = var_args_tok,
553 .var_args_token = null, // TODO this field is broken in the AST data model
531554 .extern_export_inline_token = extern_export_inline_tok,
532555 .cc_token = cc_tok,
533556 .async_attr = null,
......@@ -536,6 +559,19 @@ fn transFnProto(
536559 .align_expr = null,
537560 .section_expr = null,
538561 };
562 if (is_var_args) {
563 const var_arg_node = try rp.c.a().create(ast.Node.ParamDecl);
564 var_arg_node.* = ast.Node.ParamDecl{
565 .base = ast.Node{ .id = ast.Node.Id.ParamDecl },
566 .doc_comments = null,
567 .comptime_token = null,
568 .noalias_token = null,
569 .name_token = null,
570 .type_node = undefined,
571 .var_args_token = var_args_tok,
572 };
573 try fn_proto.params.push(&var_arg_node.base);
574 }
539575 return fn_proto;
540576}
541577
test/tests.zig+9-1
......@@ -1076,6 +1076,14 @@ pub const TranslateCContext = struct {
10761076 }
10771077
10781078 pub fn add_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void {
1079 for ([]bool{ false, true }) |stage2| {
1080 const tc = self.create(false, "source.h", name, source, expected_lines);
1081 tc.stage2 = stage2;
1082 self.addCase(tc);
1083 }
1084 }
1085
1086 pub fn addC_both(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void {
10791087 for ([]bool{ false, true }) |stage2| {
10801088 const tc = self.create(false, "source.c", name, source, expected_lines);
10811089 tc.stage2 = stage2;
......@@ -1084,7 +1092,7 @@ pub const TranslateCContext = struct {
10841092 }
10851093
10861094 pub fn add_2(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void {
1087 const tc = self.create(false, "source.c", name, source, expected_lines);
1095 const tc = self.create(false, "source.h", name, source, expected_lines);
10881096 tc.stage2 = true;
10891097 self.addCase(tc);
10901098 }
test/translate_c.zig+32-12
......@@ -1,6 +1,13 @@
11const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
4// add_both - test for stage1 and stage2, in #include mode
5// add - test stage1 only, in #include mode
6// add_2 - test stage2 only, in #include mode
7// addC_both - test for stage1 and stage2, in -c mode
8// addC - test stage1 only, in -c mode
9// addC_2 - test stage2 only, in -c mode
10
411pub fn addCases(cases: *tests.TranslateCContext) void {
512 /////////////// Cases that pass for both stage1/stage2 ////////////////
613 cases.add_both("simple function prototypes",
......@@ -11,25 +18,29 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1118 \\pub extern fn bar() c_int;
1219 );
1320
14 cases.add_both("simple function definition",
15 \\void foo(void) {};
21 /////////////// Cases that pass for only stage2 ////////////////
22 cases.add_2("Parameterless function prototypes",
23 \\void a() {}
24 \\void b(void) {}
25 \\void c();
26 \\void d(void);
1627 ,
17 \\pub export fn foo() void {}
28 \\pub export fn a() void {}
29 \\pub export fn b() void {}
30 \\pub extern fn c(...) void;
31 \\pub extern fn d() void;
1832 );
1933
20 /////////////// Cases that pass for only stage2 ////////////////
21 // (none)
22
23 /////////////// Cases that pass for only stage1 ////////////////
24
25 cases.addC("Parameterless function prototypes",
26 \\void foo() {}
27 \\void bar(void) {}
34 cases.add_2("simple function definition",
35 \\void foo(void) {}
36 \\static void bar(void) {}
2837 ,
2938 \\pub export fn foo() void {}
30 \\pub export fn bar() void {}
39 \\pub extern fn bar() void {}
3140 );
3241
42 /////////////// Cases for only stage1 which are TODO items for stage2 ////////////////
43
3344 cases.add("macro with left shift",
3445 \\#define REDISMODULE_READ (1<<0)
3546 ,
......@@ -1681,4 +1692,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
16811692 \\ }
16821693 \\}
16831694 );
1695
1696 /////////////// Cases for only stage1 because stage2 behavior is better ////////////////
1697 cases.addC("Parameterless function prototypes",
1698 \\void foo() {}
1699 \\void bar(void) {}
1700 ,
1701 \\pub export fn foo() void {}
1702 \\pub export fn bar() void {}
1703 );
16841704}