zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit df0f7f46923da9927df18809ad1d744102e78ebd (tree)
parent 5c28b8cd119979bb4b7d32edd1f4542165fc1f98
Author: Veikka Tuominen <git@vexu.eu>
Date:   Sun, 21 Mar 2021 18:42:30 +0200

translate-c: stop creating unnamed decls for typedefs child types

Diffstat:
Msrc/translate_c.zig | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mtest/translate_c.zig | 82++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
2 files changed, 127 insertions(+), 49 deletions(-)

diff --git a/src/translate_c.zig b/src/translate_c.zig @@ -270,7 +270,10 @@ pub const Context = struct { global_scope: *Scope.Root, clang_context: *clang.ASTContext, mangle_count: u32 = 0, + /// Table of record decls that have been demoted to opaques. opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, + /// Table of unnamed enums and records that are child types of typedefs. + unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{}, /// This one is different than the root scope's name table. This contains /// a list of names that we found by visiting all the top level decls without @@ -338,6 +341,7 @@ pub fn translate( context.alias_list.deinit(); context.global_names.deinit(gpa); context.opaque_demotes.deinit(gpa); + context.unnamed_typedefs.deinit(gpa); context.global_scope.deinit(); } @@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { if (decl.castToNamedDecl()) |named_decl| { const decl_name = try c.str(named_decl.getName_bytes_begin()); try c.global_names.put(c.gpa, decl_name, {}); + + // Check for typedefs with unnamed enum/record child types. + if (decl.getKind() == .Typedef) { + const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); + var child_ty = typedef_decl.getUnderlyingType().getTypePtr(); + const addr: usize = while (true) switch (child_ty.getTypeClass()) { + .Enum => { + const enum_ty = @ptrCast(*const clang.EnumType, child_ty); + const enum_decl = enum_ty.getDecl(); + // check if this decl is unnamed + if (@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()[0] != 0) return; + break @ptrToInt(enum_decl.getCanonicalDecl()); + }, + .Record => { + const record_ty = @ptrCast(*const clang.RecordType, child_ty); + const record_decl = record_ty.getDecl(); + // check if this decl is unnamed + if (@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()[0] != 0) return; + break @ptrToInt(record_decl.getCanonicalDecl()); + }, + .Elaborated => { + const elaborated_ty = @ptrCast(*const clang.ElaboratedType, child_ty); + child_ty = elaborated_ty.getNamedType().getTypePtr(); + }, + .Decayed => { + const decayed_ty = @ptrCast(*const clang.DecayedType, child_ty); + child_ty = decayed_ty.getDecayedType().getTypePtr(); + }, + .Attributed => { + const attributed_ty = @ptrCast(*const clang.AttributedType, child_ty); + child_ty = attributed_ty.getEquivalentType().getTypePtr(); + }, + .MacroQualified => { + const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, child_ty); + child_ty = macroqualified_ty.getModifiedType().getTypePtr(); + }, + else => return, + } else unreachable; + // TODO https://github.com/ziglang/zig/issues/3756 + // TODO https://github.com/ziglang/zig/issues/1802 + const name = if (isZigPrimitiveType(decl_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ decl_name, c.getMangle() }) else decl_name; + try c.unnamed_typedefs.putNoClobber(c.gpa, addr, name); + // Put this typedef in the decl_table to avoid redefinitions. + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); + } } } @@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD const toplevel = scope.id == .root; const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; - var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); - var is_unnamed = false; - // Record declarations such as `struct {...} x` have no name but they're not - // anonymous hence here isAnonymousStructOrUnion is not needed - if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); - is_unnamed = true; - } - - var container_kind_name: []const u8 = undefined; var is_union = false; + var container_kind_name: []const u8 = undefined; + var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); + if (record_decl.isUnion()) { container_kind_name = "union"; is_union = true; @@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); } - var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); + var is_unnamed = false; + var name = bare_name; + if (c.unnamed_typedefs.get(@ptrToInt(record_decl.getCanonicalDecl()))) |typedef_name| { + bare_name = typedef_name; + name = typedef_name; + } else { + // Record declarations such as `struct {...} x` have no name but they're not + // anonymous hence here isAnonymousStructOrUnion is not needed + if (bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); + is_unnamed = true; + } + name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); + } if (!toplevel) name = try bs.makeMangledName(c, name); try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); @@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E const toplevel = scope.id == .root; const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; - var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); var is_unnamed = false; - if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); - is_unnamed = true; + var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); + var name = bare_name; + if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| { + bare_name = typedef_name; + name = typedef_name; + } else { + if (bare_name.len == 0) { + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); + is_unnamed = true; + } + name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); } - - var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); if (!toplevel) _ = try bs.makeMangledName(c, name); try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); diff --git a/test/translate_c.zig b/test/translate_c.zig @@ -3,6 +3,28 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("unnamed child types of typedef receive typedef's name", + \\typedef enum { + \\ FooA, + \\ FooB, + \\} Foo; + \\typedef struct { + \\ int a, b; + \\} Bar; + , &[_][]const u8{ + \\pub const Foo = extern enum(c_int) { + \\ A, + \\ B, + \\ _, + \\}; + \\pub const FooA = @enumToInt(Foo.A); + \\pub const FooB = @enumToInt(Foo.B); + \\pub const Bar = extern struct { + \\ a: c_int, + \\ b: c_int, + \\}; + }); + cases.add("if as while stmt has semicolon", \\void foo() { \\ while (1) if (1) { @@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} Bar; , &[_][]const u8{ \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo - \\const struct_unnamed_1 = opaque {}; - \\pub const Foo = struct_unnamed_1; - \\const struct_unnamed_2 = extern struct { + \\pub const Foo = opaque {}; + \\pub const Bar = extern struct { \\ bar: ?*Foo, \\}; }); @@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} outer; \\void foo(outer *x) { x->y = x->x; } , &[_][]const u8{ - \\const struct_unnamed_3 = extern struct { + \\const struct_unnamed_2 = extern struct { \\ y: c_int, \\}; - \\const union_unnamed_2 = extern union { + \\const union_unnamed_1 = extern union { \\ x: u8, - \\ unnamed_0: struct_unnamed_3, + \\ unnamed_0: struct_unnamed_2, \\}; - \\const struct_unnamed_1 = extern struct { - \\ unnamed_0: union_unnamed_2, + \\pub const outer = extern struct { + \\ unnamed_0: union_unnamed_1, \\}; - \\pub const outer = struct_unnamed_1; \\pub export fn foo(arg_x: [*c]outer) void { \\ var x = arg_x; \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x)); @@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\struct {int x,y;} s2 = {.y = 2, .x=1}; \\foo s3 = { 123 }; , &[_][]const u8{ - \\const struct_unnamed_1 = extern struct { + \\pub const foo = extern struct { \\ x: c_int, \\}; - \\pub const foo = struct_unnamed_1; - \\const struct_unnamed_2 = extern struct { + \\const struct_unnamed_1 = extern struct { \\ x: f64, \\ y: f64, \\ z: f64, \\}; - \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{ + \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ \\ .x = 1.2, \\ .y = 1.3, \\ .z = 0, \\}; - \\const struct_unnamed_3 = extern struct { + \\const struct_unnamed_2 = extern struct { \\ sec: c_int, \\ min: c_int, \\ hour: c_int, @@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ mon: c_int, \\ year: c_int, \\}; - \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{ + \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{ \\ .sec = @as(c_int, 30), \\ .min = @as(c_int, 15), \\ .hour = @as(c_int, 17), @@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ .mon = @as(c_int, 12), \\ .year = @as(c_int, 2014), \\}; - \\const struct_unnamed_4 = extern struct { + \\const struct_unnamed_3 = extern struct { \\ x: c_int, \\ y: c_int, \\}; - \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{ + \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{ \\ .x = @as(c_int, 1), \\ .y = @as(c_int, 2), \\}; @@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ p, \\}; , &[_][]const u8{ - \\const enum_unnamed_1 = extern enum(c_int) { + \\pub const d = extern enum(c_int) { \\ a, \\ b, \\ c, \\ _, \\}; - \\pub const a = @enumToInt(enum_unnamed_1.a); - \\pub const b = @enumToInt(enum_unnamed_1.b); - \\pub const c = @enumToInt(enum_unnamed_1.c); - \\pub const d = enum_unnamed_1; - \\const enum_unnamed_2 = extern enum(c_int) { + \\pub const a = @enumToInt(d.a); + \\pub const b = @enumToInt(d.b); + \\pub const c = @enumToInt(d.c); + \\const enum_unnamed_1 = extern enum(c_int) { \\ e = 0, \\ f = 4, \\ g = 5, \\ _, \\}; - \\pub const e = @enumToInt(enum_unnamed_2.e); - \\pub const f = @enumToInt(enum_unnamed_2.f); - \\pub const g = @enumToInt(enum_unnamed_2.g); - \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); - \\const enum_unnamed_3 = extern enum(c_int) { + \\pub const e = @enumToInt(enum_unnamed_1.e); + \\pub const f = @enumToInt(enum_unnamed_1.f); + \\pub const g = @enumToInt(enum_unnamed_1.g); + \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); + \\const enum_unnamed_2 = extern enum(c_int) { \\ i, \\ j, \\ k, \\ _, \\}; - \\pub const i = @enumToInt(enum_unnamed_3.i); - \\pub const j = @enumToInt(enum_unnamed_3.j); - \\pub const k = @enumToInt(enum_unnamed_3.k); + \\pub const i = @enumToInt(enum_unnamed_2.i); + \\pub const j = @enumToInt(enum_unnamed_2.j); + \\pub const k = @enumToInt(enum_unnamed_2.k); \\pub const struct_Baz = extern struct { - \\ l: enum_unnamed_3, + \\ l: enum_unnamed_2, \\ m: d, \\}; \\pub const enum_i = extern enum(c_int) {