authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-06-21 23:51:11-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-22 11:45:33-07:00
log6aa88ecc54fde57af893456e9e5fcea5b3e1cb03
tree83a5a5f3890dd18c9f3559b3184b02fc7c9b27e4
parent7d511d642845c860ec212b9ee39e371d3f70a68b

Type/Value: garbage collect some methods


3 files changed, 7 insertions(+), 303 deletions(-)

src/Sema.zig+4-7
......@@ -8273,7 +8273,7 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
82738273
82748274 if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| {
82758275 const val = try enum_tag_val.intFromEnum(enum_tag_ty, mod);
8276 return sema.addConstant(int_tag_ty, try val.copy(sema.arena));
8276 return sema.addConstant(int_tag_ty, val);
82778277 }
82788278
82798279 try sema.requireRuntimeBlock(block, src, operand_src);
......@@ -28723,14 +28723,11 @@ fn beginComptimePtrMutation(
2872328723 // without making a call to this function.
2872428724 const arena = sema.arena;
2872528725
28726 const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena);
28726 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
2872728727 const array_len_including_sentinel =
2872828728 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
2872928729 const elems = try arena.alloc(Value, array_len_including_sentinel);
28730 if (elems.len > 0) elems[0] = repeated_val;
28731 for (elems[1..]) |*elem| {
28732 elem.* = try repeated_val.copy(arena);
28733 }
28730 @memset(elems, repeated_val.toValue());
2873428731
2873528732 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
2873628733
......@@ -36421,7 +36418,7 @@ fn valuesEqual(
3642136418 rhs: Value,
3642236419 ty: Type,
3642336420) CompileError!bool {
36424 return Value.eqlAdvanced(lhs, ty, rhs, ty, sema.mod, sema);
36421 return lhs.eql(rhs, ty, sema.mod);
3642536422}
3642636423
3642736424/// Asserts the values are comparable vectors of type `ty`.
src/type.zig-8
......@@ -120,14 +120,6 @@ pub const Type = struct {
120120 return a.toIntern() == b.toIntern();
121121 }
122122
123 pub fn hash(ty: Type, mod: *const Module) u32 {
124 _ = mod; // TODO: remove this parameter
125 // The InternPool data structure hashes based on Key to make interned objects
126 // unique. An Index can be treated simply as u32 value for the
127 // purpose of Type/Value hashing and equality.
128 return std.hash.uint32(@intFromEnum(ty.toIntern()));
129 }
130
131123 pub fn format(ty: Type, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
132124 _ = ty;
133125 _ = unused_fmt_string;
src/value.zig+3-288
......@@ -132,98 +132,6 @@ pub const Value = struct {
132132 return null;
133133 }
134134
135 /// It's intentional that this function is not passed a corresponding Type, so that
136 /// a Value can be copied from a Sema to a Decl prior to resolving struct/union field types.
137 pub fn copy(self: Value, arena: Allocator) error{OutOfMemory}!Value {
138 if (self.ip_index != .none) {
139 return Value{ .ip_index = self.ip_index, .legacy = undefined };
140 }
141 switch (self.legacy.ptr_otherwise.tag) {
142 .bytes => {
143 const bytes = self.castTag(.bytes).?.data;
144 const new_payload = try arena.create(Payload.Bytes);
145 new_payload.* = .{
146 .base = .{ .tag = .bytes },
147 .data = try arena.dupe(u8, bytes),
148 };
149 return Value{
150 .ip_index = .none,
151 .legacy = .{ .ptr_otherwise = &new_payload.base },
152 };
153 },
154 .eu_payload,
155 .opt_payload,
156 .repeated,
157 => {
158 const payload = self.cast(Payload.SubValue).?;
159 const new_payload = try arena.create(Payload.SubValue);
160 new_payload.* = .{
161 .base = payload.base,
162 .data = try payload.data.copy(arena),
163 };
164 return Value{
165 .ip_index = .none,
166 .legacy = .{ .ptr_otherwise = &new_payload.base },
167 };
168 },
169 .slice => {
170 const payload = self.castTag(.slice).?;
171 const new_payload = try arena.create(Payload.Slice);
172 new_payload.* = .{
173 .base = payload.base,
174 .data = .{
175 .ptr = try payload.data.ptr.copy(arena),
176 .len = try payload.data.len.copy(arena),
177 },
178 };
179 return Value{
180 .ip_index = .none,
181 .legacy = .{ .ptr_otherwise = &new_payload.base },
182 };
183 },
184 .aggregate => {
185 const payload = self.castTag(.aggregate).?;
186 const new_payload = try arena.create(Payload.Aggregate);
187 new_payload.* = .{
188 .base = payload.base,
189 .data = try arena.alloc(Value, payload.data.len),
190 };
191 for (new_payload.data, 0..) |*elem, i| {
192 elem.* = try payload.data[i].copy(arena);
193 }
194 return Value{
195 .ip_index = .none,
196 .legacy = .{ .ptr_otherwise = &new_payload.base },
197 };
198 },
199 .@"union" => {
200 const tag_and_val = self.castTag(.@"union").?.data;
201 const new_payload = try arena.create(Payload.Union);
202 new_payload.* = .{
203 .base = .{ .tag = .@"union" },
204 .data = .{
205 .tag = try tag_and_val.tag.copy(arena),
206 .val = try tag_and_val.val.copy(arena),
207 },
208 };
209 return Value{
210 .ip_index = .none,
211 .legacy = .{ .ptr_otherwise = &new_payload.base },
212 };
213 },
214 }
215 }
216
217 fn copyPayloadShallow(self: Value, arena: Allocator, comptime T: type) error{OutOfMemory}!Value {
218 const payload = self.cast(T).?;
219 const new_payload = try arena.create(T);
220 new_payload.* = payload.*;
221 return Value{
222 .ip_index = .none,
223 .legacy = .{ .ptr_otherwise = &new_payload.base },
224 };
225 }
226
227135 pub fn format(val: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
228136 _ = val;
229137 _ = fmt;
......@@ -1494,193 +1402,9 @@ pub const Value = struct {
14941402 }
14951403
14961404 pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
1497 return eqlAdvanced(a, ty, b, ty, mod, null) catch unreachable;
1498 }
1499
1500 /// This function is used by hash maps and so treats floating-point NaNs as equal
1501 /// to each other, and not equal to other floating-point values.
1502 /// Similarly, it treats `undef` as a distinct value from all other values.
1503 /// This function has to be able to support implicit coercion of `a` to `ty`. That is,
1504 /// `ty` will be an exactly correct Type for `b` but it may be a post-coerced Type
1505 /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication
1506 /// is required in order to make generic function instantiation efficient - specifically
1507 /// the insertion into the monomorphized function table.
1508 /// If `null` is provided for `opt_sema` then it is guaranteed no error will be returned.
1509 pub fn eqlAdvanced(
1510 a: Value,
1511 a_ty: Type,
1512 b: Value,
1513 ty: Type,
1514 mod: *Module,
1515 opt_sema: ?*Sema,
1516 ) Module.CompileError!bool {
1517 if (a.ip_index != .none or b.ip_index != .none) return a.ip_index == b.ip_index;
1518
1519 const target = mod.getTarget();
1520 const a_tag = a.tag();
1521 const b_tag = b.tag();
1522 if (a_tag == b_tag) switch (a_tag) {
1523 .aggregate => {
1524 const a_field_vals = a.castTag(.aggregate).?.data;
1525 const b_field_vals = b.castTag(.aggregate).?.data;
1526 assert(a_field_vals.len == b_field_vals.len);
1527
1528 switch (mod.intern_pool.indexToKey(ty.toIntern())) {
1529 .anon_struct_type => |anon_struct| {
1530 assert(anon_struct.types.len == a_field_vals.len);
1531 for (anon_struct.types, 0..) |field_ty, i| {
1532 if (!(try eqlAdvanced(a_field_vals[i], field_ty.toType(), b_field_vals[i], field_ty.toType(), mod, opt_sema))) {
1533 return false;
1534 }
1535 }
1536 return true;
1537 },
1538 .struct_type => |struct_type| {
1539 const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
1540 const fields = struct_obj.fields.values();
1541 assert(fields.len == a_field_vals.len);
1542 for (fields, 0..) |field, i| {
1543 if (!(try eqlAdvanced(a_field_vals[i], field.ty, b_field_vals[i], field.ty, mod, opt_sema))) {
1544 return false;
1545 }
1546 }
1547 return true;
1548 },
1549 else => {},
1550 }
1551
1552 const elem_ty = ty.childType(mod);
1553 for (a_field_vals, 0..) |a_elem, i| {
1554 const b_elem = b_field_vals[i];
1555
1556 if (!(try eqlAdvanced(a_elem, elem_ty, b_elem, elem_ty, mod, opt_sema))) {
1557 return false;
1558 }
1559 }
1560 return true;
1561 },
1562 .@"union" => {
1563 const a_union = a.castTag(.@"union").?.data;
1564 const b_union = b.castTag(.@"union").?.data;
1565 switch (ty.containerLayout(mod)) {
1566 .Packed, .Extern => {
1567 const tag_ty = ty.unionTagTypeHypothetical(mod);
1568 if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, opt_sema))) {
1569 // In this case, we must disregard mismatching tags and compare
1570 // based on the in-memory bytes of the payloads.
1571 @panic("TODO comptime comparison of extern union values with mismatching tags");
1572 }
1573 },
1574 .Auto => {
1575 const tag_ty = ty.unionTagTypeHypothetical(mod);
1576 if (!(try eqlAdvanced(a_union.tag, tag_ty, b_union.tag, tag_ty, mod, opt_sema))) {
1577 return false;
1578 }
1579 },
1580 }
1581 const active_field_ty = ty.unionFieldType(a_union.tag, mod);
1582 return eqlAdvanced(a_union.val, active_field_ty, b_union.val, active_field_ty, mod, opt_sema);
1583 },
1584 else => {},
1585 };
1586
1587 if (a.pointerDecl(mod)) |a_decl| {
1588 if (b.pointerDecl(mod)) |b_decl| {
1589 return a_decl == b_decl;
1590 } else {
1591 return false;
1592 }
1593 } else if (b.pointerDecl(mod)) |_| {
1594 return false;
1595 }
1596
1597 switch (ty.zigTypeTag(mod)) {
1598 .Type => {
1599 const a_type = a.toType();
1600 const b_type = b.toType();
1601 return a_type.eql(b_type, mod);
1602 },
1603 .Enum => {
1604 const a_val = try a.intFromEnum(ty, mod);
1605 const b_val = try b.intFromEnum(ty, mod);
1606 const int_ty = ty.intTagType(mod);
1607 return eqlAdvanced(a_val, int_ty, b_val, int_ty, mod, opt_sema);
1608 },
1609 .Array, .Vector => {
1610 const len = ty.arrayLen(mod);
1611 const elem_ty = ty.childType(mod);
1612 var i: usize = 0;
1613 while (i < len) : (i += 1) {
1614 const a_elem = try elemValue(a, mod, i);
1615 const b_elem = try elemValue(b, mod, i);
1616 if (!(try eqlAdvanced(a_elem, elem_ty, b_elem, elem_ty, mod, opt_sema))) {
1617 return false;
1618 }
1619 }
1620 return true;
1621 },
1622 .Pointer => switch (ty.ptrSize(mod)) {
1623 .Slice => {
1624 const a_len = switch (a_ty.ptrSize(mod)) {
1625 .Slice => a.sliceLen(mod),
1626 .One => a_ty.childType(mod).arrayLen(mod),
1627 else => unreachable,
1628 };
1629 if (a_len != b.sliceLen(mod)) {
1630 return false;
1631 }
1632
1633 const ptr_ty = ty.slicePtrFieldType(mod);
1634 const a_ptr = switch (a_ty.ptrSize(mod)) {
1635 .Slice => a.slicePtr(mod),
1636 .One => a,
1637 else => unreachable,
1638 };
1639 return try eqlAdvanced(a_ptr, ptr_ty, b.slicePtr(mod), ptr_ty, mod, opt_sema);
1640 },
1641 .Many, .C, .One => {},
1642 },
1643 .Struct => {
1644 // A struct can be represented with one of:
1645 // .the_one_possible_value,
1646 // .aggregate,
1647 // Note that we already checked above for matching tags, e.g. both .aggregate.
1648 return (try ty.onePossibleValue(mod)) != null;
1649 },
1650 .Union => {
1651 // Here we have to check for value equality, as-if `a` has been coerced to `ty`.
1652 if ((try ty.onePossibleValue(mod)) != null) {
1653 return true;
1654 }
1655 return false;
1656 },
1657 .Float => {
1658 switch (ty.floatBits(target)) {
1659 16 => return @bitCast(u16, a.toFloat(f16, mod)) == @bitCast(u16, b.toFloat(f16, mod)),
1660 32 => return @bitCast(u32, a.toFloat(f32, mod)) == @bitCast(u32, b.toFloat(f32, mod)),
1661 64 => return @bitCast(u64, a.toFloat(f64, mod)) == @bitCast(u64, b.toFloat(f64, mod)),
1662 80 => return @bitCast(u80, a.toFloat(f80, mod)) == @bitCast(u80, b.toFloat(f80, mod)),
1663 128 => return @bitCast(u128, a.toFloat(f128, mod)) == @bitCast(u128, b.toFloat(f128, mod)),
1664 else => unreachable,
1665 }
1666 },
1667 .ComptimeFloat => {
1668 const a_float = a.toFloat(f128, mod);
1669 const b_float = b.toFloat(f128, mod);
1670
1671 const a_nan = std.math.isNan(a_float);
1672 const b_nan = std.math.isNan(b_float);
1673 if (a_nan != b_nan) return false;
1674 if (std.math.signbit(a_float) != std.math.signbit(b_float)) return false;
1675 if (a_nan) return true;
1676 return a_float == b_float;
1677 },
1678 .Optional,
1679 .ErrorUnion,
1680 => unreachable, // handled by InternPool
1681 else => {},
1682 }
1683 return (try orderAdvanced(a, b, mod, opt_sema)).compare(.eq);
1405 assert(mod.intern_pool.typeOf(a.toIntern()) == ty.toIntern());
1406 assert(mod.intern_pool.typeOf(b.toIntern()) == ty.toIntern());
1407 return a.toIntern() == b.toIntern();
16841408 }
16851409
16861410 pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
......@@ -1736,15 +1460,6 @@ pub const Value = struct {
17361460 };
17371461 }
17381462
1739 fn hashInt(int_val: Value, hasher: *std.hash.Wyhash, mod: *Module) void {
1740 var buffer: BigIntSpace = undefined;
1741 const big = int_val.toBigInt(&buffer, mod);
1742 std.hash.autoHash(hasher, big.positive);
1743 for (big.limbs) |limb| {
1744 std.hash.autoHash(hasher, limb);
1745 }
1746 }
1747
17481463 pub const slice_ptr_index = 0;
17491464 pub const slice_len_index = 1;
17501465