authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-07 10:20:34+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-14 22:07:25-07:00
logce88c43a4ee94d82a64e7580885dc6693cc6120c
tree2ec2dd9966ff2f96186d13872031c074a81e50bc
parent610b02c432072c85f079f3ddf315cb864aa5dbf5

Sema: allow indexing tuple and vector pointers

Resolves: #13852 Resolves: #14705

5 files changed, 100 insertions(+), 17 deletions(-)

src/Sema.zig+22-15
...@@ -9991,7 +9991,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -9991,7 +9991,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
9991 indexable_ty.fmt(mod),9991 indexable_ty.fmt(mod),
9992 });9992 });
9993 errdefer msg.destroy(sema.gpa);9993 errdefer msg.destroy(sema.gpa);
9994 if (indexable_ty.zigTypeTag(mod) == .Array) {9994 if (indexable_ty.isIndexable(mod)) {
9995 try sema.errNote(block, src, msg, "consider using '&' here", .{});9995 try sema.errNote(block, src, msg, "consider using '&' here", .{});
9996 }9996 }
9997 break :msg msg;9997 break :msg msg;
...@@ -26088,8 +26088,19 @@ fn elemPtrOneLayerOnly(...@@ -26088,8 +26088,19 @@ fn elemPtrOneLayerOnly(
26088 return block.addPtrElemPtr(indexable, elem_index, result_ty);26088 return block.addPtrElemPtr(indexable, elem_index, result_ty);
26089 },26089 },
26090 .One => {26090 .One => {
26091 assert(indexable_ty.childType(mod).zigTypeTag(mod) == .Array); // Guaranteed by checkIndexable26091 const child_ty = indexable_ty.childType(mod);
26092 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);26092 switch (child_ty.zigTypeTag(mod)) {
26093 .Array, .Vector => {
26094 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
26095 },
26096 .Struct => {
26097 assert(child_ty.isTuple(mod));
26098 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
26099 const index = @intCast(u32, index_val.toUnsignedInt(mod));
26100 return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
26101 },
26102 else => unreachable, // Guaranteed by checkIndexable
26103 }
26093 },26104 },
26094 }26105 }
26095}26106}
...@@ -26139,19 +26150,15 @@ fn elemVal(...@@ -26139,19 +26150,15 @@ fn elemVal(
26139 return block.addBinOp(.ptr_elem_val, indexable, elem_index);26150 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
26140 },26151 },
26141 .One => {26152 .One => {
26142 const array_ty = indexable_ty.childType(mod); // Guaranteed by checkIndexable26153 arr_sent: {
26143 assert(array_ty.zigTypeTag(mod) == .Array);26154 const inner_ty = indexable_ty.childType(mod);
2614426155 if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent;
26145 if (array_ty.sentinel(mod)) |sentinel| {26156 const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent;
26146 // index must be defined since it can access out of bounds26157 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
26147 if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {26158 const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod));
26148 const index = @intCast(usize, index_val.toUnsignedInt(mod));26159 if (index != inner_ty.arrayLen(mod)) break :arr_sent;
26149 if (index == array_ty.arrayLen(mod)) {26160 return sema.addConstant(inner_ty.childType(mod), sentinel);
26150 return sema.addConstant(array_ty.childType(mod), sentinel);
26151 }
26152 }
26153 }26161 }
26154
26155 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);26162 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
26156 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);26163 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
26157 },26164 },
src/type.zig+10-2
...@@ -2832,7 +2832,11 @@ pub const Type = struct {...@@ -2832,7 +2832,11 @@ pub const Type = struct {
2832 .Array, .Vector => true,2832 .Array, .Vector => true,
2833 .Pointer => switch (ty.ptrSize(mod)) {2833 .Pointer => switch (ty.ptrSize(mod)) {
2834 .Slice, .Many, .C => true,2834 .Slice, .Many, .C => true,
2835 .One => ty.childType(mod).zigTypeTag(mod) == .Array,2835 .One => switch (ty.childType(mod).zigTypeTag(mod)) {
2836 .Array, .Vector => true,
2837 .Struct => ty.childType(mod).isTuple(mod),
2838 else => false,
2839 },
2836 },2840 },
2837 .Struct => ty.isTuple(mod),2841 .Struct => ty.isTuple(mod),
2838 else => false,2842 else => false,
...@@ -2845,7 +2849,11 @@ pub const Type = struct {...@@ -2845,7 +2849,11 @@ pub const Type = struct {
2845 .Pointer => switch (ty.ptrSize(mod)) {2849 .Pointer => switch (ty.ptrSize(mod)) {
2846 .Many, .C => false,2850 .Many, .C => false,
2847 .Slice => true,2851 .Slice => true,
2848 .One => ty.childType(mod).zigTypeTag(mod) == .Array,2852 .One => switch (ty.childType(mod).zigTypeTag(mod)) {
2853 .Array, .Vector => true,
2854 .Struct => ty.childType(mod).isTuple(mod),
2855 else => false,
2856 },
2849 },2857 },
2850 .Struct => ty.isTuple(mod),2858 .Struct => ty.isTuple(mod),
2851 else => false,2859 else => false,
test/behavior/for.zig+16
...@@ -463,3 +463,19 @@ test "inline for with counter as the comptime-known" {...@@ -463,3 +463,19 @@ test "inline for with counter as the comptime-known" {
463463
464 try expect(S.ok == 2);464 try expect(S.ok == 2);
465}465}
466
467test "inline for on tuple pointer" {
468 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
469 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
470 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
471 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
472
473 const S = struct { u32, u32, u32 };
474 var s: S = .{ 100, 200, 300 };
475
476 inline for (&s, 0..) |*x, i| {
477 x.* = i;
478 }
479
480 try expectEqual(S{ 0, 1, 2 }, s);
481}
test/behavior/tuple.zig+25
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const std = @import("std");
3const testing = std.testing;3const testing = std.testing;
4const assert = std.debug.assert;
4const expect = testing.expect;5const expect = testing.expect;
5const expectEqualStrings = std.testing.expectEqualStrings;6const expectEqualStrings = std.testing.expectEqualStrings;
6const expectEqual = std.testing.expectEqual;7const expectEqual = std.testing.expectEqual;
...@@ -428,3 +429,27 @@ test "sentinel slice in tuple" {...@@ -428,3 +429,27 @@ test "sentinel slice in tuple" {
428429
429 _ = S;430 _ = S;
430}431}
432
433test "tuple pointer is indexable" {
434 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
435 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
436 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
437 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
438
439 const S = struct { u32, bool };
440
441 const x: S = .{ 123, true };
442 comptime assert(@TypeOf(&(&x)[0]) == *const u32); // validate constness
443 try expectEqual(@as(u32, 123), (&x)[0]);
444 try expectEqual(true, (&x)[1]);
445
446 var y: S = .{ 123, true };
447 comptime assert(@TypeOf(&(&y)[0]) == *u32); // validate constness
448 try expectEqual(@as(u32, 123), (&y)[0]);
449 try expectEqual(true, (&y)[1]);
450
451 (&y)[0] = 100;
452 (&y)[1] = false;
453 try expectEqual(@as(u32, 100), (&y)[0]);
454 try expectEqual(false, (&y)[1]);
455}
test/behavior/vector.zig+27
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const mem = std.mem;3const mem = std.mem;
4const math = std.math;4const math = std.math;
5const assert = std.debug.assert;
5const expect = std.testing.expect;6const expect = std.testing.expect;
6const expectEqual = std.testing.expectEqual;7const expectEqual = std.testing.expectEqual;
78
...@@ -1343,3 +1344,29 @@ test "compare vectors with different element types" {...@@ -1343,3 +1344,29 @@ test "compare vectors with different element types" {
1343 var b: @Vector(2, u9) = .{ 3, 0 };1344 var b: @Vector(2, u9) = .{ 3, 0 };
1344 try expectEqual(@Vector(2, bool){ true, false }, a < b);1345 try expectEqual(@Vector(2, bool){ true, false }, a < b);
1345}1346}
1347
1348test "vector pointer is indexable" {
1349 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1350 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1351 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1352 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1353 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1354 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1355
1356 const V = @Vector(2, u32);
1357
1358 const x: V = .{ 123, 456 };
1359 comptime assert(@TypeOf(&(&x)[0]) == *const u32); // validate constness
1360 try expectEqual(@as(u32, 123), (&x)[0]);
1361 try expectEqual(@as(u32, 456), (&x)[1]);
1362
1363 var y: V = .{ 123, 456 };
1364 comptime assert(@TypeOf(&(&y)[0]) == *u32); // validate constness
1365 try expectEqual(@as(u32, 123), (&y)[0]);
1366 try expectEqual(@as(u32, 456), (&y)[1]);
1367
1368 (&y)[0] = 100;
1369 (&y)[1] = 200;
1370 try expectEqual(@as(u32, 100), (&y)[0]);
1371 try expectEqual(@as(u32, 200), (&y)[1]);
1372}