| author | |
| committer | |
| log | ce88c43a4ee94d82a64e7580885dc6693cc6120c |
| tree | 2ec2dd9966ff2f96186d13872031c074a81e50bc |
| parent | 610b02c432072c85f079f3ddf315cb864aa5dbf5 |
Resolves: #13852
Resolves: #147055 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 | 9991 | indexable_ty.fmt(mod), |
| 9992 | 9992 | }); |
| 9993 | 9993 | errdefer msg.destroy(sema.gpa); |
| 9994 | if (indexable_ty.zigTypeTag(mod) == .Array) { | |
| 9994 | if (indexable_ty.isIndexable(mod)) { | |
| 9995 | 9995 | try sema.errNote(block, src, msg, "consider using '&' here", .{}); |
| 9996 | 9996 | } |
| 9997 | 9997 | break :msg msg; |
| ... | ... | @@ -26088,8 +26088,19 @@ fn elemPtrOneLayerOnly( |
| 26088 | 26088 | return block.addPtrElemPtr(indexable, elem_index, result_ty); |
| 26089 | 26089 | }, |
| 26090 | 26090 | .One => { |
| 26091 | assert(indexable_ty.childType(mod).zigTypeTag(mod) == .Array); // Guaranteed by checkIndexable | |
| 26092 | return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety); | |
| 26091 | const child_ty = indexable_ty.childType(mod); | |
| 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 | 26150 | return block.addBinOp(.ptr_elem_val, indexable, elem_index); |
| 26140 | 26151 | }, |
| 26141 | 26152 | .One => { |
| 26142 | const array_ty = indexable_ty.childType(mod); // Guaranteed by checkIndexable | |
| 26143 | assert(array_ty.zigTypeTag(mod) == .Array); | |
| 26144 | ||
| 26145 | if (array_ty.sentinel(mod)) |sentinel| { | |
| 26146 | // index must be defined since it can access out of bounds | |
| 26147 | if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { | |
| 26148 | const index = @intCast(usize, index_val.toUnsignedInt(mod)); | |
| 26149 | if (index == array_ty.arrayLen(mod)) { | |
| 26150 | return sema.addConstant(array_ty.childType(mod), sentinel); | |
| 26151 | } | |
| 26152 | } | |
| 26153 | arr_sent: { | |
| 26154 | const inner_ty = indexable_ty.childType(mod); | |
| 26155 | if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent; | |
| 26156 | const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent; | |
| 26157 | const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; | |
| 26158 | const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod)); | |
| 26159 | if (index != inner_ty.arrayLen(mod)) break :arr_sent; | |
| 26160 | return sema.addConstant(inner_ty.childType(mod), sentinel); | |
| 26153 | 26161 | } |
| 26154 | ||
| 26155 | 26162 | const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); |
| 26156 | 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 | 2832 | .Array, .Vector => true, |
| 2833 | 2833 | .Pointer => switch (ty.ptrSize(mod)) { |
| 2834 | 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 | 2841 | .Struct => ty.isTuple(mod), |
| 2838 | 2842 | else => false, |
| ... | ... | @@ -2845,7 +2849,11 @@ pub const Type = struct { |
| 2845 | 2849 | .Pointer => switch (ty.ptrSize(mod)) { |
| 2846 | 2850 | .Many, .C => false, |
| 2847 | 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 | 2858 | .Struct => ty.isTuple(mod), |
| 2851 | 2859 | else => false, |
test/behavior/for.zig+16| ... | ... | @@ -463,3 +463,19 @@ test "inline for with counter as the comptime-known" { |
| 463 | 463 | |
| 464 | 464 | try expect(S.ok == 2); |
| 465 | 465 | } |
| 466 | ||
| 467 | test "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 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | 3 | const testing = std.testing; |
| 4 | const assert = std.debug.assert; | |
| 4 | 5 | const expect = testing.expect; |
| 5 | 6 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 6 | 7 | const expectEqual = std.testing.expectEqual; |
| ... | ... | @@ -428,3 +429,27 @@ test "sentinel slice in tuple" { |
| 428 | 429 | |
| 429 | 430 | _ = S; |
| 430 | 431 | } |
| 432 | ||
| 433 | test "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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | const assert = std.debug.assert; | |
| 5 | 6 | const expect = std.testing.expect; |
| 6 | 7 | const expectEqual = std.testing.expectEqual; |
| 7 | 8 | |
| ... | ... | @@ -1343,3 +1344,29 @@ test "compare vectors with different element types" { |
| 1343 | 1344 | var b: @Vector(2, u9) = .{ 3, 0 }; |
| 1344 | 1345 | try expectEqual(@Vector(2, bool){ true, false }, a < b); |
| 1345 | 1346 | } |
| 1347 | ||
| 1348 | test "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 | } |