Commit 0942bf73c9

Robin Voetter <robin@voetter.nl>
2021-10-24 16:34:42
stage2: improve slicing
* Allow slicing many- and c-pointers. * Allow comptime pointer arithmetic on undefined values. * Return the correct type for slicing of slices.
1 parent ed73281
Changed files (3)
src/Sema.zig
@@ -7989,11 +7989,16 @@ fn analyzePtrArithmetic(
     const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
     // TODO adjust the return type according to alignment and other factors
     const runtime_src = rs: {
-        if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
-            if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {
+        if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
+            if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| {
                 const ptr_ty = sema.typeOf(ptr);
-                const offset_int = offset_val.toUnsignedInt();
                 const new_ptr_ty = ptr_ty; // TODO modify alignment
+
+                if (ptr_val.isUndef() or offset_val.isUndef()) {
+                    return sema.addConstUndef(new_ptr_ty);
+                }
+
+                const offset_int = offset_val.toUnsignedInt();
                 if (ptr_val.getUnsignedInt()) |addr| {
                     const target = sema.mod.getTarget();
                     const elem_ty = ptr_ty.childType();
@@ -13206,8 +13211,8 @@ fn analyzeSlice(
     var elem_ty = ptr_ptr_child_ty.childType();
     switch (ptr_ptr_child_ty.zigTypeTag()) {
         .Array => {},
-        .Pointer => {
-            if (ptr_ptr_child_ty.isSinglePointer()) {
+        .Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
+            .One => {
                 const double_child_ty = ptr_ptr_child_ty.childType();
                 if (double_child_ty.zigTypeTag() == .Array) {
                     ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
@@ -13217,10 +13222,23 @@ fn analyzeSlice(
                 } else {
                     return sema.fail(block, ptr_src, "slice of single-item pointer", .{});
                 }
-            }
+            },
+            .Many, .C => {
+                ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
+                slice_ty = ptr_ptr_child_ty;
+                array_ty = ptr_ptr_child_ty;
+                elem_ty = ptr_ptr_child_ty.childType();
+            },
+            .Slice => {
+                ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
+                slice_ty = ptr_ptr_child_ty;
+                array_ty = ptr_ptr_child_ty;
+                elem_ty = ptr_ptr_child_ty.childType();
+            },
         },
         else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),
     }
+
     const ptr = if (slice_ty.isSlice())
         try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)
     else
@@ -13252,7 +13270,6 @@ fn analyzeSlice(
 
     const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
 
-    const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr);
     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
 
     const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
@@ -13276,11 +13293,21 @@ fn analyzeSlice(
             .size = .One,
         });
 
-        if (opt_new_ptr_val) |new_ptr_val| {
-            return sema.addConstant(return_ty, new_ptr_val);
-        } else {
+        const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
+        const new_ptr_val = opt_new_ptr_val orelse {
             return block.addBitCast(return_ty, new_ptr);
+        };
+
+        if (!new_ptr_val.isUndef()) {
+            return sema.addConstant(return_ty, new_ptr_val);
         }
+
+        // Special case: @as([]i32, undefined)[x..x]
+        if (new_len_int == 0) {
+            return sema.addConstUndef(return_ty);
+        }
+
+        return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{});
     }
 
     const return_ty = try Type.ptr(sema.arena, .{
test/behavior/slice.zig
@@ -109,3 +109,51 @@ test "slice of type" {
         }
     }
 }
+
+test "generic malloc free" {
+    const a = memAlloc(u8, 10) catch unreachable;
+    memFree(u8, a);
+}
+var some_mem: [100]u8 = undefined;
+fn memAlloc(comptime T: type, n: usize) anyerror![]T {
+    return @ptrCast([*]T, &some_mem[0])[0..n];
+}
+fn memFree(comptime T: type, memory: []T) void {
+    _ = memory;
+}
+
+test "slice of hardcoded address to pointer" {
+    const S = struct {
+        fn doTheTest() !void {
+            const pointer = @intToPtr([*]u8, 0x04)[0..2];
+            comptime try expect(@TypeOf(pointer) == *[2]u8);
+            const slice: []const u8 = pointer;
+            try expect(@ptrToInt(slice.ptr) == 4);
+            try expect(slice.len == 2);
+        }
+    };
+
+    try S.doTheTest();
+}
+
+test "comptime slice of pointer preserves comptime var" {
+    comptime {
+        var buff: [10]u8 = undefined;
+        var a = @ptrCast([*]u8, &buff);
+        a[0..1][0] = 1;
+        try expect(buff[0..][0..][0] == 1);
+    }
+}
+
+test "comptime pointer cast array and then slice" {
+    const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
+
+    const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
+    const sliceA: []const u8 = ptrA[0..2];
+
+    const ptrB: [*]const u8 = &array;
+    const sliceB: []const u8 = ptrB[0..2];
+
+    try expect(sliceA[1] == 2);
+    try expect(sliceB[1] == 2);
+}
test/behavior/slice_stage1.zig
@@ -25,18 +25,6 @@ test "slice string literal has correct type" {
     comptime try expect(@TypeOf(array[runtime_zero..]) == []const i32);
 }
 
-test "generic malloc free" {
-    const a = memAlloc(u8, 10) catch unreachable;
-    memFree(u8, a);
-}
-var some_mem: [100]u8 = undefined;
-fn memAlloc(comptime T: type, n: usize) anyerror![]T {
-    return @ptrCast([*]T, &some_mem[0])[0..n];
-}
-fn memFree(comptime T: type, memory: []T) void {
-    _ = memory;
-}
-
 test "result location zero sized array inside struct field implicit cast to slice" {
     const E = struct {
         entries: []u32,
@@ -307,20 +295,6 @@ test "slice syntax resulting in pointer-to-array" {
     comptime try S.doTheTest();
 }
 
-test "slice of hardcoded address to pointer" {
-    const S = struct {
-        fn doTheTest() !void {
-            const pointer = @intToPtr([*]u8, 0x04)[0..2];
-            comptime try expect(@TypeOf(pointer) == *[2]u8);
-            const slice: []const u8 = pointer;
-            try expect(@ptrToInt(slice.ptr) == 4);
-            try expect(slice.len == 2);
-        }
-    };
-
-    try S.doTheTest();
-}
-
 test "type coercion of pointer to anon struct literal to pointer to slice" {
     const S = struct {
         const U = union {
@@ -352,15 +326,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {
     comptime try S.doTheTest();
 }
 
-test "comptime slice of pointer preserves comptime var" {
-    comptime {
-        var buff: [10]u8 = undefined;
-        var a = @ptrCast([*]u8, &buff);
-        a[0..1][0] = 1;
-        try expect(buff[0..][0..][0] == 1);
-    }
-}
-
 test "array concat of slices gives slice" {
     comptime {
         var a: []const u8 = "aoeu";
@@ -370,19 +335,6 @@ test "array concat of slices gives slice" {
     }
 }
 
-test "comptime pointer cast array and then slice" {
-    const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
-
-    const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
-    const sliceA: []const u8 = ptrA[0..2];
-
-    const ptrB: [*]const u8 = &array;
-    const sliceB: []const u8 = ptrB[0..2];
-
-    try expect(sliceA[1] == 2);
-    try expect(sliceB[1] == 2);
-}
-
 test "slice bounds in comptime concatenation" {
     const bs = comptime blk: {
         const b = "........1........";