Commit df0f7f4692
Changed files (2)
test
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);
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) {