authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-01-30 14:56:36+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-03-08 00:33:56+02:00
log0a7be71bc2e58a5375ceed0b1b9850bd33717a0b
tree37036778f4688684c92e843e4d5468edc04edd86
parentcfc19eace71c92ecd7e138db6d961271a1b6c126
signature Commit is signed but in an unrecognized format.

stage2 cbe: non pointer optionals


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,20 +50,20 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
50}50}
5151
52pub fn AutoHashMap(comptime K: type, comptime V: type) type {52pub 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}
5555
56pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type {56pub 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}
5959
60/// Builtin hashmap for strings as keys.60/// Builtin hashmap for strings as keys.
61pub fn StringHashMap(comptime V: type) type {61pub 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}
6464
65pub fn StringHashMapUnmanaged(comptime V: type) type {65pub 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}
6868
69pub fn eqlString(a: []const u8, b: []const u8) bool {69pub fn eqlString(a: []const u8, b: []const u8) bool {
...@@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 {...@@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 {
74 return std.hash.Wyhash.hash(0, s);74 return std.hash.Wyhash.hash(0, s);
75}75}
7676
77pub const DefaultMaxLoadPercentage = 80;77/// Deprecated use `default_max_load_percentage`
78pub const DefaultMaxLoadPercentage = default_max_load_percentage;
79
80pub const default_max_load_percentage = 80;
7881
79/// General purpose hash table.82/// General purpose hash table.
80/// No order is guaranteed and any modification invalidates live iterators.83/// No order is guaranteed and any modification invalidates live iterators.
...@@ -89,13 +92,13 @@ pub fn HashMap(...@@ -89,13 +92,13 @@ pub fn HashMap(
89 comptime V: type,92 comptime V: type,
90 comptime hashFn: fn (key: K) u64,93 comptime hashFn: fn (key: K) u64,
91 comptime eqlFn: fn (a: K, b: K) bool,94 comptime eqlFn: fn (a: K, b: K) bool,
92 comptime MaxLoadPercentage: u64,95 comptime max_load_percentage: u64,
93) type {96) type {
94 return struct {97 return struct {
95 unmanaged: Unmanaged,98 unmanaged: Unmanaged,
96 allocator: *Allocator,99 allocator: *Allocator,
97100
98 pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, MaxLoadPercentage);101 pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, max_load_percentage);
99 pub const Entry = Unmanaged.Entry;102 pub const Entry = Unmanaged.Entry;
100 pub const Hash = Unmanaged.Hash;103 pub const Hash = Unmanaged.Hash;
101 pub const Iterator = Unmanaged.Iterator;104 pub const Iterator = Unmanaged.Iterator;
...@@ -251,9 +254,9 @@ pub fn HashMapUnmanaged(...@@ -251,9 +254,9 @@ pub fn HashMapUnmanaged(
251 comptime V: type,254 comptime V: type,
252 hashFn: fn (key: K) u64,255 hashFn: fn (key: K) u64,
253 eqlFn: fn (a: K, b: K) bool,256 eqlFn: fn (a: K, b: K) bool,
254 comptime MaxLoadPercentage: u64,257 comptime max_load_percentage: u64,
255) type {258) type {
256 comptime assert(MaxLoadPercentage > 0 and MaxLoadPercentage < 100);259 comptime assert(max_load_percentage > 0 and max_load_percentage < 100);
257260
258 return struct {261 return struct {
259 const Self = @This();262 const Self = @This();
...@@ -274,12 +277,12 @@ pub fn HashMapUnmanaged(...@@ -274,12 +277,12 @@ pub fn HashMapUnmanaged(
274 // Having a countdown to grow reduces the number of instructions to277 // Having a countdown to grow reduces the number of instructions to
275 // execute when determining if the hashmap has enough capacity already.278 // execute when determining if the hashmap has enough capacity already.
276 /// Number of available slots before a grow is needed to satisfy the279 /// Number of available slots before a grow is needed to satisfy the
277 /// `MaxLoadPercentage`.280 /// `max_load_percentage`.
278 available: Size = 0,281 available: Size = 0,
279282
280 // This is purely empirical and not a /very smart magic constant™/.283 // This is purely empirical and not a /very smart magic constant™/.
281 /// Capacity of the first grow when bootstrapping the hashmap.284 /// Capacity of the first grow when bootstrapping the hashmap.
282 const MinimalCapacity = 8;285 const minimal_capacity = 8;
283286
284 // This hashmap is specially designed for sizes that fit in a u32.287 // This hashmap is specially designed for sizes that fit in a u32.
285 const Size = u32;288 const Size = u32;
...@@ -382,7 +385,7 @@ pub fn HashMapUnmanaged(...@@ -382,7 +385,7 @@ pub fn HashMapUnmanaged(
382 found_existing: bool,385 found_existing: bool,
383 };386 };
384387
385 pub const Managed = HashMap(K, V, hashFn, eqlFn, MaxLoadPercentage);388 pub const Managed = HashMap(K, V, hashFn, eqlFn, max_load_percentage);
386389
387 pub fn promote(self: Self, allocator: *Allocator) Managed {390 pub fn promote(self: Self, allocator: *Allocator) Managed {
388 return .{391 return .{
...@@ -392,7 +395,7 @@ pub fn HashMapUnmanaged(...@@ -392,7 +395,7 @@ pub fn HashMapUnmanaged(
392 }395 }
393396
394 fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool {397 fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool {
395 return size * 100 < MaxLoadPercentage * cap;398 return size * 100 < max_load_percentage * cap;
396 }399 }
397400
398 pub fn init(allocator: *Allocator) Self {401 pub fn init(allocator: *Allocator) Self {
...@@ -425,7 +428,7 @@ pub fn HashMapUnmanaged(...@@ -425,7 +428,7 @@ pub fn HashMapUnmanaged(
425 }428 }
426429
427 fn capacityForSize(size: Size) Size {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 new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable;432 new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable;
430 return new_cap;433 return new_cap;
431 }434 }
...@@ -439,7 +442,7 @@ pub fn HashMapUnmanaged(...@@ -439,7 +442,7 @@ pub fn HashMapUnmanaged(
439 if (self.metadata) |_| {442 if (self.metadata) |_| {
440 self.initMetadatas();443 self.initMetadatas();
441 self.size = 0;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 }
445448
...@@ -712,9 +715,9 @@ pub fn HashMapUnmanaged(...@@ -712,9 +715,9 @@ pub fn HashMapUnmanaged(
712 }715 }
713716
714 // This counts the number of occupied slots, used + tombstones, which is717 // 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 fn load(self: *const Self) Size {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 assert(max_load >= self.available);721 assert(max_load >= self.available);
719 return @truncate(Size, max_load - self.available);722 return @truncate(Size, max_load - self.available);
720 }723 }
...@@ -733,7 +736,7 @@ pub fn HashMapUnmanaged(...@@ -733,7 +736,7 @@ pub fn HashMapUnmanaged(
733 const new_cap = capacityForSize(self.size);736 const new_cap = capacityForSize(self.size);
734 try other.allocate(allocator, new_cap);737 try other.allocate(allocator, new_cap);
735 other.initMetadatas();738 other.initMetadatas();
736 other.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100);739 other.available = @truncate(u32, (new_cap * max_load_percentage) / 100);
737740
738 var i: Size = 0;741 var i: Size = 0;
739 var metadata = self.metadata.?;742 var metadata = self.metadata.?;
...@@ -751,7 +754,7 @@ pub fn HashMapUnmanaged(...@@ -751,7 +754,7 @@ pub fn HashMapUnmanaged(
751 }754 }
752755
753 fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void {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 assert(new_cap > self.capacity());758 assert(new_cap > self.capacity());
756 assert(std.math.isPowerOfTwo(new_cap));759 assert(std.math.isPowerOfTwo(new_cap));
757760
...@@ -759,7 +762,7 @@ pub fn HashMapUnmanaged(...@@ -759,7 +762,7 @@ pub fn HashMapUnmanaged(
759 defer map.deinit(allocator);762 defer map.deinit(allocator);
760 try map.allocate(allocator, new_cap);763 try map.allocate(allocator, new_cap);
761 map.initMetadatas();764 map.initMetadatas();
762 map.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100);765 map.available = @truncate(u32, (new_cap * max_load_percentage) / 100);
763766
764 if (self.size != 0) {767 if (self.size != 0) {
765 const old_capacity = self.capacity();768 const old_capacity = self.capacity();
...@@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" {...@@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" {
943946
944 try map.put(0, 0);947 try map.put(0, 0);
945 expectEqual(map.count(), 1);948 expectEqual(map.count(), 1);
946 expectEqual(map.capacity(), @TypeOf(map).Unmanaged.MinimalCapacity);949 expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity);
947950
948 try map.ensureCapacity(65);951 try map.ensureCapacity(65);
949 expectEqual(map.count(), 1);952 expectEqual(map.count(), 1);
src/Compilation.zig+2
...@@ -1653,6 +1653,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1653,6 +1653,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1653 .error_msg = null,1653 .error_msg = null,
1654 .decl = decl,1654 .decl = decl,
1655 .fwd_decl = fwd_decl.toManaged(module.gpa),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 defer dg.fwd_decl.deinit();1659 defer dg.fwd_decl.deinit();
16581660
src/codegen/c.zig+70-8
...@@ -32,6 +32,34 @@ pub const CValue = union(enum) {...@@ -32,6 +32,34 @@ pub const CValue = union(enum) {
32};32};
3333
34pub const CValueMap = std.AutoHashMap(*Inst, CValue);34pub const CValueMap = std.AutoHashMap(*Inst, CValue);
35pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
36
37fn 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
60pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {
61 return .{ .data = t };
62}
3563
36/// This data is available when outputting .c code for a Module.64/// This data is available when outputting .c code for a Module.
37/// It is not available when generating .h file.65/// It is not available when generating .h file.
...@@ -115,6 +143,7 @@ pub const DeclGen = struct {...@@ -115,6 +143,7 @@ pub const DeclGen = struct {
115 decl: *Decl,143 decl: *Decl,
116 fwd_decl: std.ArrayList(u8),144 fwd_decl: std.ArrayList(u8),
117 error_msg: ?*Module.ErrorMsg,145 error_msg: ?*Module.ErrorMsg,
146 typedefs: TypedefMap,
118147
119 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {148 fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
120 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{149 dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{
...@@ -325,22 +354,55 @@ pub const DeclGen = struct {...@@ -325,22 +354,55 @@ pub const DeclGen = struct {
325 const child_type = t.optionalChild(&opt_buf);354 const child_type = t.optionalChild(&opt_buf);
326 if (t.isPtrLikeOptional()) {355 if (t.isPtrLikeOptional()) {
327 return dg.renderType(w, child_type);356 return dg.renderType(w, child_type);
357 } else if (dg.typedefs.get(t)) |some| {
358 return w.writeAll(some.name);
328 }359 }
329360
330 // TODO this needs to be typedeffed since different structs are different types.361 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
331 try w.writeAll("struct { ");362 defer buffer.deinit();
332 try dg.renderType(w, child_type);363 const bw = buffer.writer();
333 try w.writeAll(" payload; bool is_null; }");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 .ErrorSet => {379 .ErrorSet => {
336 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);380 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2);
337 try w.writeAll("uint16_t");381 try w.writeAll("uint16_t");
338 },382 },
339 .ErrorUnion => {383 .ErrorUnion => {
340 // TODO this needs to be typedeffed since different structs are different types.384 if (dg.typedefs.get(t)) |some| {
341 try w.writeAll("struct { ");385 return w.writeAll(some.name);
342 try dg.renderType(w, t.errorUnionChild());386 }
343 try w.writeAll(" payload; uint16_t error; }");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 .Null, .Undefined => unreachable, // must be const or comptime407 .Null, .Undefined => unreachable, // must be const or comptime
346 else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{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,6 +9,7 @@ const codegen = @import("../codegen/c.zig");
9const link = @import("../link.zig");9const link = @import("../link.zig");
10const trace = @import("../tracy.zig").trace;10const trace = @import("../tracy.zig").trace;
11const C = @This();11const C = @This();
12const Type = @import("../type.zig").Type;
1213
13pub const base_tag: link.File.Tag = .c;14pub const base_tag: link.File.Tag = .c;
14pub const zig_h = @embedFile("C/zig.h");15pub const zig_h = @embedFile("C/zig.h");
...@@ -28,9 +29,11 @@ pub const DeclBlock = struct {...@@ -28,9 +29,11 @@ pub const DeclBlock = struct {
28/// Per-function data.29/// Per-function data.
29pub const FnBlock = struct {30pub const FnBlock = struct {
30 fwd_decl: std.ArrayListUnmanaged(u8),31 fwd_decl: std.ArrayListUnmanaged(u8),
32 typedefs: codegen.TypedefMap.Unmanaged,
3133
32 pub const empty: FnBlock = .{34 pub const empty: FnBlock = .{
33 .fwd_decl = .{},35 .fwd_decl = .{},
36 .typedefs = .{},
34 };37 };
35};38};
3639
...@@ -74,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}...@@ -74,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {}
74pub fn freeDecl(self: *C, decl: *Module.Decl) void {77pub fn freeDecl(self: *C, decl: *Module.Decl) void {
75 decl.link.c.code.deinit(self.base.allocator);78 decl.link.c.code.deinit(self.base.allocator);
76 decl.fn_link.c.fwd_decl.deinit(self.base.allocator);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}
7886
79pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {87pub 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,8 +89,10 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
81 defer tracy.end();89 defer tracy.end();
8290
83 const fwd_decl = &decl.fn_link.c.fwd_decl;91 const fwd_decl = &decl.fn_link.c.fwd_decl;
92 const typedefs = &decl.fn_link.c.typedefs;
84 const code = &decl.link.c.code;93 const code = &decl.link.c.code;
85 fwd_decl.shrinkRetainingCapacity(0);94 fwd_decl.shrinkRetainingCapacity(0);
95 typedefs.clearRetainingCapacity();
86 code.shrinkRetainingCapacity(0);96 code.shrinkRetainingCapacity(0);
8797
88 var object: codegen.Object = .{98 var object: codegen.Object = .{
...@@ -91,6 +101,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -91,6 +101,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
91 .error_msg = null,101 .error_msg = null,
92 .decl = decl,102 .decl = decl,
93 .fwd_decl = fwd_decl.toManaged(module.gpa),103 .fwd_decl = fwd_decl.toManaged(module.gpa),
104 .typedefs = typedefs.promote(module.gpa),
94 },105 },
95 .gpa = module.gpa,106 .gpa = module.gpa,
96 .code = code.toManaged(module.gpa),107 .code = code.toManaged(module.gpa),
...@@ -98,9 +109,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -98,9 +109,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
98 .indent_writer = undefined, // set later so we can get a pointer to object.code109 .indent_writer = undefined, // set later so we can get a pointer to object.code
99 };110 };
100 object.indent_writer = .{ .underlying_writer = object.code.writer() };111 object.indent_writer = .{ .underlying_writer = object.code.writer() };
101 defer object.value_map.deinit();112 defer {
102 defer object.code.deinit();113 object.value_map.deinit();
103 defer object.dg.fwd_decl.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 }
104122
105 codegen.genDecl(&object) catch |err| switch (err) {123 codegen.genDecl(&object) catch |err| switch (err) {
106 error.AnalysisFail => {124 error.AnalysisFail => {
...@@ -111,6 +129,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {...@@ -111,6 +129,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
111 };129 };
112130
113 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();131 fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged();
132 typedefs.* = object.dg.typedefs.unmanaged;
133 object.dg.typedefs.unmanaged = .{};
114 code.* = object.code.moveToUnmanaged();134 code.* = object.code.moveToUnmanaged();
115135
116 // Free excess allocated memory for this Decl.136 // Free excess allocated memory for this Decl.
...@@ -142,7 +162,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -142,7 +162,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
142 defer all_buffers.deinit();162 defer all_buffers.deinit();
143163
144 // This is at least enough until we get to the function bodies without error handling.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);
146166
147 var file_size: u64 = zig_h.len;167 var file_size: u64 = zig_h.len;
148 all_buffers.appendAssumeCapacity(.{168 all_buffers.appendAssumeCapacity(.{
...@@ -150,22 +170,25 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -150,22 +170,25 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
150 .iov_len = zig_h.len,170 .iov_len = zig_h.len,
151 });171 });
152172
153 var error_defs_buf = std.ArrayList(u8).init(comp.gpa);173 var err_typedef_buf = std.ArrayList(u8).init(comp.gpa);
154 defer error_defs_buf.deinit();174 defer err_typedef_buf.deinit();
175 const err_typedef_writer = err_typedef_buf.writer();
176 const err_typedef_item = all_buffers.addOneAssumeCapacity();
155177
156 var it = module.global_error_set.iterator();178 render_errors: {
157 while (it.next()) |entry| {179 if (module.global_error_set.size == 0) break :render_errors;
158 try error_defs_buf.writer().print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });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 });
165186
166 var fn_count: usize = 0;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();
167190
168 // Forward decls and non-functions first.191 // Typedefs, forward decls and non-functions first.
169 // TODO: performance investigation: would keeping a list of Decls that we should192 // TODO: performance investigation: would keeping a list of Decls that we should
170 // generate, rather than querying here, be faster?193 // generate, rather than querying here, be faster?
171 for (module.decl_table.items()) |kv| {194 for (module.decl_table.items()) |kv| {
...@@ -174,6 +197,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -174,6 +197,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
174 .most_recent => |tvm| {197 .most_recent => |tvm| {
175 const buf = buf: {198 const buf = buf: {
176 if (tvm.typed_value.val.castTag(.function)) |_| {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 fn_count += 1;210 fn_count += 1;
178 break :buf decl.fn_link.c.fwd_decl.items;211 break :buf decl.fn_link.c.fwd_decl.items;
179 } else {212 } else {
...@@ -190,6 +223,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -190,6 +223,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
190 }223 }
191 }224 }
192225
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 // Now the function bodies.232 // Now the function bodies.
194 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);233 try all_buffers.ensureCapacity(all_buffers.items.len + fn_count);
195 for (module.decl_table.items()) |kv| {234 for (module.decl_table.items()) |kv| {
src/test.zig+1-2
...@@ -868,11 +868,10 @@ pub const TestContext = struct {...@@ -868,11 +868,10 @@ pub const TestContext = struct {
868 std.testing.zig_exe_path,868 std.testing.zig_exe_path,
869 "run",869 "run",
870 "-cflags",870 "-cflags",
871 "-std=c89",871 "-std=c99",
872 "-pedantic",872 "-pedantic",
873 "-Werror",873 "-Werror",
874 "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875874 "-Wno-incompatible-library-redeclaration", // https://github.com/ziglang/zig/issues/875
875 "-Wno-declaration-after-statement",
876 "--",875 "--",
877 "-lc",876 "-lc",
878 exe_path,877 exe_path,
test/stage2/cbe.zig+12
...@@ -258,6 +258,18 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -258,6 +258,18 @@ pub fn addCases(ctx: *TestContext) !void {
258 \\ return count - 5;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 ctx.c("empty start function", linux_x64,274 ctx.c("empty start function", linux_x64,
263 \\export fn _start() noreturn {275 \\export fn _start() noreturn {