From 241655168780532974d4c20c08e5c39b83acd169 Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Thu, 20 Nov 2025 12:07:10 -0800 Subject: [PATCH 1/2] Ignore qualifiers when hashing aro.QualType Closes #228 --- src/Scope.zig | 17 +++++- src/Translator.zig | 54 ++++++++++++++++--- ...access_in_struct_with_union_as_const_arg.c | 29 ++++++++++ .../translate/member_with_const_qualifier.c | 15 ++++++ 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 test/cases/translate/member_access_in_struct_with_union_as_const_arg.c create mode 100644 test/cases/translate/member_with_const_qualifier.c diff --git a/src/Scope.zig b/src/Scope.zig index e9577bc..c709c13 100644 --- a/src/Scope.zig +++ b/src/Scope.zig @@ -18,7 +18,22 @@ pub const ContainerMemberFns = struct { container_decl_ptr: *ast.Node, member_fns: std.ArrayList(*ast.Payload.Func) = .empty, }; -pub const ContainerMemberFnsHashMap = std.AutoArrayHashMapUnmanaged(aro.QualType, ContainerMemberFns); +pub const ContainerMemberFnsHashMap = std.ArrayHashMapUnmanaged( + aro.QualType, + ContainerMemberFns, + struct { + pub fn hash(self: @This(), key: aro.QualType) u32 { + const auto_hash = std.array_hash_map.getAutoHashFn(aro.QualType, @This()); + return auto_hash(self, key.unqualified()); + } + + pub fn eql(self: @This(), a: aro.QualType, b: aro.QualType, b_index: usize) bool { + const auto_eql = std.array_hash_map.getAutoEqlFn(aro.QualType, @This()); + return auto_eql(self, a.unqualified(), b.unqualified(), b_index); + } + }, + !std.array_hash_map.autoEqlIsCheap(aro.QualType), +); id: Id, parent: ?*Scope, diff --git a/src/Translator.zig b/src/Translator.zig index b693877..01248b9 100644 --- a/src/Translator.zig +++ b/src/Translator.zig @@ -19,6 +19,46 @@ const MacroTranslator = @import("MacroTranslator.zig"); const PatternList = @import("PatternList.zig"); const Scope = @import("Scope.zig"); +const AnonymousRecordFieldNames = struct { + pub const Key = struct { + parent: QualType, + field: QualType, + }; + + pub const Context = struct { + pub fn hash(ctx: Context, key: Key) u64 { + const auto_hash = std.hash_map.getAutoHashFn(Key, Context); + return auto_hash(ctx, .{ + .parent = key.parent.unqualified(), + .field = key.field.unqualified(), + }); + } + + pub fn eql(ctx: Context, a: Key, b: Key) bool { + const auto_eql = std.hash_map.getAutoEqlFn(Key, Context); + return auto_eql(ctx, .{ + .parent = a.parent.unqualified(), + .field = a.field.unqualified(), + }, .{ + .parent = b.parent.unqualified(), + .field = b.field.unqualified(), + }); + } + }; +}; + +pub const QualTypeHashContext = struct { + pub fn hash(ctx: QualTypeHashContext, key: QualType) u64 { + const auto_hash = std.hash_map.getAutoHashFn(QualType, QualTypeHashContext); + return auto_hash(ctx, key.unqualified()); + } + + pub fn eql(ctx: QualTypeHashContext, a: QualType, b: QualType) bool { + const auto_eql = std.hash_map.getAutoEqlFn(QualType, QualTypeHashContext); + return auto_eql(ctx, a.unqualified(), b.unqualified()); + } +}; + pub const Error = std.mem.Allocator.Error; pub const MacroProcessingError = Error || error{UnexpectedMacroToken}; pub const TypeError = Error || error{UnsupportedType}; @@ -44,14 +84,16 @@ mangle_count: u32 = 0, /// Table of declarations for enum, struct, union and typedef types. type_decls: std.AutoArrayHashMapUnmanaged(Node.Index, []const u8) = .empty, /// Table of record decls that have been demoted to opaques. -opaque_demotes: std.AutoHashMapUnmanaged(QualType, void) = .empty, +opaque_demotes: std.HashMapUnmanaged(QualType, void, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty, /// Table of unnamed enums and records that are child types of typedefs. -unnamed_typedefs: std.AutoHashMapUnmanaged(QualType, []const u8) = .empty, +unnamed_typedefs: std.HashMapUnmanaged(QualType, []const u8, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty, /// Table of anonymous record to generated field names. -anonymous_record_field_names: std.AutoHashMapUnmanaged(struct { - parent: QualType, - field: QualType, -}, []const u8) = .empty, +anonymous_record_field_names: std.HashMapUnmanaged( + AnonymousRecordFieldNames.Key, + []const u8, + AnonymousRecordFieldNames.Context, + std.hash_map.default_max_load_percentage, +) = .empty, /// 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 diff --git a/test/cases/translate/member_access_in_struct_with_union_as_const_arg.c b/test/cases/translate/member_access_in_struct_with_union_as_const_arg.c new file mode 100644 index 0000000..ae2f885 --- /dev/null +++ b/test/cases/translate/member_access_in_struct_with_union_as_const_arg.c @@ -0,0 +1,29 @@ +#define SUFFIXED(x) x##_suffix + +struct my_struct { + union { + int SUFFIXED(internal); + double d; + }; +}; + +int my_func(const struct my_struct* s) { + return s->SUFFIXED(internal); +} + +// translate +// +// const union_unnamed_1 = extern union { +// internal_suffix: c_int, +// d: f64, +// }; +// pub const struct_my_struct = extern struct { +// unnamed_0: union_unnamed_1 = @import("std").mem.zeroes(union_unnamed_1), +// pub const my_func = __root.my_func; +// pub const func = __root.my_func; +// }; +// pub export fn my_func(arg_s: [*c]const struct_my_struct) c_int { +// var s = arg_s; +// _ = &s; +// return s.*.unnamed_0.internal_suffix; +// } diff --git a/test/cases/translate/member_with_const_qualifier.c b/test/cases/translate/member_with_const_qualifier.c new file mode 100644 index 0000000..780d3b9 --- /dev/null +++ b/test/cases/translate/member_with_const_qualifier.c @@ -0,0 +1,15 @@ +struct Foo { + int a; +}; +extern int foo(const struct Foo *); +extern int bar(struct Foo *); + +// translate +// +// pub const struct_Foo = extern struct { +// a: c_int = 0, +// pub const foo = __root.foo; +// pub const bar = __root.bar; +// }; +// pub extern fn foo([*c]const struct_Foo) c_int; +// pub extern fn bar([*c]struct_Foo) c_int; From f1500a31327a93b919655de18bb6edca5b78d6ff Mon Sep 17 00:00:00 2001 From: Steeve Morin Date: Sat, 22 Nov 2025 15:31:56 -0800 Subject: [PATCH 2/2] Do not store hash for aro.QualType (packed structs eql is cheap) --- src/Scope.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scope.zig b/src/Scope.zig index c709c13..bcad820 100644 --- a/src/Scope.zig +++ b/src/Scope.zig @@ -32,7 +32,7 @@ pub const ContainerMemberFnsHashMap = std.ArrayHashMapUnmanaged( return auto_eql(self, a.unqualified(), b.unqualified(), b_index); } }, - !std.array_hash_map.autoEqlIsCheap(aro.QualType), + false, ); id: Id,