| author | |
| committer | |
| log | 0a7be71bc2e58a5375ceed0b1b9850bd33717a0b |
| tree | 37036778f4688684c92e843e4d5468edc04edd86 |
| parent | cfc19eace71c92ecd7e138db6d961271a1b6c126 |
| signature | Commit is signed but in an unrecognized format. |
6 files changed, 163 insertions(+), 46 deletions(-)
lib/std/hash_map.zig+24-21| ... | ... | @@ -50,20 +50,20 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | 52 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { |
| 53 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); | |
| 53 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); | |
| 54 | 54 | } |
| 55 | 55 | |
| 56 | 56 | pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type { |
| 57 | return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); | |
| 57 | return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); | |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | /// Builtin hashmap for strings as keys. |
| 61 | 61 | pub fn StringHashMap(comptime V: type) type { |
| 62 | return HashMap([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); | |
| 62 | return HashMap([]const u8, V, hashString, eqlString, default_max_load_percentage); | |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | pub fn StringHashMapUnmanaged(comptime V: type) type { |
| 66 | return HashMapUnmanaged([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); | |
| 66 | return HashMapUnmanaged([]const u8, V, hashString, eqlString, default_max_load_percentage); | |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | 69 | pub fn eqlString(a: []const u8, b: []const u8) bool { |
| ... | ... | @@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 { |
| 74 | 74 | return std.hash.Wyhash.hash(0, s); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | pub const DefaultMaxLoadPercentage = 80; | |
| 77 | /// Deprecated use `default_max_load_percentage` | |
| 78 | pub const DefaultMaxLoadPercentage = default_max_load_percentage; | |
| 79 | ||
| 80 | pub const default_max_load_percentage = 80; | |
| 78 | 81 | |
| 79 | 82 | /// General purpose hash table. |
| 80 | 83 | /// No order is guaranteed and any modification invalidates live iterators. |
| ... | ... | @@ -89,13 +92,13 @@ pub fn HashMap( |
| 89 | 92 | comptime V: type, |
| 90 | 93 | comptime hashFn: fn (key: K) u64, |
| 91 | 94 | comptime eqlFn: fn (a: K, b: K) bool, |
| 92 | comptime MaxLoadPercentage: u64, | |
| 95 | comptime max_load_percentage: u64, | |
| 93 | 96 | ) type { |
| 94 | 97 | return struct { |
| 95 | 98 | unmanaged: Unmanaged, |
| 96 | 99 | allocator: *Allocator, |
| 97 | 100 | |
| 98 | pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, MaxLoadPercentage); | |
| 101 | pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, max_load_percentage); | |
| 99 | 102 | pub const Entry = Unmanaged.Entry; |
| 100 | 103 | pub const Hash = Unmanaged.Hash; |
| 101 | 104 | pub const Iterator = Unmanaged.Iterator; |
| ... | ... | @@ -251,9 +254,9 @@ pub fn HashMapUnmanaged( |
| 251 | 254 | comptime V: type, |
| 252 | 255 | hashFn: fn (key: K) u64, |
| 253 | 256 | eqlFn: fn (a: K, b: K) bool, |
| 254 | comptime MaxLoadPercentage: u64, | |
| 257 | comptime max_load_percentage: u64, | |
| 255 | 258 | ) type { |
| 256 | comptime assert(MaxLoadPercentage > 0 and MaxLoadPercentage < 100); | |
| 259 | comptime assert(max_load_percentage > 0 and max_load_percentage < 100); | |
| 257 | 260 | |
| 258 | 261 | return struct { |
| 259 | 262 | const Self = @This(); |
| ... | ... | @@ -274,12 +277,12 @@ pub fn HashMapUnmanaged( |
| 274 | 277 | // Having a countdown to grow reduces the number of instructions to |
| 275 | 278 | // execute when determining if the hashmap has enough capacity already. |
| 276 | 279 | /// Number of available slots before a grow is needed to satisfy the |
| 277 | /// `MaxLoadPercentage`. | |
| 280 | /// `max_load_percentage`. | |
| 278 | 281 | available: Size = 0, |
| 279 | 282 | |
| 280 | 283 | // This is purely empirical and not a /very smart magic constant™/. |
| 281 | 284 | /// Capacity of the first grow when bootstrapping the hashmap. |
| 282 | const MinimalCapacity = 8; | |
| 285 | const minimal_capacity = 8; | |
| 283 | 286 | |
| 284 | 287 | // This hashmap is specially designed for sizes that fit in a u32. |
| 285 | 288 | const Size = u32; |
| ... | ... | @@ -382,7 +385,7 @@ pub fn HashMapUnmanaged( |
| 382 | 385 | found_existing: bool, |
| 383 | 386 | }; |
| 384 | 387 | |
| 385 | pub const Managed = HashMap(K, V, hashFn, eqlFn, MaxLoadPercentage); | |
| 388 | pub const Managed = HashMap(K, V, hashFn, eqlFn, max_load_percentage); | |
| 386 | 389 | |
| 387 | 390 | pub fn promote(self: Self, allocator: *Allocator) Managed { |
| 388 | 391 | return .{ |
| ... | ... | @@ -392,7 +395,7 @@ pub fn HashMapUnmanaged( |
| 392 | 395 | } |
| 393 | 396 | |
| 394 | 397 | fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { |
| 395 | return size * 100 < MaxLoadPercentage * cap; | |
| 398 | return size * 100 < max_load_percentage * cap; | |
| 396 | 399 | } |
| 397 | 400 | |
| 398 | 401 | pub fn init(allocator: *Allocator) Self { |
| ... | ... | @@ -425,7 +428,7 @@ pub fn HashMapUnmanaged( |
| 425 | 428 | } |
| 426 | 429 | |
| 427 | 430 | fn capacityForSize(size: Size) Size { |
| 428 | var new_cap = @truncate(u32, (@as(u64, size) * 100) / MaxLoadPercentage + 1); | |
| 431 | var new_cap = @truncate(u32, (@as(u64, size) * 100) / max_load_percentage + 1); | |
| 429 | 432 | new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable; |
| 430 | 433 | return new_cap; |
| 431 | 434 | } |
| ... | ... | @@ -439,7 +442,7 @@ pub fn HashMapUnmanaged( |
| 439 | 442 | if (self.metadata) |_| { |
| 440 | 443 | self.initMetadatas(); |
| 441 | 444 | self.size = 0; |
| 442 | self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100); | |
| 445 | self.available = @truncate(u32, (self.capacity() * max_load_percentage) / 100); | |
| 443 | 446 | } |
| 444 | 447 | } |
| 445 | 448 | |
| ... | ... | @@ -712,9 +715,9 @@ pub fn HashMapUnmanaged( |
| 712 | 715 | } |
| 713 | 716 | |
| 714 | 717 | // This counts the number of occupied slots, used + tombstones, which is |
| 715 | // what has to stay under the MaxLoadPercentage of capacity. | |
| 718 | // what has to stay under the max_load_percentage of capacity. | |
| 716 | 719 | fn load(self: *const Self) Size { |
| 717 | const max_load = (self.capacity() * MaxLoadPercentage) / 100; | |
| 720 | const max_load = (self.capacity() * max_load_percentage) / 100; | |
| 718 | 721 | assert(max_load >= self.available); |
| 719 | 722 | return @truncate(Size, max_load - self.available); |
| 720 | 723 | } |
| ... | ... | @@ -733,7 +736,7 @@ pub fn HashMapUnmanaged( |
| 733 | 736 | const new_cap = capacityForSize(self.size); |
| 734 | 737 | try other.allocate(allocator, new_cap); |
| 735 | 738 | other.initMetadatas(); |
| 736 | other.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); | |
| 739 | other.available = @truncate(u32, (new_cap * max_load_percentage) / 100); | |
| 737 | 740 | |
| 738 | 741 | var i: Size = 0; |
| 739 | 742 | var metadata = self.metadata.?; |
| ... | ... | @@ -751,7 +754,7 @@ pub fn HashMapUnmanaged( |
| 751 | 754 | } |
| 752 | 755 | |
| 753 | 756 | fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void { |
| 754 | const new_cap = std.math.max(new_capacity, MinimalCapacity); | |
| 757 | const new_cap = std.math.max(new_capacity, minimal_capacity); | |
| 755 | 758 | assert(new_cap > self.capacity()); |
| 756 | 759 | assert(std.math.isPowerOfTwo(new_cap)); |
| 757 | 760 | |
| ... | ... | @@ -759,7 +762,7 @@ pub fn HashMapUnmanaged( |
| 759 | 762 | defer map.deinit(allocator); |
| 760 | 763 | try map.allocate(allocator, new_cap); |
| 761 | 764 | map.initMetadatas(); |
| 762 | map.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); | |
| 765 | map.available = @truncate(u32, (new_cap * max_load_percentage) / 100); | |
| 763 | 766 | |
| 764 | 767 | if (self.size != 0) { |
| 765 | 768 | const old_capacity = self.capacity(); |
| ... | ... | @@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" { |
| 943 | 946 | |
| 944 | 947 | try map.put(0, 0); |
| 945 | 948 | expectEqual(map.count(), 1); |
| 946 | expectEqual(map.capacity(), @TypeOf(map).Unmanaged.MinimalCapacity); | |
| 949 | expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity); | |
| 947 | 950 | |
| 948 | 951 | try map.ensureCapacity(65); |
| 949 | 952 | expectEqual(map.count(), 1); |
src/Compilation.zig+2| ... | ... | @@ -1653,6 +1653,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1653 | 1653 | .error_msg = null, |
| 1654 | 1654 | .decl = decl, |
| 1655 | 1655 | .fwd_decl = fwd_decl.toManaged(module.gpa), |
| 1656 | // we don't want to emit optionals and error unions to headers since they have no ABI | |
| 1657 | .typedefs = undefined, | |
| 1656 | 1658 | }; |
| 1657 | 1659 | defer dg.fwd_decl.deinit(); |
| 1658 | 1660 |
src/codegen/c.zig+70-8| ... | ... | @@ -32,6 +32,34 @@ pub const CValue = union(enum) { |
| 32 | 32 | }; |
| 33 | 33 | |
| 34 | 34 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| 35 | pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); | |
| 36 | ||
| 37 | fn formatTypeAsCIdentifier( | |
| 38 | data: Type, | |
| 39 | comptime fmt: []const u8, | |
| 40 | options: std.fmt.FormatOptions, | |
| 41 | writer: anytype, | |
| 42 | ) !void { | |
| 43 | var buffer = [1]u8{0} ** 128; | |
| 44 | // We don't care if it gets cut off, it's still more unique than a number | |
| 45 | var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer; | |
| 46 | ||
| 47 | for (buf) |c, i| { | |
| 48 | switch (c) { | |
| 49 | 0 => return writer.writeAll(buf[0..i]), | |
| 50 | 'a'...'z', 'A'...'Z', '_', '$' => {}, | |
| 51 | '0'...'9' => if (i == 0) { | |
| 52 | buf[i] = '_'; | |
| 53 | }, | |
| 54 | else => buf[i] = '_', | |
| 55 | } | |
| 56 | } | |
| 57 | return writer.writeAll(buf); | |
| 58 | } | |
| 59 | ||
| 60 | pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { | |
| 61 | return .{ .data = t }; | |
| 62 | } | |
| 35 | 63 | |
| 36 | 64 | /// This data is available when outputting .c code for a Module. |
| 37 | 65 | /// It is not available when generating .h file. |
| ... | ... | @@ -115,6 +143,7 @@ pub const DeclGen = struct { |
| 115 | 143 | decl: *Decl, |
| 116 | 144 | fwd_decl: std.ArrayList(u8), |
| 117 | 145 | error_msg: ?*Module.ErrorMsg, |
| 146 | typedefs: TypedefMap, | |
| 118 | 147 | |
| 119 | 148 | fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { |
| 120 | 149 | dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{ |
| ... | ... | @@ -325,22 +354,55 @@ pub const DeclGen = struct { |
| 325 | 354 | const child_type = t.optionalChild(&opt_buf); |
| 326 | 355 | if (t.isPtrLikeOptional()) { |
| 327 | 356 | return dg.renderType(w, child_type); |
| 357 | } else if (dg.typedefs.get(t)) |some| { | |
| 358 | return w.writeAll(some.name); | |
| 328 | 359 | } |
| 329 | 360 | |
| 330 | // TODO this needs to be typedeffed since different structs are different types. | |
| 331 | try w.writeAll("struct { "); | |
| 332 | try dg.renderType(w, child_type); | |
| 333 | try w.writeAll(" payload; bool is_null; }"); | |
| 361 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | |
| 362 | defer buffer.deinit(); | |
| 363 | const bw = buffer.writer(); | |
| 364 | ||
| 365 | try bw.writeAll("typedef struct { "); | |
| 366 | try dg.renderType(bw, child_type); | |
| 367 | try bw.writeAll(" payload; bool is_null; } "); | |
| 368 | const name_index = buffer.items.len; | |
| 369 | try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); | |
| 370 | ||
| 371 | const rendered = buffer.toOwnedSlice(); | |
| 372 | errdefer dg.typedefs.allocator.free(rendered); | |
| 373 | const name = rendered[name_index .. rendered.len - 2]; | |
| 374 | ||
| 375 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | |
| 376 | try w.writeAll(name); | |
| 377 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 334 | 378 | }, |
| 335 | 379 | .ErrorSet => { |
| 336 | 380 | comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2); |
| 337 | 381 | try w.writeAll("uint16_t"); |
| 338 | 382 | }, |
| 339 | 383 | .ErrorUnion => { |
| 340 | // TODO this needs to be typedeffed since different structs are different types. | |
| 341 | try w.writeAll("struct { "); | |
| 342 | try dg.renderType(w, t.errorUnionChild()); | |
| 343 | try w.writeAll(" payload; uint16_t error; }"); | |
| 384 | if (dg.typedefs.get(t)) |some| { | |
| 385 | return w.writeAll(some.name); | |
| 386 | } | |
| 387 | const child_type = t.errorUnionChild(); | |
| 388 | ||
| 389 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | |
| 390 | defer buffer.deinit(); | |
| 391 | const bw = buffer.writer(); | |
| 392 | ||
| 393 | try bw.writeAll("typedef struct { "); | |
| 394 | try dg.renderType(bw, t.errorUnionChild()); | |
| 395 | try bw.writeAll(" payload; uint16_t error; } "); | |
| 396 | const name_index = buffer.items.len; | |
| 397 | try bw.print("zig_err_union_{s}_t;\n", .{typeToCIdentifier(child_type)}); | |
| 398 | ||
| 399 | const rendered = buffer.toOwnedSlice(); | |
| 400 | errdefer dg.typedefs.allocator.free(rendered); | |
| 401 | const name = rendered[name_index .. rendered.len - 2]; | |
| 402 | ||
| 403 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | |
| 404 | try w.writeAll(name); | |
| 405 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 344 | 406 | }, |
| 345 | 407 | .Null, .Undefined => unreachable, // must be const or comptime |
| 346 | 408 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ |
src/link/C.zig+54-15| ... | ... | @@ -9,6 +9,7 @@ const codegen = @import("../codegen/c.zig"); |
| 9 | 9 | const link = @import("../link.zig"); |
| 10 | 10 | const trace = @import("../tracy.zig").trace; |
| 11 | 11 | const C = @This(); |
| 12 | const Type = @import("../type.zig").Type; | |
| 12 | 13 | |
| 13 | 14 | pub const base_tag: link.File.Tag = .c; |
| 14 | 15 | pub const zig_h = @embedFile("C/zig.h"); |
| ... | ... | @@ -28,9 +29,11 @@ pub const DeclBlock = struct { |
| 28 | 29 | /// Per-function data. |
| 29 | 30 | pub const FnBlock = struct { |
| 30 | 31 | fwd_decl: std.ArrayListUnmanaged(u8), |
| 32 | typedefs: codegen.TypedefMap.Unmanaged, | |
| 31 | 33 | |
| 32 | 34 | pub const empty: FnBlock = .{ |
| 33 | 35 | .fwd_decl = .{}, |
| 36 | .typedefs = .{}, | |
| 34 | 37 | }; |
| 35 | 38 | }; |
| 36 | 39 | |
| ... | ... | @@ -74,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {} |
| 74 | 77 | pub fn freeDecl(self: *C, decl: *Module.Decl) void { |
| 75 | 78 | decl.link.c.code.deinit(self.base.allocator); |
| 76 | 79 | decl.fn_link.c.fwd_decl.deinit(self.base.allocator); |
| 80 | var it = decl.fn_link.c.typedefs.iterator(); | |
| 81 | while (it.next()) |some| { | |
| 82 | self.base.allocator.free(some.value.rendered); | |
| 83 | } | |
| 84 | decl.fn_link.c.typedefs.deinit(self.base.allocator); | |
| 77 | 85 | } |
| 78 | 86 | |
| 79 | 87 | pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -81,8 +89,10 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 81 | 89 | defer tracy.end(); |
| 82 | 90 | |
| 83 | 91 | const fwd_decl = &decl.fn_link.c.fwd_decl; |
| 92 | const typedefs = &decl.fn_link.c.typedefs; | |
| 84 | 93 | const code = &decl.link.c.code; |
| 85 | 94 | fwd_decl.shrinkRetainingCapacity(0); |
| 95 | typedefs.clearRetainingCapacity(); | |
| 86 | 96 | code.shrinkRetainingCapacity(0); |
| 87 | 97 | |
| 88 | 98 | var object: codegen.Object = .{ |
| ... | ... | @@ -91,6 +101,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 91 | 101 | .error_msg = null, |
| 92 | 102 | .decl = decl, |
| 93 | 103 | .fwd_decl = fwd_decl.toManaged(module.gpa), |
| 104 | .typedefs = typedefs.promote(module.gpa), | |
| 94 | 105 | }, |
| 95 | 106 | .gpa = module.gpa, |
| 96 | 107 | .code = code.toManaged(module.gpa), |
| ... | ... | @@ -98,9 +109,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 98 | 109 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 99 | 110 | }; |
| 100 | 111 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 101 | defer object.value_map.deinit(); | |
| 102 | defer object.code.deinit(); | |
| 103 | defer object.dg.fwd_decl.deinit(); | |
| 112 | defer { | |
| 113 | object.value_map.deinit(); | |
| 114 | object.code.deinit(); | |
| 115 | object.dg.fwd_decl.deinit(); | |
| 116 | var it = object.dg.typedefs.iterator(); | |
| 117 | while (it.next()) |some| { | |
| 118 | module.gpa.free(some.value.rendered); | |
| 119 | } | |
| 120 | object.dg.typedefs.deinit(); | |
| 121 | } | |
| 104 | 122 | |
| 105 | 123 | codegen.genDecl(&object) catch |err| switch (err) { |
| 106 | 124 | error.AnalysisFail => { |
| ... | ... | @@ -111,6 +129,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 111 | 129 | }; |
| 112 | 130 | |
| 113 | 131 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 132 | typedefs.* = object.dg.typedefs.unmanaged; | |
| 133 | object.dg.typedefs.unmanaged = .{}; | |
| 114 | 134 | code.* = object.code.moveToUnmanaged(); |
| 115 | 135 | |
| 116 | 136 | // Free excess allocated memory for this Decl. |
| ... | ... | @@ -142,7 +162,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 142 | 162 | defer all_buffers.deinit(); |
| 143 | 163 | |
| 144 | 164 | // This is at least enough until we get to the function bodies without error handling. |
| 145 | try all_buffers.ensureCapacity(module.decl_table.count() + 1); | |
| 165 | try all_buffers.ensureCapacity(module.decl_table.count() + 2); | |
| 146 | 166 | |
| 147 | 167 | var file_size: u64 = zig_h.len; |
| 148 | 168 | all_buffers.appendAssumeCapacity(.{ |
| ... | ... | @@ -150,22 +170,25 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 150 | 170 | .iov_len = zig_h.len, |
| 151 | 171 | }); |
| 152 | 172 | |
| 153 | var error_defs_buf = std.ArrayList(u8).init(comp.gpa); | |
| 154 | defer error_defs_buf.deinit(); | |
| 173 | var err_typedef_buf = std.ArrayList(u8).init(comp.gpa); | |
| 174 | defer err_typedef_buf.deinit(); | |
| 175 | const err_typedef_writer = err_typedef_buf.writer(); | |
| 176 | const err_typedef_item = all_buffers.addOneAssumeCapacity(); | |
| 155 | 177 | |
| 156 | var it = module.global_error_set.iterator(); | |
| 157 | while (it.next()) |entry| { | |
| 158 | try error_defs_buf.writer().print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); | |
| 178 | render_errors: { | |
| 179 | if (module.global_error_set.size == 0) break :render_errors; | |
| 180 | var it = module.global_error_set.iterator(); | |
| 181 | while (it.next()) |entry| { | |
| 182 | try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value }); | |
| 183 | } | |
| 184 | try err_typedef_writer.writeByte('\n'); | |
| 159 | 185 | } |
| 160 | try error_defs_buf.writer().writeByte('\n'); | |
| 161 | all_buffers.appendAssumeCapacity(.{ | |
| 162 | .iov_base = error_defs_buf.items.ptr, | |
| 163 | .iov_len = error_defs_buf.items.len, | |
| 164 | }); | |
| 165 | 186 | |
| 166 | 187 | var fn_count: usize = 0; |
| 188 | var typedefs = std.HashMap(Type, []const u8, Type.hash, Type.eql, std.hash_map.default_max_load_percentage).init(comp.gpa); | |
| 189 | defer typedefs.deinit(); | |
| 167 | 190 | |
| 168 | // Forward decls and non-functions first. | |
| 191 | // Typedefs, forward decls and non-functions first. | |
| 169 | 192 | // TODO: performance investigation: would keeping a list of Decls that we should |
| 170 | 193 | // generate, rather than querying here, be faster? |
| 171 | 194 | for (module.decl_table.items()) |kv| { |
| ... | ... | @@ -174,6 +197,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 174 | 197 | .most_recent => |tvm| { |
| 175 | 198 | const buf = buf: { |
| 176 | 199 | if (tvm.typed_value.val.castTag(.function)) |_| { |
| 200 | var it = decl.fn_link.c.typedefs.iterator(); | |
| 201 | while (it.next()) |new| { | |
| 202 | if (typedefs.get(new.key)) |previous| { | |
| 203 | try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value.name }); | |
| 204 | } else { | |
| 205 | try typedefs.ensureCapacity(typedefs.capacity() + 1); | |
| 206 | try err_typedef_writer.writeAll(new.value.rendered); | |
| 207 | typedefs.putAssumeCapacityNoClobber(new.key, new.value.name); | |
| 208 | } | |
| 209 | } | |
| 177 | 210 | fn_count += 1; |
| 178 | 211 | break :buf decl.fn_link.c.fwd_decl.items; |
| 179 | 212 | } else { |
| ... | ... | @@ -190,6 +223,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 190 | 223 | } |
| 191 | 224 | } |
| 192 | 225 | |
| 226 | err_typedef_item.* = .{ | |
| 227 | .iov_base = err_typedef_buf.items.ptr, | |
| 228 | .iov_len = err_typedef_buf.items.len, | |
| 229 | }; | |
| 230 | file_size += err_typedef_buf.items.len; | |
| 231 | ||
| 193 | 232 | // Now the function bodies. |
| 194 | 233 | try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); |
| 195 | 234 | for (module.decl_table.items()) |kv| { |
src/test.zig+1-2| ... | ... | @@ -868,11 +868,10 @@ pub const TestContext = struct { |
| 868 | 868 | std.testing.zig_exe_path, |
| 869 | 869 | "run", |
| 870 | 870 | "-cflags", |
| 871 | "-std=c89", | |
| 871 | "-std=c99", | |
| 872 | 872 | "-pedantic", |
| 873 | 873 | "-Werror", |
| 874 | 874 | "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875 |
| 875 | "-Wno-declaration-after-statement", | |
| 876 | 875 | "--", |
| 877 | 876 | "-lc", |
| 878 | 877 | exe_path, |
test/stage2/cbe.zig+12| ... | ... | @@ -258,6 +258,18 @@ pub fn addCases(ctx: *TestContext) !void { |
| 258 | 258 | \\ return count - 5; |
| 259 | 259 | \\} |
| 260 | 260 | , ""); |
| 261 | ||
| 262 | // Same with non pointer optionals | |
| 263 | case.addCompareOutput( | |
| 264 | \\export fn main() c_int { | |
| 265 | \\ var count: c_int = 0; | |
| 266 | \\ var opt_ptr: ?c_int = count; | |
| 267 | \\ while (opt_ptr) |_| : (count += 1) { | |
| 268 | \\ if (count == 4) opt_ptr = null; | |
| 269 | \\ } | |
| 270 | \\ return count - 5; | |
| 271 | \\} | |
| 272 | , ""); | |
| 261 | 273 | } |
| 262 | 274 | ctx.c("empty start function", linux_x64, |
| 263 | 275 | \\export fn _start() noreturn { |