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:...@@ -7902,9 +7902,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
79027902
7903 repeated: {7903 repeated: {
7904 switch (aggregate.storage) {7904 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|
7906 if (byte != bytes.at(0, ip)) break :repeated,7906 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|
7908 if (elem != elems[0]) break :repeated,7908 if (elem != elems[0]) break :repeated,
7909 .repeated_elem => {},7909 .repeated_elem => {},
7910 }7910 }
src/Sema.zig+28-7
...@@ -13570,13 +13570,23 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13570,13 +13570,23 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1357013570
13571 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);13571 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
13572 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);13572 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) {13573 const result_len, const result_len_with_sent = blk: {
13574 error.Overflow => return sema.fail(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(
13575 block,13585 block,
13576 src,13586 src,
13577 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",13587 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
13578 .{ lhs_len, rhs_len },13588 .{ lhs_len, rhs_len },
13579 ),13589 );
13580 };13590 };
1358113591
13582 const result_ty = try pt.arrayType(.{13592 const result_ty = try pt.arrayType(.{
...@@ -13614,7 +13624,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13614,7 +13624,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13614 else13624 else
13615 rhs_val;13625 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);
13618 var elem_i: u32 = 0;13628 var elem_i: u32 = 0;
13619 while (elem_i < lhs_len) : (elem_i += 1) {13629 while (elem_i < lhs_len) : (elem_i += 1) {
13620 const lhs_elem_i = elem_i;13630 const lhs_elem_i = elem_i;
...@@ -13642,6 +13652,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -13642,6 +13652,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
13642 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;13652 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;
13643 element_vals[elem_i] = coerced_elem_val.toIntern();13653 element_vals[elem_i] = coerced_elem_val.toIntern();
13644 }13654 }
13655 if (res_sent_val) |v| {
13656 element_vals[result_len] = v.toIntern();
13657 }
13645 return sema.addConstantMaybeRef(13658 return sema.addConstantMaybeRef(
13646 try pt.aggregateValue(result_ty, element_vals),13659 try pt.aggregateValue(result_ty, element_vals),
13647 ptr_addrspace != null,13660 ptr_addrspace != null,
...@@ -30644,11 +30657,18 @@ fn coerceArrayLike(...@@ -30644,11 +30657,18 @@ fn coerceArrayLike(
30644 return block.addTyOp(.array_to_vector, dest_ty, inst);30657 return block.addTyOp(.array_to_vector, dest_ty, inst);
30645 }30658 }
3064630659
30647 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);30660 const dest_len_including_sent = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel(zcu));
30648 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);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);
30649 var runtime_src: ?LazySrcLoc = null;30663 var runtime_src: ?LazySrcLoc = null;
3065030664
30651 for (element_vals, element_refs, 0..) |*val, *ref, i| {30665 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 }
30652 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());30672 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
30653 const src = inst_src; // TODO better source location30673 const src = inst_src; // TODO better source location
30654 const elem_src = inst_src; // TODO better source location30674 const elem_src = inst_src; // TODO better source location
...@@ -30686,6 +30706,7 @@ fn coerceTupleToArray(...@@ -30686,6 +30706,7 @@ fn coerceTupleToArray(
30686 const inst_ty = sema.typeOf(inst);30706 const inst_ty = sema.typeOf(inst);
30687 const inst_len = inst_ty.arrayLen(zcu);30707 const inst_len = inst_ty.arrayLen(zcu);
30688 const dest_len = dest_ty.arrayLen(zcu);30708 const dest_len = dest_ty.arrayLen(zcu);
30709 const dest_len_including_sent = dest_ty.arrayLenIncludingSentinel(zcu);
3068930710
30690 if (dest_len != inst_len) {30711 if (dest_len != inst_len) {
30691 const msg = msg: {30712 const msg = msg: {
...@@ -30698,7 +30719,7 @@ fn coerceTupleToArray(...@@ -30698,7 +30719,7 @@ fn coerceTupleToArray(
30698 return sema.failWithOwnedErrorMsg(block, msg);30719 return sema.failWithOwnedErrorMsg(block, msg);
30699 }30720 }
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);
30702 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);30723 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
30703 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);30724 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
30704 const dest_elem_ty = dest_ty.childType(zcu);30725 const dest_elem_ty = dest_ty.childType(zcu);
src/Sema/comptime_ptr_access.zig+3-4
...@@ -1031,13 +1031,12 @@ fn unflattenArray(...@@ -1031,13 +1031,12 @@ fn unflattenArray(
1031 }1031 }
10321032
1033 const elem_ty = ty.childType(zcu);1033 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)));
1035 for (buf) |*elem| {1035 for (buf) |*elem| {
1036 elem.* = (try unflattenArray(sema, elem_ty, elems, next_idx)).toIntern();1036 elem.* = (try unflattenArray(sema, elem_ty, elems, next_idx)).toIntern();
1037 }1037 }
1038 if (ty.sentinel(zcu) != null) {1038 if (ty.sentinel(zcu)) |sent| {
1039 // TODO: validate sentinel1039 assert(sent.toIntern() == buf[buf.len - 1]);
1040 _ = try unflattenArray(sema, elem_ty, elems, next_idx);
1041 }1040 }
1042 return pt.aggregateValue(ty, buf);1041 return pt.aggregateValue(ty, buf);
1043}1042}
test/behavior/array.zig+18
...@@ -591,6 +591,24 @@ test "tuple to array handles sentinel" {...@@ -591,6 +591,24 @@ test "tuple to array handles sentinel" {
591 var b: [3:0]u8 = a;591 var b: [3:0]u8 = a;
592 };592 };
593 try expect(S.b[0] == 1);593 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);
594}612}
595613
596test "array init of container level array variable" {614test "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" {...@@ -606,3 +606,25 @@ test "reinterpret pointer as optional pointer via double-pointer coercion" {
606 comptime assert(@TypeOf(loaded) == ?*anyopaque);606 comptime assert(@TypeOf(loaded) == ?*anyopaque);
607 comptime assert(@intFromPtr(loaded) == 0x1000);607 comptime assert(@intFromPtr(loaded) == 0x1000);
608}608}
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}