authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-23 03:15:36+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-23 06:42:43+02:00
logb23c07ee6dbee16e5fd3601fcef67aee22bdd348
treed5a23b7b728cc32aaf4162a77773eb1f791f4c5f
parentd760b840ec08ee5485354b113ebddcf8672b1cf4
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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
......@@ -7899,9 +7899,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
78997899
79007900 repeated: {
79017901 switch (aggregate.storage) {
7902 .bytes => |bytes| for (bytes.toSlice(len, ip)[1..]) |byte|
7902 .bytes => |bytes| for (bytes.toSlice(len_including_sentinel, ip)[1..]) |byte|
79037903 if (byte != bytes.at(0, ip)) break :repeated,
7904 .elems => |elems| for (elems[1..@intCast(len)]) |elem|
7904 .elems => |elems| for (elems[1..@intCast(len_including_sentinel)]) |elem|
79057905 if (elem != elems[0]) break :repeated,
79067906 .repeated_elem => {},
79077907 }
src/Sema.zig+28-7
......@@ -13560,13 +13560,23 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1356013560
1356113561 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
1356213562 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
13563 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
13564 error.Overflow => return sema.fail(
13563 const result_len, const result_len_with_sent = blk: {
13564 if (std.math.add(usize, lhs_len, rhs_len)) |result_len| {
13565 const sent_len = @intFromBool(res_sent_val != null);
13566 if (std.math.add(usize, result_len, sent_len)) |with_sent| {
13567 break :blk .{ result_len, with_sent };
13568 } else |err| switch (err) {
13569 error.Overflow => {},
13570 }
13571 } else |err| switch (err) {
13572 error.Overflow => {},
13573 }
13574 return sema.fail(
1356513575 block,
1356613576 src,
1356713577 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
1356813578 .{ lhs_len, rhs_len },
13569 ),
13579 );
1357013580 };
1357113581
1357213582 const result_ty = try pt.arrayType(.{
......@@ -13604,7 +13614,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1360413614 else
1360513615 rhs_val;
1360613616
13607 const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
13617 const element_vals = try sema.arena.alloc(InternPool.Index, result_len_with_sent);
1360813618 var elem_i: u32 = 0;
1360913619 while (elem_i < lhs_len) : (elem_i += 1) {
1361013620 const lhs_elem_i = elem_i;
......@@ -13632,6 +13642,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1363213642 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;
1363313643 element_vals[elem_i] = coerced_elem_val.toIntern();
1363413644 }
13645 if (res_sent_val) |v| {
13646 element_vals[result_len] = v.toIntern();
13647 }
1363513648 return sema.addConstantMaybeRef(
1363613649 try pt.aggregateValue(result_ty, element_vals),
1363713650 ptr_addrspace != null,
......@@ -30616,11 +30629,18 @@ fn coerceArrayLike(
3061630629 return block.addTyOp(.array_to_vector, dest_ty, inst);
3061730630 }
3061830631
30619 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
30620 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
30632 const dest_len_including_sent = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel(zcu));
30633 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len_including_sent);
30634 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len_including_sent);
3062130635 var runtime_src: ?LazySrcLoc = null;
3062230636
3062330637 for (element_vals, element_refs, 0..) |*val, *ref, i| {
30638 if (i == dest_len) {
30639 const sentinel_val = dest_ty.sentinel(zcu).?;
30640 val.* = sentinel_val.toIntern();
30641 ref.* = Air.internedToRef(sentinel_val.toIntern());
30642 break;
30643 }
3062430644 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
3062530645 const src = inst_src; // TODO better source location
3062630646 const elem_src = inst_src; // TODO better source location
......@@ -30658,6 +30678,7 @@ fn coerceTupleToArray(
3065830678 const inst_ty = sema.typeOf(inst);
3065930679 const inst_len = inst_ty.arrayLen(zcu);
3066030680 const dest_len = dest_ty.arrayLen(zcu);
30681 const dest_len_including_sent = dest_ty.arrayLenIncludingSentinel(zcu);
3066130682
3066230683 if (dest_len != inst_len) {
3066330684 const msg = msg: {
......@@ -30670,7 +30691,7 @@ fn coerceTupleToArray(
3067030691 return sema.failWithOwnedErrorMsg(block, msg);
3067130692 }
3067230693
30673 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
30694 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len_including_sent);
3067430695 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
3067530696 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
3067630697 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}