mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 00:26:57 +00:00
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
This commit is contained in:
parent
2ef2f9d71f
commit
10e9d47b49
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {};
|
||||
,
|
||||
\\pub export fn foo() void {}
|
||||
);
|
||||
|
||||
/////////////// Cases that pass for only stage2 ////////////////
|
||||
// (none)
|
||||
cases.add_2("Parameterless function prototypes",
|
||||
\\void a() {}
|
||||
\\void b(void) {}
|
||||
\\void c();
|
||||
\\void d(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 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 {}
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user