authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-25 19:41:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-25 19:41:19-04:00
logdf198ea60e05664b5b72a43aae815fa06d94c19c
treeedf13ce2d73ab17a56d6b07b4734b97260bde3eb
parent30d01c8fea68baab396081da040f49defa494088
parent21bf3b80666c14c9b2a2e1ec984a6b4bb23a5bb7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10034 from Snektron/stage2-slice

stage2: slice and optional improvements

7 files changed, 127 insertions(+), 77 deletions(-)

src/Sema.zig+43-11
...@@ -8013,14 +8013,24 @@ fn analyzePtrArithmetic(...@@ -8013,14 +8013,24 @@ fn analyzePtrArithmetic(
8013 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);8013 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
8014 // TODO adjust the return type according to alignment and other factors8014 // TODO adjust the return type according to alignment and other factors
8015 const runtime_src = rs: {8015 const runtime_src = rs: {
8016 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {8016 if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
8017 if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {8017 if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| {
8018 const ptr_ty = sema.typeOf(ptr);8018 const ptr_ty = sema.typeOf(ptr);
8019 const offset_int = offset_val.toUnsignedInt();
8020 const new_ptr_ty = ptr_ty; // TODO modify alignment8019 const new_ptr_ty = ptr_ty; // TODO modify alignment
8020
8021 if (ptr_val.isUndef() or offset_val.isUndef()) {
8022 return sema.addConstUndef(new_ptr_ty);
8023 }
8024
8025 const offset_int = offset_val.toUnsignedInt();
8021 if (ptr_val.getUnsignedInt()) |addr| {8026 if (ptr_val.getUnsignedInt()) |addr| {
8022 const target = sema.mod.getTarget();8027 const target = sema.mod.getTarget();
8023 const elem_ty = ptr_ty.childType();8028 const ptr_child_ty = ptr_ty.childType();
8029 const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array)
8030 ptr_child_ty.childType()
8031 else
8032 ptr_child_ty;
8033
8024 const elem_size = elem_ty.abiSize(target);8034 const elem_size = elem_ty.abiSize(target);
8025 const new_addr = switch (air_tag) {8035 const new_addr = switch (air_tag) {
8026 .ptr_add => addr + elem_size * offset_int,8036 .ptr_add => addr + elem_size * offset_int,
...@@ -13217,8 +13227,8 @@ fn analyzeSlice(...@@ -13217,8 +13227,8 @@ fn analyzeSlice(
13217 var elem_ty = ptr_ptr_child_ty.childType();13227 var elem_ty = ptr_ptr_child_ty.childType();
13218 switch (ptr_ptr_child_ty.zigTypeTag()) {13228 switch (ptr_ptr_child_ty.zigTypeTag()) {
13219 .Array => {},13229 .Array => {},
13220 .Pointer => {13230 .Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
13221 if (ptr_ptr_child_ty.isSinglePointer()) {13231 .One => {
13222 const double_child_ty = ptr_ptr_child_ty.childType();13232 const double_child_ty = ptr_ptr_child_ty.childType();
13223 if (double_child_ty.zigTypeTag() == .Array) {13233 if (double_child_ty.zigTypeTag() == .Array) {
13224 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);13234 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
...@@ -13228,10 +13238,23 @@ fn analyzeSlice(...@@ -13228,10 +13238,23 @@ fn analyzeSlice(
13228 } else {13238 } else {
13229 return sema.fail(block, ptr_src, "slice of single-item pointer", .{});13239 return sema.fail(block, ptr_src, "slice of single-item pointer", .{});
13230 }13240 }
13231 }13241 },
13242 .Many, .C => {
13243 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
13244 slice_ty = ptr_ptr_child_ty;
13245 array_ty = ptr_ptr_child_ty;
13246 elem_ty = ptr_ptr_child_ty.childType();
13247 },
13248 .Slice => {
13249 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
13250 slice_ty = ptr_ptr_child_ty;
13251 array_ty = ptr_ptr_child_ty;
13252 elem_ty = ptr_ptr_child_ty.childType();
13253 },
13232 },13254 },
13233 else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),13255 else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),
13234 }13256 }
13257
13235 const ptr = if (slice_ty.isSlice())13258 const ptr = if (slice_ty.isSlice())
13236 try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)13259 try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)
13237 else13260 else
...@@ -13263,7 +13286,6 @@ fn analyzeSlice(...@@ -13263,7 +13286,6 @@ fn analyzeSlice(
1326313286
13264 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);13287 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
1326513288
13266 const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr);
13267 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);13289 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
1326813290
13269 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;13291 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
...@@ -13287,11 +13309,21 @@ fn analyzeSlice(...@@ -13287,11 +13309,21 @@ fn analyzeSlice(
13287 .size = .One,13309 .size = .One,
13288 });13310 });
1328913311
13290 if (opt_new_ptr_val) |new_ptr_val| {13312 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
13291 return sema.addConstant(return_ty, new_ptr_val);13313 const new_ptr_val = opt_new_ptr_val orelse {
13292 } else {
13293 return block.addBitCast(return_ty, new_ptr);13314 return block.addBitCast(return_ty, new_ptr);
13315 };
13316
13317 if (!new_ptr_val.isUndef()) {
13318 return sema.addConstant(return_ty, new_ptr_val);
13294 }13319 }
13320
13321 // Special case: @as([]i32, undefined)[x..x]
13322 if (new_len_int == 0) {
13323 return sema.addConstUndef(return_ty);
13324 }
13325
13326 return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{});
13295 }13327 }
1329613328
13297 const return_ty = try Type.ptr(sema.arena, .{13329 const return_ty = try Type.ptr(sema.arena, .{
src/codegen/llvm.zig+9-8
...@@ -767,7 +767,7 @@ pub const DeclGen = struct {...@@ -767,7 +767,7 @@ pub const DeclGen = struct {
767 }767 }
768 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());768 const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace());
769 const elem_ty = t.childType();769 const elem_ty = t.childType();
770 const llvm_elem_ty = if (elem_ty.hasCodeGenBits())770 const llvm_elem_ty = if (elem_ty.hasCodeGenBits() or elem_ty.zigTypeTag() == .Array)
771 try dg.llvmType(elem_ty)771 try dg.llvmType(elem_ty)
772 else772 else
773 dg.context.intType(8);773 dg.context.intType(8);
...@@ -1480,7 +1480,7 @@ pub const DeclGen = struct {...@@ -1480,7 +1480,7 @@ pub const DeclGen = struct {
1480 }1480 }
14811481
1482 const llvm_type = try self.llvmType(tv.ty);1482 const llvm_type = try self.llvmType(tv.ty);
1483 if (!tv.ty.childType().hasCodeGenBits()) {1483 if (!tv.ty.childType().hasCodeGenBits() or !decl.ty.hasCodeGenBits()) {
1484 return self.lowerPtrToVoid(tv.ty);1484 return self.lowerPtrToVoid(tv.ty);
1485 }1485 }
14861486
...@@ -1502,7 +1502,7 @@ pub const DeclGen = struct {...@@ -1502,7 +1502,7 @@ pub const DeclGen = struct {
1502 // for non-optional pointers. We also need to respect the alignment, even though1502 // for non-optional pointers. We also need to respect the alignment, even though
1503 // the address will never be dereferenced.1503 // the address will never be dereferenced.
1504 const llvm_usize = try dg.llvmType(Type.usize);1504 const llvm_usize = try dg.llvmType(Type.usize);
1505 const llvm_ptr_ty = dg.context.intType(8).pointerType(0);1505 const llvm_ptr_ty = try dg.llvmType(ptr_ty);
1506 if (alignment != 0) {1506 if (alignment != 0) {
1507 return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty);1507 return llvm_usize.constInt(alignment, .False).constIntToPtr(llvm_ptr_ty);
1508 }1508 }
...@@ -2475,6 +2475,12 @@ pub const FuncGen = struct {...@@ -2475,6 +2475,12 @@ pub const FuncGen = struct {
2475 const operand = try self.resolveInst(un_op);2475 const operand = try self.resolveInst(un_op);
2476 const operand_ty = self.air.typeOf(un_op);2476 const operand_ty = self.air.typeOf(un_op);
2477 const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;2477 const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
2478 if (optional_ty.isPtrLikeOptional()) {
2479 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
2480 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
2481 return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
2482 }
2483
2478 var buf: Type.Payload.ElemType = undefined;2484 var buf: Type.Payload.ElemType = undefined;
2479 const payload_ty = optional_ty.optionalChild(&buf);2485 const payload_ty = optional_ty.optionalChild(&buf);
2480 if (!payload_ty.hasCodeGenBits()) {2486 if (!payload_ty.hasCodeGenBits()) {
...@@ -2484,11 +2490,6 @@ pub const FuncGen = struct {...@@ -2484,11 +2490,6 @@ pub const FuncGen = struct {
2484 return operand;2490 return operand;
2485 }2491 }
2486 }2492 }
2487 if (optional_ty.isPtrLikeOptional()) {
2488 const optional_llvm_ty = try self.dg.llvmType(optional_ty);
2489 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;
2490 return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), "");
2491 }
24922493
2493 if (operand_is_ptr or isByRef(optional_ty)) {2494 if (operand_is_ptr or isByRef(optional_ty)) {
2494 const index_type = self.context.intType(32);2495 const index_type = self.context.intType(32);
src/type.zig+5-1
...@@ -2347,11 +2347,13 @@ pub const Type = extern union {...@@ -2347,11 +2347,13 @@ pub const Type = extern union {
2347 }2347 }
2348 }2348 }
23492349
2350 /// Asserts that the type is an optional2350 /// Asserts that the type is an optional or a pointer that can be null.
2351 pub fn isPtrLikeOptional(self: Type) bool {2351 pub fn isPtrLikeOptional(self: Type) bool {
2352 switch (self.tag()) {2352 switch (self.tag()) {
2353 .optional_single_const_pointer,2353 .optional_single_const_pointer,
2354 .optional_single_mut_pointer,2354 .optional_single_mut_pointer,
2355 .c_const_pointer,
2356 .c_mut_pointer,
2355 => return true,2357 => return true,
23562358
2357 .optional => {2359 .optional => {
...@@ -2367,6 +2369,8 @@ pub const Type = extern union {...@@ -2367,6 +2369,8 @@ pub const Type = extern union {
2367 .Many, .One => return !info.@"allowzero",2369 .Many, .One => return !info.@"allowzero",
2368 }2370 }
2369 },2371 },
2372
2373 .pointer => return self.castTag(.pointer).?.data.size == .C,
2370 else => unreachable,2374 else => unreachable,
2371 }2375 }
2372 }2376 }
test/behavior.zig+1
...@@ -65,6 +65,7 @@ test {...@@ -65,6 +65,7 @@ test {
65 if (builtin.zig_is_stage2) {65 if (builtin.zig_is_stage2) {
66 // When all comptime_memory.zig tests pass, #9646 can be closed.66 // When all comptime_memory.zig tests pass, #9646 can be closed.
67 // _ = @import("behavior/comptime_memory.zig");67 // _ = @import("behavior/comptime_memory.zig");
68 _ = @import("behavior/slice_stage2.zig");
68 } else {69 } else {
69 _ = @import("behavior/align_stage1.zig");70 _ = @import("behavior/align_stage1.zig");
70 _ = @import("behavior/alignof.zig");71 _ = @import("behavior/alignof.zig");
test/behavior/slice.zig+57
...@@ -109,3 +109,60 @@ test "slice of type" {...@@ -109,3 +109,60 @@ test "slice of type" {
109 }109 }
110 }110 }
111}111}
112
113test "generic malloc free" {
114 const a = memAlloc(u8, 10) catch unreachable;
115 memFree(u8, a);
116}
117var some_mem: [100]u8 = undefined;
118fn memAlloc(comptime T: type, n: usize) anyerror![]T {
119 return @ptrCast([*]T, &some_mem[0])[0..n];
120}
121fn memFree(comptime T: type, memory: []T) void {
122 _ = memory;
123}
124
125test "slice of hardcoded address to pointer" {
126 const S = struct {
127 fn doTheTest() !void {
128 const pointer = @intToPtr([*]u8, 0x04)[0..2];
129 comptime try expect(@TypeOf(pointer) == *[2]u8);
130 const slice: []const u8 = pointer;
131 try expect(@ptrToInt(slice.ptr) == 4);
132 try expect(slice.len == 2);
133 }
134 };
135
136 try S.doTheTest();
137}
138
139test "comptime slice of pointer preserves comptime var" {
140 comptime {
141 var buff: [10]u8 = undefined;
142 var a = @ptrCast([*]u8, &buff);
143 a[0..1][0] = 1;
144 try expect(buff[0..][0..][0] == 1);
145 }
146}
147
148test "comptime pointer cast array and then slice" {
149 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
150
151 const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
152 const sliceA: []const u8 = ptrA[0..2];
153
154 const ptrB: [*]const u8 = &array;
155 const sliceB: []const u8 = ptrB[0..2];
156
157 try expect(sliceA[1] == 2);
158 try expect(sliceB[1] == 2);
159}
160
161test "slicing zero length array" {
162 const s1 = ""[0..];
163 const s2 = ([_]u32{})[0..];
164 try expect(s1.len == 0);
165 try expect(s2.len == 0);
166 try expect(mem.eql(u8, s1, ""));
167 try expect(mem.eql(u32, s2, &[_]u32{}));
168}
test/behavior/slice_stage1.zig-57
...@@ -4,15 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;...@@ -4,15 +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 "slicing zero length array" {
8 const s1 = ""[0..];
9 const s2 = ([_]u32{})[0..];
10 try expect(s1.len == 0);
11 try expect(s2.len == 0);
12 try expect(mem.eql(u8, s1, ""));
13 try expect(mem.eql(u32, s2, &[_]u32{}));
14}
15
16test "slice string literal has correct type" {7test "slice string literal has correct type" {
17 comptime {8 comptime {
18 try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);9 try expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
...@@ -25,18 +16,6 @@ test "slice string literal has correct type" {...@@ -25,18 +16,6 @@ test "slice string literal has correct type" {
25 comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);16 comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);
26}17}
2718
28test "generic malloc free" {
29 const a = memAlloc(u8, 10) catch unreachable;
30 memFree(u8, a);
31}
32var some_mem: [100]u8 = undefined;
33fn memAlloc(comptime T: type, n: usize) anyerror![]T {
34 return @ptrCast([*]T, &some_mem[0])[0..n];
35}
36fn memFree(comptime T: type, memory: []T) void {
37 _ = memory;
38}
39
40test "result location zero sized array inside struct field implicit cast to slice" {19test "result location zero sized array inside struct field implicit cast to slice" {
41 const E = struct {20 const E = struct {
42 entries: []u32,21 entries: []u32,
...@@ -307,20 +286,6 @@ test "slice syntax resulting in pointer-to-array" {...@@ -307,20 +286,6 @@ test "slice syntax resulting in pointer-to-array" {
307 comptime try S.doTheTest();286 comptime try S.doTheTest();
308}287}
309288
310test "slice of hardcoded address to pointer" {
311 const S = struct {
312 fn doTheTest() !void {
313 const pointer = @intToPtr([*]u8, 0x04)[0..2];
314 comptime try expect(@TypeOf(pointer) == *[2]u8);
315 const slice: []const u8 = pointer;
316 try expect(@ptrToInt(slice.ptr) == 4);
317 try expect(slice.len == 2);
318 }
319 };
320
321 try S.doTheTest();
322}
323
324test "type coercion of pointer to anon struct literal to pointer to slice" {289test "type coercion of pointer to anon struct literal to pointer to slice" {
325 const S = struct {290 const S = struct {
326 const U = union {291 const U = union {
...@@ -352,15 +317,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {...@@ -352,15 +317,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {
352 comptime try S.doTheTest();317 comptime try S.doTheTest();
353}318}
354319
355test "comptime slice of pointer preserves comptime var" {
356 comptime {
357 var buff: [10]u8 = undefined;
358 var a = @ptrCast([*]u8, &buff);
359 a[0..1][0] = 1;
360 try expect(buff[0..][0..][0] == 1);
361 }
362}
363
364test "array concat of slices gives slice" {320test "array concat of slices gives slice" {
365 comptime {321 comptime {
366 var a: []const u8 = "aoeu";322 var a: []const u8 = "aoeu";
...@@ -370,19 +326,6 @@ test "array concat of slices gives slice" {...@@ -370,19 +326,6 @@ test "array concat of slices gives slice" {
370 }326 }
371}327}
372328
373test "comptime pointer cast array and then slice" {
374 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
375
376 const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
377 const sliceA: []const u8 = ptrA[0..2];
378
379 const ptrB: [*]const u8 = &array;
380 const sliceB: []const u8 = ptrB[0..2];
381
382 try expect(sliceA[1] == 2);
383 try expect(sliceB[1] == 2);
384}
385
386test "slice bounds in comptime concatenation" {329test "slice bounds in comptime concatenation" {
387 const bs = comptime blk: {330 const bs = comptime blk: {
388 const b = "........1........";331 const b = "........1........";
test/behavior/slice_stage2.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const x = @intToPtr([*]i32, 0x1000)[0..0x500];
5const y = x[0x100..];
6test "compile time slice of pointer to hard coded address" {
7 try expect(@ptrToInt(x) == 0x1000);
8 try expect(x.len == 0x500);
9
10 try expect(@ptrToInt(y) == 0x1400);
11 try expect(y.len == 0x400);
12}