authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-30 21:14:22+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-31 09:30:13+02:00
log3ff381385aad13955360973d1604410335f2ddff
tree060e1ce37a6d79608b0e59b1e2d5db95fa1c8de4
parent80dd43213727b914418175e6e35c35de95204f7c
signaturelock-open Commit is signed but in an unrecognized format.

translate-c: correctly handle pointers to opaque demoted structs


2 files changed, 102 insertions(+), 35 deletions(-)

src/translate_c.zig+82-35
...@@ -19,8 +19,6 @@ pub const Error = error{OutOfMemory};...@@ -19,8 +19,6 @@ pub const Error = error{OutOfMemory};
19const TypeError = Error || error{UnsupportedType};19const TypeError = Error || error{UnsupportedType};
20const TransError = TypeError || error{UnsupportedTranslation};20const TransError = TypeError || error{UnsupportedTranslation};
2121
22const DeclTable = std.AutoArrayHashMap(usize, []const u8);
23
24const SymbolTable = std.StringArrayHashMap(*ast.Node);22const SymbolTable = std.StringArrayHashMap(*ast.Node);
25const AliasList = std.ArrayList(struct {23const AliasList = std.ArrayList(struct {
26 alias: []const u8,24 alias: []const u8,
...@@ -254,24 +252,25 @@ const Scope = struct {...@@ -254,24 +252,25 @@ const Scope = struct {
254pub const Context = struct {252pub const Context = struct {
255 gpa: *mem.Allocator,253 gpa: *mem.Allocator,
256 arena: *mem.Allocator,254 arena: *mem.Allocator,
257 token_ids: std.ArrayListUnmanaged(Token.Id),255 token_ids: std.ArrayListUnmanaged(Token.Id) = .{},
258 token_locs: std.ArrayListUnmanaged(Token.Loc),256 token_locs: std.ArrayListUnmanaged(Token.Loc) = .{},
259 errors: std.ArrayListUnmanaged(ast.Error),257 errors: std.ArrayListUnmanaged(ast.Error) = .{},
260 source_buffer: *std.ArrayList(u8),258 source_buffer: *std.ArrayList(u8),
261 err: Error,259 err: Error,
262 source_manager: *clang.SourceManager,260 source_manager: *clang.SourceManager,
263 decl_table: DeclTable,261 decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
264 alias_list: AliasList,262 alias_list: AliasList,
265 global_scope: *Scope.Root,263 global_scope: *Scope.Root,
266 clang_context: *clang.ASTContext,264 clang_context: *clang.ASTContext,
267 mangle_count: u32 = 0,265 mangle_count: u32 = 0,
268 root_decls: std.ArrayListUnmanaged(*ast.Node),266 root_decls: std.ArrayListUnmanaged(*ast.Node) = .{},
267 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
269268
270 /// This one is different than the root scope's name table. This contains269 /// This one is different than the root scope's name table. This contains
271 /// a list of names that we found by visiting all the top level decls without270 /// a list of names that we found by visiting all the top level decls without
272 /// translating them. The other maps are updated as we translate; this one is updated271 /// translating them. The other maps are updated as we translate; this one is updated
273 /// up front in a pre-processing step.272 /// up front in a pre-processing step.
274 global_names: std.StringArrayHashMap(void),273 global_names: std.StringArrayHashMapUnmanaged(void) = .{},
275274
276 fn getMangle(c: *Context) u32 {275 fn getMangle(c: *Context) u32 {
277 c.mangle_count += 1;276 c.mangle_count += 1;
...@@ -362,24 +361,21 @@ pub fn translate(...@@ -362,24 +361,21 @@ pub fn translate(
362 .source_buffer = &source_buffer,361 .source_buffer = &source_buffer,
363 .source_manager = ast_unit.getSourceManager(),362 .source_manager = ast_unit.getSourceManager(),
364 .err = undefined,363 .err = undefined,
365 .decl_table = DeclTable.init(gpa),
366 .alias_list = AliasList.init(gpa),364 .alias_list = AliasList.init(gpa),
367 .global_scope = try arena.allocator.create(Scope.Root),365 .global_scope = try arena.allocator.create(Scope.Root),
368 .clang_context = ast_unit.getASTContext(),366 .clang_context = ast_unit.getASTContext(),
369 .global_names = std.StringArrayHashMap(void).init(gpa),
370 .token_ids = .{},
371 .token_locs = .{},
372 .errors = .{},
373 .root_decls = .{},
374 };367 };
375 context.global_scope.* = Scope.Root.init(&context);368 context.global_scope.* = Scope.Root.init(&context);
376 defer context.decl_table.deinit();369 defer {
377 defer context.alias_list.deinit();370 context.decl_table.deinit(gpa);
378 defer context.token_ids.deinit(gpa);371 context.alias_list.deinit();
379 defer context.token_locs.deinit(gpa);372 context.token_ids.deinit(gpa);
380 defer context.errors.deinit(gpa);373 context.token_locs.deinit(gpa);
381 defer context.global_names.deinit();374 context.errors.deinit(gpa);
382 defer context.root_decls.deinit(gpa);375 context.global_names.deinit(gpa);
376 context.root_decls.deinit(gpa);
377 context.opaque_demotes.deinit(gpa);
378 }
383379
384 try prepopulateGlobalNameTable(ast_unit, &context);380 try prepopulateGlobalNameTable(ast_unit, &context);
385381
...@@ -437,7 +433,7 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {...@@ -437,7 +433,7 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
437 const macro = @ptrCast(*clang.MacroDefinitionRecord, entity);433 const macro = @ptrCast(*clang.MacroDefinitionRecord, entity);
438 const raw_name = macro.getName_getNameStart();434 const raw_name = macro.getName_getNameStart();
439 const name = try c.str(raw_name);435 const name = try c.str(raw_name);
440 _ = try c.global_names.put(name, {});436 _ = try c.global_names.put(c.gpa, name, {});
441 },437 },
442 else => {},438 else => {},
443 }439 }
...@@ -465,7 +461,7 @@ fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {...@@ -465,7 +461,7 @@ fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool {
465fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {461fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
466 if (decl.castToNamedDecl()) |named_decl| {462 if (decl.castToNamedDecl()) |named_decl| {
467 const decl_name = try c.str(named_decl.getName_bytes_begin());463 const decl_name = try c.str(named_decl.getName_bytes_begin());
468 _ = try c.global_names.put(decl_name, {});464 _ = try c.global_names.put(c.gpa, decl_name, {});
469 }465 }
470}466}
471467
...@@ -804,7 +800,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -804,7 +800,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
804}800}
805801
806fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node {802fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node {
807 _ = try c.decl_table.put(@ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);803 _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);
808 return transCreateNodeIdentifier(c, builtin_name);804 return transCreateNodeIdentifier(c, builtin_name);
809}805}
810806
...@@ -851,7 +847,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev...@@ -851,7 +847,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev
851 return transCreateNodeIdentifier(c, checked_name);847 return transCreateNodeIdentifier(c, checked_name);
852 }848 }
853849
854 _ = try c.decl_table.put(@ptrToInt(typedef_decl.getCanonicalDecl()), checked_name);850 _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name);
855 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;851 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
856 try addTopLevelDecl(c, checked_name, node);852 try addTopLevelDecl(c, checked_name, node);
857 return transCreateNodeIdentifier(c, checked_name);853 return transCreateNodeIdentifier(c, checked_name);
...@@ -918,7 +914,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -918,7 +914,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
918 }914 }
919915
920 const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });916 const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });
921 _ = try c.decl_table.put(@ptrToInt(record_decl.getCanonicalDecl()), name);917 _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
922918
923 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;919 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
924 const mut_tok = try appendToken(c, .Keyword_const, "const");920 const mut_tok = try appendToken(c, .Keyword_const, "const");
...@@ -930,6 +926,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -930,6 +926,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
930 const init_node = blk: {926 const init_node = blk: {
931 const rp = makeRestorePoint(c);927 const rp = makeRestorePoint(c);
932 const record_def = record_decl.getDefinition() orelse {928 const record_def = record_decl.getDefinition() orelse {
929 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
933 const opaque_type = try transCreateNodeOpaqueType(c);930 const opaque_type = try transCreateNodeOpaqueType(c);
934 semicolon = try appendToken(c, .Semicolon, ";");931 semicolon = try appendToken(c, .Semicolon, ";");
935 break :blk opaque_type;932 break :blk opaque_type;
...@@ -954,6 +951,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -954,6 +951,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
954 const field_qt = field_decl.getType();951 const field_qt = field_decl.getType();
955952
956 if (field_decl.isBitField()) {953 if (field_decl.isBitField()) {
954 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
957 const opaque_type = try transCreateNodeOpaqueType(c);955 const opaque_type = try transCreateNodeOpaqueType(c);
958 semicolon = try appendToken(c, .Semicolon, ";");956 semicolon = try appendToken(c, .Semicolon, ";");
959 try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});957 try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name});
...@@ -961,6 +959,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -961,6 +959,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
961 }959 }
962960
963 if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {961 if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {
962 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
964 const opaque_type = try transCreateNodeOpaqueType(c);963 const opaque_type = try transCreateNodeOpaqueType(c);
965 semicolon = try appendToken(c, .Semicolon, ";");964 semicolon = try appendToken(c, .Semicolon, ";");
966 try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name});965 try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name});
...@@ -979,6 +978,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -979,6 +978,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
979 _ = try appendToken(c, .Colon, ":");978 _ = try appendToken(c, .Colon, ":");
980 const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {979 const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
981 error.UnsupportedType => {980 error.UnsupportedType => {
981 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
982 const opaque_type = try transCreateNodeOpaqueType(c);982 const opaque_type = try transCreateNodeOpaqueType(c);
983 semicolon = try appendToken(c, .Semicolon, ";");983 semicolon = try appendToken(c, .Semicolon, ";");
984 try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name });984 try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name });
...@@ -988,13 +988,13 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -988,13 +988,13 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
988 };988 };
989989
990 const align_expr = blk_2: {990 const align_expr = blk_2: {
991 const alignment = field_decl.getAlignedAttribute(rp.c.clang_context);991 const alignment = field_decl.getAlignedAttribute(c.clang_context);
992 if (alignment != 0) {992 if (alignment != 0) {
993 _ = try appendToken(rp.c, .Keyword_align, "align");993 _ = try appendToken(c, .Keyword_align, "align");
994 _ = try appendToken(rp.c, .LParen, "(");994 _ = try appendToken(c, .LParen, "(");
995 // Clang reports the alignment in bits995 // Clang reports the alignment in bits
996 const expr = try transCreateNodeInt(rp.c, alignment / 8);996 const expr = try transCreateNodeInt(c, alignment / 8);
997 _ = try appendToken(rp.c, .RParen, ")");997 _ = try appendToken(c, .RParen, ")");
998998
999 break :blk_2 expr;999 break :blk_2 expr;
1000 }1000 }
...@@ -1013,6 +1013,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as...@@ -1013,6 +1013,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
10131013
1014 if (is_anon) {1014 if (is_anon) {
1015 _ = try c.decl_table.put(1015 _ = try c.decl_table.put(
1016 c.gpa,
1016 @ptrToInt(field_decl.getCanonicalDecl()),1017 @ptrToInt(field_decl.getCanonicalDecl()),
1017 raw_name,1018 raw_name,
1018 );1019 );
...@@ -1065,7 +1066,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node...@@ -1065,7 +1066,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1065 }1066 }
10661067
1067 const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});1068 const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});
1068 _ = try c.decl_table.put(@ptrToInt(enum_decl.getCanonicalDecl()), name);1069 _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
10691070
1070 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;1071 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
1071 const mut_tok = try appendToken(c, .Keyword_const, "const");1072 const mut_tok = try appendToken(c, .Keyword_const, "const");
...@@ -1204,8 +1205,10 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node...@@ -1204,8 +1205,10 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
1204 };1205 };
1205 mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);1206 mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
1206 break :blk &container_node.base;1207 break :blk &container_node.base;
1207 } else1208 } else blk: {
1208 try transCreateNodeOpaqueType(c);1209 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {});
1210 break :blk try transCreateNodeOpaqueType(c);
1211 };
12091212
1210 const semicolon_token = try appendToken(c, .Semicolon, ";");1213 const semicolon_token = try appendToken(c, .Semicolon, ";");
1211 const node = try ast.Node.VarDecl.create(c.arena, .{1214 const node = try ast.Node.VarDecl.create(c.arena, .{
...@@ -4767,7 +4770,7 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo...@@ -4767,7 +4770,7 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo
4767 optional_node.rhs = try transQualType(rp, child_qt, source_loc);4770 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
4768 return &optional_node.base;4771 return &optional_node.base;
4769 }4772 }
4770 if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc)) {4773 if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) {
4771 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");4774 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4772 const pointer_node = try transCreateNodePtrType(4775 const pointer_node = try transCreateNodePtrType(
4773 rp.c,4776 rp.c,
...@@ -4853,6 +4856,50 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo...@@ -4853,6 +4856,50 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo
4853 }4856 }
4854}4857}
48554858
4859fn qualTypeWasDemotedToOpaque(c: *Context, qt: clang.QualType) bool {
4860 const ty = qt.getTypePtr();
4861 switch (qt.getTypeClass()) {
4862 .Typedef => {
4863 const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
4864
4865 const typedef_decl = typedef_ty.getDecl();
4866 const underlying_type = typedef_decl.getUnderlyingType();
4867 return qualTypeWasDemotedToOpaque(c, underlying_type);
4868 },
4869 .Record => {
4870 const record_ty = @ptrCast(*const clang.RecordType, ty);
4871
4872 const record_decl = record_ty.getDecl();
4873 const canonical = @ptrToInt(record_decl.getCanonicalDecl());
4874 return c.opaque_demotes.contains(canonical);
4875 },
4876 .Enum => {
4877 const enum_ty = @ptrCast(*const clang.EnumType, ty);
4878
4879 const enum_decl = enum_ty.getDecl();
4880 const canonical = @ptrToInt(enum_decl.getCanonicalDecl());
4881 return c.opaque_demotes.contains(canonical);
4882 },
4883 .Elaborated => {
4884 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
4885 return qualTypeWasDemotedToOpaque(c, elaborated_ty.getNamedType());
4886 },
4887 .Decayed => {
4888 const decayed_ty = @ptrCast(*const clang.DecayedType, ty);
4889 return qualTypeWasDemotedToOpaque(c, decayed_ty.getDecayedType());
4890 },
4891 .Attributed => {
4892 const attributed_ty = @ptrCast(*const clang.AttributedType, ty);
4893 return qualTypeWasDemotedToOpaque(c, attributed_ty.getEquivalentType());
4894 },
4895 .MacroQualified => {
4896 const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty);
4897 return qualTypeWasDemotedToOpaque(c, macroqualified_ty.getModifiedType());
4898 },
4899 else => return false,
4900 }
4901}
4902
4856fn isCVoid(qt: clang.QualType) bool {4903fn isCVoid(qt: clang.QualType) bool {
4857 const ty = qt.getTypePtr();4904 const ty = qt.getTypePtr();
4858 if (ty.getTypeClass() == .Builtin) {4905 if (ty.getTypeClass() == .Builtin) {
test/translate_c.zig+20
...@@ -3,6 +3,26 @@ const std = @import("std");...@@ -3,6 +3,26 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("pointer to opaque demoted struct",
7 \\typedef struct {
8 \\ _Atomic int foo;
9 \\} Foo;
10 \\
11 \\typedef struct {
12 \\ Foo *bar;
13 \\} Bar;
14 , &[_][]const u8{
15 \\const struct_unnamed_1 = //
16 ,
17 \\warning: unsupported type: 'Atomic'
18 \\ opaque {}; //
19 ,
20 \\pub const Foo = struct_unnamed_1;
21 \\const struct_unnamed_2 = extern struct {
22 \\ bar: ?*Foo,
23 \\};
24 });
25
6 cases.add("macro expressions respect C operator precedence",26 cases.add("macro expressions respect C operator precedence",
7 \\#define FOO *((foo) + 2)27 \\#define FOO *((foo) + 2)
8 \\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9)28 \\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9)