diff --git a/src/InternPool.zig b/src/InternPool.zig index 88c454fc2f3b414710886a524c80f4ecc7b09134..aef6f6f036d13e50925662d7ddfbcab37d978de7 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -7899,9 +7899,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key: repeated: { switch (aggregate.storage) { - .bytes => |bytes| for (bytes.toSlice(len, ip)[1..]) |byte| + .bytes => |bytes| for (bytes.toSlice(len_including_sentinel, ip)[1..]) |byte| if (byte != bytes.at(0, ip)) break :repeated, - .elems => |elems| for (elems[1..@intCast(len)]) |elem| + .elems => |elems| for (elems[1..@intCast(len_including_sentinel)]) |elem| if (elem != elems[0]) break :repeated, .repeated_elem => {}, } diff --git a/src/Sema.zig b/src/Sema.zig index 78649a79aac1945799b1a899cc87148e7e954e8d..f3c7b5e0af02e65bacf757c230c1b4769f89d103 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13560,13 +13560,23 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len); - const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { - error.Overflow => return sema.fail( + const result_len, const result_len_with_sent = blk: { + if (std.math.add(usize, lhs_len, rhs_len)) |result_len| { + const sent_len = @intFromBool(res_sent_val != null); + if (std.math.add(usize, result_len, sent_len)) |with_sent| { + break :blk .{ result_len, with_sent }; + } else |err| switch (err) { + error.Overflow => {}, + } + } else |err| switch (err) { + error.Overflow => {}, + } + return sema.fail( block, src, "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", .{ lhs_len, rhs_len }, - ), + ); }; const result_ty = try pt.arrayType(.{ @@ -13604,7 +13614,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai else rhs_val; - const element_vals = try sema.arena.alloc(InternPool.Index, result_len); + const element_vals = try sema.arena.alloc(InternPool.Index, result_len_with_sent); var elem_i: u32 = 0; while (elem_i < lhs_len) : (elem_i += 1) { const lhs_elem_i = elem_i; @@ -13632,6 +13642,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?; element_vals[elem_i] = coerced_elem_val.toIntern(); } + if (res_sent_val) |v| { + element_vals[result_len] = v.toIntern(); + } return sema.addConstantMaybeRef( try pt.aggregateValue(result_ty, element_vals), ptr_addrspace != null, @@ -30616,11 +30629,18 @@ fn coerceArrayLike( return block.addTyOp(.array_to_vector, dest_ty, inst); } - const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); - const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); + const dest_len_including_sent = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel(zcu)); + const element_vals = try sema.arena.alloc(InternPool.Index, dest_len_including_sent); + const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len_including_sent); var runtime_src: ?LazySrcLoc = null; for (element_vals, element_refs, 0..) |*val, *ref, i| { + if (i == dest_len) { + const sentinel_val = dest_ty.sentinel(zcu).?; + val.* = sentinel_val.toIntern(); + ref.* = Air.internedToRef(sentinel_val.toIntern()); + break; + } const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern()); const src = inst_src; // TODO better source location const elem_src = inst_src; // TODO better source location @@ -30658,6 +30678,7 @@ fn coerceTupleToArray( const inst_ty = sema.typeOf(inst); const inst_len = inst_ty.arrayLen(zcu); const dest_len = dest_ty.arrayLen(zcu); + const dest_len_including_sent = dest_ty.arrayLenIncludingSentinel(zcu); if (dest_len != inst_len) { const msg = msg: { @@ -30670,7 +30691,7 @@ fn coerceTupleToArray( return sema.failWithOwnedErrorMsg(block, msg); } - const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); + const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len_including_sent); const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); const dest_elem_ty = dest_ty.childType(zcu); diff --git a/src/Sema/comptime_ptr_access.zig b/src/Sema/comptime_ptr_access.zig index 96d407f9ffb2fe271fa4f1325343928bbb700d97..d421af53adb7046cf08fcb9ec0a5df3382a3e8a0 100644 --- a/src/Sema/comptime_ptr_access.zig +++ b/src/Sema/comptime_ptr_access.zig @@ -1031,13 +1031,12 @@ fn unflattenArray( } const elem_ty = ty.childType(zcu); - const buf = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu))); + const buf = try arena.alloc(InternPool.Index, @intCast(ty.arrayLenIncludingSentinel(zcu))); for (buf) |*elem| { elem.* = (try unflattenArray(sema, elem_ty, elems, next_idx)).toIntern(); } - if (ty.sentinel(zcu) != null) { - // TODO: validate sentinel - _ = try unflattenArray(sema, elem_ty, elems, next_idx); + if (ty.sentinel(zcu)) |sent| { + assert(sent.toIntern() == buf[buf.len - 1]); } return pt.aggregateValue(ty, buf); } diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 10cbdd6f731658795f030c4167c6d204079767bc..97c595d474b3e06978e372dc47dca9c78d4eb31c 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -591,6 +591,24 @@ test "tuple to array handles sentinel" { var b: [3:0]u8 = a; }; try expect(S.b[0] == 1); + try expect(S.b[1] == 2); + try expect(S.b[2] == 3); + try expect(S.b[3] == 0); +} + +test "array-like to array handles sentinel" { + if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + + const S = struct { + const a: @Vector(3, u8) = .{ 1, 2, 3 }; + var b: [3:0]u8 = a; + }; + try expect(S.b[0] == 1); + try expect(S.b[1] == 2); + try expect(S.b[2] == 3); + try expect(S.b[3] == 0); } test "array init of container level array variable" { diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 63044d026e18e7d77e9e049805867d8dc6749fc2..e88fe23c86c294627dad536044be9528416edd1d 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -606,3 +606,25 @@ test "reinterpret pointer as optional pointer via double-pointer coercion" { comptime assert(@TypeOf(loaded) == ?*anyopaque); comptime assert(@intFromPtr(loaded) == 0x1000); } + +test "comptime store to sentinel-terminated array in an extern struct" { + comptime { + var x: extern struct { + a: [2:0]u8 = @splat(50), + } = .{}; + x.a[0] = 100; + assert(x.a[0] == 100); + assert(x.a[1] == 50); + assert(x.a[2] == 0); + } +} + +test "comptime store to repeated string bytes" { + comptime { + var x = "aa".*; + x[0] = 'b'; + assert(x[0] == 'b'); + assert(x[1] == 'a'); + assert(x[2] == 0); + } +}