authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-03 21:05:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-03 21:05:10-07:00
log0893326e0ea9b261c5d334067c294c7d8972d5a1
treed91c43cbe138a37aca438f8adc6c747abafa346b
parent71e0cca7a7957e2f024d2985318e478aa6fb1451

Sema: slice improvements

* resolve_inferred_alloc now gives a proper mutability attribute to the corresponding alloc instruction. Previously, it would fail to mark things const. * slicing: fix the detection for when the end index equals the length of the underlying object. Previously it was using `end - start` but it should just use the end index directly. It also takes into account when slicing a comptime-known slice. * `Type.sentinel`: fix not handling all slice tags

4 files changed, 85 insertions(+), 40 deletions(-)

src/Sema.zig+70-28
...@@ -2503,6 +2503,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2503,6 +2503,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2503 try sema.requireRuntimeBlock(block, src);2503 try sema.requireRuntimeBlock(block, src);
2504 try sema.resolveTypeLayout(block, ty_src, final_elem_ty);2504 try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
25052505
2506 const final_ptr_ty = try Type.ptr(sema.arena, .{
2507 .pointee_type = final_elem_ty,
2508 .mutable = var_is_mut,
2509 .@"align" = inferred_alloc.data.alignment,
2510 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2511 });
2512
2506 if (var_is_mut) {2513 if (var_is_mut) {
2507 try sema.validateVarType(block, ty_src, final_elem_ty, false);2514 try sema.validateVarType(block, ty_src, final_elem_ty, false);
2508 } else ct: {2515 } else ct: {
...@@ -2534,8 +2541,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2534,8 +2541,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2534 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;2541 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
2535 if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;2542 if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
25362543
2537 const bitcast_ty_ref = air_datas[bitcast_inst].ty_op.ty;
2538
2539 const new_decl = d: {2544 const new_decl = d: {
2540 var anon_decl = try block.startAnonDecl(src);2545 var anon_decl = try block.startAnonDecl(src);
2541 defer anon_decl.deinit();2546 defer anon_decl.deinit();
...@@ -2551,17 +2556,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2551,17 +2556,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2551 // block so that codegen does not see it.2556 // block so that codegen does not see it.
2552 block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);2557 block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
2553 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);2558 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);
2554 air_datas[ptr_inst].ty_pl.ty = bitcast_ty_ref;2559 // Would be nice if we could just assign `bitcast_ty_ref` to
2560 // `air_datas[ptr_inst].ty_pl.ty`, wouldn't it? Alas, that is almost correct,
2561 // except that the pointer is mutable and we need to make it constant here.
2562 air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty);
25552563
2556 return;2564 return;
2557 }2565 }
25582566
2559 // Change it to a normal alloc.2567 // Change it to a normal alloc.
2560 const final_ptr_ty = try Type.ptr(sema.arena, .{
2561 .pointee_type = final_elem_ty,
2562 .@"align" = inferred_alloc.data.alignment,
2563 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2564 });
2565 sema.air_instructions.set(ptr_inst, .{2568 sema.air_instructions.set(ptr_inst, .{
2566 .tag = .alloc,2569 .tag = .alloc,
2567 .data = .{ .ty = final_ptr_ty },2570 .data = .{ .ty = final_ptr_ty },
...@@ -15609,12 +15612,16 @@ fn analyzeSlice(...@@ -15609,12 +15612,16 @@ fn analyzeSlice(
15609 var slice_ty = ptr_ptr_ty;15612 var slice_ty = ptr_ptr_ty;
15610 var ptr_or_slice = ptr_ptr;15613 var ptr_or_slice = ptr_ptr;
15611 var elem_ty = ptr_ptr_child_ty.childType();15614 var elem_ty = ptr_ptr_child_ty.childType();
15615 var ptr_sentinel: ?Value = null;
15612 switch (ptr_ptr_child_ty.zigTypeTag()) {15616 switch (ptr_ptr_child_ty.zigTypeTag()) {
15613 .Array => {},15617 .Array => {
15618 ptr_sentinel = ptr_ptr_child_ty.sentinel();
15619 },
15614 .Pointer => switch (ptr_ptr_child_ty.ptrSize()) {15620 .Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
15615 .One => {15621 .One => {
15616 const double_child_ty = ptr_ptr_child_ty.childType();15622 const double_child_ty = ptr_ptr_child_ty.childType();
15617 if (double_child_ty.zigTypeTag() == .Array) {15623 if (double_child_ty.zigTypeTag() == .Array) {
15624 ptr_sentinel = double_child_ty.sentinel();
15618 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);15625 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
15619 slice_ty = ptr_ptr_child_ty;15626 slice_ty = ptr_ptr_child_ty;
15620 array_ty = double_child_ty;15627 array_ty = double_child_ty;
...@@ -15624,12 +15631,14 @@ fn analyzeSlice(...@@ -15624,12 +15631,14 @@ fn analyzeSlice(
15624 }15631 }
15625 },15632 },
15626 .Many, .C => {15633 .Many, .C => {
15634 ptr_sentinel = ptr_ptr_child_ty.sentinel();
15627 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);15635 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
15628 slice_ty = ptr_ptr_child_ty;15636 slice_ty = ptr_ptr_child_ty;
15629 array_ty = ptr_ptr_child_ty;15637 array_ty = ptr_ptr_child_ty;
15630 elem_ty = ptr_ptr_child_ty.childType();15638 elem_ty = ptr_ptr_child_ty.childType();
15631 },15639 },
15632 .Slice => {15640 .Slice => {
15641 ptr_sentinel = ptr_ptr_child_ty.sentinel();
15633 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);15642 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
15634 slice_ty = ptr_ptr_child_ty;15643 slice_ty = ptr_ptr_child_ty;
15635 array_ty = ptr_ptr_child_ty;15644 array_ty = ptr_ptr_child_ty;
...@@ -15647,29 +15656,67 @@ fn analyzeSlice(...@@ -15647,29 +15656,67 @@ fn analyzeSlice(
15647 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);15656 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
15648 const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src);15657 const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src);
1564915658
15659 // true if and only if the end index of the slice, implicitly or explicitly, equals
15660 // the length of the underlying object being sliced. we might learn the length of the
15661 // underlying object because it is an array (which has the length in the type), or
15662 // we might learn of the length because it is a comptime-known slice value.
15663 var end_is_len = uncasted_end_opt == .none;
15650 const end = e: {15664 const end = e: {
15651 if (uncasted_end_opt != .none) {
15652 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
15653 }
15654
15655 if (array_ty.zigTypeTag() == .Array) {15665 if (array_ty.zigTypeTag() == .Array) {
15656 break :e try sema.addConstant(15666 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
15657 Type.usize,15667
15658 try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()),15668 if (!end_is_len) {
15659 );15669 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
15670 if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| {
15671 if (end_val.eql(len_val, Type.usize)) {
15672 end_is_len = true;
15673 }
15674 }
15675 break :e end;
15676 }
15677
15678 break :e try sema.addConstant(Type.usize, len_val);
15660 } else if (slice_ty.isSlice()) {15679 } else if (slice_ty.isSlice()) {
15680 if (!end_is_len) {
15681 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
15682 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
15683 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
15684 var int_payload: Value.Payload.U64 = .{
15685 .base = .{ .tag = .int_u64 },
15686 .data = slice_val.sliceLen(),
15687 };
15688 const slice_len_val = Value.initPayload(&int_payload.base);
15689 if (end_val.eql(slice_len_val, Type.usize)) {
15690 end_is_len = true;
15691 }
15692 }
15693 }
15694 break :e end;
15695 }
15661 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);15696 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
15662 }15697 }
15698 if (!end_is_len) {
15699 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
15700 }
15663 return sema.fail(block, end_src, "slice of pointer must include end value", .{});15701 return sema.fail(block, end_src, "slice of pointer must include end value", .{});
15664 };15702 };
1566515703
15666 const slice_sentinel = if (sentinel_opt != .none) blk: {15704 const sentinel = s: {
15667 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);15705 if (sentinel_opt != .none) {
15668 break :blk try sema.resolveConstValue(block, sentinel_src, casted);15706 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
15669 } else null;15707 break :s try sema.resolveConstValue(block, sentinel_src, casted);
15708 }
15709 // If we are slicing to the end of something that is sentinel-terminated
15710 // then the resulting slice type is also sentinel-terminated.
15711 if (end_is_len) {
15712 if (ptr_sentinel) |sent| {
15713 break :s sent;
15714 }
15715 }
15716 break :s null;
15717 };
1567015718
15671 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);15719 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
15672
15673 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);15720 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
1567415721
15675 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;15722 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
...@@ -15678,11 +15725,6 @@ fn analyzeSlice(...@@ -15678,11 +15725,6 @@ fn analyzeSlice(
15678 if (opt_new_len_val) |new_len_val| {15725 if (opt_new_len_val) |new_len_val| {
15679 const new_len_int = new_len_val.toUnsignedInt();15726 const new_len_int = new_len_val.toUnsignedInt();
1568015727
15681 const sentinel = if (array_ty.zigTypeTag() == .Array and new_len_int == array_ty.arrayLen())
15682 array_ty.sentinel()
15683 else
15684 slice_sentinel;
15685
15686 const return_ty = try Type.ptr(sema.arena, .{15728 const return_ty = try Type.ptr(sema.arena, .{
15687 .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty),15729 .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty),
15688 .sentinel = null,15730 .sentinel = null,
...@@ -15713,7 +15755,7 @@ fn analyzeSlice(...@@ -15713,7 +15755,7 @@ fn analyzeSlice(
1571315755
15714 const return_ty = try Type.ptr(sema.arena, .{15756 const return_ty = try Type.ptr(sema.arena, .{
15715 .pointee_type = elem_ty,15757 .pointee_type = elem_ty,
15716 .sentinel = slice_sentinel,15758 .sentinel = sentinel,
15717 .@"align" = new_ptr_ty_info.@"align",15759 .@"align" = new_ptr_ty_info.@"align",
15718 .@"addrspace" = new_ptr_ty_info.@"addrspace",15760 .@"addrspace" = new_ptr_ty_info.@"addrspace",
15719 .mutable = new_ptr_ty_info.mutable,15761 .mutable = new_ptr_ty_info.mutable,
src/type.zig+3
...@@ -3042,6 +3042,9 @@ pub const Type = extern union {...@@ -3042,6 +3042,9 @@ pub const Type = extern union {
3042 .array_u8,3042 .array_u8,
3043 .manyptr_u8,3043 .manyptr_u8,
3044 .manyptr_const_u8,3044 .manyptr_const_u8,
3045 .const_slice_u8,
3046 .const_slice,
3047 .mut_slice,
3045 => return null,3048 => return null,
30463049
3047 .pointer => return self.castTag(.pointer).?.data.sentinel,3050 .pointer => return self.castTag(.pointer).?.data.sentinel,
test/behavior/slice.zig+12
...@@ -192,3 +192,15 @@ test "compile time slice of pointer to hard coded address" {...@@ -192,3 +192,15 @@ test "compile time slice of pointer to hard coded address" {
192 try expect(@ptrToInt(y) == 0x1400);192 try expect(@ptrToInt(y) == 0x1400);
193 try expect(y.len == 0x400);193 try expect(y.len == 0x400);
194}194}
195
196test "slice string literal has correct type" {
197 comptime {
198 try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
199 const array = [_]i32{ 1, 2, 3, 4 };
200 try expect(@TypeOf(array[0..]) == *const [4]i32);
201 }
202 var runtime_zero: usize = 0;
203 comptime try expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8);
204 const array = [_]i32{ 1, 2, 3, 4 };
205 comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);
206}
test/behavior/slice_stage1.zig-12
...@@ -4,18 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;...@@ -4,18 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
5const mem = std.mem;5const mem = std.mem;
66
7test "slice string literal has correct type" {
8 comptime {
9 try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
10 const array = [_]i32{ 1, 2, 3, 4 };
11 try expect(@TypeOf(array[0..]) == *const [4]i32);
12 }
13 var runtime_zero: usize = 0;
14 comptime try expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8);
15 const array = [_]i32{ 1, 2, 3, 4 };
16 comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);
17}
18
19test "result location zero sized array inside struct field implicit cast to slice" {7test "result location zero sized array inside struct field implicit cast to slice" {
20 const E = struct {8 const E = struct {
21 entries: []u32,9 entries: []u32,