diff --git a/src/Sema.zig b/src/Sema.zig index c81b50fb18214901ef61e9a4a8e07903fd0240c6..29baca211860aee36f398f78a43792926a5896fa 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -28258,9 +28258,94 @@ fn coerceExtra( }, else => {}, }, - .one => {}, + // []T to *[n]T + .one => slice_to_array_ptr: { + if (!inst_ty.isSlice(zcu)) break :slice_to_array_ptr; + if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :slice_to_array_ptr; + const array_ty: Type = .fromInterned(dest_info.child); + if (array_ty.zigTypeTag(zcu) != .array) break :slice_to_array_ptr; + const inst_val = maybe_inst_val orelse { + if (!opts.report_err) return error.NotCoercible; + return sema.fail( + block, + inst_src, + "coercion from slice to array pointer type '{f}' requires length to be known at compile-time", + .{dest_ty.fmt(pt)}, + ); + }; + + const slice: InternPool.Key.Slice = slice: { + switch (ip.indexToKey(inst_val.toIntern())) { + .undef => {}, + .slice => |slice| if (slice.len != .undef_usize) break :slice slice, + else => unreachable, + } + if (!opts.report_err) return error.NotCoercible; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(inst_src, "slice with undefined length cannot cast into array pointer type '{f}'", .{ + dest_ty.fmt(pt), + }); + errdefer msg.destroy(gpa); + try sema.errNote(inst_src, msg, "length of slice must be defined and match length of array type", .{}); + break :msg msg; + }); + }; + const slice_len = Value.fromInterned(slice.len).toUnsignedInt(zcu); + if (array_ty.arrayLen(zcu) != slice_len) { + if (!opts.report_err) return error.NotCoercible; + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(inst_src, "slice of length {d} cannot cast into array pointer type '{f}'", .{ + slice_len, dest_ty.fmt(pt), + }); + errdefer msg.destroy(gpa); + try sema.errNote(inst_src, msg, "length of slice must match length of array type", .{}); + break :msg msg; + }); + } + + const inst_elem_ty = inst_ty.childType(zcu); + const dest_elem_ty = array_ty.childType(zcu); + const dest_is_mut = !dest_info.flags.is_const; + switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { + .ok => {}, + else => |elem_res| { + in_memory_result = .{ .ptr_child = .{ + .child = try elem_res.dupe(sema.arena), + .actual = inst_elem_ty, + .wanted = dest_elem_ty, + } }; + break :slice_to_array_ptr; + }, + } + + if (array_ty.sentinel(zcu)) |array_sentinel| { + if (inst_ty.sentinel(zcu)) |slice_sentinel| { + if (array_sentinel.toIntern() != + (try pt.getCoerced(slice_sentinel, dest_elem_ty)).toIntern()) + { + in_memory_result = .{ .ptr_sentinel = .{ + .actual = slice_sentinel, + .wanted = array_sentinel, + .ty = dest_elem_ty, + } }; + break :slice_to_array_ptr; + } + } else { + in_memory_result = .{ .ptr_sentinel = .{ + .actual = .@"unreachable", + .wanted = array_sentinel, + .ty = dest_elem_ty, + } }; + break :slice_to_array_ptr; + } + } + + const array_ptr = try pt.sliceToArrayPtr(slice); + return sema.coerceCompatiblePtrs(block, dest_ty, .fromValue(array_ptr), inst_src); + }, .slice => to_slice: { if (inst_ty.zigTypeTag(zcu) == .array) { + if (!opts.report_err) return error.NotCoercible; return sema.fail( block, inst_src, @@ -28288,6 +28373,7 @@ fn coerceExtra( // pointer to tuple to slice if (!dest_info.flags.is_const) { + if (!opts.report_err) return error.NotCoercible; const err_msg = err_msg: { const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)}); errdefer err_msg.destroy(sema.gpa); @@ -28383,6 +28469,7 @@ fn coerceExtra( if (maybe_inst_val) |val| { const result_val = try val.floatCast(dest_ty, pt); if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) { + if (!opts.report_err) return error.NotCoercible; return sema.fail( block, inst_src, @@ -28440,12 +28527,15 @@ fn coerceExtra( break :fits result_big_int.toConst().eql(operand_big_int); }, }; - if (!fits) return sema.fail( - block, - inst_src, - "type '{f}' cannot represent integer value '{f}'", - .{ dest_ty.fmt(pt), val.fmtValue(pt) }, - ); + if (!fits) { + if (!opts.report_err) return error.NotCoercible; + return sema.fail( + block, + inst_src, + "type '{f}' cannot represent integer value '{f}'", + .{ dest_ty.fmt(pt), val.fmtValue(pt) }, + ); + } return .fromValue(result_val); }, else => {}, @@ -28456,6 +28546,7 @@ fn coerceExtra( const val = sema.resolveValue(inst).?; const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal; const field_index = dest_ty.enumFieldIndex(string, zcu) orelse { + if (!opts.report_err) return error.NotCoercible; return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{ string.fmt(&zcu.intern_pool), dest_ty.fmt(pt), }); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 7021b76c69dff0f63e1bb26165aa5fae57e6a1d6..247c3d1be8f3a15ab8b53f9d0a47f2bdea02cc4f 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -1133,3 +1133,41 @@ test "address of dereferenced slice is array pointer" { comptime assert(array_ptr[3] == 0); } } + +test "coerce slice with comptime-known length to array pointer" { + { + const slice: []const u16 = &.{ 1, 2, 3 }; + const array_ptr: *const [3]u16 = slice; + + comptime assert(array_ptr[0] == 1); + comptime assert(array_ptr[1] == 2); + comptime assert(array_ptr[2] == 3); + } + { + const slice: [:0]const u16 = &.{ 1, 2, 3 }; + const array_ptr: *const [3:0]u16 = slice; + + comptime assert(array_ptr[0] == 1); + comptime assert(array_ptr[1] == 2); + comptime assert(array_ptr[2] == 3); + comptime assert(array_ptr[3] == 0); + } + { + const slice: [:0]const u16 = &.{ 1, 2, 3 }; + const array_ptr: *const [3]u16 = slice; + + comptime assert(array_ptr[0] == 1); + comptime assert(array_ptr[1] == 2); + comptime assert(array_ptr[2] == 3); + } +} + +test "modify slice through coerced array pointer" { + comptime { + var array: [3]u16 = .{ 1, 2, 3 }; + const slice: []u16 = &array; + const array_ptr: *[3]u16 = slice; + array_ptr[2] = 0; + assert(slice[2] == 0); + } +} diff --git a/test/cases/compile_errors/coerce_pointers_with_uncoercable_child_pointers.zig b/test/cases/compile_errors/coerce_pointers_with_uncoercable_child_pointers.zig index bef21b2777ff5503899741f812dcbf7bc3b61b68..77f48e656c0a81c94353a555542a4b3e73ace4e8 100644 --- a/test/cases/compile_errors/coerce_pointers_with_uncoercable_child_pointers.zig +++ b/test/cases/compile_errors/coerce_pointers_with_uncoercable_child_pointers.zig @@ -28,6 +28,16 @@ export fn entry5() void { _ = q; } +export fn entry6(p: **[3]u8) void { + const q: *[]u8 = p; + _ = q; +} + +export fn entry7(p: *[]u8) void { + const q: **[3]u8 = p; + _ = q; +} + // error // // :3:22: error: expected type '**i32', found '**u32' @@ -50,3 +60,7 @@ export fn entry5() void { // :27:24: note: pointer type child '*[1:42]u8' cannot cast into pointer type child '*[1]u8' // :27:24: note: pointer type child '[1:42]u8' cannot cast into pointer type child '[1]u8' // :27:24: note: source array cannot be guaranteed to maintain '42' sentinel +// :32:22: error: expected type '*[]u8', found '**[3]u8' +// :32:22: note: pointer type child '*[3]u8' cannot cast into pointer type child '[]u8' +// :37:24: error: expected type '**[3]u8', found '*[]u8' +// :37:24: note: pointer type child '[]u8' cannot cast into pointer type child '*[3]u8' diff --git a/test/cases/compile_errors/slice_to_array_pointer.zig b/test/cases/compile_errors/slice_to_array_pointer.zig new file mode 100644 index 0000000000000000000000000000000000000000..9e4750a3645820240e505ab6d471be99d18877cc --- /dev/null +++ b/test/cases/compile_errors/slice_to_array_pointer.zig @@ -0,0 +1,70 @@ +export fn entry1() void { + var array: [2]u16 = .{ 1, 2 }; + const slice: []const u16 = &array; + foo(slice); +} + +export fn entry2() void { + const slice: []const u16 = undefined; + foo(slice); +} + +export fn entry3() void { + comptime var slice: []const u16 = &.{ 1, 2 }; + slice.len = undefined; + foo(slice); +} + +export fn entry4() void { + const slice: []const u16 = &.{ 1, 2, 3 }; + foo(slice); +} + +export fn entry5() void { + const slice: []const u8 = &.{ 1, 2 }; + foo(slice); +} + +fn foo(x: *const [2]u16) void { + _ = x; +} + +export fn entry6() void { + const slice: [:0]const u16 = &.{ 1, 2, 3 }; + bar(slice); +} + +export fn entry7() void { + const slice: [:1]const u16 = &.{ 1, 2 }; + bar(slice); +} + +export fn entry8() void { + const slice: []const u16 = &.{ 1, 2 }; + bar(slice); +} + +fn bar(x: *const [2:0]u16) void { + _ = x; +} + +// error +// +// :4:9: error: coercion from slice to array pointer type '*const [2]u16' requires length to be known at compile-time +// :9:9: error: slice with undefined length cannot cast into array pointer type '*const [2]u16' +// :9:9: note: length of slice must be defined and match length of array type +// :15:9: error: slice with undefined length cannot cast into array pointer type '*const [2]u16' +// :15:9: note: length of slice must be defined and match length of array type +// :20:9: error: slice of length 3 cannot cast into array pointer type '*const [2]u16' +// :20:9: note: length of slice must match length of array type +// :25:9: error: expected type '*const [2]u16', found '[]const u8' +// :25:9: note: pointer type child 'u8' cannot cast into pointer type child 'u16' +// :28:11: note: parameter type declared here +// :34:9: error: slice of length 3 cannot cast into array pointer type '*const [2:0]u16' +// :34:9: note: length of slice must match length of array type +// :39:9: error: expected type '*const [2:0]u16', found '[:1]const u16' +// :39:9: note: pointer sentinel '1' cannot cast into pointer sentinel '0' +// :47:11: note: parameter type declared here +// :44:9: error: expected type '*const [2:0]u16', found '[]const u16' +// :44:9: note: destination pointer requires '0' sentinel +// :47:11: note: parameter type declared here