Commit 10e9d47b49
Changed files (3)
src-self-hosted
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;
}
test/tests.zig
@@ -1076,6 +1076,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;
@@ -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);
}
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 {}
+ );
}