authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-20 23:24:39-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-10 20:47:54-07:00
logdfd91abfe15e653cba7b61fef73340ea07c6e3e9
tree574fbae55c7cf30d53994cd2e8be291c53411fb4
parentcbf304d8c3f7f1e1746a98dcad979ecf79ed16b5

InternPool: add more pointer values


7 files changed, 583 insertions(+), 113 deletions(-)

src/Air.zig+1-1
......@@ -1292,7 +1292,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index, ip: InternPool) Type {
12921292 .try_ptr,
12931293 => return air.getRefType(datas[inst].ty_pl.ty),
12941294
1295 .interned => return ip.indexToKey(datas[inst].interned).typeOf().toType(),
1295 .interned => return ip.typeOf(datas[inst].interned).toType(),
12961296
12971297 .not,
12981298 .bitcast,
src/InternPool.zig+207-69
......@@ -510,6 +510,16 @@ pub const Key = union(enum) {
510510 runtime_index: RuntimeIndex,
511511 },
512512 int: Index,
513 eu_payload: Index,
514 opt_payload: Index,
515 comptime_field: Index,
516 elem: BaseIndex,
517 field: BaseIndex,
518
519 pub const BaseIndex = struct {
520 base: Index,
521 index: u64,
522 };
513523 };
514524 };
515525
......@@ -599,6 +609,7 @@ pub const Key = union(enum) {
599609
600610 .ptr => |ptr| {
601611 std.hash.autoHash(hasher, ptr.ty);
612 std.hash.autoHash(hasher, ptr.len);
602613 // Int-to-ptr pointers are hashed separately than decl-referencing pointers.
603614 // This is sound due to pointer provenance rules.
604615 std.hash.autoHash(hasher, @as(@typeInfo(Key.Ptr.Addr).Union.tag_type.?, ptr.addr));
......@@ -607,6 +618,11 @@ pub const Key = union(enum) {
607618 .decl => |decl| std.hash.autoHash(hasher, decl),
608619 .mut_decl => |mut_decl| std.hash.autoHash(hasher, mut_decl),
609620 .int => |int| std.hash.autoHash(hasher, int),
621 .eu_payload => |eu_payload| std.hash.autoHash(hasher, eu_payload),
622 .opt_payload => |opt_payload| std.hash.autoHash(hasher, opt_payload),
623 .comptime_field => |comptime_field| std.hash.autoHash(hasher, comptime_field),
624 .elem => |elem| std.hash.autoHash(hasher, elem),
625 .field => |field| std.hash.autoHash(hasher, field),
610626 }
611627 },
612628
......@@ -719,7 +735,7 @@ pub const Key = union(enum) {
719735
720736 .ptr => |a_info| {
721737 const b_info = b.ptr;
722 if (a_info.ty != b_info.ty) return false;
738 if (a_info.ty != b_info.ty or a_info.len != b_info.len) return false;
723739
724740 const AddrTag = @typeInfo(Key.Ptr.Addr).Union.tag_type.?;
725741 if (@as(AddrTag, a_info.addr) != @as(AddrTag, b_info.addr)) return false;
......@@ -729,6 +745,11 @@ pub const Key = union(enum) {
729745 .decl => |a_decl| a_decl == b_info.addr.decl,
730746 .mut_decl => |a_mut_decl| std.meta.eql(a_mut_decl, b_info.addr.mut_decl),
731747 .int => |a_int| a_int == b_info.addr.int,
748 .eu_payload => |a_eu_payload| a_eu_payload == b_info.addr.eu_payload,
749 .opt_payload => |a_opt_payload| a_opt_payload == b_info.addr.opt_payload,
750 .comptime_field => |a_comptime_field| a_comptime_field == b_info.addr.comptime_field,
751 .elem => |a_elem| std.meta.eql(a_elem, b_info.addr.elem),
752 .field => |a_field| std.meta.eql(a_field, b_info.addr.field),
732753 };
733754 },
734755
......@@ -1375,6 +1396,26 @@ pub const Tag = enum(u8) {
13751396 /// Only pointer types are allowed to have this encoding. Optional types must use
13761397 /// `opt_payload` or `opt_null`.
13771398 ptr_int,
1399 /// A pointer to the payload of an error union.
1400 /// data is Index of a pointer value to the error union.
1401 /// In order to use this encoding, one must ensure that the `InternPool`
1402 /// already contains the payload pointer type corresponding to this payload.
1403 ptr_eu_payload,
1404 /// A pointer to the payload of an optional.
1405 /// data is Index of a pointer value to the optional.
1406 /// In order to use this encoding, one must ensure that the `InternPool`
1407 /// already contains the payload pointer type corresponding to this payload.
1408 ptr_opt_payload,
1409 /// data is extra index of PtrComptimeField, which contains the pointer type and field value.
1410 ptr_comptime_field,
1411 /// A pointer to an array element.
1412 /// data is extra index of PtrBaseIndex, which contains the base array and element index.
1413 /// In order to use this encoding, one must ensure that the `InternPool`
1414 /// already contains the elem pointer type corresponding to this payload.
1415 ptr_elem,
1416 /// A pointer to a container field.
1417 /// data is extra index of PtrBaseIndex, which contains the base container and field index.
1418 ptr_field,
13781419 /// A slice.
13791420 /// data is extra index of PtrSlice, which contains the ptr and len values
13801421 /// In order to use this encoding, one must ensure that the `InternPool`
......@@ -1753,6 +1794,17 @@ pub const PtrInt = struct {
17531794 addr: Index,
17541795};
17551796
1797pub const PtrComptimeField = struct {
1798 ty: Index,
1799 field_val: Index,
1800};
1801
1802pub const PtrBaseIndex = struct {
1803 ty: Index,
1804 base: Index,
1805 index: Index,
1806};
1807
17561808pub const PtrSlice = struct {
17571809 ptr: Index,
17581810 len: Index,
......@@ -1956,10 +2008,10 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
19562008 },
19572009
19582010 .type_slice => {
1959 const ptr_ty_index = @intToEnum(Index, data);
1960 var result = indexToKey(ip, ptr_ty_index);
1961 result.ptr_type.size = .Slice;
1962 return result;
2011 const ptr_type_index = @intToEnum(Index, data);
2012 var result = indexToKey(ip, ptr_type_index).ptr_type;
2013 result.size = .Slice;
2014 return .{ .ptr_type = result };
19632015 },
19642016
19652017 .type_optional => .{ .opt_type = @intToEnum(Index, data) },
......@@ -2063,7 +2115,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
20632115 // The existence of `opt_payload` guarantees that the optional type will be
20642116 // stored in the `InternPool`.
20652117 const opt_ty = ip.getAssumeExists(.{
2066 .opt_type = indexToKey(ip, payload_val).typeOf(),
2118 .opt_type = ip.typeOf(payload_val),
20672119 });
20682120 return .{ .opt = .{
20692121 .ty = opt_ty,
......@@ -2108,14 +2160,59 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
21082160 .addr = .{ .int = info.addr },
21092161 } };
21102162 },
2163 .ptr_eu_payload => {
2164 const ptr_eu_index = @intToEnum(Index, data);
2165 var ptr_type = ip.indexToKey(ip.typeOf(ptr_eu_index)).ptr_type;
2166 ptr_type.elem_type = ip.indexToKey(ptr_type.elem_type).error_union_type.payload_type;
2167 return .{ .ptr = .{
2168 .ty = ip.getAssumeExists(.{ .ptr_type = ptr_type }),
2169 .addr = .{ .eu_payload = ptr_eu_index },
2170 } };
2171 },
2172 .ptr_opt_payload => {
2173 const ptr_opt_index = @intToEnum(Index, data);
2174 var ptr_type = ip.indexToKey(ip.typeOf(ptr_opt_index)).ptr_type;
2175 ptr_type.elem_type = ip.indexToKey(ptr_type.elem_type).opt_type;
2176 return .{ .ptr = .{
2177 .ty = ip.getAssumeExists(.{ .ptr_type = ptr_type }),
2178 .addr = .{ .opt_payload = ptr_opt_index },
2179 } };
2180 },
2181 .ptr_comptime_field => {
2182 const info = ip.extraData(PtrComptimeField, data);
2183 return .{ .ptr = .{
2184 .ty = info.ty,
2185 .addr = .{ .comptime_field = info.field_val },
2186 } };
2187 },
2188 .ptr_elem => {
2189 const info = ip.extraData(PtrBaseIndex, data);
2190 return .{ .ptr = .{
2191 .ty = info.ty,
2192 .addr = .{ .elem = .{
2193 .base = info.base,
2194 .index = ip.indexToKey(info.index).int.storage.u64,
2195 } },
2196 } };
2197 },
2198 .ptr_field => {
2199 const info = ip.extraData(PtrBaseIndex, data);
2200 return .{ .ptr = .{
2201 .ty = info.ty,
2202 .addr = .{ .field = .{
2203 .base = info.base,
2204 .index = ip.indexToKey(info.index).int.storage.u64,
2205 } },
2206 } };
2207 },
21112208 .ptr_slice => {
21122209 const info = ip.extraData(PtrSlice, data);
21132210 const ptr = ip.indexToKey(info.ptr).ptr;
2114 var ptr_ty = ip.indexToKey(ptr.ty);
2115 assert(ptr_ty.ptr_type.size == .Many);
2116 ptr_ty.ptr_type.size = .Slice;
2211 var ptr_type = ip.indexToKey(ptr.ty).ptr_type;
2212 assert(ptr_type.size == .Many);
2213 ptr_type.size = .Slice;
21172214 return .{ .ptr = .{
2118 .ty = ip.getAssumeExists(ptr_ty),
2215 .ty = ip.getAssumeExists(.{ .ptr_type = ptr_type }),
21192216 .addr = ptr.addr,
21202217 .len = info.len,
21212218 } };
......@@ -2301,9 +2398,7 @@ fn indexToKeyBigInt(ip: InternPool, limb_index: u32, positive: bool) Key {
23012398pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
23022399 const adapter: KeyAdapter = .{ .intern_pool = ip };
23032400 const gop = try ip.map.getOrPutAdapted(gpa, key, adapter);
2304 if (gop.found_existing) {
2305 return @intToEnum(Index, gop.index);
2306 }
2401 if (gop.found_existing) return @intToEnum(Index, gop.index);
23072402 try ip.items.ensureUnusedCapacity(gpa, 1);
23082403 switch (key) {
23092404 .int_type => |int_type| {
......@@ -2322,11 +2417,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
23222417 if (ptr_type.size == .Slice) {
23232418 var new_key = key;
23242419 new_key.ptr_type.size = .Many;
2325 const ptr_ty_index = try get(ip, gpa, new_key);
2420 const ptr_type_index = try get(ip, gpa, new_key);
23262421 try ip.items.ensureUnusedCapacity(gpa, 1);
23272422 ip.items.appendAssumeCapacity(.{
23282423 .tag = .type_slice,
2329 .data = @enumToInt(ptr_ty_index),
2424 .data = @enumToInt(ptr_type_index),
23302425 });
23312426 return @intToEnum(Index, ip.items.len - 1);
23322427 }
......@@ -2584,64 +2679,98 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
25842679
25852680 .extern_func => @panic("TODO"),
25862681
2587 .ptr => |ptr| switch (ptr.len) {
2588 .none => {
2589 assert(ip.indexToKey(ptr.ty).ptr_type.size != .Slice);
2590 switch (ptr.addr) {
2591 .@"var" => |@"var"| ip.items.appendAssumeCapacity(.{
2592 .tag = .ptr_var,
2593 .data = try ip.addExtra(gpa, PtrVar{
2594 .ty = ptr.ty,
2595 .init = @"var".init,
2596 .owner_decl = @"var".owner_decl,
2597 .lib_name = @"var".lib_name,
2598 .flags = .{
2599 .is_const = @"var".is_const,
2600 .is_threadlocal = @"var".is_threadlocal,
2601 .is_weak_linkage = @"var".is_weak_linkage,
2602 },
2682 .ptr => |ptr| {
2683 const ptr_type = ip.indexToKey(ptr.ty).ptr_type;
2684 switch (ptr.len) {
2685 .none => {
2686 assert(ptr_type.size != .Slice);
2687 switch (ptr.addr) {
2688 .@"var" => |@"var"| ip.items.appendAssumeCapacity(.{
2689 .tag = .ptr_var,
2690 .data = try ip.addExtra(gpa, PtrVar{
2691 .ty = ptr.ty,
2692 .init = @"var".init,
2693 .owner_decl = @"var".owner_decl,
2694 .lib_name = @"var".lib_name,
2695 .flags = .{
2696 .is_const = @"var".is_const,
2697 .is_threadlocal = @"var".is_threadlocal,
2698 .is_weak_linkage = @"var".is_weak_linkage,
2699 },
2700 }),
26032701 }),
2604 }),
2605 .decl => |decl| ip.items.appendAssumeCapacity(.{
2606 .tag = .ptr_decl,
2607 .data = try ip.addExtra(gpa, PtrDecl{
2608 .ty = ptr.ty,
2609 .decl = decl,
2702 .decl => |decl| ip.items.appendAssumeCapacity(.{
2703 .tag = .ptr_decl,
2704 .data = try ip.addExtra(gpa, PtrDecl{
2705 .ty = ptr.ty,
2706 .decl = decl,
2707 }),
26102708 }),
2611 }),
2612 .mut_decl => |mut_decl| ip.items.appendAssumeCapacity(.{
2613 .tag = .ptr_mut_decl,
2614 .data = try ip.addExtra(gpa, PtrMutDecl{
2615 .ty = ptr.ty,
2616 .decl = mut_decl.decl,
2617 .runtime_index = mut_decl.runtime_index,
2709 .mut_decl => |mut_decl| ip.items.appendAssumeCapacity(.{
2710 .tag = .ptr_mut_decl,
2711 .data = try ip.addExtra(gpa, PtrMutDecl{
2712 .ty = ptr.ty,
2713 .decl = mut_decl.decl,
2714 .runtime_index = mut_decl.runtime_index,
2715 }),
26182716 }),
2619 }),
2620 .int => |int| ip.items.appendAssumeCapacity(.{
2621 .tag = .ptr_int,
2622 .data = try ip.addExtra(gpa, PtrInt{
2623 .ty = ptr.ty,
2624 .addr = int,
2717 .int => |int| ip.items.appendAssumeCapacity(.{
2718 .tag = .ptr_int,
2719 .data = try ip.addExtra(gpa, PtrInt{
2720 .ty = ptr.ty,
2721 .addr = int,
2722 }),
26252723 }),
2626 }),
2627 }
2628 },
2629 else => {
2630 assert(ip.indexToKey(ptr.ty).ptr_type.size == .Slice);
2631 var new_key = key;
2632 new_key.ptr.ty = ip.slicePtrType(ptr.ty);
2633 new_key.ptr.len = .none;
2634 assert(ip.indexToKey(new_key.ptr.ty).ptr_type.size == .Many);
2635 const ptr_index = try get(ip, gpa, new_key);
2636 try ip.items.ensureUnusedCapacity(gpa, 1);
2637 ip.items.appendAssumeCapacity(.{
2638 .tag = .ptr_slice,
2639 .data = try ip.addExtra(gpa, PtrSlice{
2640 .ptr = ptr_index,
2641 .len = ptr.len,
2642 }),
2643 });
2644 },
2724 .eu_payload, .opt_payload => |data| ip.items.appendAssumeCapacity(.{
2725 .tag = switch (ptr.addr) {
2726 .eu_payload => .ptr_eu_payload,
2727 .opt_payload => .ptr_opt_payload,
2728 else => unreachable,
2729 },
2730 .data = @enumToInt(data),
2731 }),
2732 .comptime_field => |field_val| ip.items.appendAssumeCapacity(.{
2733 .tag = .ptr_comptime_field,
2734 .data = try ip.addExtra(gpa, PtrComptimeField{
2735 .ty = ptr.ty,
2736 .field_val = field_val,
2737 }),
2738 }),
2739 .elem, .field => |base_index| {
2740 const index_index = try get(ip, gpa, .{ .int = .{
2741 .ty = .usize_type,
2742 .storage = .{ .u64 = base_index.index },
2743 } });
2744 try ip.items.ensureUnusedCapacity(gpa, 1);
2745 ip.items.appendAssumeCapacity(.{
2746 .tag = .ptr_elem,
2747 .data = try ip.addExtra(gpa, PtrBaseIndex{
2748 .ty = ptr.ty,
2749 .base = base_index.base,
2750 .index = index_index,
2751 }),
2752 });
2753 },
2754 }
2755 },
2756 else => {
2757 assert(ptr_type.size == .Slice);
2758 var new_key = key;
2759 new_key.ptr.ty = ip.slicePtrType(ptr.ty);
2760 new_key.ptr.len = .none;
2761 assert(ip.indexToKey(new_key.ptr.ty).ptr_type.size == .Many);
2762 const ptr_index = try get(ip, gpa, new_key);
2763 try ip.items.ensureUnusedCapacity(gpa, 1);
2764 ip.items.appendAssumeCapacity(.{
2765 .tag = .ptr_slice,
2766 .data = try ip.addExtra(gpa, PtrSlice{
2767 .ptr = ptr_index,
2768 .len = ptr.len,
2769 }),
2770 });
2771 },
2772 }
2773 assert(ptr.ty == ip.indexToKey(@intToEnum(Index, ip.items.len - 1)).ptr.ty);
26452774 },
26462775
26472776 .opt => |opt| {
......@@ -3683,6 +3812,11 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
36833812 .ptr_decl => @sizeOf(PtrDecl),
36843813 .ptr_mut_decl => @sizeOf(PtrMutDecl),
36853814 .ptr_int => @sizeOf(PtrInt),
3815 .ptr_eu_payload => 0,
3816 .ptr_opt_payload => 0,
3817 .ptr_comptime_field => @sizeOf(PtrComptimeField),
3818 .ptr_elem => @sizeOf(PtrBaseIndex),
3819 .ptr_field => @sizeOf(PtrBaseIndex),
36863820 .ptr_slice => @sizeOf(PtrSlice),
36873821 .opt_null => 0,
36883822 .opt_payload => 0,
......@@ -3757,6 +3891,10 @@ pub fn unionPtr(ip: *InternPool, index: Module.Union.Index) *Module.Union {
37573891 return ip.allocated_unions.at(@enumToInt(index));
37583892}
37593893
3894pub fn unionPtrConst(ip: InternPool, index: Module.Union.Index) *const Module.Union {
3895 return ip.allocated_unions.at(@enumToInt(index));
3896}
3897
37603898pub fn inferredErrorSetPtr(ip: *InternPool, index: Module.Fn.InferredErrorSet.Index) *Module.Fn.InferredErrorSet {
37613899 return ip.allocated_inferred_error_sets.at(@enumToInt(index));
37623900}
src/Module.zig+2-2
......@@ -6783,7 +6783,7 @@ pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allo
67836783
67846784pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type {
67856785 if (std.debug.runtime_safety and info.sentinel != .none) {
6786 const sent_ty = mod.intern_pool.indexToKey(info.sentinel).typeOf();
6786 const sent_ty = mod.intern_pool.typeOf(info.sentinel);
67876787 assert(sent_ty == info.child);
67886788 }
67896789 const i = try intern(mod, .{ .array_type = info });
......@@ -6802,7 +6802,7 @@ pub fn optionalType(mod: *Module, child_type: InternPool.Index) Allocator.Error!
68026802
68036803pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type {
68046804 if (std.debug.runtime_safety and info.sentinel != .none) {
6805 const sent_ty = mod.intern_pool.indexToKey(info.sentinel).typeOf();
6805 const sent_ty = mod.intern_pool.typeOf(info.sentinel);
68066806 assert(sent_ty == info.elem_type);
68076807 }
68086808 const i = try intern(mod, .{ .ptr_type = info });
src/Sema.zig+186-8
......@@ -28473,6 +28473,178 @@ fn beginComptimePtrLoad(
2847328473 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
2847428474 };
2847528475 },
28476 .eu_payload, .opt_payload => |container_ptr| blk: {
28477 const container_ty = mod.intern_pool.typeOf(container_ptr).toType().childType(mod);
28478 const payload_ty = ptr.ty.toType().childType(mod);
28479 var deref = try sema.beginComptimePtrLoad(block, src, container_ptr.toValue(), container_ty);
28480
28481 // eu_payload_ptr and opt_payload_ptr never have a well-defined layout
28482 if (deref.parent != null) {
28483 deref.parent = null;
28484 deref.ty_without_well_defined_layout = container_ty;
28485 }
28486
28487 if (deref.pointee) |*tv| {
28488 const coerce_in_mem_ok =
28489 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
28490 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
28491 if (coerce_in_mem_ok) {
28492 const payload_val = switch (ptr_val.tag()) {
28493 .eu_payload_ptr => if (tv.val.castTag(.eu_payload)) |some| some.data else {
28494 return sema.fail(block, src, "attempt to unwrap error: {s}", .{tv.val.castTag(.@"error").?.data.name});
28495 },
28496 .opt_payload_ptr => if (tv.val.castTag(.opt_payload)) |some| some.data else opt: {
28497 if (tv.val.isNull(mod)) return sema.fail(block, src, "attempt to use null value", .{});
28498 break :opt tv.val;
28499 },
28500 else => unreachable,
28501 };
28502 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
28503 break :blk deref;
28504 }
28505 }
28506 deref.pointee = null;
28507 break :blk deref;
28508 },
28509 .comptime_field => |comptime_field| blk: {
28510 const field_ty = mod.intern_pool.typeOf(comptime_field).toType();
28511 break :blk ComptimePtrLoadKit{
28512 .parent = null,
28513 .pointee = .{ .ty = field_ty, .val = comptime_field.toValue() },
28514 .is_mutable = false,
28515 .ty_without_well_defined_layout = field_ty,
28516 };
28517 },
28518 .elem => |elem_ptr| blk: {
28519 const elem_ty = ptr.ty.toType().childType(mod);
28520 var deref = try sema.beginComptimePtrLoad(block, src, elem_ptr.base.toValue(), null);
28521
28522 // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
28523 // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
28524 // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
28525 switch (mod.intern_pool.indexToKey(elem_ptr.base)) {
28526 .ptr => |base_ptr| switch (base_ptr.addr) {
28527 .elem => |base_elem| assert(!mod.intern_pool.typeOf(base_elem.base).toType().elemType2(mod).eql(elem_ty, mod)),
28528 else => {},
28529 },
28530 else => {},
28531 }
28532
28533 if (elem_ptr.index != 0) {
28534 if (elem_ty.hasWellDefinedLayout(mod)) {
28535 if (deref.parent) |*parent| {
28536 // Update the byte offset (in-place)
28537 const elem_size = try sema.typeAbiSize(elem_ty);
28538 const offset = parent.byte_offset + elem_size * elem_ptr.index;
28539 parent.byte_offset = try sema.usizeCast(block, src, offset);
28540 }
28541 } else {
28542 deref.parent = null;
28543 deref.ty_without_well_defined_layout = elem_ty;
28544 }
28545 }
28546
28547 // If we're loading an elem that was derived from a different type
28548 // than the true type of the underlying decl, we cannot deref directly
28549 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: {
28550 const deref_elem_ty = deref.pointee.?.ty.childType(mod);
28551 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
28552 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
28553 } else false;
28554 if (!ty_matches) {
28555 deref.pointee = null;
28556 break :blk deref;
28557 }
28558
28559 var array_tv = deref.pointee.?;
28560 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);
28561 if (maybe_array_ty) |load_ty| {
28562 // It's possible that we're loading a [N]T, in which case we'd like to slice
28563 // the pointee array directly from our parent array.
28564 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
28565 const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
28566 deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{
28567 .ty = try Type.array(sema.arena, N, null, elem_ty, mod),
28568 .val = try array_tv.val.sliceArray(mod, sema.arena, elem_ptr.index, elem_ptr.index + N),
28569 } else null;
28570 break :blk deref;
28571 }
28572 }
28573
28574 if (elem_ptr.index >= check_len) {
28575 deref.pointee = null;
28576 break :blk deref;
28577 }
28578 if (elem_ptr.index == check_len - 1) {
28579 if (array_tv.ty.sentinel(mod)) |sent| {
28580 deref.pointee = TypedValue{
28581 .ty = elem_ty,
28582 .val = sent,
28583 };
28584 break :blk deref;
28585 }
28586 }
28587 deref.pointee = TypedValue{
28588 .ty = elem_ty,
28589 .val = try array_tv.val.elemValue(mod, elem_ptr.index),
28590 };
28591 break :blk deref;
28592 },
28593 .field => |field_ptr| blk: {
28594 const field_index = @intCast(u32, field_ptr.index);
28595 const container_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
28596 var deref = try sema.beginComptimePtrLoad(block, src, field_ptr.base.toValue(), container_ty);
28597
28598 if (container_ty.hasWellDefinedLayout(mod)) {
28599 const struct_obj = mod.typeToStruct(container_ty);
28600 if (struct_obj != null and struct_obj.?.layout == .Packed) {
28601 // packed structs are not byte addressable
28602 deref.parent = null;
28603 } else if (deref.parent) |*parent| {
28604 // Update the byte offset (in-place)
28605 try sema.resolveTypeLayout(container_ty);
28606 const field_offset = container_ty.structFieldOffset(field_index, mod);
28607 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
28608 }
28609 } else {
28610 deref.parent = null;
28611 deref.ty_without_well_defined_layout = container_ty;
28612 }
28613
28614 const tv = deref.pointee orelse {
28615 deref.pointee = null;
28616 break :blk deref;
28617 };
28618 const coerce_in_mem_ok =
28619 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
28620 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
28621 if (!coerce_in_mem_ok) {
28622 deref.pointee = null;
28623 break :blk deref;
28624 }
28625
28626 if (container_ty.isSlice(mod)) {
28627 const slice_val = tv.val.castTag(.slice).?.data;
28628 deref.pointee = switch (field_index) {
28629 Value.Payload.Slice.ptr_index => TypedValue{
28630 .ty = container_ty.slicePtrFieldType(mod),
28631 .val = slice_val.ptr,
28632 },
28633 Value.Payload.Slice.len_index => TypedValue{
28634 .ty = Type.usize,
28635 .val = slice_val.len,
28636 },
28637 else => unreachable,
28638 };
28639 } else {
28640 const field_ty = container_ty.structFieldType(field_index, mod);
28641 deref.pointee = TypedValue{
28642 .ty = field_ty,
28643 .val = try tv.val.fieldValue(tv.ty, mod, field_index),
28644 };
28645 }
28646 break :blk deref;
28647 },
2847628648 },
2847728649 else => unreachable,
2847828650 },
......@@ -28559,11 +28731,12 @@ fn coerceArrayPtrToSlice(
2855928731 if (try sema.resolveMaybeUndefVal(inst)) |val| {
2856028732 const ptr_array_ty = sema.typeOf(inst);
2856128733 const array_ty = ptr_array_ty.childType(mod);
28562 const slice_val = try Value.Tag.slice.create(sema.arena, .{
28563 .ptr = val,
28564 .len = try mod.intValue(Type.usize, array_ty.arrayLen(mod)),
28565 });
28566 return sema.addConstant(dest_ty, slice_val);
28734 const slice_val = try mod.intern(.{ .ptr = .{
28735 .ty = dest_ty.ip_index,
28736 .addr = mod.intern_pool.indexToKey(val.ip_index).ptr.addr,
28737 .len = (try mod.intValue(Type.usize, array_ty.arrayLen(mod))).ip_index,
28738 } });
28739 return sema.addConstant(dest_ty, slice_val.toValue());
2856728740 }
2856828741 try sema.requireRuntimeBlock(block, inst_src, null);
2856928742 return block.addTyOp(.array_to_slice, dest_ty, inst);
......@@ -29769,6 +29942,7 @@ fn analyzeSlice(
2976929942
2977029943 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
2977129944 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
29945 const new_ptr_ty = sema.typeOf(new_ptr);
2977229946
2977329947 // true if and only if the end index of the slice, implicitly or explicitly, equals
2977429948 // the length of the underlying object being sliced. we might learn the length of the
......@@ -29914,7 +30088,7 @@ fn analyzeSlice(
2991430088 const end_int = end_val.getUnsignedInt(mod).?;
2991530089 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
2991630090
29917 const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
30091 const elem_ptr = try ptr_val.elemPtr(new_ptr_ty, sema.arena, sentinel_index, sema.mod);
2991830092 const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
2991930093 const actual_sentinel = switch (res) {
2992030094 .runtime_load => break :sentinel_check,
......@@ -29960,7 +30134,7 @@ fn analyzeSlice(
2996030134 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
2996130135 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
2996230136
29963 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo(mod);
30137 const new_ptr_ty_info = new_ptr_ty.ptrInfo(mod);
2996430138 const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize(mod) != .C;
2996530139
2996630140 if (opt_new_len_val) |new_len_val| {
......@@ -30009,7 +30183,11 @@ fn analyzeSlice(
3000930183 };
3001030184
3001130185 if (!new_ptr_val.isUndef(mod)) {
30012 return sema.addConstant(return_ty, new_ptr_val);
30186 return sema.addConstant(return_ty, (try mod.intern_pool.getCoerced(
30187 mod.gpa,
30188 try new_ptr_val.intern(new_ptr_ty, mod),
30189 return_ty.ip_index,
30190 )).toValue());
3001330191 }
3001430192
3001530193 // Special case: @as([]i32, undefined)[x..x]
src/codegen/llvm.zig+134-2
......@@ -3374,9 +3374,15 @@ pub const DeclGen = struct {
33743374 val;
33753375 break :ptr addrspace_casted_ptr;
33763376 },
3377 .decl => |decl| try lowerDeclRefValue(dg, tv, decl),
3378 .mut_decl => |mut_decl| try lowerDeclRefValue(dg, tv, mut_decl.decl),
3377 .decl => |decl| try dg.lowerDeclRefValue(tv, decl),
3378 .mut_decl => |mut_decl| try dg.lowerDeclRefValue(tv, mut_decl.decl),
33793379 .int => |int| dg.lowerIntAsPtr(mod.intern_pool.indexToKey(int).int),
3380 .eu_payload,
3381 .opt_payload,
3382 .elem,
3383 .field,
3384 => try dg.lowerParentPtr(tv.val, tv.ty.ptrInfo(mod).bit_offset % 8 == 0),
3385 .comptime_field => unreachable,
33803386 };
33813387 switch (ptr.len) {
33823388 .none => return ptr_val,
......@@ -4091,6 +4097,132 @@ pub const DeclGen = struct {
40914097 .decl => |decl| dg.lowerParentPtrDecl(ptr_val, decl),
40924098 .mut_decl => |mut_decl| dg.lowerParentPtrDecl(ptr_val, mut_decl.decl),
40934099 .int => |int| dg.lowerIntAsPtr(mod.intern_pool.indexToKey(int).int),
4100 .eu_payload => |eu_ptr| {
4101 const parent_llvm_ptr = try dg.lowerParentPtr(eu_ptr.toValue(), true);
4102
4103 const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod);
4104 const payload_ty = eu_ty.errorUnionPayload(mod);
4105 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4106 // In this case, we represent pointer to error union the same as pointer
4107 // to the payload.
4108 return parent_llvm_ptr;
4109 }
4110
4111 const payload_offset: u8 = if (payload_ty.abiAlignment(mod) > Type.anyerror.abiSize(mod)) 2 else 1;
4112 const llvm_u32 = dg.context.intType(32);
4113 const indices: [2]*llvm.Value = .{
4114 llvm_u32.constInt(0, .False),
4115 llvm_u32.constInt(payload_offset, .False),
4116 };
4117 const eu_llvm_ty = try dg.lowerType(eu_ty);
4118 return eu_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4119 },
4120 .opt_payload => |opt_ptr| {
4121 const parent_llvm_ptr = try dg.lowerParentPtr(opt_ptr.toValue(), true);
4122
4123 const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod);
4124 const payload_ty = opt_ty.optionalChild(mod);
4125 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod) or
4126 payload_ty.optionalReprIsPayload(mod))
4127 {
4128 // In this case, we represent pointer to optional the same as pointer
4129 // to the payload.
4130 return parent_llvm_ptr;
4131 }
4132
4133 const llvm_u32 = dg.context.intType(32);
4134 const indices: [2]*llvm.Value = .{
4135 llvm_u32.constInt(0, .False),
4136 llvm_u32.constInt(0, .False),
4137 };
4138 const opt_llvm_ty = try dg.lowerType(opt_ty);
4139 return opt_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4140 },
4141 .comptime_field => unreachable,
4142 .elem => |elem_ptr| {
4143 const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.base.toValue(), true);
4144
4145 const llvm_usize = try dg.lowerType(Type.usize);
4146 const indices: [1]*llvm.Value = .{
4147 llvm_usize.constInt(elem_ptr.index, .False),
4148 };
4149 const elem_llvm_ty = try dg.lowerType(ptr.ty.toType().childType(mod));
4150 return elem_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4151 },
4152 .field => |field_ptr| {
4153 const parent_llvm_ptr = try dg.lowerParentPtr(field_ptr.base.toValue(), byte_aligned);
4154 const parent_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);
4155
4156 const field_index = @intCast(u32, field_ptr.index);
4157 const llvm_u32 = dg.context.intType(32);
4158 switch (parent_ty.zigTypeTag(mod)) {
4159 .Union => {
4160 if (parent_ty.containerLayout(mod) == .Packed) {
4161 return parent_llvm_ptr;
4162 }
4163
4164 const layout = parent_ty.unionGetLayout(mod);
4165 if (layout.payload_size == 0) {
4166 // In this case a pointer to the union and a pointer to any
4167 // (void) payload is the same.
4168 return parent_llvm_ptr;
4169 }
4170 const llvm_pl_index = if (layout.tag_size == 0)
4171 0
4172 else
4173 @boolToInt(layout.tag_align >= layout.payload_align);
4174 const indices: [2]*llvm.Value = .{
4175 llvm_u32.constInt(0, .False),
4176 llvm_u32.constInt(llvm_pl_index, .False),
4177 };
4178 const parent_llvm_ty = try dg.lowerType(parent_ty);
4179 return parent_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4180 },
4181 .Struct => {
4182 if (parent_ty.containerLayout(mod) == .Packed) {
4183 if (!byte_aligned) return parent_llvm_ptr;
4184 const llvm_usize = dg.context.intType(target.cpu.arch.ptrBitWidth());
4185 const base_addr = parent_llvm_ptr.constPtrToInt(llvm_usize);
4186 // count bits of fields before this one
4187 const prev_bits = b: {
4188 var b: usize = 0;
4189 for (parent_ty.structFields(mod).values()[0..field_index]) |field| {
4190 if (field.is_comptime or !field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
4191 b += @intCast(usize, field.ty.bitSize(mod));
4192 }
4193 break :b b;
4194 };
4195 const byte_offset = llvm_usize.constInt(prev_bits / 8, .False);
4196 const field_addr = base_addr.constAdd(byte_offset);
4197 const final_llvm_ty = dg.context.pointerType(0);
4198 return field_addr.constIntToPtr(final_llvm_ty);
4199 }
4200
4201 const parent_llvm_ty = try dg.lowerType(parent_ty);
4202 if (llvmField(parent_ty, field_index, mod)) |llvm_field| {
4203 const indices: [2]*llvm.Value = .{
4204 llvm_u32.constInt(0, .False),
4205 llvm_u32.constInt(llvm_field.index, .False),
4206 };
4207 return parent_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4208 } else {
4209 const llvm_index = llvm_u32.constInt(@boolToInt(parent_ty.hasRuntimeBitsIgnoreComptime(mod)), .False);
4210 const indices: [1]*llvm.Value = .{llvm_index};
4211 return parent_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4212 }
4213 },
4214 .Pointer => {
4215 assert(parent_ty.isSlice(mod));
4216 const indices: [2]*llvm.Value = .{
4217 llvm_u32.constInt(0, .False),
4218 llvm_u32.constInt(field_index, .False),
4219 };
4220 const parent_llvm_ty = try dg.lowerType(parent_ty);
4221 return parent_llvm_ty.constInBoundsGEP(parent_llvm_ptr, &indices, indices.len);
4222 },
4223 else => unreachable,
4224 }
4225 },
40944226 },
40954227 else => unreachable,
40964228 };
src/print_zir.zig+1-1
......@@ -1192,7 +1192,7 @@ const Writer = struct {
11921192 .field => {
11931193 const field_name = self.code.nullTerminatedString(extra.data.field_name_start);
11941194 try self.writeInstRef(stream, extra.data.obj_ptr);
1195 try stream.print(", {}", .{std.zig.fmtId(field_name)});
1195 try stream.print(", \"{}\"", .{std.zig.fmtEscapes(field_name)});
11961196 },
11971197 }
11981198 try stream.writeAll(", [");
src/value.zig+52-30
......@@ -559,37 +559,46 @@ pub const Value = struct {
559559 /// Asserts that the value is representable as an array of bytes.
560560 /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
561561 pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
562 switch (val.tag()) {
563 .bytes => {
564 const bytes = val.castTag(.bytes).?.data;
565 const adjusted_len = bytes.len - @boolToInt(ty.sentinel(mod) != null);
566 const adjusted_bytes = bytes[0..adjusted_len];
567 return allocator.dupe(u8, adjusted_bytes);
568 },
569 .str_lit => {
570 const str_lit = val.castTag(.str_lit).?.data;
571 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
572 return allocator.dupe(u8, bytes);
573 },
574 .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data),
575 .repeated => {
576 const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(mod));
577 const result = try allocator.alloc(u8, @intCast(usize, ty.arrayLen(mod)));
578 @memset(result, byte);
579 return result;
580 },
581 .decl_ref => {
582 const decl_index = val.castTag(.decl_ref).?.data;
583 const decl = mod.declPtr(decl_index);
584 const decl_val = try decl.value();
585 return decl_val.toAllocatedBytes(decl.ty, allocator, mod);
562 switch (val.ip_index) {
563 .none => switch (val.tag()) {
564 .bytes => {
565 const bytes = val.castTag(.bytes).?.data;
566 const adjusted_len = bytes.len - @boolToInt(ty.sentinel(mod) != null);
567 const adjusted_bytes = bytes[0..adjusted_len];
568 return allocator.dupe(u8, adjusted_bytes);
569 },
570 .str_lit => {
571 const str_lit = val.castTag(.str_lit).?.data;
572 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
573 return allocator.dupe(u8, bytes);
574 },
575 .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data),
576 .repeated => {
577 const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(mod));
578 const result = try allocator.alloc(u8, @intCast(usize, ty.arrayLen(mod)));
579 @memset(result, byte);
580 return result;
581 },
582 .decl_ref => {
583 const decl_index = val.castTag(.decl_ref).?.data;
584 const decl = mod.declPtr(decl_index);
585 const decl_val = try decl.value();
586 return decl_val.toAllocatedBytes(decl.ty, allocator, mod);
587 },
588 .the_only_possible_value => return &[_]u8{},
589 .slice => {
590 const slice = val.castTag(.slice).?.data;
591 return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(mod), allocator, mod);
592 },
593 else => return arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
586594 },
587 .the_only_possible_value => return &[_]u8{},
588 .slice => {
589 const slice = val.castTag(.slice).?.data;
590 return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(mod), allocator, mod);
595 else => switch (mod.intern_pool.indexToKey(val.ip_index)) {
596 .ptr => |ptr| switch (ptr.len) {
597 .none => unreachable,
598 else => return arrayToAllocatedBytes(val, ptr.len.toValue().toUnsignedInt(mod), allocator, mod),
599 },
600 else => unreachable,
591601 },
592 else => return arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod),
593602 }
594603 }
595604
......@@ -605,6 +614,16 @@ pub const Value = struct {
605614 pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
606615 if (val.ip_index != .none) return mod.intern_pool.getCoerced(mod.gpa, val.ip_index, ty.ip_index);
607616 switch (val.tag()) {
617 .elem_ptr => {
618 const pl = val.castTag(.elem_ptr).?.data;
619 return mod.intern(.{ .ptr = .{
620 .ty = ty.ip_index,
621 .addr = .{ .elem = .{
622 .base = pl.array_ptr.ip_index,
623 .index = pl.index,
624 } },
625 } });
626 },
608627 .slice => {
609628 const pl = val.castTag(.slice).?.data;
610629 const ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod);
......@@ -2601,7 +2620,10 @@ pub const Value = struct {
26012620 .@"var" => unreachable,
26022621 .decl => |decl| mod.declPtr(decl).val.elemValue(mod, index),
26032622 .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).val.elemValue(mod, index),
2604 .int => unreachable,
2623 .int, .eu_payload, .opt_payload => unreachable,
2624 .comptime_field => |field_val| field_val.toValue().elemValue(mod, index),
2625 .elem => |elem| elem.base.toValue().elemValue(mod, index + elem.index),
2626 .field => unreachable,
26052627 },
26062628 .aggregate => |aggregate| switch (aggregate.storage) {
26072629 .elems => |elems| elems[index].toValue(),