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
25032503 try sema.requireRuntimeBlock(block, src);
25042504 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
25062513 if (var_is_mut) {
25072514 try sema.validateVarType(block, ty_src, final_elem_ty, false);
25082515 } else ct: {
......@@ -2534,8 +2541,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
25342541 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
25352542 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
25392544 const new_decl = d: {
25402545 var anon_decl = try block.startAnonDecl(src);
25412546 defer anon_decl.deinit();
......@@ -2551,17 +2556,15 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
25512556 // block so that codegen does not see it.
25522557 block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
25532558 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
25562564 return;
25572565 }
25582566
25592567 // 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 });
25652568 sema.air_instructions.set(ptr_inst, .{
25662569 .tag = .alloc,
25672570 .data = .{ .ty = final_ptr_ty },
......@@ -15609,12 +15612,16 @@ fn analyzeSlice(
1560915612 var slice_ty = ptr_ptr_ty;
1561015613 var ptr_or_slice = ptr_ptr;
1561115614 var elem_ty = ptr_ptr_child_ty.childType();
15615 var ptr_sentinel: ?Value = null;
1561215616 switch (ptr_ptr_child_ty.zigTypeTag()) {
15613 .Array => {},
15617 .Array => {
15618 ptr_sentinel = ptr_ptr_child_ty.sentinel();
15619 },
1561415620 .Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
1561515621 .One => {
1561615622 const double_child_ty = ptr_ptr_child_ty.childType();
1561715623 if (double_child_ty.zigTypeTag() == .Array) {
15624 ptr_sentinel = double_child_ty.sentinel();
1561815625 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
1561915626 slice_ty = ptr_ptr_child_ty;
1562015627 array_ty = double_child_ty;
......@@ -15624,12 +15631,14 @@ fn analyzeSlice(
1562415631 }
1562515632 },
1562615633 .Many, .C => {
15634 ptr_sentinel = ptr_ptr_child_ty.sentinel();
1562715635 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
1562815636 slice_ty = ptr_ptr_child_ty;
1562915637 array_ty = ptr_ptr_child_ty;
1563015638 elem_ty = ptr_ptr_child_ty.childType();
1563115639 },
1563215640 .Slice => {
15641 ptr_sentinel = ptr_ptr_child_ty.sentinel();
1563315642 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
1563415643 slice_ty = ptr_ptr_child_ty;
1563515644 array_ty = ptr_ptr_child_ty;
......@@ -15647,29 +15656,67 @@ fn analyzeSlice(
1564715656 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
1564815657 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;
1565015664 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
1565515665 if (array_ty.zigTypeTag() == .Array) {
15656 break :e try sema.addConstant(
15657 Type.usize,
15658 try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()),
15659 );
15666 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
15667
15668 if (!end_is_len) {
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);
1566015679 } 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 }
1566115696 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
1566215697 }
15698 if (!end_is_len) {
15699 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
15700 }
1566315701 return sema.fail(block, end_src, "slice of pointer must include end value", .{});
1566415702 };
1566515703
15666 const slice_sentinel = if (sentinel_opt != .none) blk: {
15667 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
15668 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
15669 } else null;
15704 const sentinel = s: {
15705 if (sentinel_opt != .none) {
15706 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
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
1567115719 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
15672
1567315720 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
1567415721
1567515722 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
......@@ -15678,11 +15725,6 @@ fn analyzeSlice(
1567815725 if (opt_new_len_val) |new_len_val| {
1567915726 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
1568615728 const return_ty = try Type.ptr(sema.arena, .{
1568715729 .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty),
1568815730 .sentinel = null,
......@@ -15713,7 +15755,7 @@ fn analyzeSlice(
1571315755
1571415756 const return_ty = try Type.ptr(sema.arena, .{
1571515757 .pointee_type = elem_ty,
15716 .sentinel = slice_sentinel,
15758 .sentinel = sentinel,
1571715759 .@"align" = new_ptr_ty_info.@"align",
1571815760 .@"addrspace" = new_ptr_ty_info.@"addrspace",
1571915761 .mutable = new_ptr_ty_info.mutable,
src/type.zig+3
......@@ -3042,6 +3042,9 @@ pub const Type = extern union {
30423042 .array_u8,
30433043 .manyptr_u8,
30443044 .manyptr_const_u8,
3045 .const_slice_u8,
3046 .const_slice,
3047 .mut_slice,
30453048 => return null,
30463049
30473050 .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" {
192192 try expect(@ptrToInt(y) == 0x1400);
193193 try expect(y.len == 0x400);
194194}
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;
44const expectEqual = std.testing.expectEqual;
55const 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
197test "result location zero sized array inside struct field implicit cast to slice" {
208 const E = struct {
219 entries: []u32,