authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-28 15:39:43-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-28 15:39:43-05:00
log2dd5e8b6f865a24498da06a9a0ce3609d23662c9
tree96de992b22f7cfc4bcb672892c64fb2cdf621913
parente1375942e584d4fffd3ecd06277ced7cf35d7e6c
parentd5100dc81555f0e8197d5f189b1432070e8d72dd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11011 from Vexu/stage2

stage2: tuple/slice mul/cat

17 files changed, 331 insertions(+), 70 deletions(-)

lib/std/hash_map.zig+2-2
...@@ -738,7 +738,7 @@ pub fn HashMapUnmanaged(...@@ -738,7 +738,7 @@ pub fn HashMapUnmanaged(
738 value: V,738 value: V,
739 };739 };
740740
741 const Header = packed struct {741 const Header = struct {
742 values: [*]V,742 values: [*]V,
743 keys: [*]K,743 keys: [*]K,
744 capacity: Size,744 capacity: Size,
...@@ -932,7 +932,7 @@ pub fn HashMapUnmanaged(...@@ -932,7 +932,7 @@ pub fn HashMapUnmanaged(
932 }932 }
933933
934 fn header(self: *const Self) *Header {934 fn header(self: *const Self) *Header {
935 return @ptrCast(*Header, @ptrCast([*]Header, self.metadata.?) - 1);935 return @ptrCast(*Header, @ptrCast([*]Header, @alignCast(@alignOf(Header), self.metadata.?)) - 1);
936 }936 }
937937
938 fn keys(self: *const Self) [*]K {938 fn keys(self: *const Self) [*]K {
lib/std/heap/general_purpose_allocator.zig+19-6
...@@ -341,9 +341,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -341,9 +341,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
341 const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index);341 const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index);
342 const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);342 const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);
343 const addr = bucket.page + slot_index * size_class;343 const addr = bucket.page + slot_index * size_class;
344 log.err("memory address 0x{x} leaked: {s}", .{344 if (builtin.zig_backend == .stage1) {
345 @ptrToInt(addr), stack_trace,345 log.err("memory address 0x{x} leaked: {s}", .{
346 });346 @ptrToInt(addr), stack_trace,
347 });
348 } else { // TODO
349 log.err("memory address 0x{x} leaked", .{
350 @ptrToInt(addr),
351 });
352 }
347 leaks = true;353 leaks = true;
348 }354 }
349 if (bit_index == math.maxInt(u3))355 if (bit_index == math.maxInt(u3))
...@@ -372,9 +378,16 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -372,9 +378,16 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
372 var it = self.large_allocations.valueIterator();378 var it = self.large_allocations.valueIterator();
373 while (it.next()) |large_alloc| {379 while (it.next()) |large_alloc| {
374 if (config.retain_metadata and large_alloc.freed) continue;380 if (config.retain_metadata and large_alloc.freed) continue;
375 log.err("memory address 0x{x} leaked: {s}", .{381 const stack_trace = large_alloc.getStackTrace(.alloc);
376 @ptrToInt(large_alloc.bytes.ptr), large_alloc.getStackTrace(.alloc),382 if (builtin.zig_backend == .stage1) {
377 });383 log.err("memory address 0x{x} leaked: {s}", .{
384 @ptrToInt(large_alloc.bytes.ptr), stack_trace,
385 });
386 } else { // TODO
387 log.err("memory address 0x{x} leaked", .{
388 @ptrToInt(large_alloc.bytes.ptr),
389 });
390 }
378 leaks = true;391 leaks = true;
379 }392 }
380 return leaks;393 return leaks;
lib/std/special/test_runner.zig+3-2
...@@ -46,9 +46,10 @@ pub fn main() void {...@@ -46,9 +46,10 @@ pub fn main() void {
4646
47 var leaks: usize = 0;47 var leaks: usize = 0;
48 for (test_fn_list) |test_fn, i| {48 for (test_fn_list) |test_fn, i| {
49 if (builtin.zig_backend != .stage2_llvm) std.testing.allocator_instance = .{};49 const gpa_works = builtin.zig_backend == .stage1 or builtin.os.tag != .macos;
50 if (gpa_works) std.testing.allocator_instance = .{};
50 defer {51 defer {
51 if (builtin.zig_backend != .stage2_llvm and std.testing.allocator_instance.deinit()) {52 if (gpa_works and std.testing.allocator_instance.deinit()) {
52 leaks += 1;53 leaks += 1;
53 }54 }
54 }55 }
src/Air.zig+4
...@@ -218,6 +218,9 @@ pub const Inst = struct {...@@ -218,6 +218,9 @@ pub const Inst = struct {
218 /// Yields the return address of the current function.218 /// Yields the return address of the current function.
219 /// Uses the `no_op` field.219 /// Uses the `no_op` field.
220 ret_addr,220 ret_addr,
221 /// Implements @frameAddress builtin.
222 /// Uses the `no_op` field.
223 frame_addr,
221 /// Function call.224 /// Function call.
222 /// Result type is the return type of the function being called.225 /// Result type is the return type of the function being called.
223 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.226 /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
...@@ -939,6 +942,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -939,6 +942,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
939 .ptrtoint,942 .ptrtoint,
940 .slice_len,943 .slice_len,
941 .ret_addr,944 .ret_addr,
945 .frame_addr,
942 => return Type.initTag(.usize),946 => return Type.initTag(.usize),
943947
944 .bool_to_int => return Type.initTag(.u1),948 .bool_to_int => return Type.initTag(.u1),
src/Liveness.zig+1
...@@ -317,6 +317,7 @@ fn analyzeInst(...@@ -317,6 +317,7 @@ fn analyzeInst(
317 .unreach,317 .unreach,
318 .fence,318 .fence,
319 .ret_addr,319 .ret_addr,
320 .frame_addr,
320 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),321 => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
321322
322 .not,323 .not,
src/Sema.zig+187-22
...@@ -8059,6 +8059,79 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -8059,6 +8059,79 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
8059 return block.addTyOp(.not, operand_type, operand);8059 return block.addTyOp(.not, operand_type, operand);
8060}8060}
80618061
8062fn analyzeTupleCat(
8063 sema: *Sema,
8064 block: *Block,
8065 src_node: i32,
8066 lhs: Air.Inst.Ref,
8067 rhs: Air.Inst.Ref,
8068) CompileError!Air.Inst.Ref {
8069 const lhs_ty = sema.typeOf(lhs);
8070 const rhs_ty = sema.typeOf(rhs);
8071 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
8072 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
8073
8074 const lhs_tuple = lhs_ty.tupleFields();
8075 const rhs_tuple = rhs_ty.tupleFields();
8076 const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len;
8077
8078 if (dest_fields == 0) {
8079 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
8080 }
8081 const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
8082
8083 const types = try sema.arena.alloc(Type, final_len);
8084 const values = try sema.arena.alloc(Value, final_len);
8085
8086 const opt_runtime_src = rs: {
8087 var runtime_src: ?LazySrcLoc = null;
8088 for (lhs_tuple.types) |ty, i| {
8089 types[i] = ty;
8090 values[i] = lhs_tuple.values[i];
8091 const operand_src = lhs_src; // TODO better source location
8092 if (values[i].tag() == .unreachable_value) {
8093 runtime_src = operand_src;
8094 }
8095 }
8096 const offset = lhs_tuple.types.len;
8097 for (rhs_tuple.types) |ty, i| {
8098 types[i + offset] = ty;
8099 values[i + offset] = rhs_tuple.values[i];
8100 const operand_src = rhs_src; // TODO better source location
8101 if (rhs_tuple.values[i].tag() == .unreachable_value) {
8102 runtime_src = operand_src;
8103 }
8104 }
8105 break :rs runtime_src;
8106 };
8107
8108 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
8109 .types = types,
8110 .values = values,
8111 });
8112
8113 const runtime_src = opt_runtime_src orelse {
8114 const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
8115 return sema.addConstant(tuple_ty, tuple_val);
8116 };
8117
8118 try sema.requireRuntimeBlock(block, runtime_src);
8119
8120 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
8121 for (lhs_tuple.types) |_, i| {
8122 const operand_src = lhs_src; // TODO better source location
8123 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
8124 }
8125 const offset = lhs_tuple.types.len;
8126 for (rhs_tuple.types) |_, i| {
8127 const operand_src = rhs_src; // TODO better source location
8128 element_refs[i + offset] =
8129 try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
8130 }
8131
8132 return block.addAggregateInit(tuple_ty, element_refs);
8133}
8134
8062fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8135fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8063 const tracy = trace(@src());8136 const tracy = trace(@src());
8064 defer tracy.end();8137 defer tracy.end();
...@@ -8069,12 +8142,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8069,12 +8142,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8069 const rhs = sema.resolveInst(extra.rhs);8142 const rhs = sema.resolveInst(extra.rhs);
8070 const lhs_ty = sema.typeOf(lhs);8143 const lhs_ty = sema.typeOf(lhs);
8071 const rhs_ty = sema.typeOf(rhs);8144 const rhs_ty = sema.typeOf(rhs);
8145
8146 if (lhs_ty.isTuple() and rhs_ty.isTuple()) {
8147 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
8148 }
8149
8072 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };8150 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
8073 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };8151 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
80748152
8075 const lhs_info = getArrayCatInfo(lhs_ty) orelse8153 const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
8076 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});8154 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
8077 const rhs_info = getArrayCatInfo(rhs_ty) orelse8155 const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse
8078 return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty});8156 return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty});
8079 if (!lhs_info.elem_type.eql(rhs_info.elem_type)) {8157 if (!lhs_info.elem_type.eql(rhs_info.elem_type)) {
8080 return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty });8158 return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty });
...@@ -8095,9 +8173,10 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8095,9 +8173,10 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8095 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);8173 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
8096 const final_len = lhs_len + rhs_len;8174 const final_len = lhs_len + rhs_len;
8097 const final_len_including_sent = final_len + @boolToInt(res_sent != null);8175 const final_len_including_sent = final_len + @boolToInt(res_sent != null);
8098 const is_pointer = lhs_ty.zigTypeTag() == .Pointer;8176 const lhs_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice();
8099 const lhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;8177 const rhs_single_ptr = rhs_ty.zigTypeTag() == .Pointer and !rhs_ty.isSlice();
8100 const rhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;8178 const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
8179 const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;
8101 var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);8180 var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
8102 defer anon_decl.deinit();8181 defer anon_decl.deinit();
81038182
...@@ -8129,7 +8208,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8129,7 +8208,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8129 });8208 });
8130 const val = try Value.Tag.array.create(anon_decl.arena(), buf);8209 const val = try Value.Tag.array.create(anon_decl.arena(), buf);
8131 const decl = try anon_decl.finish(ty, val);8210 const decl = try anon_decl.finish(ty, val);
8132 if (is_pointer) {8211 if (lhs_single_ptr or rhs_single_ptr) {
8133 return sema.analyzeDeclRef(decl);8212 return sema.analyzeDeclRef(decl);
8134 } else {8213 } else {
8135 return sema.analyzeDeclVal(block, .unneeded, decl);8214 return sema.analyzeDeclVal(block, .unneeded, decl);
...@@ -8142,11 +8221,20 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8142,11 +8221,20 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8142 }8221 }
8143}8222}
81448223
8145fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {8224fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo {
8225 const t = sema.typeOf(inst);
8146 return switch (t.zigTypeTag()) {8226 return switch (t.zigTypeTag()) {
8147 .Array => t.arrayInfo(),8227 .Array => t.arrayInfo(),
8148 .Pointer => blk: {8228 .Pointer => blk: {
8149 const ptrinfo = t.ptrInfo().data;8229 const ptrinfo = t.ptrInfo().data;
8230 if (ptrinfo.size == .Slice) {
8231 const val = try sema.resolveConstValue(block, src, inst);
8232 return Type.ArrayInfo{
8233 .elem_type = t.childType(),
8234 .sentinel = t.sentinel(),
8235 .len = val.sliceLen(),
8236 };
8237 }
8150 if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;8238 if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
8151 if (ptrinfo.size != .One) return null;8239 if (ptrinfo.size != .One) return null;
8152 break :blk ptrinfo.pointee_type.arrayInfo();8240 break :blk ptrinfo.pointee_type.arrayInfo();
...@@ -8155,6 +8243,73 @@ fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {...@@ -8155,6 +8243,73 @@ fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {
8155 };8243 };
8156}8244}
81578245
8246fn analyzeTupleMul(
8247 sema: *Sema,
8248 block: *Block,
8249 src_node: i32,
8250 operand: Air.Inst.Ref,
8251 factor: u64,
8252) CompileError!Air.Inst.Ref {
8253 const operand_ty = sema.typeOf(operand);
8254 const operand_tuple = operand_ty.tupleFields();
8255 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
8256 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
8257
8258 const tuple_len = operand_tuple.types.len;
8259 const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch
8260 return sema.fail(block, rhs_src, "operation results in overflow", .{});
8261
8262 if (final_len_u64 == 0) {
8263 return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
8264 }
8265 const final_len = try sema.usizeCast(block, rhs_src, final_len_u64);
8266
8267 const types = try sema.arena.alloc(Type, final_len);
8268 const values = try sema.arena.alloc(Value, final_len);
8269
8270 const opt_runtime_src = rs: {
8271 var runtime_src: ?LazySrcLoc = null;
8272 for (operand_tuple.types) |ty, i| {
8273 types[i] = ty;
8274 values[i] = operand_tuple.values[i];
8275 const operand_src = lhs_src; // TODO better source location
8276 if (values[i].tag() == .unreachable_value) {
8277 runtime_src = operand_src;
8278 }
8279 }
8280 var i: usize = 1;
8281 while (i < factor) : (i += 1) {
8282 mem.copy(Type, types[tuple_len * i ..], operand_tuple.types);
8283 mem.copy(Value, values[tuple_len * i ..], operand_tuple.values);
8284 }
8285 break :rs runtime_src;
8286 };
8287
8288 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
8289 .types = types,
8290 .values = values,
8291 });
8292
8293 const runtime_src = opt_runtime_src orelse {
8294 const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
8295 return sema.addConstant(tuple_ty, tuple_val);
8296 };
8297
8298 try sema.requireRuntimeBlock(block, runtime_src);
8299
8300 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
8301 for (operand_tuple.types) |_, i| {
8302 const operand_src = lhs_src; // TODO better source location
8303 element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
8304 }
8305 var i: usize = 1;
8306 while (i < factor) : (i += 1) {
8307 mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);
8308 }
8309
8310 return block.addAggregateInit(tuple_ty, element_refs);
8311}
8312
8158fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8313fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8159 const tracy = trace(@src());8314 const tracy = trace(@src());
8160 defer tracy.end();8315 defer tracy.end();
...@@ -8169,7 +8324,12 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8169,7 +8324,12 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
81698324
8170 // In `**` rhs has to be comptime-known, but lhs can be runtime-known8325 // In `**` rhs has to be comptime-known, but lhs can be runtime-known
8171 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);8326 const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);
8172 const mulinfo = getArrayCatInfo(lhs_ty) orelse8327
8328 if (lhs_ty.isTuple()) {
8329 return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor);
8330 }
8331
8332 const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
8173 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});8333 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
81748334
8175 const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch8335 const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch
...@@ -8180,7 +8340,8 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8180,7 +8340,8 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8180 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);8340 const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);
8181 const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len);8341 const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len);
81828342
8183 const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;8343 const is_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice();
8344 const lhs_sub_val = if (is_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
81848345
8185 var anon_decl = try block.startAnonDecl(src);8346 var anon_decl = try block.startAnonDecl(src);
8186 defer anon_decl.deinit();8347 defer anon_decl.deinit();
...@@ -8220,7 +8381,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -8220,7 +8381,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
8220 break :blk try Value.Tag.array.create(anon_decl.arena(), buf);8381 break :blk try Value.Tag.array.create(anon_decl.arena(), buf);
8221 };8382 };
8222 const decl = try anon_decl.finish(final_ty, val);8383 const decl = try anon_decl.finish(final_ty, val);
8223 if (lhs_ty.zigTypeTag() == .Pointer) {8384 if (is_single_ptr) {
8224 return sema.analyzeDeclRef(decl);8385 return sema.analyzeDeclRef(decl);
8225 } else {8386 } else {
8226 return sema.analyzeDeclVal(block, .unneeded, decl);8387 return sema.analyzeDeclVal(block, .unneeded, decl);
...@@ -9832,14 +9993,21 @@ fn zirRetAddr(...@@ -9832,14 +9993,21 @@ fn zirRetAddr(
9832 block: *Block,9993 block: *Block,
9833 extended: Zir.Inst.Extended.InstData,9994 extended: Zir.Inst.Extended.InstData,
9834) CompileError!Air.Inst.Ref {9995) CompileError!Air.Inst.Ref {
9835 const tracy = trace(@src());
9836 defer tracy.end();
9837
9838 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };9996 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
9839 try sema.requireRuntimeBlock(block, src);9997 try sema.requireRuntimeBlock(block, src);
9840 return try block.addNoOp(.ret_addr);9998 return try block.addNoOp(.ret_addr);
9841}9999}
984210000
10001fn zirFrameAddress(
10002 sema: *Sema,
10003 block: *Block,
10004 extended: Zir.Inst.Extended.InstData,
10005) CompileError!Air.Inst.Ref {
10006 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
10007 try sema.requireRuntimeBlock(block, src);
10008 return try block.addNoOp(.frame_addr);
10009}
10010
9843fn zirBuiltinSrc(10011fn zirBuiltinSrc(
9844 sema: *Sema,10012 sema: *Sema,
9845 block: *Block,10013 block: *Block,
...@@ -11728,15 +11896,6 @@ fn zirFrame(...@@ -11728,15 +11896,6 @@ fn zirFrame(
11728 return sema.fail(block, src, "TODO: Sema.zirFrame", .{});11896 return sema.fail(block, src, "TODO: Sema.zirFrame", .{});
11729}11897}
1173011898
11731fn zirFrameAddress(
11732 sema: *Sema,
11733 block: *Block,
11734 extended: Zir.Inst.Extended.InstData,
11735) CompileError!Air.Inst.Ref {
11736 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
11737 return sema.fail(block, src, "TODO: Sema.zirFrameAddress", .{});
11738}
11739
11740fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11899fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11741 const inst_data = sema.code.instructions.items(.data)[inst].un_node;11900 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
11742 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };11901 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
...@@ -14743,6 +14902,11 @@ fn tupleFieldVal(...@@ -14743,6 +14902,11 @@ fn tupleFieldVal(
14743 tuple_ty, field_name, @errorName(err),14902 tuple_ty, field_name, @errorName(err),
14744 });14903 });
14745 };14904 };
14905 if (field_index >= tuple_ty.structFieldCount()) {
14906 return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{
14907 tuple_ty, field_name,
14908 });
14909 }
14746 return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);14910 return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
14747}14911}
1474814912
...@@ -19093,6 +19257,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr...@@ -19093,6 +19257,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
19093/// Used to convert a u64 value to a usize value, emitting a compile error if the number19257/// Used to convert a u64 value to a usize value, emitting a compile error if the number
19094/// is too big to fit.19258/// is too big to fit.
19095fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {19259fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
19260 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
19096 return std.math.cast(usize, int) catch |err| switch (err) {19261 return std.math.cast(usize, int) catch |err| switch (err) {
19097 error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}),19262 error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}),
19098 };19263 };
src/arch/aarch64/CodeGen.zig+10-3
...@@ -579,7 +579,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -579,7 +579,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
579 .block => try self.airBlock(inst),579 .block => try self.airBlock(inst),
580 .br => try self.airBr(inst),580 .br => try self.airBr(inst),
581 .breakpoint => try self.airBreakpoint(),581 .breakpoint => try self.airBreakpoint(),
582 .ret_addr => try self.airRetAddr(),582 .ret_addr => try self.airRetAddr(inst),
583 .frame_addr => try self.airFrameAddress(inst),
583 .fence => try self.airFence(),584 .fence => try self.airFence(),
584 .call => try self.airCall(inst),585 .call => try self.airCall(inst),
585 .cond_br => try self.airCondBr(inst),586 .cond_br => try self.airCondBr(inst),
...@@ -2178,8 +2179,14 @@ fn airBreakpoint(self: *Self) !void {...@@ -2178,8 +2179,14 @@ fn airBreakpoint(self: *Self) !void {
2178 return self.finishAirBookkeeping();2179 return self.finishAirBookkeeping();
2179}2180}
21802181
2181fn airRetAddr(self: *Self) !void {2182fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
2182 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});2183 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{});
2184 return self.finishAir(inst, result, .{ .none, .none, .none });
2185}
2186
2187fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
2188 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{});
2189 return self.finishAir(inst, result, .{ .none, .none, .none });
2183}2190}
21842191
2185fn airFence(self: *Self) !void {2192fn airFence(self: *Self) !void {
src/arch/arm/CodeGen.zig+10-3
...@@ -565,7 +565,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -565,7 +565,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
565 .block => try self.airBlock(inst),565 .block => try self.airBlock(inst),
566 .br => try self.airBr(inst),566 .br => try self.airBr(inst),
567 .breakpoint => try self.airBreakpoint(),567 .breakpoint => try self.airBreakpoint(),
568 .ret_addr => try self.airRetAddr(),568 .ret_addr => try self.airRetAddr(inst),
569 .frame_addr => try self.airFrameAddress(inst),
569 .fence => try self.airFence(),570 .fence => try self.airFence(),
570 .call => try self.airCall(inst),571 .call => try self.airCall(inst),
571 .cond_br => try self.airCondBr(inst),572 .cond_br => try self.airCondBr(inst),
...@@ -2449,8 +2450,14 @@ fn airBreakpoint(self: *Self) !void {...@@ -2449,8 +2450,14 @@ fn airBreakpoint(self: *Self) !void {
2449 return self.finishAirBookkeeping();2450 return self.finishAirBookkeeping();
2450}2451}
24512452
2452fn airRetAddr(self: *Self) !void {2453fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
2453 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});2454 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{});
2455 return self.finishAir(inst, result, .{ .none, .none, .none });
2456}
2457
2458fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
2459 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{});
2460 return self.finishAir(inst, result, .{ .none, .none, .none });
2454}2461}
24552462
2456fn airFence(self: *Self) !void {2463fn airFence(self: *Self) !void {
src/arch/riscv64/CodeGen.zig+10-3
...@@ -550,7 +550,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -550,7 +550,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
550 .block => try self.airBlock(inst),550 .block => try self.airBlock(inst),
551 .br => try self.airBr(inst),551 .br => try self.airBr(inst),
552 .breakpoint => try self.airBreakpoint(),552 .breakpoint => try self.airBreakpoint(),
553 .ret_addr => try self.airRetAddr(),553 .ret_addr => try self.airRetAddr(inst),
554 .frame_addr => try self.airFrameAddress(inst),
554 .fence => try self.airFence(),555 .fence => try self.airFence(),
555 .call => try self.airCall(inst),556 .call => try self.airCall(inst),
556 .cond_br => try self.airCondBr(inst),557 .cond_br => try self.airCondBr(inst),
...@@ -1438,8 +1439,14 @@ fn airBreakpoint(self: *Self) !void {...@@ -1438,8 +1439,14 @@ fn airBreakpoint(self: *Self) !void {
1438 return self.finishAirBookkeeping();1439 return self.finishAirBookkeeping();
1439}1440}
14401441
1441fn airRetAddr(self: *Self) !void {1442fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
1442 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});1443 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{});
1444 return self.finishAir(inst, result, .{ .none, .none, .none });
1445}
1446
1447fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
1448 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{});
1449 return self.finishAir(inst, result, .{ .none, .none, .none });
1443}1450}
14441451
1445fn airFence(self: *Self) !void {1452fn airFence(self: *Self) !void {
src/arch/wasm/CodeGen.zig+1
...@@ -1683,6 +1683,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1683,6 +1683,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1683 .assembly,1683 .assembly,
1684 .shl_sat,1684 .shl_sat,
1685 .ret_addr,1685 .ret_addr,
1686 .frame_addr,
1686 .clz,1687 .clz,
1687 .ctz,1688 .ctz,
1688 .popcount,1689 .popcount,
src/arch/x86_64/CodeGen.zig+10-3
...@@ -662,7 +662,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -662,7 +662,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
662 .block => try self.airBlock(inst),662 .block => try self.airBlock(inst),
663 .br => try self.airBr(inst),663 .br => try self.airBr(inst),
664 .breakpoint => try self.airBreakpoint(),664 .breakpoint => try self.airBreakpoint(),
665 .ret_addr => try self.airRetAddr(),665 .ret_addr => try self.airRetAddr(inst),
666 .frame_addr => try self.airFrameAddress(inst),
666 .fence => try self.airFence(),667 .fence => try self.airFence(),
667 .call => try self.airCall(inst),668 .call => try self.airCall(inst),
668 .cond_br => try self.airCondBr(inst),669 .cond_br => try self.airCondBr(inst),
...@@ -3127,8 +3128,14 @@ fn airBreakpoint(self: *Self) !void {...@@ -3127,8 +3128,14 @@ fn airBreakpoint(self: *Self) !void {
3127 return self.finishAirBookkeeping();3128 return self.finishAirBookkeeping();
3128}3129}
31293130
3130fn airRetAddr(self: *Self) !void {3131fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
3131 return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch});3132 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for x86_64", .{});
3133 return self.finishAir(inst, result, .{ .none, .none, .none });
3134}
3135
3136fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
3137 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for x86_64", .{});
3138 return self.finishAir(inst, result, .{ .none, .none, .none });
3132}3139}
31333140
3134fn airFence(self: *Self) !void {3141fn airFence(self: *Self) !void {
src/codegen/c.zig+11-2
...@@ -1588,7 +1588,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1588,7 +1588,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1588 .arg => airArg(f),1588 .arg => airArg(f),
15891589
1590 .breakpoint => try airBreakpoint(f),1590 .breakpoint => try airBreakpoint(f),
1591 .ret_addr => try airRetAddr(f),1591 .ret_addr => try airRetAddr(f, inst),
1592 .frame_addr => try airFrameAddress(f, inst),
1592 .unreach => try airUnreach(f),1593 .unreach => try airUnreach(f),
1593 .fence => try airFence(f, inst),1594 .fence => try airFence(f, inst),
15941595
...@@ -2717,12 +2718,20 @@ fn airBreakpoint(f: *Function) !CValue {...@@ -2717,12 +2718,20 @@ fn airBreakpoint(f: *Function) !CValue {
2717 return CValue.none;2718 return CValue.none;
2718}2719}
27192720
2720fn airRetAddr(f: *Function) !CValue {2721fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
2722 if (f.liveness.isUnused(inst)) return CValue.none;
2721 const local = try f.allocLocal(Type.usize, .Const);2723 const local = try f.allocLocal(Type.usize, .Const);
2722 try f.object.writer().writeAll(" = zig_return_address();\n");2724 try f.object.writer().writeAll(" = zig_return_address();\n");
2723 return local;2725 return local;
2724}2726}
27252727
2728fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
2729 if (f.liveness.isUnused(inst)) return CValue.none;
2730 const local = try f.allocLocal(Type.usize, .Const);
2731 try f.object.writer().writeAll(" = zig_frame_address();\n");
2732 return local;
2733}
2734
2726fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {2735fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
2727 const atomic_order = f.air.instructions.items(.data)[inst].fence;2736 const atomic_order = f.air.instructions.items(.data)[inst].fence;
2728 const writer = f.object.writer();2737 const writer = f.object.writer();
src/codegen/llvm.zig+19-5
...@@ -2135,6 +2135,7 @@ pub const FuncGen = struct {...@@ -2135,6 +2135,7 @@ pub const FuncGen = struct {
2135 .switch_br => try self.airSwitchBr(inst),2135 .switch_br => try self.airSwitchBr(inst),
2136 .breakpoint => try self.airBreakpoint(inst),2136 .breakpoint => try self.airBreakpoint(inst),
2137 .ret_addr => try self.airRetAddr(inst),2137 .ret_addr => try self.airRetAddr(inst),
2138 .frame_addr => try self.airFrameAddress(inst),
2138 .call => try self.airCall(inst),2139 .call => try self.airCall(inst),
2139 .cond_br => try self.airCondBr(inst),2140 .cond_br => try self.airCondBr(inst),
2140 .intcast => try self.airIntCast(inst),2141 .intcast => try self.airIntCast(inst),
...@@ -4100,12 +4101,25 @@ pub const FuncGen = struct {...@@ -4100,12 +4101,25 @@ pub const FuncGen = struct {
4100 }4101 }
41014102
4102 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {4103 fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4103 _ = inst;4104 if (self.liveness.isUnused(inst)) return null;
4104 const i32_zero = self.context.intType(32).constNull();4105
4105 const usize_llvm_ty = try self.dg.llvmType(Type.usize);4106 const llvm_i32 = self.context.intType(32);
4106 const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{});4107 const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{});
4107 const ptr_val = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{i32_zero}, 1, .Fast, .Auto, "");4108 const params = [_]*const llvm.Value{llvm_i32.constNull()};
4108 return self.builder.buildPtrToInt(ptr_val, usize_llvm_ty, "");4109 const ptr_val = self.builder.buildCall(llvm_fn, &params, params.len, .Fast, .Auto, "");
4110 const llvm_usize = try self.dg.llvmType(Type.usize);
4111 return self.builder.buildPtrToInt(ptr_val, llvm_usize, "");
4112 }
4113
4114 fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
4115 if (self.liveness.isUnused(inst)) return null;
4116
4117 const llvm_i32 = self.context.intType(32);
4118 const llvm_fn = self.getIntrinsic("llvm.frameaddress", &.{llvm_i32});
4119 const params = [_]*const llvm.Value{llvm_i32.constNull()};
4120 const ptr_val = self.builder.buildCall(llvm_fn, &params, params.len, .Fast, .Auto, "");
4121 const llvm_usize = try self.dg.llvmType(Type.usize);
4122 return self.builder.buildPtrToInt(ptr_val, llvm_usize, "");
4109 }4123 }
41104124
4111 fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {4125 fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/print_air.zig+1
...@@ -171,6 +171,7 @@ const Writer = struct {...@@ -171,6 +171,7 @@ const Writer = struct {
171 .breakpoint,171 .breakpoint,
172 .unreach,172 .unreach,
173 .ret_addr,173 .ret_addr,
174 .frame_addr,
174 => try w.writeNoOp(s, inst),175 => try w.writeNoOp(s, inst),
175176
176 .const_ty,177 .const_ty,
src/type.zig+13-3
...@@ -4531,7 +4531,15 @@ pub const Type = extern union {...@@ -4531,7 +4531,15 @@ pub const Type = extern union {
4531 };4531 };
45324532
4533 pub fn isTuple(ty: Type) bool {4533 pub fn isTuple(ty: Type) bool {
4534 return ty.tag() == .tuple;4534 return ty.tag() == .tuple or ty.tag() == .empty_struct_literal;
4535 }
4536
4537 pub fn tupleFields(ty: Type) Payload.Tuple.Data {
4538 return switch (ty.tag()) {
4539 .tuple => ty.castTag(.tuple).?.data,
4540 .empty_struct_literal => .{ .types = &.{}, .values = &.{} },
4541 else => unreachable,
4542 };
4535 }4543 }
45364544
4537 /// The sub-types are named after what fields they contain.4545 /// The sub-types are named after what fields they contain.
...@@ -4683,11 +4691,13 @@ pub const Type = extern union {...@@ -4683,11 +4691,13 @@ pub const Type = extern union {
46834691
4684 pub const Tuple = struct {4692 pub const Tuple = struct {
4685 base: Payload = .{ .tag = .tuple },4693 base: Payload = .{ .tag = .tuple },
4686 data: struct {4694 data: Data,
4695
4696 pub const Data = struct {
4687 types: []Type,4697 types: []Type,
4688 /// unreachable_value elements are used to indicate runtime-known.4698 /// unreachable_value elements are used to indicate runtime-known.
4689 values: []Value,4699 values: []Value,
4690 },4700 };
4691 };4701 };
46924702
4693 pub const Union = struct {4703 pub const Union = struct {
src/value.zig+21-7
...@@ -1829,7 +1829,7 @@ pub const Value = extern union {...@@ -1829,7 +1829,7 @@ pub const Value = extern union {
1829 assert(a_tag != .undef);1829 assert(a_tag != .undef);
1830 assert(b_tag != .undef);1830 assert(b_tag != .undef);
1831 if (a_tag == b_tag) switch (a_tag) {1831 if (a_tag == b_tag) switch (a_tag) {
1832 .void_value, .null_value, .the_only_possible_value => return true,1832 .void_value, .null_value, .the_only_possible_value, .empty_struct_value => return true,
1833 .enum_literal => {1833 .enum_literal => {
1834 const a_name = a.castTag(.enum_literal).?.data;1834 const a_name = a.castTag(.enum_literal).?.data;
1835 const b_name = b.castTag(.enum_literal).?.data;1835 const b_name = b.castTag(.enum_literal).?.data;
...@@ -1892,10 +1892,18 @@ pub const Value = extern union {...@@ -1892,10 +1892,18 @@ pub const Value = extern union {
1892 return a_payload == b_payload;1892 return a_payload == b_payload;
1893 },1893 },
1894 .@"struct" => {1894 .@"struct" => {
1895 const fields = ty.structFields().values();
1896 const a_field_vals = a.castTag(.@"struct").?.data;1895 const a_field_vals = a.castTag(.@"struct").?.data;
1897 const b_field_vals = b.castTag(.@"struct").?.data;1896 const b_field_vals = b.castTag(.@"struct").?.data;
1898 assert(a_field_vals.len == b_field_vals.len);1897 assert(a_field_vals.len == b_field_vals.len);
1898 if (ty.isTuple()) {
1899 const types = ty.tupleFields().types;
1900 assert(types.len == a_field_vals.len);
1901 for (types) |field_ty, i| {
1902 if (!eql(a_field_vals[i], b_field_vals[i], field_ty)) return false;
1903 }
1904 return true;
1905 }
1906 const fields = ty.structFields().values();
1899 assert(fields.len == a_field_vals.len);1907 assert(fields.len == a_field_vals.len);
1900 for (fields) |field, i| {1908 for (fields) |field, i| {
1901 if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;1909 if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false;
...@@ -1967,11 +1975,10 @@ pub const Value = extern union {...@@ -1967,11 +1975,10 @@ pub const Value = extern union {
1967 return true;1975 return true;
1968 },1976 },
1969 .Struct => {1977 .Struct => {
1970 // must be a struct with no fields since we checked for if1978 // A tuple can be represented with .empty_struct_value,
1971 // both have the struct tag above.1979 // the_one_possible_value, .@"struct" in which case we could
1972 const fields = ty.structFields().values();1980 // end up here and the values are equal if the type has zero fields.
1973 assert(fields.len == 0);1981 return ty.structFieldCount() != 0;
1974 return true;
1975 },1982 },
1976 else => return order(a, b).compare(.eq),1983 else => return order(a, b).compare(.eq),
1977 }1984 }
...@@ -2024,6 +2031,13 @@ pub const Value = extern union {...@@ -2024,6 +2031,13 @@ pub const Value = extern union {
2024 }2031 }
2025 },2032 },
2026 .Struct => {2033 .Struct => {
2034 if (ty.isTuple()) {
2035 const fields = ty.tupleFields();
2036 for (fields.values) |field_val, i| {
2037 field_val.hash(fields.types[i], hasher);
2038 }
2039 return;
2040 }
2027 const fields = ty.structFields().values();2041 const fields = ty.structFields().values();
2028 if (fields.len == 0) return;2042 if (fields.len == 0) return;
2029 const field_values = val.castTag(.@"struct").?.data;2043 const field_values = val.castTag(.@"struct").?.data;
test/behavior/tuple.zig+9-9
...@@ -23,28 +23,30 @@ test "tuple concatenation" {...@@ -23,28 +23,30 @@ test "tuple concatenation" {
23}23}
2424
25test "tuple multiplication" {25test "tuple multiplication" {
26 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
27
28 const S = struct {26 const S = struct {
29 fn doTheTest() !void {27 fn doTheTest() !void {
30 {28 {
31 const t = .{} ** 4;29 const t = .{} ** 4;
32 try expectEqual(0, @typeInfo(@TypeOf(t)).Struct.fields.len);30 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 0);
33 }31 }
34 {32 {
35 const t = .{'a'} ** 4;33 const t = .{'a'} ** 4;
36 try expectEqual(4, @typeInfo(@TypeOf(t)).Struct.fields.len);34 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 4);
37 inline for (t) |x| try expectEqual('a', x);35 inline for (t) |x| try expect(x == 'a');
38 }36 }
39 {37 {
40 const t = .{ 1, 2, 3 } ** 4;38 const t = .{ 1, 2, 3 } ** 4;
41 try expectEqual(12, @typeInfo(@TypeOf(t)).Struct.fields.len);39 try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 12);
42 inline for (t) |x, i| try expectEqual(1 + i % 3, x);40 inline for (t) |x, i| try expect(x == 1 + i % 3);
43 }41 }
44 }42 }
45 };43 };
46 try S.doTheTest();44 try S.doTheTest();
47 comptime try S.doTheTest();45 comptime try S.doTheTest();
46}
47
48test "tuple concatenation" {
49 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
4850
49 const T = struct {51 const T = struct {
50 fn consume_tuple(tuple: anytype, len: usize) !void {52 fn consume_tuple(tuple: anytype, len: usize) !void {
...@@ -86,8 +88,6 @@ test "tuple multiplication" {...@@ -86,8 +88,6 @@ test "tuple multiplication" {
86}88}
8789
88test "pass tuple to comptime var parameter" {90test "pass tuple to comptime var parameter" {
89 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
90
91 const S = struct {91 const S = struct {
92 fn Foo(comptime args: anytype) !void {92 fn Foo(comptime args: anytype) !void {
93 try expect(args[0] == 1);93 try expect(args[0] == 1);