| author | |
| committer | |
| log | 0d4a94f32fc71f81d54db038f013854b8e9f0ac4 |
| tree | 163668781596539d8d6f95e2de506aad544be355 |
| parent | b0f80ef0d5517b5ce8ba60dfae6dd986346f8d4c |
* 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 | } |
| 2040 | 2041 | ||
| ... | @@ -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 |
| 2044 | 2045 | ||
| 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 | } |
| 2050 | 2051 | ||
| 2051 | fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 2052 | fn 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 | } |
| 2079 | 2081 | ||
| ... | @@ -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 | } |
| 2099 | 2102 | ||
| ... | @@ -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(); |
| 2136 | 2139 | ||
| 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(); | ||
| 2149 | 2154 | ||
| 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; | ||
| 2153 | 2168 | ||
| 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); | ||
| 2161 | 2182 | ||
| 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 | } | ||
| 2172 | 2185 | ||
| 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 | } |
| 2191 | 2202 | ||
| ... | @@ -8844,6 +8855,7 @@ fn zirArrayInit( | ... | @@ -8844,6 +8855,7 @@ fn zirArrayInit( |
| 8844 | }; | 8855 | }; |
| 8845 | 8856 | ||
| 8846 | try sema.requireRuntimeBlock(block, runtime_src); | 8857 | try sema.requireRuntimeBlock(block, runtime_src); |
| 8858 | try sema.resolveTypeLayout(block, src, elem_ty); | ||
| 8847 | 8859 | ||
| 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, |
| 10198 | 10211 | ||
| 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 | } |
| 1864 | 1864 | ||
| 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; | ||
| 1868 | 1867 | ||
| 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 | } |
| 1581 | 1581 | ||
| 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 { |
| 1667 | 1667 | ||
| 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 | }, |
| 1700 | 1701 | ||
| 1701 | .@"struct" => { | 1702 | .@"struct" => { |
| 1702 | // TODO take into account field alignment | 1703 | const fields = self.structFields(); |
| 1703 | // also make this possible to fail, and lazy | 1704 | if (self.castTag(.@"struct")) |payload| { |
| 1704 | // I think we need to move all the functions from type.zig which can | 1705 | 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 just | 1707 | 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), |
| 1728 | 1733 | ||
| 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 pointer | 1765 | .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer |
| 1759 | .function => unreachable, // represents machine code; not a pointer | 1766 | .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, |
| 1779 | 1784 | ||
| 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; |
| 1789 | 1799 | ||
| 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 { |
| 3331 | 3341 | ||
| 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 { |
| 1399 | 1399 | ||
| 1400 | switch (zig_ty_tag) { | 1400 | switch (zig_ty_tag) { |
| 1401 | .BoundFn => unreachable, // TODO remove this from the language | 1401 | .BoundFn => unreachable, // TODO remove this from the language |
| 1402 | .Opaque => unreachable, // Cannot hash opaque types | ||
| 1402 | 1403 | ||
| 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 { |
| 1633 | 1643 | ||
| 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 index | 1663 | .empty_array => unreachable, // out of bounds array index |
| 1639 | .empty_struct_value => unreachable, // out of bounds array index | 1664 | .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 | }, |
| 1645 | 1670 | ||
| 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 | }, | ||
| 1647 | 1683 | ||
| 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, |
| 1650 | 1686 | ||
| 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), |
| 1653 | 1689 | ||
| 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), |
| 1656 | 1692 | ||
| 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 | |||
| 108 | test "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; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 5 | const expectEqual = testing.expectEqual; |
| 6 | 6 | ||
| 7 | test "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 | |||
| 15 | test "nested arrays" { | 7 | test "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| { |