authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-13 21:20:38-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-13 21:20:38-07:00
log0d4a94f32fc71f81d54db038f013854b8e9f0ac4
tree163668781596539d8d6f95e2de506aad544be355
parentb0f80ef0d5517b5ce8ba60dfae6dd986346f8d4c

stage2: improve handling of 0-bit types and arrays

* Make `alloc` AIR instructions call `resolveTypeLayout`. * `Sema.zirResolveInferredAlloc` now calls `requireRuntimeBlock` in the case that it operates on a non-comptime instruction. * `Type.abiSize` and `Type.abiAlignment` now return 0 for `void` * Sema: implement `resolveTypeFields` for unions. * LLVM Backend: support `ptr_elem_ptr` when the element type is 0-bit. * Type: improve `abiAlignment` implementation for structs to properly handle fields with non-default alignment. * Value: implement hashing array, vector, and structs.

6 files changed, 173 insertions(+), 95 deletions(-)

src/Sema.zig+74-46
...@@ -2035,6 +2035,7 @@ fn zirAllocExtended(...@@ -2035,6 +2035,7 @@ fn zirAllocExtended(
2035 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2035 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
2036 });2036 });
2037 try sema.requireRuntimeBlock(block, src);2037 try sema.requireRuntimeBlock(block, src);
2038 try sema.resolveTypeLayout(block, src, var_ty);
2038 return block.addTy(.alloc, ptr_type);2039 return block.addTy(.alloc, ptr_type);
2039}2040}
20402041
...@@ -2044,8 +2045,8 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -2044,8 +2045,8 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
20442045
2045 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2046 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2046 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2047 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2047 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);2048 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2048 return sema.analyzeComptimeAlloc(block, var_type);2049 return sema.analyzeComptimeAlloc(block, var_ty);
2049}2050}
20502051
2051fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2052fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -2065,15 +2066,16 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -2065,15 +2066,16 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
2065 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2066 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2066 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2067 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2067 const var_decl_src = inst_data.src();2068 const var_decl_src = inst_data.src();
2068 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);2069 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2069 if (block.is_comptime) {2070 if (block.is_comptime) {
2070 return sema.analyzeComptimeAlloc(block, var_type);2071 return sema.analyzeComptimeAlloc(block, var_ty);
2071 }2072 }
2072 const ptr_type = try Type.ptr(sema.arena, .{2073 const ptr_type = try Type.ptr(sema.arena, .{
2073 .pointee_type = var_type,2074 .pointee_type = var_ty,
2074 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2075 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
2075 });2076 });
2076 try sema.requireRuntimeBlock(block, var_decl_src);2077 try sema.requireRuntimeBlock(block, var_decl_src);
2078 try sema.resolveTypeLayout(block, ty_src, var_ty);
2077 return block.addTy(.alloc, ptr_type);2079 return block.addTy(.alloc, ptr_type);
2078}2080}
20792081
...@@ -2084,16 +2086,17 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -2084,16 +2086,17 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
2084 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2086 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2085 const var_decl_src = inst_data.src();2087 const var_decl_src = inst_data.src();
2086 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2088 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2087 const var_type = try sema.resolveType(block, ty_src, inst_data.operand);2089 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
2088 if (block.is_comptime) {2090 if (block.is_comptime) {
2089 return sema.analyzeComptimeAlloc(block, var_type);2091 return sema.analyzeComptimeAlloc(block, var_ty);
2090 }2092 }
2091 try sema.validateVarType(block, ty_src, var_type, false);2093 try sema.validateVarType(block, ty_src, var_ty, false);
2092 const ptr_type = try Type.ptr(sema.arena, .{2094 const ptr_type = try Type.ptr(sema.arena, .{
2093 .pointee_type = var_type,2095 .pointee_type = var_ty,
2094 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2096 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
2095 });2097 });
2096 try sema.requireRuntimeBlock(block, var_decl_src);2098 try sema.requireRuntimeBlock(block, var_decl_src);
2099 try sema.resolveTypeLayout(block, ty_src, var_ty);
2097 return block.addTy(.alloc, ptr_type);2100 return block.addTy(.alloc, ptr_type);
2098}2101}
20992102
...@@ -2135,6 +2138,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2135,6 +2138,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2135 defer tracy.end();2138 defer tracy.end();
21362139
2137 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2140 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2141 const src = inst_data.src();
2138 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };2142 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
2139 const ptr = sema.resolveInst(inst_data.operand);2143 const ptr = sema.resolveInst(inst_data.operand);
2140 const ptr_inst = Air.refToIndex(ptr).?;2144 const ptr_inst = Air.refToIndex(ptr).?;
...@@ -2146,46 +2150,53 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2146,46 +2150,53 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2146 .inferred_alloc_mut => true,2150 .inferred_alloc_mut => true,
2147 else => unreachable,2151 else => unreachable,
2148 };2152 };
2153 const target = sema.mod.getTarget();
21492154
2150 if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {2155 switch (ptr_val.tag()) {
2151 const decl = iac.data;2156 .inferred_alloc_comptime => {
2152 try sema.mod.declareDeclDependency(sema.owner_decl, decl);2157 const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
2158 const decl = iac.data;
2159 try sema.mod.declareDeclDependency(sema.owner_decl, decl);
2160
2161 const final_elem_ty = try decl.ty.copy(sema.arena);
2162 const final_ptr_ty = try Type.ptr(sema.arena, .{
2163 .pointee_type = final_elem_ty,
2164 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2165 });
2166 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
2167 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;
21532168
2154 const final_elem_ty = try decl.ty.copy(sema.arena);2169 if (var_is_mut) {
2155 const final_ptr_ty = try Type.ptr(sema.arena, .{2170 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
2156 .pointee_type = final_elem_ty,2171 .decl = decl,
2157 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2172 .runtime_index = block.runtime_index,
2158 });2173 });
2159 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);2174 } else {
2160 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;2175 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl);
2176 }
2177 },
2178 .inferred_alloc => {
2179 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
2180 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
2181 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
21612182
2162 if (var_is_mut) {2183 try sema.requireRuntimeBlock(block, src);
2163 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{2184 try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
2164 .decl = decl,
2165 .runtime_index = block.runtime_index,
2166 });
2167 } else {
2168 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl);
2169 }
2170 return;
2171 }
21722185
2173 if (ptr_val.castTag(.inferred_alloc)) |inferred_alloc| {2186 if (var_is_mut) {
2174 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;2187 try sema.validateVarType(block, ty_src, final_elem_ty, false);
2175 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);2188 }
2176 if (var_is_mut) {2189 // Change it to a normal alloc.
2177 try sema.validateVarType(block, ty_src, final_elem_ty, false);2190 const final_ptr_ty = try Type.ptr(sema.arena, .{
2178 }2191 .pointee_type = final_elem_ty,
2179 // Change it to a normal alloc.2192 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2180 const final_ptr_ty = try Type.ptr(sema.arena, .{2193 });
2181 .pointee_type = final_elem_ty,2194 sema.air_instructions.set(ptr_inst, .{
2182 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),2195 .tag = .alloc,
2183 });2196 .data = .{ .ty = final_ptr_ty },
2184 sema.air_instructions.set(ptr_inst, .{2197 });
2185 .tag = .alloc,2198 },
2186 .data = .{ .ty = final_ptr_ty },2199 else => unreachable,
2187 });
2188 return;
2189 }2200 }
2190}2201}
21912202
...@@ -8844,6 +8855,7 @@ fn zirArrayInit(...@@ -8844,6 +8855,7 @@ fn zirArrayInit(
8844 };8855 };
88458856
8846 try sema.requireRuntimeBlock(block, runtime_src);8857 try sema.requireRuntimeBlock(block, runtime_src);
8858 try sema.resolveTypeLayout(block, src, elem_ty);
88478859
8848 const alloc_ty = try Type.ptr(sema.arena, .{8860 const alloc_ty = try Type.ptr(sema.arena, .{
8849 .pointee_type = array_ty,8861 .pointee_type = array_ty,
...@@ -10194,6 +10206,7 @@ fn validateVarType(...@@ -10194,6 +10206,7 @@ fn validateVarType(
10194 .Enum,10206 .Enum,
10195 .Frame,10207 .Frame,
10196 .AnyFrame,10208 .AnyFrame,
10209 .Void,
10197 => return,10210 => return,
1019810211
10199 .BoundFn,10212 .BoundFn,
...@@ -10202,7 +10215,6 @@ fn validateVarType(...@@ -10202,7 +10215,6 @@ fn validateVarType(
10202 .EnumLiteral,10215 .EnumLiteral,
10203 .NoReturn,10216 .NoReturn,
10204 .Type,10217 .Type,
10205 .Void,
10206 .Undefined,10218 .Undefined,
10207 .Null,10219 .Null,
10208 => break,10220 => break,
...@@ -12585,6 +12597,22 @@ pub fn resolveTypeLayout(...@@ -12585,6 +12597,22 @@ pub fn resolveTypeLayout(
12585 }12597 }
12586 struct_obj.status = .have_layout;12598 struct_obj.status = .have_layout;
12587 },12599 },
12600 .Union => {
12601 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
12602 const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
12603 switch (union_obj.status) {
12604 .none, .have_field_types => {},
12605 .field_types_wip, .layout_wip => {
12606 return sema.fail(block, src, "union {} depends on itself", .{ty});
12607 },
12608 .have_layout => return,
12609 }
12610 union_obj.status = .layout_wip;
12611 for (union_obj.fields.values()) |field| {
12612 try sema.resolveTypeLayout(block, src, field.ty);
12613 }
12614 union_obj.status = .have_layout;
12615 },
12588 else => {},12616 else => {},
12589 }12617 }
12590}12618}
src/codegen/llvm.zig+5-3
...@@ -1863,14 +1863,16 @@ pub const FuncGen = struct {...@@ -1863,14 +1863,16 @@ pub const FuncGen = struct {
1863 }1863 }
18641864
1865 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1865 fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1866 if (self.liveness.isUnused(inst))1866 if (self.liveness.isUnused(inst)) return null;
1867 return null;
18681867
1869 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1868 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1870 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1869 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1870 const lhs_ty = self.air.typeOf(bin_op.lhs);
1871 if (!lhs_ty.hasCodeGenBits()) return null;
1872
1871 const base_ptr = try self.resolveInst(bin_op.lhs);1873 const base_ptr = try self.resolveInst(bin_op.lhs);
1872 const rhs = try self.resolveInst(bin_op.rhs);1874 const rhs = try self.resolveInst(bin_op.rhs);
1873 if (self.air.typeOf(bin_op.lhs).isSinglePointer()) {1875 if (lhs_ty.isSinglePointer()) {
1874 // If this is a single-item pointer to an array, we need another index in the GEP.1876 // If this is a single-item pointer to an array, we need another index in the GEP.
1875 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };1877 const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
1876 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");1878 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
src/type.zig+39-27
...@@ -1579,7 +1579,7 @@ pub const Type = extern union {...@@ -1579,7 +1579,7 @@ pub const Type = extern union {
1579 };1579 };
1580 }1580 }
15811581
1582 /// Asserts that hasCodeGenBits() is true.1582 /// Returns 0 for 0-bit types.
1583 pub fn abiAlignment(self: Type, target: Target) u32 {1583 pub fn abiAlignment(self: Type, target: Target) u32 {
1584 return switch (self.tag()) {1584 return switch (self.tag()) {
1585 .u1,1585 .u1,
...@@ -1667,6 +1667,7 @@ pub const Type = extern union {...@@ -1667,6 +1667,7 @@ pub const Type = extern union {
16671667
1668 .int_signed, .int_unsigned => {1668 .int_signed, .int_unsigned => {
1669 const bits: u16 = self.cast(Payload.Bits).?.data;1669 const bits: u16 = self.cast(Payload.Bits).?.data;
1670 if (bits == 0) return 0;
1670 if (bits <= 8) return 1;1671 if (bits <= 8) return 1;
1671 if (bits <= 16) return 2;1672 if (bits <= 16) return 2;
1672 if (bits <= 32) return 4;1673 if (bits <= 32) return 4;
...@@ -1699,23 +1700,27 @@ pub const Type = extern union {...@@ -1699,23 +1700,27 @@ pub const Type = extern union {
1699 },1700 },
17001701
1701 .@"struct" => {1702 .@"struct" => {
1702 // TODO take into account field alignment1703 const fields = self.structFields();
1703 // also make this possible to fail, and lazy1704 if (self.castTag(.@"struct")) |payload| {
1704 // I think we need to move all the functions from type.zig which can1705 const struct_obj = payload.data;
1705 // fail into Sema.1706 assert(struct_obj.status == .have_layout);
1706 // Probably will need to introduce multi-stage struct resolution just1707 const is_packed = struct_obj.layout == .Packed;
1707 // like we have in stage1.1708 if (is_packed) @panic("TODO packed structs");
1708 const struct_obj = self.castTag(.@"struct").?.data;1709 }
1709 var biggest: u32 = 0;1710 var big_align: u32 = 0;
1710 for (struct_obj.fields.values()) |field| {1711 for (fields.values()) |field| {
1711 if (!field.ty.hasCodeGenBits()) continue;1712 if (!field.ty.hasCodeGenBits()) continue;
1712 const field_align = field.ty.abiAlignment(target);1713
1713 if (field_align > biggest) {1714 const field_align = a: {
1714 return field_align;1715 if (field.abi_align.tag() == .abi_align_default) {
1715 }1716 break :a field.ty.abiAlignment(target);
1717 } else {
1718 break :a @intCast(u32, field.abi_align.toUnsignedInt());
1719 }
1720 };
1721 big_align = @maximum(big_align, field_align);
1716 }1722 }
1717 assert(biggest != 0);1723 return big_align;
1718 return biggest;
1719 },1724 },
1720 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {1725 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
1721 var buffer: Payload.Bits = undefined;1726 var buffer: Payload.Bits = undefined;
...@@ -1726,8 +1731,12 @@ pub const Type = extern union {...@@ -1726,8 +1731,12 @@ pub const Type = extern union {
1726 .@"union" => return self.castTag(.@"union").?.data.abiAlignment(target, false),1731 .@"union" => return self.castTag(.@"union").?.data.abiAlignment(target, false),
1727 .union_tagged => return self.castTag(.union_tagged).?.data.abiAlignment(target, true),1732 .union_tagged => return self.castTag(.union_tagged).?.data.abiAlignment(target, true),
17281733
1729 .c_void,1734 .empty_struct,
1730 .void,1735 .void,
1736 => return 0,
1737
1738 .empty_struct_literal,
1739 .c_void,
1731 .type,1740 .type,
1732 .comptime_int,1741 .comptime_int,
1733 .comptime_float,1742 .comptime_float,
...@@ -1735,8 +1744,6 @@ pub const Type = extern union {...@@ -1735,8 +1744,6 @@ pub const Type = extern union {
1735 .@"null",1744 .@"null",
1736 .@"undefined",1745 .@"undefined",
1737 .enum_literal,1746 .enum_literal,
1738 .empty_struct,
1739 .empty_struct_literal,
1740 .inferred_alloc_const,1747 .inferred_alloc_const,
1741 .inferred_alloc_mut,1748 .inferred_alloc_mut,
1742 .@"opaque",1749 .@"opaque",
...@@ -1758,7 +1765,6 @@ pub const Type = extern union {...@@ -1758,7 +1765,6 @@ pub const Type = extern union {
1758 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer1765 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
1759 .function => unreachable, // represents machine code; not a pointer1766 .function => unreachable, // represents machine code; not a pointer
1760 .c_void => unreachable,1767 .c_void => unreachable,
1761 .void => unreachable,
1762 .type => unreachable,1768 .type => unreachable,
1763 .comptime_int => unreachable,1769 .comptime_int => unreachable,
1764 .comptime_float => unreachable,1770 .comptime_float => unreachable,
...@@ -1767,7 +1773,6 @@ pub const Type = extern union {...@@ -1767,7 +1773,6 @@ pub const Type = extern union {
1767 .@"undefined" => unreachable,1773 .@"undefined" => unreachable,
1768 .enum_literal => unreachable,1774 .enum_literal => unreachable,
1769 .single_const_pointer_to_comptime_int => unreachable,1775 .single_const_pointer_to_comptime_int => unreachable,
1770 .empty_struct => unreachable,
1771 .empty_struct_literal => unreachable,1776 .empty_struct_literal => unreachable,
1772 .inferred_alloc_const => unreachable,1777 .inferred_alloc_const => unreachable,
1773 .inferred_alloc_mut => unreachable,1778 .inferred_alloc_mut => unreachable,
...@@ -1777,14 +1782,19 @@ pub const Type = extern union {...@@ -1777,14 +1782,19 @@ pub const Type = extern union {
1777 .type_info => unreachable,1782 .type_info => unreachable,
1778 .bound_fn => unreachable,1783 .bound_fn => unreachable,
17791784
1785 .empty_struct, .void => 0,
1786
1780 .@"struct" => {1787 .@"struct" => {
1781 const s = self.castTag(.@"struct").?.data;1788 const fields = self.structFields();
1782 assert(s.status == .have_layout);1789 if (self.castTag(.@"struct")) |payload| {
1783 const is_packed = s.layout == .Packed;1790 const struct_obj = payload.data;
1784 if (is_packed) @panic("TODO packed structs");1791 assert(struct_obj.status == .have_layout);
1792 const is_packed = struct_obj.layout == .Packed;
1793 if (is_packed) @panic("TODO packed structs");
1794 }
1785 var size: u64 = 0;1795 var size: u64 = 0;
1786 var big_align: u32 = 0;1796 var big_align: u32 = 0;
1787 for (s.fields.values()) |field| {1797 for (fields.values()) |field| {
1788 if (!field.ty.hasCodeGenBits()) continue;1798 if (!field.ty.hasCodeGenBits()) continue;
17891799
1790 const field_align = a: {1800 const field_align = a: {
...@@ -1829,7 +1839,7 @@ pub const Type = extern union {...@@ -1829,7 +1839,7 @@ pub const Type = extern union {
1829 .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1,1839 .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1,
1830 .array, .vector => {1840 .array, .vector => {
1831 const payload = self.cast(Payload.Array).?.data;1841 const payload = self.cast(Payload.Array).?.data;
1832 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));1842 const elem_size = @maximum(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
1833 return payload.len * elem_size;1843 return payload.len * elem_size;
1834 },1844 },
1835 .array_sentinel => {1845 .array_sentinel => {
...@@ -3331,6 +3341,7 @@ pub const Type = extern union {...@@ -3331,6 +3341,7 @@ pub const Type = extern union {
33313341
3332 pub fn structFields(ty: Type) Module.Struct.Fields {3342 pub fn structFields(ty: Type) Module.Struct.Fields {
3333 switch (ty.tag()) {3343 switch (ty.tag()) {
3344 .empty_struct => return .{},
3334 .@"struct" => {3345 .@"struct" => {
3335 const struct_obj = ty.castTag(.@"struct").?.data;3346 const struct_obj = ty.castTag(.@"struct").?.data;
3336 return struct_obj.fields;3347 return struct_obj.fields;
...@@ -3345,6 +3356,7 @@ pub const Type = extern union {...@@ -3345,6 +3356,7 @@ pub const Type = extern union {
3345 const struct_obj = ty.castTag(.@"struct").?.data;3356 const struct_obj = ty.castTag(.@"struct").?.data;
3346 return struct_obj.fields.count();3357 return struct_obj.fields.count();
3347 },3358 },
3359 .empty_struct => return 0,
3348 else => unreachable,3360 else => unreachable,
3349 }3361 }
3350 }3362 }
src/value.zig+47-11
...@@ -1399,6 +1399,7 @@ pub const Value = extern union {...@@ -1399,6 +1399,7 @@ pub const Value = extern union {
13991399
1400 switch (zig_ty_tag) {1400 switch (zig_ty_tag) {
1401 .BoundFn => unreachable, // TODO remove this from the language1401 .BoundFn => unreachable, // TODO remove this from the language
1402 .Opaque => unreachable, // Cannot hash opaque types
14021403
1403 .Void,1404 .Void,
1404 .NoReturn,1405 .NoReturn,
...@@ -1451,10 +1452,22 @@ pub const Value = extern union {...@@ -1451,10 +1452,22 @@ pub const Value = extern union {
1451 else => unreachable,1452 else => unreachable,
1452 },1453 },
1453 .Array, .Vector => {1454 .Array, .Vector => {
1454 @panic("TODO implement hashing array/vector values");1455 const len = ty.arrayLen();
1456 const elem_ty = ty.childType();
1457 var index: usize = 0;
1458 var elem_value_buf: ElemValueBuffer = undefined;
1459 while (index < len) : (index += 1) {
1460 const elem_val = val.elemValueBuffer(index, &elem_value_buf);
1461 elem_val.hash(elem_ty, hasher);
1462 }
1455 },1463 },
1456 .Struct => {1464 .Struct => {
1457 @panic("TODO implement hashing struct values");1465 const fields = ty.structFields().values();
1466 if (fields.len == 0) return;
1467 const field_values = val.castTag(.@"struct").?.data;
1468 for (field_values) |field_val, i| {
1469 field_val.hash(fields[i].ty, hasher);
1470 }
1458 },1471 },
1459 .Optional => {1472 .Optional => {
1460 if (val.castTag(.opt_payload)) |payload| {1473 if (val.castTag(.opt_payload)) |payload| {
...@@ -1486,7 +1499,7 @@ pub const Value = extern union {...@@ -1486,7 +1499,7 @@ pub const Value = extern union {
1486 }1499 }
1487 },1500 },
1488 .Union => {1501 .Union => {
1489 const union_obj = val.castTag(.@"union").?.data;1502 const union_obj = val.cast(Payload.Union).?.data;
1490 if (ty.unionTagType()) |tag_ty| {1503 if (ty.unionTagType()) |tag_ty| {
1491 union_obj.tag.hash(tag_ty, hasher);1504 union_obj.tag.hash(tag_ty, hasher);
1492 }1505 }
...@@ -1496,9 +1509,6 @@ pub const Value = extern union {...@@ -1496,9 +1509,6 @@ pub const Value = extern union {
1496 .Fn => {1509 .Fn => {
1497 @panic("TODO implement hashing function values");1510 @panic("TODO implement hashing function values");
1498 },1511 },
1499 .Opaque => {
1500 @panic("TODO implement hashing opaque values");
1501 },
1502 .Frame => {1512 .Frame => {
1503 @panic("TODO implement hashing frame values");1513 @panic("TODO implement hashing frame values");
1504 },1514 },
...@@ -1633,7 +1643,22 @@ pub const Value = extern union {...@@ -1633,7 +1643,22 @@ pub const Value = extern union {
16331643
1634 /// Asserts the value is a single-item pointer to an array, or an array,1644 /// Asserts the value is a single-item pointer to an array, or an array,
1635 /// or an unknown-length pointer, and returns the element value at the index.1645 /// or an unknown-length pointer, and returns the element value at the index.
1636 pub fn elemValue(val: Value, arena: *Allocator, index: usize) error{OutOfMemory}!Value {1646 pub fn elemValue(val: Value, arena: *Allocator, index: usize) !Value {
1647 return elemValueAdvanced(val, index, arena, undefined);
1648 }
1649
1650 pub const ElemValueBuffer = Payload.U64;
1651
1652 pub fn elemValueBuffer(val: Value, index: usize, buffer: *ElemValueBuffer) Value {
1653 return elemValueAdvanced(val, index, null, buffer) catch unreachable;
1654 }
1655
1656 pub fn elemValueAdvanced(
1657 val: Value,
1658 index: usize,
1659 arena: ?*Allocator,
1660 buffer: *ElemValueBuffer,
1661 ) error{OutOfMemory}!Value {
1637 switch (val.tag()) {1662 switch (val.tag()) {
1638 .empty_array => unreachable, // out of bounds array index1663 .empty_array => unreachable, // out of bounds array index
1639 .empty_struct_value => unreachable, // out of bounds array index1664 .empty_struct_value => unreachable, // out of bounds array index
...@@ -1643,16 +1668,27 @@ pub const Value = extern union {...@@ -1643,16 +1668,27 @@ pub const Value = extern union {
1643 return val.castTag(.empty_array_sentinel).?.data;1668 return val.castTag(.empty_array_sentinel).?.data;
1644 },1669 },
16451670
1646 .bytes => return Tag.int_u64.create(arena, val.castTag(.bytes).?.data[index]),1671 .bytes => {
1672 const byte = val.castTag(.bytes).?.data[index];
1673 if (arena) |a| {
1674 return Tag.int_u64.create(a, byte);
1675 } else {
1676 buffer.* = .{
1677 .base = .{ .tag = .int_u64 },
1678 .data = byte,
1679 };
1680 return initPayload(&buffer.base);
1681 }
1682 },
16471683
1648 // No matter the index; all the elements are the same!1684 // No matter the index; all the elements are the same!
1649 .repeated => return val.castTag(.repeated).?.data,1685 .repeated => return val.castTag(.repeated).?.data,
16501686
1651 .array => return val.castTag(.array).?.data[index],1687 .array => return val.castTag(.array).?.data[index],
1652 .slice => return val.castTag(.slice).?.data.ptr.elemValue(arena, index),1688 .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(index, arena, buffer),
16531689
1654 .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValue(arena, index),1690 .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
1655 .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValue(arena, index),1691 .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
16561692
1657 else => unreachable,1693 else => unreachable,
1658 }1694 }
test/behavior/array.zig+8
...@@ -104,3 +104,11 @@ test "array with sentinels" {...@@ -104,3 +104,11 @@ test "array with sentinels" {
104 try S.doTheTest(false);104 try S.doTheTest(false);
105 comptime try S.doTheTest(true);105 comptime try S.doTheTest(true);
106}106}
107
108test "void arrays" {
109 var array: [4]void = undefined;
110 array[0] = void{};
111 array[1] = array[2];
112 try expect(@sizeOf(@TypeOf(array)) == 0);
113 try expect(array.len == 4);
114}
test/behavior/array_stage1.zig-8
...@@ -4,14 +4,6 @@ const mem = std.mem;...@@ -4,14 +4,6 @@ const mem = std.mem;
4const expect = testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;5const expectEqual = testing.expectEqual;
66
7test "void arrays" {
8 var array: [4]void = undefined;
9 array[0] = void{};
10 array[1] = array[2];
11 try expect(@sizeOf(@TypeOf(array)) == 0);
12 try expect(array.len == 4);
13}
14
15test "nested arrays" {7test "nested arrays" {
16 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };8 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
17 for (array_of_strings) |s, i| {9 for (array_of_strings) |s, i| {