authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-23 03:15:36+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-23 03:15:36+02:00
log27c099a7331ead56e3e47413396b2c1974dee073
treedc0a326c20b2fbbc5708f9d3d02bc8e612072acf
parent2de7cf9425737a1ce98fda505b5a0166eaad137c
parent711130742b747249e472c6d6b8d77d6692a28744

Merge pull request 'InternPool: include sentinel when checking for repeating elems' (#37342) from squeek502/zig:ip-elems-repeated-sentinel into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37342 Reviewed-by: Jacob Young <jacobly@ziglang.org> Reviewed-by: Andrew Kelley <andrew@ziglang.org>

5 files changed, 73 insertions(+), 13 deletions(-)

src/InternPool.zig+2-2
......@@ -7902,9 +7902,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
79027902
79037903 repeated: {
79047904 switch (aggregate.storage) {
7905 .bytes => |bytes| for (bytes.toSlice(len, ip)[1..]) |byte|
7905 .bytes => |bytes| for (bytes.toSlice(len_including_sentinel, ip)[1..]) |byte|
79067906 if (byte != bytes.at(0, ip)) break :repeated,
7907 .elems => |elems| for (elems[1..@intCast(len)]) |elem|
7907 .elems => |elems| for (elems[1..@intCast(len_including_sentinel)]) |elem|
79087908 if (elem != elems[0]) break :repeated,
79097909 .repeated_elem => {},
79107910 }
src/Sema.zig+28-7
......@@ -13570,13 +13570,23 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1357013570
1357113571 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
1357213572 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
13573 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
13574 error.Overflow => return sema.fail(
13573 const result_len, const result_len_with_sent = blk: {
13574 if (std.math.add(usize, lhs_len, rhs_len)) |result_len| {
13575 const sent_len = @intFromBool(res_sent_val != null);
13576 if (std.math.add(usize, result_len, sent_len)) |with_sent| {
13577 break :blk .{ result_len, with_sent };
13578 } else |err| switch (err) {
13579 error.Overflow => {},
13580 }
13581 } else |err| switch (err) {
13582 error.Overflow => {},
13583 }
13584 return sema.fail(
1357513585 block,
1357613586 src,
1357713587 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
1357813588 .{ lhs_len, rhs_len },
13579 ),
13589 );
1358013590 };
1358113591
1358213592 const result_ty = try pt.arrayType(.{
......@@ -13614,7 +13624,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1361413624 else
1361513625 rhs_val;
1361613626
13617 const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
13627 const element_vals = try sema.arena.alloc(InternPool.Index, result_len_with_sent);
1361813628 var elem_i: u32 = 0;
1361913629 while (elem_i < lhs_len) : (elem_i += 1) {
1362013630 const lhs_elem_i = elem_i;
......@@ -13642,6 +13652,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1364213652 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;
1364313653 element_vals[elem_i] = coerced_elem_val.toIntern();
1364413654 }
13655 if (res_sent_val) |v| {
13656 element_vals[result_len] = v.toIntern();
13657 }
1364513658 return sema.addConstantMaybeRef(
1364613659 try pt.aggregateValue(result_ty, element_vals),
1364713660 ptr_addrspace != null,
......@@ -30644,11 +30657,18 @@ fn coerceArrayLike(
3064430657 return block.addTyOp(.array_to_vector, dest_ty, inst);
3064530658 }
3064630659
30647 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
30648 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
30660 const dest_len_including_sent = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel(zcu));
30661 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len_including_sent);
30662 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len_including_sent);
3064930663 var runtime_src: ?LazySrcLoc = null;
3065030664
3065130665 for (element_vals, element_refs, 0..) |*val, *ref, i| {
30666 if (i == dest_len) {
30667 const sentinel_val = dest_ty.sentinel(zcu).?;
30668 val.* = sentinel_val.toIntern();
30669 ref.* = Air.internedToRef(sentinel_val.toIntern());
30670 break;
30671 }
3065230672 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
3065330673 const src = inst_src; // TODO better source location
3065430674 const elem_src = inst_src; // TODO better source location
......@@ -30686,6 +30706,7 @@ fn coerceTupleToArray(
3068630706 const inst_ty = sema.typeOf(inst);
3068730707 const inst_len = inst_ty.arrayLen(zcu);
3068830708 const dest_len = dest_ty.arrayLen(zcu);
30709 const dest_len_including_sent = dest_ty.arrayLenIncludingSentinel(zcu);
3068930710
3069030711 if (dest_len != inst_len) {
3069130712 const msg = msg: {
......@@ -30698,7 +30719,7 @@ fn coerceTupleToArray(
3069830719 return sema.failWithOwnedErrorMsg(block, msg);
3069930720 }
3070030721
30701 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
30722 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len_including_sent);
3070230723 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
3070330724 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
3070430725 const dest_elem_ty = dest_ty.childType(zcu);
src/Sema/comptime_ptr_access.zig+3-4
......@@ -1031,13 +1031,12 @@ fn unflattenArray(
10311031 }
10321032
10331033 const elem_ty = ty.childType(zcu);
1034 const buf = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
1034 const buf = try arena.alloc(InternPool.Index, @intCast(ty.arrayLenIncludingSentinel(zcu)));
10351035 for (buf) |*elem| {
10361036 elem.* = (try unflattenArray(sema, elem_ty, elems, next_idx)).toIntern();
10371037 }
1038 if (ty.sentinel(zcu) != null) {
1039 // TODO: validate sentinel
1040 _ = try unflattenArray(sema, elem_ty, elems, next_idx);
1038 if (ty.sentinel(zcu)) |sent| {
1039 assert(sent.toIntern() == buf[buf.len - 1]);
10411040 }
10421041 return pt.aggregateValue(ty, buf);
10431042}
test/behavior/array.zig+18
......@@ -591,6 +591,24 @@ test "tuple to array handles sentinel" {
591591 var b: [3:0]u8 = a;
592592 };
593593 try expect(S.b[0] == 1);
594 try expect(S.b[1] == 2);
595 try expect(S.b[2] == 3);
596 try expect(S.b[3] == 0);
597}
598
599test "array-like to array handles sentinel" {
600 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
601 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
602 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
603
604 const S = struct {
605 const a: @Vector(3, u8) = .{ 1, 2, 3 };
606 var b: [3:0]u8 = a;
607 };
608 try expect(S.b[0] == 1);
609 try expect(S.b[1] == 2);
610 try expect(S.b[2] == 3);
611 try expect(S.b[3] == 0);
594612}
595613
596614test "array init of container level array variable" {
test/behavior/comptime_memory.zig+22
......@@ -606,3 +606,25 @@ test "reinterpret pointer as optional pointer via double-pointer coercion" {
606606 comptime assert(@TypeOf(loaded) == ?*anyopaque);
607607 comptime assert(@intFromPtr(loaded) == 0x1000);
608608}
609
610test "comptime store to sentinel-terminated array in an extern struct" {
611 comptime {
612 var x: extern struct {
613 a: [2:0]u8 = @splat(50),
614 } = .{};
615 x.a[0] = 100;
616 assert(x.a[0] == 100);
617 assert(x.a[1] == 50);
618 assert(x.a[2] == 0);
619 }
620}
621
622test "comptime store to repeated string bytes" {
623 comptime {
624 var x = "aa".*;
625 x[0] = 'b';
626 assert(x[0] == 'b');
627 assert(x[1] == 'a');
628 assert(x[2] == 0);
629 }
630}