| author | |
| committer | |
| log | e9a038c33bbf171695b08540536f307b9e418173 |
| tree | d2ca77448fca354101e96040b83a7f7edf408647 |
| parent | a5cb4ab95e80c4f75356b80251c3628811956b19 |
| parent | fc62ff77c3921758624a81970f3098300992ee47 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Stage2 cbe: optionals and errors9 files changed, 447 insertions(+), 31 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 | } |
| 51 | 51 | ||
| 52 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { | 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 | pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type { | 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 | /// Builtin hashmap for strings as keys. | 60 | /// Builtin hashmap for strings as keys. |
| 61 | pub fn StringHashMap(comptime V: type) type { | 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 | pub fn StringHashMapUnmanaged(comptime V: type) type { | 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 | pub fn eqlString(a: []const u8, b: []const u8) bool { | 69 | pub 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 | } |
| 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 | /// 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, |
| 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 | 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); |
| 257 | 260 | ||
| 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 to | 277 | // 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 the | 279 | /// 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, |
| 279 | 282 | ||
| 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; |
| 283 | 286 | ||
| 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 | }; |
| 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 | 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 | } |
| 393 | 396 | ||
| 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 | } |
| 397 | 400 | ||
| 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 | } |
| 426 | 429 | ||
| 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 | } |
| 445 | 448 | ||
| ... | @@ -712,9 +715,9 @@ pub fn HashMapUnmanaged( | ... | @@ -712,9 +715,9 @@ pub fn HashMapUnmanaged( |
| 712 | } | 715 | } |
| 713 | 716 | ||
| 714 | // This counts the number of occupied slots, used + tombstones, which is | 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 | 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); |
| 737 | 740 | ||
| 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 | } |
| 752 | 755 | ||
| 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)); |
| 757 | 760 | ||
| ... | @@ -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); |
| 763 | 766 | ||
| 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" { |
| 943 | 946 | ||
| 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); |
| 947 | 950 | ||
| 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(); |
| 1658 | 1660 |
src/codegen.zig+2| ... | @@ -2267,6 +2267,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2267,6 +2267,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2267 | // No side effects, so if it's unreferenced, do nothing. | 2267 | // No side effects, so if it's unreferenced, do nothing. |
| 2268 | if (inst.base.isUnused()) | 2268 | if (inst.base.isUnused()) |
| 2269 | return MCValue{ .dead = {} }; | 2269 | return MCValue{ .dead = {} }; |
| 2270 | if (inst.lhs.ty.zigTypeTag() == .ErrorSet or inst.rhs.ty.zigTypeTag() == .ErrorSet) | ||
| 2271 | return self.fail(inst.base.src, "TODO implement cmp for errors", .{}); | ||
| 2270 | switch (arch) { | 2272 | switch (arch) { |
| 2271 | .x86_64 => { | 2273 | .x86_64 => { |
| 2272 | try self.code.ensureCapacity(self.code.items.len + 8); | 2274 | try self.code.ensureCapacity(self.code.items.len + 8); |
src/codegen/c.zig+271-1| ... | @@ -32,6 +32,34 @@ pub const CValue = union(enum) { | ... | @@ -32,6 +32,34 @@ pub const CValue = union(enum) { |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); | 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 | /// 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, | ||
| 118 | 147 | ||
| 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, .{ |
| ... | @@ -140,7 +169,7 @@ pub const DeclGen = struct { | ... | @@ -140,7 +169,7 @@ pub const DeclGen = struct { |
| 140 | return writer.print("{d}", .{val.toUnsignedInt()}); | 169 | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 141 | }, | 170 | }, |
| 142 | .Pointer => switch (val.tag()) { | 171 | .Pointer => switch (val.tag()) { |
| 143 | .undef, .zero => try writer.writeAll("0"), | 172 | .null_value, .zero => try writer.writeAll("NULL"), |
| 144 | .one => try writer.writeAll("1"), | 173 | .one => try writer.writeAll("1"), |
| 145 | .decl_ref => { | 174 | .decl_ref => { |
| 146 | const decl = val.castTag(.decl_ref).?.data; | 175 | const decl = val.castTag(.decl_ref).?.data; |
| ... | @@ -201,6 +230,52 @@ pub const DeclGen = struct { | ... | @@ -201,6 +230,52 @@ pub const DeclGen = struct { |
| 201 | } | 230 | } |
| 202 | }, | 231 | }, |
| 203 | .Bool => return writer.print("{}", .{val.toBool()}), | 232 | .Bool => return writer.print("{}", .{val.toBool()}), |
| 233 | .Optional => { | ||
| 234 | var opt_buf: Type.Payload.ElemType = undefined; | ||
| 235 | const child_type = t.optionalChild(&opt_buf); | ||
| 236 | if (t.isPtrLikeOptional()) { | ||
| 237 | return dg.renderValue(writer, child_type, val); | ||
| 238 | } | ||
| 239 | try writer.writeByte('('); | ||
| 240 | try dg.renderType(writer, t); | ||
| 241 | if (val.tag() == .null_value) { | ||
| 242 | try writer.writeAll("){ .is_null = true }"); | ||
| 243 | } else { | ||
| 244 | try writer.writeAll("){ .is_null = false, .payload = "); | ||
| 245 | try dg.renderValue(writer, child_type, val); | ||
| 246 | try writer.writeAll(" }"); | ||
| 247 | } | ||
| 248 | }, | ||
| 249 | .ErrorSet => { | ||
| 250 | const payload = val.castTag(.@"error").?; | ||
| 251 | // error values will be #defined at the top of the file | ||
| 252 | return writer.print("zig_error_{s}", .{payload.data.name}); | ||
| 253 | }, | ||
| 254 | .ErrorUnion => { | ||
| 255 | const error_type = t.errorUnionSet(); | ||
| 256 | const payload_type = t.errorUnionChild(); | ||
| 257 | const data = val.castTag(.error_union).?.data; | ||
| 258 | try writer.writeByte('('); | ||
| 259 | try dg.renderType(writer, t); | ||
| 260 | try writer.writeAll("){"); | ||
| 261 | if (val.getError()) |_| { | ||
| 262 | try writer.writeAll(" .error = "); | ||
| 263 | try dg.renderValue( | ||
| 264 | writer, | ||
| 265 | error_type, | ||
| 266 | data, | ||
| 267 | ); | ||
| 268 | try writer.writeAll(" }"); | ||
| 269 | } else { | ||
| 270 | try writer.writeAll(" .payload = "); | ||
| 271 | try dg.renderValue( | ||
| 272 | writer, | ||
| 273 | payload_type, | ||
| 274 | data, | ||
| 275 | ); | ||
| 276 | try writer.writeAll(", .error = 0 }"); | ||
| 277 | } | ||
| 278 | }, | ||
| 204 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ | 279 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ |
| 205 | @tagName(e), | 280 | @tagName(e), |
| 206 | }), | 281 | }), |
| ... | @@ -299,6 +374,62 @@ pub const DeclGen = struct { | ... | @@ -299,6 +374,62 @@ pub const DeclGen = struct { |
| 299 | try dg.renderType(w, t.elemType()); | 374 | try dg.renderType(w, t.elemType()); |
| 300 | try w.writeAll(" *"); | 375 | try w.writeAll(" *"); |
| 301 | }, | 376 | }, |
| 377 | .Optional => { | ||
| 378 | var opt_buf: Type.Payload.ElemType = undefined; | ||
| 379 | const child_type = t.optionalChild(&opt_buf); | ||
| 380 | if (t.isPtrLikeOptional()) { | ||
| 381 | return dg.renderType(w, child_type); | ||
| 382 | } else if (dg.typedefs.get(t)) |some| { | ||
| 383 | return w.writeAll(some.name); | ||
| 384 | } | ||
| 385 | |||
| 386 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 387 | defer buffer.deinit(); | ||
| 388 | const bw = buffer.writer(); | ||
| 389 | |||
| 390 | try bw.writeAll("typedef struct { "); | ||
| 391 | try dg.renderType(bw, child_type); | ||
| 392 | try bw.writeAll(" payload; bool is_null; } "); | ||
| 393 | const name_index = buffer.items.len; | ||
| 394 | try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); | ||
| 395 | |||
| 396 | const rendered = buffer.toOwnedSlice(); | ||
| 397 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 398 | const name = rendered[name_index .. rendered.len - 2]; | ||
| 399 | |||
| 400 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | ||
| 401 | try w.writeAll(name); | ||
| 402 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | ||
| 403 | }, | ||
| 404 | .ErrorSet => { | ||
| 405 | comptime std.debug.assert(Type.initTag(.anyerror).abiSize(std.Target.current) == 2); | ||
| 406 | try w.writeAll("uint16_t"); | ||
| 407 | }, | ||
| 408 | .ErrorUnion => { | ||
| 409 | if (dg.typedefs.get(t)) |some| { | ||
| 410 | return w.writeAll(some.name); | ||
| 411 | } | ||
| 412 | const child_type = t.errorUnionChild(); | ||
| 413 | const set_type = t.errorUnionSet(); | ||
| 414 | |||
| 415 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 416 | defer buffer.deinit(); | ||
| 417 | const bw = buffer.writer(); | ||
| 418 | |||
| 419 | try bw.writeAll("typedef struct { "); | ||
| 420 | try dg.renderType(bw, child_type); | ||
| 421 | try bw.writeAll(" payload; uint16_t error; } "); | ||
| 422 | const name_index = buffer.items.len; | ||
| 423 | try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) }); | ||
| 424 | |||
| 425 | const rendered = buffer.toOwnedSlice(); | ||
| 426 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 427 | const name = rendered[name_index .. rendered.len - 2]; | ||
| 428 | |||
| 429 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | ||
| 430 | try w.writeAll(name); | ||
| 431 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | ||
| 432 | }, | ||
| 302 | .Null, .Undefined => unreachable, // must be const or comptime | 433 | .Null, .Undefined => unreachable, // must be const or comptime |
| 303 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ | 434 | else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ |
| 304 | @tagName(e), | 435 | @tagName(e), |
| ... | @@ -429,6 +560,21 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi | ... | @@ -429,6 +560,21 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 429 | .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "), | 560 | .bit_or => try genBinOp(o, inst.castTag(.bit_or).?, " | "), |
| 430 | .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), | 561 | .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), |
| 431 | .not => try genUnOp(o, inst.castTag(.not).?, "!"), | 562 | .not => try genUnOp(o, inst.castTag(.not).?, "!"), |
| 563 | .is_null => try genIsNull(o, inst.castTag(.is_null).?), | ||
| 564 | .is_non_null => try genIsNull(o, inst.castTag(.is_non_null).?), | ||
| 565 | .is_null_ptr => try genIsNull(o, inst.castTag(.is_null_ptr).?), | ||
| 566 | .is_non_null_ptr => try genIsNull(o, inst.castTag(.is_non_null_ptr).?), | ||
| 567 | .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), | ||
| 568 | .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), | ||
| 569 | .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), | ||
| 570 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), | ||
| 571 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), | ||
| 572 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), | ||
| 573 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), | ||
| 574 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), | ||
| 575 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), | ||
| 576 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), | ||
| 577 | .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), | ||
| 432 | else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), | 578 | else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), |
| 433 | }; | 579 | }; |
| 434 | switch (result_value) { | 580 | switch (result_value) { |
| ... | @@ -802,6 +948,130 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { | ... | @@ -802,6 +948,130 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { |
| 802 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); | 948 | return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{}); |
| 803 | } | 949 | } |
| 804 | 950 | ||
| 951 | fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 952 | const writer = o.writer(); | ||
| 953 | const invert_logic = inst.base.tag == .is_non_null or inst.base.tag == .is_non_null_ptr; | ||
| 954 | const operator = if (invert_logic) "!=" else "=="; | ||
| 955 | const maybe_deref = if (inst.base.tag == .is_null_ptr or inst.base.tag == .is_non_null_ptr) "[0]" else ""; | ||
| 956 | const operand = try o.resolveInst(inst.operand); | ||
| 957 | |||
| 958 | const local = try o.allocLocal(Type.initTag(.bool), .Const); | ||
| 959 | try writer.writeAll(" = ("); | ||
| 960 | try o.writeCValue(writer, operand); | ||
| 961 | |||
| 962 | if (inst.operand.ty.isPtrLikeOptional()) { | ||
| 963 | // operand is a regular pointer, test `operand !=/== NULL` | ||
| 964 | try writer.print("){s} {s} NULL;\n", .{ maybe_deref, operator }); | ||
| 965 | } else { | ||
| 966 | try writer.print("){s}.is_null {s} true;\n", .{ maybe_deref, operator }); | ||
| 967 | } | ||
| 968 | return local; | ||
| 969 | } | ||
| 970 | |||
| 971 | fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 972 | const writer = o.writer(); | ||
| 973 | const operand = try o.resolveInst(inst.operand); | ||
| 974 | |||
| 975 | const opt_ty = if (inst.operand.ty.zigTypeTag() == .Pointer) | ||
| 976 | inst.operand.ty.elemType() | ||
| 977 | else | ||
| 978 | inst.operand.ty; | ||
| 979 | |||
| 980 | if (opt_ty.isPtrLikeOptional()) { | ||
| 981 | // the operand is just a regular pointer, no need to do anything special. | ||
| 982 | // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C | ||
| 983 | return operand; | ||
| 984 | } | ||
| 985 | |||
| 986 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; | ||
| 987 | const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; | ||
| 988 | |||
| 989 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 990 | try writer.print(" = {s}(", .{maybe_addrof}); | ||
| 991 | try o.writeCValue(writer, operand); | ||
| 992 | |||
| 993 | try writer.print("){s}payload;\n", .{maybe_deref}); | ||
| 994 | return local; | ||
| 995 | } | ||
| 996 | |||
| 997 | // *(E!T) -> E NOT *E | ||
| 998 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 999 | const writer = o.writer(); | ||
| 1000 | const operand = try o.resolveInst(inst.operand); | ||
| 1001 | |||
| 1002 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; | ||
| 1003 | |||
| 1004 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1005 | try writer.writeAll(" = ("); | ||
| 1006 | try o.writeCValue(writer, operand); | ||
| 1007 | |||
| 1008 | try writer.print("){s}error;\n", .{maybe_deref}); | ||
| 1009 | return local; | ||
| 1010 | } | ||
| 1011 | fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 1012 | const writer = o.writer(); | ||
| 1013 | const operand = try o.resolveInst(inst.operand); | ||
| 1014 | |||
| 1015 | const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else "."; | ||
| 1016 | const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else ""; | ||
| 1017 | |||
| 1018 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1019 | try writer.print(" = {s}(", .{maybe_addrof}); | ||
| 1020 | try o.writeCValue(writer, operand); | ||
| 1021 | |||
| 1022 | try writer.print("){s}payload;\n", .{maybe_deref}); | ||
| 1023 | return local; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | fn genWrapOptional(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 1027 | const writer = o.writer(); | ||
| 1028 | const operand = try o.resolveInst(inst.operand); | ||
| 1029 | |||
| 1030 | if (inst.base.ty.isPtrLikeOptional()) { | ||
| 1031 | // the operand is just a regular pointer, no need to do anything special. | ||
| 1032 | return operand; | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | // .wrap_optional is used to convert non-optionals into optionals so it can never be null. | ||
| 1036 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1037 | try writer.writeAll(" = { .is_null = false, .payload ="); | ||
| 1038 | try o.writeCValue(writer, operand); | ||
| 1039 | try writer.writeAll("};\n"); | ||
| 1040 | return local; | ||
| 1041 | } | ||
| 1042 | fn genWrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 1043 | const writer = o.writer(); | ||
| 1044 | const operand = try o.resolveInst(inst.operand); | ||
| 1045 | |||
| 1046 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1047 | try writer.writeAll(" = { .error = "); | ||
| 1048 | try o.writeCValue(writer, operand); | ||
| 1049 | try writer.writeAll(" };\n"); | ||
| 1050 | return local; | ||
| 1051 | } | ||
| 1052 | fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 1053 | const writer = o.writer(); | ||
| 1054 | const operand = try o.resolveInst(inst.operand); | ||
| 1055 | |||
| 1056 | const local = try o.allocLocal(inst.base.ty, .Const); | ||
| 1057 | try writer.writeAll(" = { .error = 0, .payload = "); | ||
| 1058 | try o.writeCValue(writer, operand); | ||
| 1059 | try writer.writeAll(" };\n"); | ||
| 1060 | return local; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { | ||
| 1064 | const writer = o.writer(); | ||
| 1065 | const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; | ||
| 1066 | const operand = try o.resolveInst(inst.operand); | ||
| 1067 | |||
| 1068 | const local = try o.allocLocal(Type.initTag(.bool), .Const); | ||
| 1069 | try writer.writeAll(" = ("); | ||
| 1070 | try o.writeCValue(writer, operand); | ||
| 1071 | try writer.print("){s}.error != 0;\n", .{maybe_deref}); | ||
| 1072 | return local; | ||
| 1073 | } | ||
| 1074 | |||
| 805 | fn IndentWriter(comptime UnderlyingWriter: type) type { | 1075 | fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 806 | return struct { | 1076 | return struct { |
| 807 | const Self = @This(); | 1077 | const Self = @This(); |
src/link/C.zig+64-5| ... | @@ -9,6 +9,7 @@ const codegen = @import("../codegen/c.zig"); | ... | @@ -9,6 +9,7 @@ const codegen = @import("../codegen/c.zig"); |
| 9 | const link = @import("../link.zig"); | 9 | const link = @import("../link.zig"); |
| 10 | const trace = @import("../tracy.zig").trace; | 10 | const trace = @import("../tracy.zig").trace; |
| 11 | const C = @This(); | 11 | const C = @This(); |
| 12 | const Type = @import("../type.zig").Type; | ||
| 12 | 13 | ||
| 13 | pub const base_tag: link.File.Tag = .c; | 14 | pub const base_tag: link.File.Tag = .c; |
| 14 | pub const zig_h = @embedFile("C/zig.h"); | 15 | pub 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. |
| 29 | pub const FnBlock = struct { | 30 | pub const FnBlock = struct { |
| 30 | fwd_decl: std.ArrayListUnmanaged(u8), | 31 | fwd_decl: std.ArrayListUnmanaged(u8), |
| 32 | typedefs: codegen.TypedefMap.Unmanaged, | ||
| 31 | 33 | ||
| 32 | pub const empty: FnBlock = .{ | 34 | pub const empty: FnBlock = .{ |
| 33 | .fwd_decl = .{}, | 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,6 +77,11 @@ pub fn allocateDeclIndexes(self: *C, decl: *Module.Decl) !void {} |
| 74 | pub fn freeDecl(self: *C, decl: *Module.Decl) void { | 77 | pub 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 | } |
| 78 | 86 | ||
| 79 | pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | 87 | pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| ... | @@ -81,8 +89,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | ... | @@ -81,8 +89,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 81 | defer tracy.end(); | 89 | defer tracy.end(); |
| 82 | 90 | ||
| 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 | { | ||
| 96 | var it = typedefs.iterator(); | ||
| 97 | while (it.next()) |entry| { | ||
| 98 | module.gpa.free(entry.value.rendered); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | typedefs.clearRetainingCapacity(); | ||
| 86 | code.shrinkRetainingCapacity(0); | 102 | code.shrinkRetainingCapacity(0); |
| 87 | 103 | ||
| 88 | var object: codegen.Object = .{ | 104 | var object: codegen.Object = .{ |
| ... | @@ -91,6 +107,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | ... | @@ -91,6 +107,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 91 | .error_msg = null, | 107 | .error_msg = null, |
| 92 | .decl = decl, | 108 | .decl = decl, |
| 93 | .fwd_decl = fwd_decl.toManaged(module.gpa), | 109 | .fwd_decl = fwd_decl.toManaged(module.gpa), |
| 110 | .typedefs = typedefs.promote(module.gpa), | ||
| 94 | }, | 111 | }, |
| 95 | .gpa = module.gpa, | 112 | .gpa = module.gpa, |
| 96 | .code = code.toManaged(module.gpa), | 113 | .code = code.toManaged(module.gpa), |
| ... | @@ -98,9 +115,16 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | ... | @@ -98,9 +115,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.code | 115 | .indent_writer = undefined, // set later so we can get a pointer to object.code |
| 99 | }; | 116 | }; |
| 100 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; | 117 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 101 | defer object.value_map.deinit(); | 118 | defer { |
| 102 | defer object.code.deinit(); | 119 | object.value_map.deinit(); |
| 103 | defer object.dg.fwd_decl.deinit(); | 120 | object.code.deinit(); |
| 121 | object.dg.fwd_decl.deinit(); | ||
| 122 | var it = object.dg.typedefs.iterator(); | ||
| 123 | while (it.next()) |some| { | ||
| 124 | module.gpa.free(some.value.rendered); | ||
| 125 | } | ||
| 126 | object.dg.typedefs.deinit(); | ||
| 127 | } | ||
| 104 | 128 | ||
| 105 | codegen.genDecl(&object) catch |err| switch (err) { | 129 | codegen.genDecl(&object) catch |err| switch (err) { |
| 106 | error.AnalysisFail => { | 130 | error.AnalysisFail => { |
| ... | @@ -111,6 +135,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { | ... | @@ -111,6 +135,8 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 111 | }; | 135 | }; |
| 112 | 136 | ||
| 113 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); | 137 | fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); |
| 138 | typedefs.* = object.dg.typedefs.unmanaged; | ||
| 139 | object.dg.typedefs.unmanaged = .{}; | ||
| 114 | code.* = object.code.moveToUnmanaged(); | 140 | code.* = object.code.moveToUnmanaged(); |
| 115 | 141 | ||
| 116 | // Free excess allocated memory for this Decl. | 142 | // Free excess allocated memory for this Decl. |
| ... | @@ -142,7 +168,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -142,7 +168,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 142 | defer all_buffers.deinit(); | 168 | defer all_buffers.deinit(); |
| 143 | 169 | ||
| 144 | // This is at least enough until we get to the function bodies without error handling. | 170 | // 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); | 171 | try all_buffers.ensureCapacity(module.decl_table.count() + 2); |
| 146 | 172 | ||
| 147 | var file_size: u64 = zig_h.len; | 173 | var file_size: u64 = zig_h.len; |
| 148 | all_buffers.appendAssumeCapacity(.{ | 174 | all_buffers.appendAssumeCapacity(.{ |
| ... | @@ -150,9 +176,26 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -150,9 +176,26 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 150 | .iov_len = zig_h.len, | 176 | .iov_len = zig_h.len, |
| 151 | }); | 177 | }); |
| 152 | 178 | ||
| 179 | var err_typedef_buf = std.ArrayList(u8).init(comp.gpa); | ||
| 180 | defer err_typedef_buf.deinit(); | ||
| 181 | const err_typedef_writer = err_typedef_buf.writer(); | ||
| 182 | const err_typedef_item = all_buffers.addOneAssumeCapacity(); | ||
| 183 | |||
| 184 | render_errors: { | ||
| 185 | if (module.global_error_set.size == 0) break :render_errors; | ||
| 186 | var it = module.global_error_set.iterator(); | ||
| 187 | while (it.next()) |entry| { | ||
| 188 | // + 1 because 0 represents no error | ||
| 189 | try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 }); | ||
| 190 | } | ||
| 191 | try err_typedef_writer.writeByte('\n'); | ||
| 192 | } | ||
| 193 | |||
| 153 | var fn_count: usize = 0; | 194 | var fn_count: usize = 0; |
| 195 | var typedefs = std.HashMap(Type, []const u8, Type.hash, Type.eql, std.hash_map.default_max_load_percentage).init(comp.gpa); | ||
| 196 | defer typedefs.deinit(); | ||
| 154 | 197 | ||
| 155 | // Forward decls and non-functions first. | 198 | // Typedefs, forward decls and non-functions first. |
| 156 | // TODO: performance investigation: would keeping a list of Decls that we should | 199 | // TODO: performance investigation: would keeping a list of Decls that we should |
| 157 | // generate, rather than querying here, be faster? | 200 | // generate, rather than querying here, be faster? |
| 158 | for (module.decl_table.items()) |kv| { | 201 | for (module.decl_table.items()) |kv| { |
| ... | @@ -161,6 +204,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -161,6 +204,16 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 161 | .most_recent => |tvm| { | 204 | .most_recent => |tvm| { |
| 162 | const buf = buf: { | 205 | const buf = buf: { |
| 163 | if (tvm.typed_value.val.castTag(.function)) |_| { | 206 | if (tvm.typed_value.val.castTag(.function)) |_| { |
| 207 | var it = decl.fn_link.c.typedefs.iterator(); | ||
| 208 | while (it.next()) |new| { | ||
| 209 | if (typedefs.get(new.key)) |previous| { | ||
| 210 | try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value.name }); | ||
| 211 | } else { | ||
| 212 | try typedefs.ensureCapacity(typedefs.capacity() + 1); | ||
| 213 | try err_typedef_writer.writeAll(new.value.rendered); | ||
| 214 | typedefs.putAssumeCapacityNoClobber(new.key, new.value.name); | ||
| 215 | } | ||
| 216 | } | ||
| 164 | fn_count += 1; | 217 | fn_count += 1; |
| 165 | break :buf decl.fn_link.c.fwd_decl.items; | 218 | break :buf decl.fn_link.c.fwd_decl.items; |
| 166 | } else { | 219 | } else { |
| ... | @@ -177,6 +230,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -177,6 +230,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 177 | } | 230 | } |
| 178 | } | 231 | } |
| 179 | 232 | ||
| 233 | err_typedef_item.* = .{ | ||
| 234 | .iov_base = err_typedef_buf.items.ptr, | ||
| 235 | .iov_len = err_typedef_buf.items.len, | ||
| 236 | }; | ||
| 237 | file_size += err_typedef_buf.items.len; | ||
| 238 | |||
| 180 | // Now the function bodies. | 239 | // Now the function bodies. |
| 181 | try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); | 240 | try all_buffers.ensureCapacity(all_buffers.items.len + fn_count); |
| 182 | for (module.decl_table.items()) |kv| { | 241 | 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/875 | 874 | "-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, |
src/type.zig+24-1| ... | @@ -1686,8 +1686,8 @@ pub const Type = extern union { | ... | @@ -1686,8 +1686,8 @@ pub const Type = extern union { |
| 1686 | return ty.optionalChild(&buf).isValidVarType(is_extern); | 1686 | return ty.optionalChild(&buf).isValidVarType(is_extern); |
| 1687 | }, | 1687 | }, |
| 1688 | .Pointer, .Array => ty = ty.elemType(), | 1688 | .Pointer, .Array => ty = ty.elemType(), |
| 1689 | .ErrorUnion => ty = ty.errorUnionChild(), | ||
| 1689 | 1690 | ||
| 1690 | .ErrorUnion => @panic("TODO fn isValidVarType"), | ||
| 1691 | .Fn => @panic("TODO fn isValidVarType"), | 1691 | .Fn => @panic("TODO fn isValidVarType"), |
| 1692 | .Struct => @panic("TODO struct isValidVarType"), | 1692 | .Struct => @panic("TODO struct isValidVarType"), |
| 1693 | .Union => @panic("TODO union isValidVarType"), | 1693 | .Union => @panic("TODO union isValidVarType"), |
| ... | @@ -1813,6 +1813,29 @@ pub const Type = extern union { | ... | @@ -1813,6 +1813,29 @@ pub const Type = extern union { |
| 1813 | } | 1813 | } |
| 1814 | } | 1814 | } |
| 1815 | 1815 | ||
| 1816 | /// Asserts that the type is an error union. | ||
| 1817 | pub fn errorUnionChild(self: Type) Type { | ||
| 1818 | return switch (self.tag()) { | ||
| 1819 | .anyerror_void_error_union => Type.initTag(.anyerror), | ||
| 1820 | .error_union => { | ||
| 1821 | const payload = self.castTag(.error_union).?; | ||
| 1822 | return payload.data.payload; | ||
| 1823 | }, | ||
| 1824 | else => unreachable, | ||
| 1825 | }; | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | pub fn errorUnionSet(self: Type) Type { | ||
| 1829 | return switch (self.tag()) { | ||
| 1830 | .anyerror_void_error_union => Type.initTag(.anyerror), | ||
| 1831 | .error_union => { | ||
| 1832 | const payload = self.castTag(.error_union).?; | ||
| 1833 | return payload.data.error_set; | ||
| 1834 | }, | ||
| 1835 | else => unreachable, | ||
| 1836 | }; | ||
| 1837 | } | ||
| 1838 | |||
| 1816 | /// Asserts the type is an array or vector. | 1839 | /// Asserts the type is an array or vector. |
| 1817 | pub fn arrayLen(self: Type) u64 { | 1840 | pub fn arrayLen(self: Type) u64 { |
| 1818 | return switch (self.tag()) { | 1841 | return switch (self.tag()) { |
src/zir_sema.zig+2-1| ... | @@ -2329,7 +2329,8 @@ fn zirCmp( | ... | @@ -2329,7 +2329,8 @@ fn zirCmp( |
| 2329 | return mod.constBool(scope, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | 2329 | return mod.constBool(scope, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); |
| 2330 | } | 2330 | } |
| 2331 | } | 2331 | } |
| 2332 | return mod.fail(scope, inst.base.src, "TODO implement equality comparison between runtime errors", .{}); | 2332 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); |
| 2333 | return mod.addBinOp(b, inst.base.src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs); | ||
| 2333 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | 2334 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { |
| 2334 | // This operation allows any combination of integer and float types, regardless of the | 2335 | // This operation allows any combination of integer and float types, regardless of the |
| 2335 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | 2336 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for |
test/stage2/cbe.zig+57| ... | @@ -244,6 +244,63 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -244,6 +244,63 @@ pub fn addCases(ctx: *TestContext) !void { |
| 244 | \\} | 244 | \\} |
| 245 | , ""); | 245 | , ""); |
| 246 | } | 246 | } |
| 247 | //{ | ||
| 248 | // var case = ctx.exeFromCompiledC("optionals", .{}); | ||
| 249 | |||
| 250 | // // Simple while loop | ||
| 251 | // case.addCompareOutput( | ||
| 252 | // \\export fn main() c_int { | ||
| 253 | // \\ var count: c_int = 0; | ||
| 254 | // \\ var opt_ptr: ?*c_int = &count; | ||
| 255 | // \\ while (opt_ptr) |_| : (count += 1) { | ||
| 256 | // \\ if (count == 4) opt_ptr = null; | ||
| 257 | // \\ } | ||
| 258 | // \\ return count - 5; | ||
| 259 | // \\} | ||
| 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 | // , ""); | ||
| 273 | //} | ||
| 274 | { | ||
| 275 | var case = ctx.exeFromCompiledC("errors", .{}); | ||
| 276 | case.addCompareOutput( | ||
| 277 | \\export fn main() c_int { | ||
| 278 | \\ var e1 = error.Foo; | ||
| 279 | \\ var e2 = error.Bar; | ||
| 280 | \\ assert(e1 != e2); | ||
| 281 | \\ assert(e1 == error.Foo); | ||
| 282 | \\ assert(e2 == error.Bar); | ||
| 283 | \\ return 0; | ||
| 284 | \\} | ||
| 285 | \\fn assert(b: bool) void { | ||
| 286 | \\ if (!b) unreachable; | ||
| 287 | \\} | ||
| 288 | , ""); | ||
| 289 | case.addCompareOutput( | ||
| 290 | \\export fn main() c_int { | ||
| 291 | \\ var e: anyerror!c_int = 0; | ||
| 292 | \\ const i = e catch 69; | ||
| 293 | \\ return i; | ||
| 294 | \\} | ||
| 295 | , ""); | ||
| 296 | case.addCompareOutput( | ||
| 297 | \\export fn main() c_int { | ||
| 298 | \\ var e: anyerror!c_int = error.Foo; | ||
| 299 | \\ const i = e catch 69; | ||
| 300 | \\ return 69 - i; | ||
| 301 | \\} | ||
| 302 | , ""); | ||
| 303 | } | ||
| 247 | ctx.c("empty start function", linux_x64, | 304 | ctx.c("empty start function", linux_x64, |
| 248 | \\export fn _start() noreturn { | 305 | \\export fn _start() noreturn { |
| 249 | \\ unreachable; | 306 | \\ unreachable; |