mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
translate-c: better handling of restore points
This commit is contained in:
parent
2933d6b848
commit
82219b1fd5
@ -208,14 +208,14 @@ 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 rp = makeRestorePoint(c);
|
||||
const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl)));
|
||||
const fn_decl_loc = ZigClangFunctionDecl_getLocation(fn_decl);
|
||||
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,
|
||||
rp,
|
||||
@ptrCast(*const ZigClangFunctionProtoType, fn_type),
|
||||
fn_decl_loc,
|
||||
fn_decl,
|
||||
@ -242,32 +242,35 @@ 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 {
|
||||
return transType(c, ZigClangQualType_getTypePtr(qt), source_loc);
|
||||
fn transQualType(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
|
||||
return transType(rp, ZigClangQualType_getTypePtr(qt), source_loc);
|
||||
}
|
||||
|
||||
fn qualTypeCanon(qt: ZigClangQualType) *const ZigClangType {
|
||||
const canon = ZigClangQualType_getCanonicalType(qt);
|
||||
return ZigClangQualType_getTypePtr(canon);
|
||||
}
|
||||
|
||||
const RestorePoint = struct {
|
||||
context: *Context,
|
||||
c: *Context,
|
||||
token_index: ast.TokenIndex,
|
||||
src_buf_index: usize,
|
||||
|
||||
fn activate(self: RestorePoint) void {
|
||||
self.context.tree.tokens.shrink(self.token_index);
|
||||
self.context.source_buffer.shrink(self.src_buf_index);
|
||||
self.c.tree.tokens.shrink(self.token_index);
|
||||
self.c.source_buffer.shrink(self.src_buf_index);
|
||||
}
|
||||
};
|
||||
|
||||
fn makeRestorePoint(c: *Context) RestorePoint {
|
||||
return RestorePoint{
|
||||
.context = c,
|
||||
.c = c,
|
||||
.token_index = c.tree.tokens.len,
|
||||
.src_buf_index = c.source_buffer.len(),
|
||||
};
|
||||
}
|
||||
|
||||
fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) !*ast.Node {
|
||||
const rp = makeRestorePoint(c);
|
||||
|
||||
fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSourceLocation) Error!*ast.Node {
|
||||
switch (ZigClangType_getTypeClass(ty)) {
|
||||
.Builtin => {
|
||||
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
|
||||
@ -275,23 +278,26 @@ fn transType(c: *Context, ty: *const ZigClangType, source_loc: ZigClangSourceLoc
|
||||
else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type"),
|
||||
}
|
||||
},
|
||||
.FunctionProto => return transFnProto(c, @ptrCast(*const ZigClangFunctionType, ty), source_loc, null, false),
|
||||
.FunctionProto => {
|
||||
const fn_proto_ty = @ptrCast(*const ZigClangFunctionProtoType, ty);
|
||||
const fn_proto = try transFnProto(rp, fn_proto_ty, source_loc, null, null);
|
||||
return &fn_proto.base;
|
||||
},
|
||||
else => {
|
||||
const type_name = c.str(ZigClangType_getTypeClassName(ty));
|
||||
const type_name = rp.c.str(ZigClangType_getTypeClassName(ty));
|
||||
return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", type_name);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn transFnProto(
|
||||
c: *Context,
|
||||
rp: RestorePoint,
|
||||
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,
|
||||
@ -323,13 +329,13 @@ fn transFnProto(
|
||||
// TODO check for align attribute
|
||||
|
||||
// extern fn name(...) T
|
||||
const cc_tok = if (cc == .Stdcall) try appendToken(c, .Keyword_stdcallcc, "stdcallcc") else null;
|
||||
const cc_tok = if (cc == .Stdcall) try appendToken(rp.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) {
|
||||
.None => switch (rp.c.mode) {
|
||||
.import => false,
|
||||
.translate => has_body,
|
||||
},
|
||||
@ -340,48 +346,44 @@ fn transFnProto(
|
||||
};
|
||||
};
|
||||
const extern_export_inline_tok = if (is_export)
|
||||
try appendToken(c, .Keyword_export, "export")
|
||||
try appendToken(rp.c, .Keyword_export, "export")
|
||||
else if (cc == .C)
|
||||
try appendToken(c, .Keyword_extern, "extern")
|
||||
try appendToken(rp.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 fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
|
||||
const name_tok = if (fn_name) |n| try appendToken(rp.c, .Identifier, "{}", n) else null;
|
||||
const lparen_tok = try appendToken(rp.c, .LParen, "(");
|
||||
const var_args_tok = if (is_var_args) try appendToken(rp.c, .Ellipsis3, "...") else null;
|
||||
const rparen_tok = try appendToken(rp.c, .RParen, ")");
|
||||
|
||||
const return_type_node = blk: {
|
||||
if (ZigClangFunctionType_getNoReturnAttr(fn_ty)) {
|
||||
break :blk try appendIdentifier(c, "noreturn");
|
||||
break :blk try appendIdentifier(rp.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 return_qt = ZigClangFunctionType_getReturnType(fn_ty);
|
||||
if (ZigClangType_isVoidType(qualTypeCanon(return_qt))) {
|
||||
break :blk try appendIdentifier(rp.c, "void");
|
||||
} else {
|
||||
break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
|
||||
error.UnsupportedType => {
|
||||
try emitWarning(rp.c, source_loc, "unsupported function proto return type");
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fn_proto = try c.a().create(ast.Node.FnProto);
|
||||
const fn_proto = try rp.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()),
|
||||
.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,
|
||||
.extern_export_inline_token = extern_export_inline_tok,
|
||||
@ -396,14 +398,14 @@ fn transFnProto(
|
||||
}
|
||||
|
||||
fn revertAndWarn(
|
||||
restore_point: RestorePoint,
|
||||
rp: RestorePoint,
|
||||
err: var,
|
||||
source_loc: ZigClangSourceLocation,
|
||||
comptime format: []const u8,
|
||||
args: ...,
|
||||
) (@typeOf(err) || error{OutOfMemory}) {
|
||||
restore_point.activate();
|
||||
try emitWarning(restore_point.context, source_loc, format, args);
|
||||
rp.activate();
|
||||
try emitWarning(rp.c, source_loc, format, args);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -2,12 +2,26 @@ const tests = @import("tests.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
/////////////// Cases that pass for both stage1/stage2 ////////////////
|
||||
cases.add_both("simple noreturn fn",
|
||||
\\void __attribute__((noreturn)) foo(void);
|
||||
,
|
||||
\\extern fn foo() noreturn;
|
||||
);
|
||||
|
||||
/////////////// Cases that pass for only stage2 ////////////////
|
||||
// (none)
|
||||
|
||||
/////////////// Cases that pass for only stage1 ////////////////
|
||||
|
||||
cases.addC("Parameterless function prototypes",
|
||||
\\void foo() {}
|
||||
\\void bar(void) {}
|
||||
,
|
||||
\\pub export fn foo() void {}
|
||||
\\pub export fn bar() void {}
|
||||
);
|
||||
|
||||
cases.add("macro with left shift",
|
||||
\\#define REDISMODULE_READ (1<<0)
|
||||
,
|
||||
@ -1523,14 +1537,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addC("Parameterless function prototypes",
|
||||
\\void foo() {}
|
||||
\\void bar(void) {}
|
||||
,
|
||||
\\pub export fn foo() void {}
|
||||
\\pub export fn bar() void {}
|
||||
);
|
||||
|
||||
cases.addC(
|
||||
"u integer suffix after 0 (zero) in macro definition",
|
||||
"#define ZERO 0U",
|
||||
@ -1667,34 +1673,4 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\ }
|
||||
\\}
|
||||
);
|
||||
|
||||
// cases.add("empty array with initializer",
|
||||
// "int a[4] = {};"
|
||||
// ,
|
||||
// "pub var a: [4]c_int = [1]c_int{0} ** 4;"
|
||||
// );
|
||||
|
||||
// cases.add("array with initialization",
|
||||
// "int a[4] = {1, 2, 3, 4};"
|
||||
// ,
|
||||
// "pub var a: [4]c_int = [4]c_int{1, 2, 3, 4};"
|
||||
// );
|
||||
|
||||
// cases.add("array with incomplete initialization",
|
||||
// "int a[4] = {3, 4};"
|
||||
// ,
|
||||
// "pub var a: [4]c_int = [2]c_int{3, 4} ++ ([1]c_int{0} ** 2);"
|
||||
// );
|
||||
|
||||
// cases.add("2D array with initialization",
|
||||
// "int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };"
|
||||
// ,
|
||||
// "pub var a: [3][3]c_int = [3][3]c_int{[3]c_int{1, 2, 3}, [3]c_int{4, 5, 6}, [3]c_int{7, 8, 9}};"
|
||||
// );
|
||||
|
||||
// cases.add("2D array with incomplete initialization",
|
||||
// "int a[3][3] = { {1, 2}, {4, 5, 6} };"
|
||||
// ,
|
||||
// "pub var a: [3][3]c_int = [2][3]c_int{[2]c_int{1, 2} ++ [1]c_int{0}, [3]c_int{4, 5, 6}} ++ [1][3]c_int{[1]c_int{0} ** 3};"
|
||||
// );
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user