authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-06-16 19:23:46+02:00
committergravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-08-02 14:41:10+02:00
logadef113102f7b5c55f502c9b4ac58f61349eab1d
tree5cc24d15d586d7e783b2ac3a9fab3548bfee8fab
parente69d19d1ee06ebb50af191d2911355d817eb1249

Sema: allow coercion from []T to *[n]T if length is comptime-known

A comptime-known `[]T` should behave as similarly as possible to a comptime- known `*[n]T`. It is now possible to coerce the former to the latter if their length matches.

4 files changed, 220 insertions(+), 7 deletions(-)

src/Sema.zig+98-7
......@@ -28258,9 +28258,94 @@ fn coerceExtra(
2825828258 },
2825928259 else => {},
2826028260 },
28261 .one => {},
28261 // []T to *[n]T
28262 .one => slice_to_array_ptr: {
28263 if (!inst_ty.isSlice(zcu)) break :slice_to_array_ptr;
28264 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :slice_to_array_ptr;
28265 const array_ty: Type = .fromInterned(dest_info.child);
28266 if (array_ty.zigTypeTag(zcu) != .array) break :slice_to_array_ptr;
28267 const inst_val = maybe_inst_val orelse {
28268 if (!opts.report_err) return error.NotCoercible;
28269 return sema.fail(
28270 block,
28271 inst_src,
28272 "coercion from slice to array pointer type '{f}' requires length to be known at compile-time",
28273 .{dest_ty.fmt(pt)},
28274 );
28275 };
28276
28277 const slice: InternPool.Key.Slice = slice: {
28278 switch (ip.indexToKey(inst_val.toIntern())) {
28279 .undef => {},
28280 .slice => |slice| if (slice.len != .undef_usize) break :slice slice,
28281 else => unreachable,
28282 }
28283 if (!opts.report_err) return error.NotCoercible;
28284 return sema.failWithOwnedErrorMsg(block, msg: {
28285 const msg = try sema.errMsg(inst_src, "slice with undefined length cannot cast into array pointer type '{f}'", .{
28286 dest_ty.fmt(pt),
28287 });
28288 errdefer msg.destroy(gpa);
28289 try sema.errNote(inst_src, msg, "length of slice must be defined and match length of array type", .{});
28290 break :msg msg;
28291 });
28292 };
28293 const slice_len = Value.fromInterned(slice.len).toUnsignedInt(zcu);
28294 if (array_ty.arrayLen(zcu) != slice_len) {
28295 if (!opts.report_err) return error.NotCoercible;
28296 return sema.failWithOwnedErrorMsg(block, msg: {
28297 const msg = try sema.errMsg(inst_src, "slice of length {d} cannot cast into array pointer type '{f}'", .{
28298 slice_len, dest_ty.fmt(pt),
28299 });
28300 errdefer msg.destroy(gpa);
28301 try sema.errNote(inst_src, msg, "length of slice must match length of array type", .{});
28302 break :msg msg;
28303 });
28304 }
28305
28306 const inst_elem_ty = inst_ty.childType(zcu);
28307 const dest_elem_ty = array_ty.childType(zcu);
28308 const dest_is_mut = !dest_info.flags.is_const;
28309 switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) {
28310 .ok => {},
28311 else => |elem_res| {
28312 in_memory_result = .{ .ptr_child = .{
28313 .child = try elem_res.dupe(sema.arena),
28314 .actual = inst_elem_ty,
28315 .wanted = dest_elem_ty,
28316 } };
28317 break :slice_to_array_ptr;
28318 },
28319 }
28320
28321 if (array_ty.sentinel(zcu)) |array_sentinel| {
28322 if (inst_ty.sentinel(zcu)) |slice_sentinel| {
28323 if (array_sentinel.toIntern() !=
28324 (try pt.getCoerced(slice_sentinel, dest_elem_ty)).toIntern())
28325 {
28326 in_memory_result = .{ .ptr_sentinel = .{
28327 .actual = slice_sentinel,
28328 .wanted = array_sentinel,
28329 .ty = dest_elem_ty,
28330 } };
28331 break :slice_to_array_ptr;
28332 }
28333 } else {
28334 in_memory_result = .{ .ptr_sentinel = .{
28335 .actual = .@"unreachable",
28336 .wanted = array_sentinel,
28337 .ty = dest_elem_ty,
28338 } };
28339 break :slice_to_array_ptr;
28340 }
28341 }
28342
28343 const array_ptr = try pt.sliceToArrayPtr(slice);
28344 return sema.coerceCompatiblePtrs(block, dest_ty, .fromValue(array_ptr), inst_src);
28345 },
2826228346 .slice => to_slice: {
2826328347 if (inst_ty.zigTypeTag(zcu) == .array) {
28348 if (!opts.report_err) return error.NotCoercible;
2826428349 return sema.fail(
2826528350 block,
2826628351 inst_src,
......@@ -28288,6 +28373,7 @@ fn coerceExtra(
2828828373
2828928374 // pointer to tuple to slice
2829028375 if (!dest_info.flags.is_const) {
28376 if (!opts.report_err) return error.NotCoercible;
2829128377 const err_msg = err_msg: {
2829228378 const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)});
2829328379 errdefer err_msg.destroy(sema.gpa);
......@@ -28383,6 +28469,7 @@ fn coerceExtra(
2838328469 if (maybe_inst_val) |val| {
2838428470 const result_val = try val.floatCast(dest_ty, pt);
2838528471 if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) {
28472 if (!opts.report_err) return error.NotCoercible;
2838628473 return sema.fail(
2838728474 block,
2838828475 inst_src,
......@@ -28440,12 +28527,15 @@ fn coerceExtra(
2844028527 break :fits result_big_int.toConst().eql(operand_big_int);
2844128528 },
2844228529 };
28443 if (!fits) return sema.fail(
28444 block,
28445 inst_src,
28446 "type '{f}' cannot represent integer value '{f}'",
28447 .{ dest_ty.fmt(pt), val.fmtValue(pt) },
28448 );
28530 if (!fits) {
28531 if (!opts.report_err) return error.NotCoercible;
28532 return sema.fail(
28533 block,
28534 inst_src,
28535 "type '{f}' cannot represent integer value '{f}'",
28536 .{ dest_ty.fmt(pt), val.fmtValue(pt) },
28537 );
28538 }
2844928539 return .fromValue(result_val);
2845028540 },
2845128541 else => {},
......@@ -28456,6 +28546,7 @@ fn coerceExtra(
2845628546 const val = sema.resolveValue(inst).?;
2845728547 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal;
2845828548 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse {
28549 if (!opts.report_err) return error.NotCoercible;
2845928550 return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{
2846028551 string.fmt(&zcu.intern_pool), dest_ty.fmt(pt),
2846128552 });
test/behavior/slice.zig+38
......@@ -1133,3 +1133,41 @@ test "address of dereferenced slice is array pointer" {
11331133 comptime assert(array_ptr[3] == 0);
11341134 }
11351135}
1136
1137test "coerce slice with comptime-known length to array pointer" {
1138 {
1139 const slice: []const u16 = &.{ 1, 2, 3 };
1140 const array_ptr: *const [3]u16 = slice;
1141
1142 comptime assert(array_ptr[0] == 1);
1143 comptime assert(array_ptr[1] == 2);
1144 comptime assert(array_ptr[2] == 3);
1145 }
1146 {
1147 const slice: [:0]const u16 = &.{ 1, 2, 3 };
1148 const array_ptr: *const [3:0]u16 = slice;
1149
1150 comptime assert(array_ptr[0] == 1);
1151 comptime assert(array_ptr[1] == 2);
1152 comptime assert(array_ptr[2] == 3);
1153 comptime assert(array_ptr[3] == 0);
1154 }
1155 {
1156 const slice: [:0]const u16 = &.{ 1, 2, 3 };
1157 const array_ptr: *const [3]u16 = slice;
1158
1159 comptime assert(array_ptr[0] == 1);
1160 comptime assert(array_ptr[1] == 2);
1161 comptime assert(array_ptr[2] == 3);
1162 }
1163}
1164
1165test "modify slice through coerced array pointer" {
1166 comptime {
1167 var array: [3]u16 = .{ 1, 2, 3 };
1168 const slice: []u16 = &array;
1169 const array_ptr: *[3]u16 = slice;
1170 array_ptr[2] = 0;
1171 assert(slice[2] == 0);
1172 }
1173}
test/cases/compile_errors/coerce_pointers_with_uncoercable_child_pointers.zig+14
......@@ -28,6 +28,16 @@ export fn entry5() void {
2828 _ = q;
2929}
3030
31export fn entry6(p: **[3]u8) void {
32 const q: *[]u8 = p;
33 _ = q;
34}
35
36export fn entry7(p: *[]u8) void {
37 const q: **[3]u8 = p;
38 _ = q;
39}
40
3141// error
3242//
3343// :3:22: error: expected type '**i32', found '**u32'
......@@ -50,3 +60,7 @@ export fn entry5() void {
5060// :27:24: note: pointer type child '*[1:42]u8' cannot cast into pointer type child '*[1]u8'
5161// :27:24: note: pointer type child '[1:42]u8' cannot cast into pointer type child '[1]u8'
5262// :27:24: note: source array cannot be guaranteed to maintain '42' sentinel
63// :32:22: error: expected type '*[]u8', found '**[3]u8'
64// :32:22: note: pointer type child '*[3]u8' cannot cast into pointer type child '[]u8'
65// :37:24: error: expected type '**[3]u8', found '*[]u8'
66// :37:24: note: pointer type child '[]u8' cannot cast into pointer type child '*[3]u8'
test/cases/compile_errors/slice_to_array_pointer.zig created+70
......@@ -0,0 +1,70 @@
1export fn entry1() void {
2 var array: [2]u16 = .{ 1, 2 };
3 const slice: []const u16 = &array;
4 foo(slice);
5}
6
7export fn entry2() void {
8 const slice: []const u16 = undefined;
9 foo(slice);
10}
11
12export fn entry3() void {
13 comptime var slice: []const u16 = &.{ 1, 2 };
14 slice.len = undefined;
15 foo(slice);
16}
17
18export fn entry4() void {
19 const slice: []const u16 = &.{ 1, 2, 3 };
20 foo(slice);
21}
22
23export fn entry5() void {
24 const slice: []const u8 = &.{ 1, 2 };
25 foo(slice);
26}
27
28fn foo(x: *const [2]u16) void {
29 _ = x;
30}
31
32export fn entry6() void {
33 const slice: [:0]const u16 = &.{ 1, 2, 3 };
34 bar(slice);
35}
36
37export fn entry7() void {
38 const slice: [:1]const u16 = &.{ 1, 2 };
39 bar(slice);
40}
41
42export fn entry8() void {
43 const slice: []const u16 = &.{ 1, 2 };
44 bar(slice);
45}
46
47fn bar(x: *const [2:0]u16) void {
48 _ = x;
49}
50
51// error
52//
53// :4:9: error: coercion from slice to array pointer type '*const [2]u16' requires length to be known at compile-time
54// :9:9: error: slice with undefined length cannot cast into array pointer type '*const [2]u16'
55// :9:9: note: length of slice must be defined and match length of array type
56// :15:9: error: slice with undefined length cannot cast into array pointer type '*const [2]u16'
57// :15:9: note: length of slice must be defined and match length of array type
58// :20:9: error: slice of length 3 cannot cast into array pointer type '*const [2]u16'
59// :20:9: note: length of slice must match length of array type
60// :25:9: error: expected type '*const [2]u16', found '[]const u8'
61// :25:9: note: pointer type child 'u8' cannot cast into pointer type child 'u16'
62// :28:11: note: parameter type declared here
63// :34:9: error: slice of length 3 cannot cast into array pointer type '*const [2:0]u16'
64// :34:9: note: length of slice must match length of array type
65// :39:9: error: expected type '*const [2:0]u16', found '[:1]const u16'
66// :39:9: note: pointer sentinel '1' cannot cast into pointer sentinel '0'
67// :47:11: note: parameter type declared here
68// :44:9: error: expected type '*const [2:0]u16', found '[]const u16'
69// :44:9: note: destination pointer requires '0' sentinel
70// :47:11: note: parameter type declared here