authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-06 20:43:13-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-06 20:44:15-05:00
log6ee3cabe5cc0b2c9af30b1fa0233381faa18e700
tree8070f9f0120559a410f07603b4b593f80ec68446
parent7277670843d259d19093c8900b1f8445e41202ae
signaturelock-open Commit is signed but in an unrecognized format.

allow type coercion from *[0]T to E![]const T

This is an unambiguous, safe cast.

5 files changed, 135 insertions(+), 12 deletions(-)

lib/std/fs/path.zig+1-1
...@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {...@@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool {
32/// This is different from mem.join in that the separator will not be repeated if32/// This is different from mem.join in that the separator will not be repeated if
33/// it is found at the end or beginning of a pair of consecutive paths.33/// it is found at the end or beginning of a pair of consecutive paths.
34fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {34fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {
35 if (paths.len == 0) return (([*]u8)(undefined))[0..0];35 if (paths.len == 0) return &[0]u8{};
3636
37 const total_len = blk: {37 const total_len = blk: {
38 var sum: usize = paths[0].len;38 var sum: usize = paths[0].len;
lib/std/heap.zig+3-5
...@@ -41,8 +41,7 @@ var direct_allocator_state = Allocator{...@@ -41,8 +41,7 @@ var direct_allocator_state = Allocator{
4141
42const DirectAllocator = struct {42const DirectAllocator = struct {
43 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {43 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
44 if (n == 0)44 if (n == 0) return &[0]u8{};
45 return (([*]u8)(undefined))[0..0];
4645
47 if (builtin.os == .windows) {46 if (builtin.os == .windows) {
48 const w = os.windows;47 const w = os.windows;
...@@ -261,8 +260,7 @@ pub const HeapAllocator = switch (builtin.os) {...@@ -261,8 +260,7 @@ pub const HeapAllocator = switch (builtin.os) {
261260
262 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {261 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
263 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);262 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
264 if (n == 0)263 if (n == 0) return &[0]u8{};
265 return (([*]u8)(undefined))[0..0];
266264
267 const amt = n + alignment + @sizeOf(usize);265 const amt = n + alignment + @sizeOf(usize);
268 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);266 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
...@@ -677,7 +675,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type {...@@ -677,7 +675,7 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
677 ) catch {675 ) catch {
678 const result = try self.fallback_allocator.reallocFn(676 const result = try self.fallback_allocator.reallocFn(
679 self.fallback_allocator,677 self.fallback_allocator,
680 ([*]u8)(undefined)[0..0],678 &[0]u8{},
681 undefined,679 undefined,
682 new_size,680 new_size,
683 new_align,681 new_align,
lib/std/mem.zig+3-3
...@@ -122,7 +122,7 @@ pub const Allocator = struct {...@@ -122,7 +122,7 @@ pub const Allocator = struct {
122 }122 }
123123
124 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;124 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
125 const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, a);125 const byte_slice = try self.reallocFn(self, &[0]u8{}, undefined, byte_count, a);
126 assert(byte_slice.len == byte_count);126 assert(byte_slice.len == byte_count);
127 @memset(byte_slice.ptr, undefined, byte_slice.len);127 @memset(byte_slice.ptr, undefined, byte_slice.len);
128 if (alignment == null) {128 if (alignment == null) {
...@@ -976,7 +976,7 @@ pub const SplitIterator = struct {...@@ -976,7 +976,7 @@ pub const SplitIterator = struct {
976/// Naively combines a series of slices with a separator.976/// Naively combines a series of slices with a separator.
977/// Allocates memory for the result, which must be freed by the caller.977/// Allocates memory for the result, which must be freed by the caller.
978pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {978pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
979 if (slices.len == 0) return (([*]u8)(undefined))[0..0];979 if (slices.len == 0) return &[0]u8{};
980980
981 const total_len = blk: {981 const total_len = blk: {
982 var sum: usize = separator.len * (slices.len - 1);982 var sum: usize = separator.len * (slices.len - 1);
...@@ -1011,7 +1011,7 @@ test "mem.join" {...@@ -1011,7 +1011,7 @@ test "mem.join" {
10111011
1012/// Copies each T from slices into a new slice that exactly holds all the elements.1012/// Copies each T from slices into a new slice that exactly holds all the elements.
1013pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {1013pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
1014 if (slices.len == 0) return (([*]T)(undefined))[0..0];1014 if (slices.len == 0) return &[0]T{};
10151015
1016 const total_len = blk: {1016 const total_len = blk: {
1017 var sum: usize = 0;1017 var sum: usize = 0;
src/ir.cpp+107-3
...@@ -10494,6 +10494,50 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT...@@ -10494,6 +10494,50 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
10494 continue;10494 continue;
10495 }10495 }
1049610496
10497 // *[N]T to []T
10498 // *[N]T to E![]T
10499 if (cur_type->id == ZigTypeIdPointer &&
10500 cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
10501 ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) ||
10502 is_slice(prev_type)))
10503 {
10504 ZigType *array_type = cur_type->data.pointer.child_type;
10505 ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ?
10506 prev_type->data.error_union.payload_type : prev_type;
10507 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
10508 if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
10509 types_match_const_cast_only(ira,
10510 slice_ptr_type->data.pointer.child_type,
10511 array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
10512 {
10513 convert_to_const_slice = false;
10514 continue;
10515 }
10516 }
10517
10518 // *[N]T to []T
10519 // *[N]T to E![]T
10520 if (prev_type->id == ZigTypeIdPointer &&
10521 prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
10522 ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) ||
10523 is_slice(cur_type)))
10524 {
10525 ZigType *array_type = prev_type->data.pointer.child_type;
10526 ZigType *slice_type = (cur_type->id == ZigTypeIdErrorUnion) ?
10527 cur_type->data.error_union.payload_type : cur_type;
10528 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
10529 if ((slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
10530 types_match_const_cast_only(ira,
10531 slice_ptr_type->data.pointer.child_type,
10532 array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
10533 {
10534 prev_inst = cur_inst;
10535 convert_to_const_slice = false;
10536 continue;
10537 }
10538 }
10539
10540 // [N]T to []T
10497 if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) &&10541 if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) &&
10498 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||10542 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
10499 cur_type->data.array.len == 0) &&10543 cur_type->data.array.len == 0) &&
...@@ -10505,6 +10549,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT...@@ -10505,6 +10549,7 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
10505 continue;10549 continue;
10506 }10550 }
1050710551
10552 // [N]T to []T
10508 if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) &&10553 if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) &&
10509 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||10554 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
10510 prev_type->data.array.len == 0) &&10555 prev_type->data.array.len == 0) &&
...@@ -12642,12 +12687,71 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -12642,12 +12687,71 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
12642 }12687 }
1264312688
12644 // *[N]T to []T12689 // *[N]T to []T
12645 if (is_slice(wanted_type) &&12690 // *[N]T to E![]T
12691 if ((is_slice(wanted_type) ||
12692 (wanted_type->id == ZigTypeIdErrorUnion &&
12693 is_slice(wanted_type->data.error_union.payload_type))) &&
12694 actual_type->id == ZigTypeIdPointer &&
12695 actual_type->data.pointer.ptr_len == PtrLenSingle &&
12696 actual_type->data.pointer.child_type->id == ZigTypeIdArray)
12697 {
12698 ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ?
12699 wanted_type->data.error_union.payload_type : wanted_type;
12700 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
12701 assert(slice_ptr_type->id == ZigTypeIdPointer);
12702 ZigType *array_type = actual_type->data.pointer.child_type;
12703 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
12704 || !actual_type->data.pointer.is_const);
12705 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
12706 array_type->data.array.child_type, source_node,
12707 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
12708 {
12709 // If the pointers both have ABI align, it works.
12710 // Or if the array length is 0, alignment doesn't matter.
12711 bool ok_align = array_type->data.array.len == 0 ||
12712 (slice_ptr_type->data.pointer.explicit_alignment == 0 &&
12713 actual_type->data.pointer.explicit_alignment == 0);
12714 if (!ok_align) {
12715 // If either one has non ABI align, we have to resolve them both
12716 if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
12717 ResolveStatusAlignmentKnown)))
12718 {
12719 return ira->codegen->invalid_instruction;
12720 }
12721 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
12722 ResolveStatusAlignmentKnown)))
12723 {
12724 return ira->codegen->invalid_instruction;
12725 }
12726 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
12727 }
12728 if (ok_align) {
12729 if (wanted_type->id == ZigTypeIdErrorUnion) {
12730 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, slice_type, value, nullptr);
12731 if (type_is_invalid(cast1->value.type))
12732 return ira->codegen->invalid_instruction;
12733
12734 IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc);
12735 if (type_is_invalid(cast2->value.type))
12736 return ira->codegen->invalid_instruction;
12737
12738 return cast2;
12739 } else {
12740 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, result_loc);
12741 }
12742 }
12743 }
12744 }
12745
12746 // *[N]T to E![]T
12747 if (wanted_type->id == ZigTypeIdErrorUnion &&
12748 is_slice(wanted_type->data.error_union.payload_type) &&
12646 actual_type->id == ZigTypeIdPointer &&12749 actual_type->id == ZigTypeIdPointer &&
12647 actual_type->data.pointer.ptr_len == PtrLenSingle &&12750 actual_type->data.pointer.ptr_len == PtrLenSingle &&
12648 actual_type->data.pointer.child_type->id == ZigTypeIdArray)12751 actual_type->data.pointer.child_type->id == ZigTypeIdArray)
12649 {12752 {
12650 ZigType *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;12753 ZigType *slice_type = wanted_type->data.error_union.payload_type;
12754 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
12651 assert(slice_ptr_type->id == ZigTypeIdPointer);12755 assert(slice_ptr_type->id == ZigTypeIdPointer);
12652 ZigType *array_type = actual_type->data.pointer.child_type;12756 ZigType *array_type = actual_type->data.pointer.child_type;
12653 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 012757 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
...@@ -12674,7 +12778,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -12674,7 +12778,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
12674 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);12778 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
12675 }12779 }
12676 if (ok_align) {12780 if (ok_align) {
12677 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);12781 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, result_loc);
12678 }12782 }
12679 }12783 }
12680 }12784 }
test/stage1/behavior/cast.zig+21
...@@ -538,3 +538,24 @@ test "implicit cast comptime_int to comptime_float" {...@@ -538,3 +538,24 @@ test "implicit cast comptime_int to comptime_float" {
538 comptime expect(comptime_float(10) == f32(10));538 comptime expect(comptime_float(10) == f32(10));
539 expect(2 == 2.0);539 expect(2 == 2.0);
540}540}
541
542test "implicit cast *[0]T to E![]const u8" {
543 var x = (anyerror![]const u8)(&[0]u8{});
544 expect((x catch unreachable).len == 0);
545}
546
547test "peer cast *[0]T to E![]const T" {
548 var buffer: [5]u8 = "abcde";
549 var buf: anyerror![]const u8 = buffer[0..];
550 var b = false;
551 var y = if (b) &[0]u8{} else buf;
552 expect(mem.eql(u8, "abcde", y catch unreachable));
553}
554
555test "peer cast *[0]T to []const T" {
556 var buffer: [5]u8 = "abcde";
557 var buf: []const u8 = buffer[0..];
558 var b = false;
559 var y = if (b) &[0]u8{} else buf;
560 expect(mem.eql(u8, "abcde", y));
561}