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

This commit is contained in:
Veikka Tuominen 2021-03-21 18:42:30 +02:00
parent 5c28b8cd11
commit df0f7f4692
2 changed files with 127 additions and 49 deletions

View File

@ -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);

View File

@ -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) {