| author | |
| committer | |
| log | df0f7f46923da9927df18809ad1d744102e78ebd |
| tree | 3b780c2a5b2ebc92328a58effe4b1b0e0ca28afe |
| parent | 5c28b8cd119979bb4b7d32edd1f4542165fc1f98 |
2 files changed, 127 insertions(+), 49 deletions(-)
src/translate_c.zig+77-17| ... | ... | @@ -270,7 +270,10 @@ pub const Context = struct { |
| 270 | 270 | global_scope: *Scope.Root, |
| 271 | 271 | clang_context: *clang.ASTContext, |
| 272 | 272 | mangle_count: u32 = 0, |
| 273 | /// Table of record decls that have been demoted to opaques. | |
| 273 | 274 | opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, |
| 275 | /// Table of unnamed enums and records that are child types of typedefs. | |
| 276 | unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{}, | |
| 274 | 277 | |
| 275 | 278 | /// This one is different than the root scope's name table. This contains |
| 276 | 279 | /// a list of names that we found by visiting all the top level decls without |
| ... | ... | @@ -338,6 +341,7 @@ pub fn translate( |
| 338 | 341 | context.alias_list.deinit(); |
| 339 | 342 | context.global_names.deinit(gpa); |
| 340 | 343 | context.opaque_demotes.deinit(gpa); |
| 344 | context.unnamed_typedefs.deinit(gpa); | |
| 341 | 345 | context.global_scope.deinit(); |
| 342 | 346 | } |
| 343 | 347 | |
| ... | ... | @@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { |
| 401 | 405 | if (decl.castToNamedDecl()) |named_decl| { |
| 402 | 406 | const decl_name = try c.str(named_decl.getName_bytes_begin()); |
| 403 | 407 | try c.global_names.put(c.gpa, decl_name, {}); |
| 408 | ||
| 409 | // Check for typedefs with unnamed enum/record child types. | |
| 410 | if (decl.getKind() == .Typedef) { | |
| 411 | const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); | |
| 412 | var child_ty = typedef_decl.getUnderlyingType().getTypePtr(); | |
| 413 | const addr: usize = while (true) switch (child_ty.getTypeClass()) { | |
| 414 | .Enum => { | |
| 415 | const enum_ty = @ptrCast(*const clang.EnumType, child_ty); | |
| 416 | const enum_decl = enum_ty.getDecl(); | |
| 417 | // check if this decl is unnamed | |
| 418 | if (@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()[0] != 0) return; | |
| 419 | break @ptrToInt(enum_decl.getCanonicalDecl()); | |
| 420 | }, | |
| 421 | .Record => { | |
| 422 | const record_ty = @ptrCast(*const clang.RecordType, child_ty); | |
| 423 | const record_decl = record_ty.getDecl(); | |
| 424 | // check if this decl is unnamed | |
| 425 | if (@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()[0] != 0) return; | |
| 426 | break @ptrToInt(record_decl.getCanonicalDecl()); | |
| 427 | }, | |
| 428 | .Elaborated => { | |
| 429 | const elaborated_ty = @ptrCast(*const clang.ElaboratedType, child_ty); | |
| 430 | child_ty = elaborated_ty.getNamedType().getTypePtr(); | |
| 431 | }, | |
| 432 | .Decayed => { | |
| 433 | const decayed_ty = @ptrCast(*const clang.DecayedType, child_ty); | |
| 434 | child_ty = decayed_ty.getDecayedType().getTypePtr(); | |
| 435 | }, | |
| 436 | .Attributed => { | |
| 437 | const attributed_ty = @ptrCast(*const clang.AttributedType, child_ty); | |
| 438 | child_ty = attributed_ty.getEquivalentType().getTypePtr(); | |
| 439 | }, | |
| 440 | .MacroQualified => { | |
| 441 | const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, child_ty); | |
| 442 | child_ty = macroqualified_ty.getModifiedType().getTypePtr(); | |
| 443 | }, | |
| 444 | else => return, | |
| 445 | } else unreachable; | |
| 446 | // TODO https://github.com/ziglang/zig/issues/3756 | |
| 447 | // TODO https://github.com/ziglang/zig/issues/1802 | |
| 448 | const name = if (isZigPrimitiveType(decl_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ decl_name, c.getMangle() }) else decl_name; | |
| 449 | try c.unnamed_typedefs.putNoClobber(c.gpa, addr, name); | |
| 450 | // Put this typedef in the decl_table to avoid redefinitions. | |
| 451 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); | |
| 452 | } | |
| 404 | 453 | } |
| 405 | 454 | } |
| 406 | 455 | |
| ... | ... | @@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 752 | 801 | const toplevel = scope.id == .root; |
| 753 | 802 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; |
| 754 | 803 | |
| 755 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); | |
| 756 | var is_unnamed = false; | |
| 757 | // Record declarations such as `struct {...} x` have no name but they're not | |
| 758 | // anonymous hence here isAnonymousStructOrUnion is not needed | |
| 759 | if (bare_name.len == 0) { | |
| 760 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 761 | is_unnamed = true; | |
| 762 | } | |
| 763 | ||
| 764 | var container_kind_name: []const u8 = undefined; | |
| 765 | 804 | var is_union = false; |
| 805 | var container_kind_name: []const u8 = undefined; | |
| 806 | var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); | |
| 807 | ||
| 766 | 808 | if (record_decl.isUnion()) { |
| 767 | 809 | container_kind_name = "union"; |
| 768 | 810 | is_union = true; |
| ... | ... | @@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 773 | 815 | return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); |
| 774 | 816 | } |
| 775 | 817 | |
| 776 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | |
| 818 | var is_unnamed = false; | |
| 819 | var name = bare_name; | |
| 820 | if (c.unnamed_typedefs.get(@ptrToInt(record_decl.getCanonicalDecl()))) |typedef_name| { | |
| 821 | bare_name = typedef_name; | |
| 822 | name = typedef_name; | |
| 823 | } else { | |
| 824 | // Record declarations such as `struct {...} x` have no name but they're not | |
| 825 | // anonymous hence here isAnonymousStructOrUnion is not needed | |
| 826 | if (bare_name.len == 0) { | |
| 827 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 828 | is_unnamed = true; | |
| 829 | } | |
| 830 | name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); | |
| 831 | } | |
| 777 | 832 | if (!toplevel) name = try bs.makeMangledName(c, name); |
| 778 | 833 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); |
| 779 | 834 | |
| ... | ... | @@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E |
| 874 | 929 | const toplevel = scope.id == .root; |
| 875 | 930 | const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; |
| 876 | 931 | |
| 877 | var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); | |
| 878 | 932 | var is_unnamed = false; |
| 879 | if (bare_name.len == 0) { | |
| 880 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 881 | is_unnamed = true; | |
| 933 | var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); | |
| 934 | var name = bare_name; | |
| 935 | if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| { | |
| 936 | bare_name = typedef_name; | |
| 937 | name = typedef_name; | |
| 938 | } else { | |
| 939 | if (bare_name.len == 0) { | |
| 940 | bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); | |
| 941 | is_unnamed = true; | |
| 942 | } | |
| 943 | name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); | |
| 882 | 944 | } |
| 883 | ||
| 884 | var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); | |
| 885 | 945 | if (!toplevel) _ = try bs.makeMangledName(c, name); |
| 886 | 946 | try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); |
| 887 | 947 |
test/translate_c.zig+50-32| ... | ... | @@ -3,6 +3,28 @@ const std = @import("std"); |
| 3 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | |
| 5 | 5 | pub fn addCases(cases: *tests.TranslateCContext) void { |
| 6 | cases.add("unnamed child types of typedef receive typedef's name", | |
| 7 | \\typedef enum { | |
| 8 | \\ FooA, | |
| 9 | \\ FooB, | |
| 10 | \\} Foo; | |
| 11 | \\typedef struct { | |
| 12 | \\ int a, b; | |
| 13 | \\} Bar; | |
| 14 | , &[_][]const u8{ | |
| 15 | \\pub const Foo = extern enum(c_int) { | |
| 16 | \\ A, | |
| 17 | \\ B, | |
| 18 | \\ _, | |
| 19 | \\}; | |
| 20 | \\pub const FooA = @enumToInt(Foo.A); | |
| 21 | \\pub const FooB = @enumToInt(Foo.B); | |
| 22 | \\pub const Bar = extern struct { | |
| 23 | \\ a: c_int, | |
| 24 | \\ b: c_int, | |
| 25 | \\}; | |
| 26 | }); | |
| 27 | ||
| 6 | 28 | cases.add("if as while stmt has semicolon", |
| 7 | 29 | \\void foo() { |
| 8 | 30 | \\ while (1) if (1) { |
| ... | ... | @@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 218 | 240 | \\} Bar; |
| 219 | 241 | , &[_][]const u8{ |
| 220 | 242 | \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo |
| 221 | \\const struct_unnamed_1 = opaque {}; | |
| 222 | \\pub const Foo = struct_unnamed_1; | |
| 223 | \\const struct_unnamed_2 = extern struct { | |
| 243 | \\pub const Foo = opaque {}; | |
| 244 | \\pub const Bar = extern struct { | |
| 224 | 245 | \\ bar: ?*Foo, |
| 225 | 246 | \\}; |
| 226 | 247 | }); |
| ... | ... | @@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 519 | 540 | \\} outer; |
| 520 | 541 | \\void foo(outer *x) { x->y = x->x; } |
| 521 | 542 | , &[_][]const u8{ |
| 522 | \\const struct_unnamed_3 = extern struct { | |
| 543 | \\const struct_unnamed_2 = extern struct { | |
| 523 | 544 | \\ y: c_int, |
| 524 | 545 | \\}; |
| 525 | \\const union_unnamed_2 = extern union { | |
| 546 | \\const union_unnamed_1 = extern union { | |
| 526 | 547 | \\ x: u8, |
| 527 | \\ unnamed_0: struct_unnamed_3, | |
| 548 | \\ unnamed_0: struct_unnamed_2, | |
| 528 | 549 | \\}; |
| 529 | \\const struct_unnamed_1 = extern struct { | |
| 530 | \\ unnamed_0: union_unnamed_2, | |
| 550 | \\pub const outer = extern struct { | |
| 551 | \\ unnamed_0: union_unnamed_1, | |
| 531 | 552 | \\}; |
| 532 | \\pub const outer = struct_unnamed_1; | |
| 533 | 553 | \\pub export fn foo(arg_x: [*c]outer) void { |
| 534 | 554 | \\ var x = arg_x; |
| 535 | 555 | \\ 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 { |
| 565 | 585 | \\struct {int x,y;} s2 = {.y = 2, .x=1}; |
| 566 | 586 | \\foo s3 = { 123 }; |
| 567 | 587 | , &[_][]const u8{ |
| 568 | \\const struct_unnamed_1 = extern struct { | |
| 588 | \\pub const foo = extern struct { | |
| 569 | 589 | \\ x: c_int, |
| 570 | 590 | \\}; |
| 571 | \\pub const foo = struct_unnamed_1; | |
| 572 | \\const struct_unnamed_2 = extern struct { | |
| 591 | \\const struct_unnamed_1 = extern struct { | |
| 573 | 592 | \\ x: f64, |
| 574 | 593 | \\ y: f64, |
| 575 | 594 | \\ z: f64, |
| 576 | 595 | \\}; |
| 577 | \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{ | |
| 596 | \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{ | |
| 578 | 597 | \\ .x = 1.2, |
| 579 | 598 | \\ .y = 1.3, |
| 580 | 599 | \\ .z = 0, |
| 581 | 600 | \\}; |
| 582 | \\const struct_unnamed_3 = extern struct { | |
| 601 | \\const struct_unnamed_2 = extern struct { | |
| 583 | 602 | \\ sec: c_int, |
| 584 | 603 | \\ min: c_int, |
| 585 | 604 | \\ hour: c_int, |
| ... | ... | @@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 587 | 606 | \\ mon: c_int, |
| 588 | 607 | \\ year: c_int, |
| 589 | 608 | \\}; |
| 590 | \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{ | |
| 609 | \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{ | |
| 591 | 610 | \\ .sec = @as(c_int, 30), |
| 592 | 611 | \\ .min = @as(c_int, 15), |
| 593 | 612 | \\ .hour = @as(c_int, 17), |
| ... | ... | @@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 595 | 614 | \\ .mon = @as(c_int, 12), |
| 596 | 615 | \\ .year = @as(c_int, 2014), |
| 597 | 616 | \\}; |
| 598 | \\const struct_unnamed_4 = extern struct { | |
| 617 | \\const struct_unnamed_3 = extern struct { | |
| 599 | 618 | \\ x: c_int, |
| 600 | 619 | \\ y: c_int, |
| 601 | 620 | \\}; |
| 602 | \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{ | |
| 621 | \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{ | |
| 603 | 622 | \\ .x = @as(c_int, 1), |
| 604 | 623 | \\ .y = @as(c_int, 2), |
| 605 | 624 | \\}; |
| ... | ... | @@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1639 | 1658 | \\ p, |
| 1640 | 1659 | \\}; |
| 1641 | 1660 | , &[_][]const u8{ |
| 1642 | \\const enum_unnamed_1 = extern enum(c_int) { | |
| 1661 | \\pub const d = extern enum(c_int) { | |
| 1643 | 1662 | \\ a, |
| 1644 | 1663 | \\ b, |
| 1645 | 1664 | \\ c, |
| 1646 | 1665 | \\ _, |
| 1647 | 1666 | \\}; |
| 1648 | \\pub const a = @enumToInt(enum_unnamed_1.a); | |
| 1649 | \\pub const b = @enumToInt(enum_unnamed_1.b); | |
| 1650 | \\pub const c = @enumToInt(enum_unnamed_1.c); | |
| 1651 | \\pub const d = enum_unnamed_1; | |
| 1652 | \\const enum_unnamed_2 = extern enum(c_int) { | |
| 1667 | \\pub const a = @enumToInt(d.a); | |
| 1668 | \\pub const b = @enumToInt(d.b); | |
| 1669 | \\pub const c = @enumToInt(d.c); | |
| 1670 | \\const enum_unnamed_1 = extern enum(c_int) { | |
| 1653 | 1671 | \\ e = 0, |
| 1654 | 1672 | \\ f = 4, |
| 1655 | 1673 | \\ g = 5, |
| 1656 | 1674 | \\ _, |
| 1657 | 1675 | \\}; |
| 1658 | \\pub const e = @enumToInt(enum_unnamed_2.e); | |
| 1659 | \\pub const f = @enumToInt(enum_unnamed_2.f); | |
| 1660 | \\pub const g = @enumToInt(enum_unnamed_2.g); | |
| 1661 | \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); | |
| 1662 | \\const enum_unnamed_3 = extern enum(c_int) { | |
| 1676 | \\pub const e = @enumToInt(enum_unnamed_1.e); | |
| 1677 | \\pub const f = @enumToInt(enum_unnamed_1.f); | |
| 1678 | \\pub const g = @enumToInt(enum_unnamed_1.g); | |
| 1679 | \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); | |
| 1680 | \\const enum_unnamed_2 = extern enum(c_int) { | |
| 1663 | 1681 | \\ i, |
| 1664 | 1682 | \\ j, |
| 1665 | 1683 | \\ k, |
| 1666 | 1684 | \\ _, |
| 1667 | 1685 | \\}; |
| 1668 | \\pub const i = @enumToInt(enum_unnamed_3.i); | |
| 1669 | \\pub const j = @enumToInt(enum_unnamed_3.j); | |
| 1670 | \\pub const k = @enumToInt(enum_unnamed_3.k); | |
| 1686 | \\pub const i = @enumToInt(enum_unnamed_2.i); | |
| 1687 | \\pub const j = @enumToInt(enum_unnamed_2.j); | |
| 1688 | \\pub const k = @enumToInt(enum_unnamed_2.k); | |
| 1671 | 1689 | \\pub const struct_Baz = extern struct { |
| 1672 | \\ l: enum_unnamed_3, | |
| 1690 | \\ l: enum_unnamed_2, | |
| 1673 | 1691 | \\ m: d, |
| 1674 | 1692 | \\}; |
| 1675 | 1693 | \\pub const enum_i = extern enum(c_int) { |