authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-11 16:09:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-13 22:13:44-07:00
logb019a19b5546d51865175359ec1ae8e5aa3f4128
tree72bc02abdac799f9aa3eeee9714cc8c13fe4bc49
parent75b6637d6013b735d36da0ab6e5655002a1b59e9

Sema: comptime loads and stores for `elem_ptr`

The index is checked against actual array lengths, and now handles coerced or casted pointers to single items.

3 files changed, 142 insertions(+), 92 deletions(-)

src/Sema.zig+130-80
......@@ -14085,88 +14085,112 @@ fn beginComptimePtrMutation(
1408514085 .elem_ptr => {
1408614086 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
1408714087 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
14088 const elem_ty = parent.ty.childType();
14089 switch (parent.val.tag()) {
14090 .undef => {
14091 // An array has been initialized to undefined at comptime and now we
14092 // are for the first time setting an element. We must change the representation
14093 // of the array from `undef` to `array`.
14094 const arena = parent.beginArena(sema.gpa);
14095 defer parent.finishArena();
14088 switch (parent.ty.zigTypeTag()) {
14089 .Array, .Vector => {
14090 const check_len = parent.ty.arrayLenIncludingSentinel();
14091 if (elem_ptr.index >= check_len) {
14092 // TODO have the parent include the decl so we can say "declared here"
14093 return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
14094 elem_ptr.index, check_len,
14095 });
14096 }
14097 const elem_ty = parent.ty.childType();
14098 switch (parent.val.tag()) {
14099 .undef => {
14100 // An array has been initialized to undefined at comptime and now we
14101 // are for the first time setting an element. We must change the representation
14102 // of the array from `undef` to `array`.
14103 const arena = parent.beginArena(sema.gpa);
14104 defer parent.finishArena();
14105
14106 const array_len_including_sentinel =
14107 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14108 const elems = try arena.alloc(Value, array_len_including_sentinel);
14109 mem.set(Value, elems, Value.undef);
14110
14111 parent.val.* = try Value.Tag.array.create(arena, elems);
1409614112
14097 const array_len_including_sentinel =
14098 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14099 const elems = try arena.alloc(Value, array_len_including_sentinel);
14100 mem.set(Value, elems, Value.undef);
14113 return ComptimePtrMutationKit{
14114 .decl_ref_mut = parent.decl_ref_mut,
14115 .val = &elems[elem_ptr.index],
14116 .ty = elem_ty,
14117 };
14118 },
14119 .bytes => {
14120 // An array is memory-optimized to store a slice of bytes, but we are about
14121 // to modify an individual field and the representation has to change.
14122 // If we wanted to avoid this, there would need to be special detection
14123 // elsewhere to identify when writing a value to an array element that is stored
14124 // using the `bytes` tag, and handle it without making a call to this function.
14125 const arena = parent.beginArena(sema.gpa);
14126 defer parent.finishArena();
14127
14128 const bytes = parent.val.castTag(.bytes).?.data;
14129 const dest_len = parent.ty.arrayLenIncludingSentinel();
14130 // bytes.len may be one greater than dest_len because of the case when
14131 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
14132 assert(bytes.len >= dest_len);
14133 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
14134 for (elems) |*elem, i| {
14135 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
14136 }
1410114137
14102 parent.val.* = try Value.Tag.array.create(arena, elems);
14138 parent.val.* = try Value.Tag.array.create(arena, elems);
1410314139
14104 return ComptimePtrMutationKit{
14105 .decl_ref_mut = parent.decl_ref_mut,
14106 .val = &elems[elem_ptr.index],
14107 .ty = elem_ty,
14108 };
14109 },
14110 .bytes => {
14111 // An array is memory-optimized to store a slice of bytes, but we are about
14112 // to modify an individual field and the representation has to change.
14113 // If we wanted to avoid this, there would need to be special detection
14114 // elsewhere to identify when writing a value to an array element that is stored
14115 // using the `bytes` tag, and handle it without making a call to this function.
14116 const arena = parent.beginArena(sema.gpa);
14117 defer parent.finishArena();
14140 return ComptimePtrMutationKit{
14141 .decl_ref_mut = parent.decl_ref_mut,
14142 .val = &elems[elem_ptr.index],
14143 .ty = elem_ty,
14144 };
14145 },
14146 .repeated => {
14147 // An array is memory-optimized to store only a single element value, and
14148 // that value is understood to be the same for the entire length of the array.
14149 // However, now we want to modify an individual field and so the
14150 // representation has to change. If we wanted to avoid this, there would
14151 // need to be special detection elsewhere to identify when writing a value to an
14152 // array element that is stored using the `repeated` tag, and handle it
14153 // without making a call to this function.
14154 const arena = parent.beginArena(sema.gpa);
14155 defer parent.finishArena();
14156
14157 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
14158 const array_len_including_sentinel =
14159 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14160 const elems = try arena.alloc(Value, array_len_including_sentinel);
14161 mem.set(Value, elems, repeated_val);
14162
14163 parent.val.* = try Value.Tag.array.create(arena, elems);
1411814164
14119 const bytes = parent.val.castTag(.bytes).?.data;
14120 const dest_len = parent.ty.arrayLenIncludingSentinel();
14121 // bytes.len may be one greater than dest_len because of the case when
14122 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
14123 assert(bytes.len >= dest_len);
14124 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
14125 for (elems) |*elem, i| {
14126 elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
14127 }
14165 return ComptimePtrMutationKit{
14166 .decl_ref_mut = parent.decl_ref_mut,
14167 .val = &elems[elem_ptr.index],
14168 .ty = elem_ty,
14169 };
14170 },
1412814171
14129 parent.val.* = try Value.Tag.array.create(arena, elems);
14172 .array => return ComptimePtrMutationKit{
14173 .decl_ref_mut = parent.decl_ref_mut,
14174 .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
14175 .ty = elem_ty,
14176 },
1413014177
14131 return ComptimePtrMutationKit{
14132 .decl_ref_mut = parent.decl_ref_mut,
14133 .val = &elems[elem_ptr.index],
14134 .ty = elem_ty,
14135 };
14178 else => unreachable,
14179 }
1413614180 },
14137 .repeated => {
14138 // An array is memory-optimized to store only a single element value, and
14139 // that value is understood to be the same for the entire length of the array.
14140 // However, now we want to modify an individual field and so the
14141 // representation has to change. If we wanted to avoid this, there would
14142 // need to be special detection elsewhere to identify when writing a value to an
14143 // array element that is stored using the `repeated` tag, and handle it
14144 // without making a call to this function.
14145 const arena = parent.beginArena(sema.gpa);
14146 defer parent.finishArena();
14147
14148 const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
14149 const array_len_including_sentinel =
14150 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
14151 const elems = try arena.alloc(Value, array_len_including_sentinel);
14152 mem.set(Value, elems, repeated_val);
14153
14154 parent.val.* = try Value.Tag.array.create(arena, elems);
14155
14181 else => {
14182 if (elem_ptr.index != 0) {
14183 // TODO include a "declared here" note for the decl
14184 return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
14185 elem_ptr.index,
14186 });
14187 }
1415614188 return ComptimePtrMutationKit{
1415714189 .decl_ref_mut = parent.decl_ref_mut,
14158 .val = &elems[elem_ptr.index],
14159 .ty = elem_ty,
14190 .val = parent.val,
14191 .ty = parent.ty,
1416014192 };
1416114193 },
14162
14163 .array => return ComptimePtrMutationKit{
14164 .decl_ref_mut = parent.decl_ref_mut,
14165 .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
14166 .ty = elem_ty,
14167 },
14168
14169 else => unreachable,
1417014194 }
1417114195 },
1417214196 .field_ptr => {
......@@ -14296,15 +14320,41 @@ fn beginComptimePtrLoad(
1429614320 .elem_ptr => {
1429714321 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
1429814322 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);
14299 const elem_ty = parent.ty.childType();
14300 const elem_size = elem_ty.abiSize(target);
14301 return ComptimePtrLoadKit{
14302 .root_val = parent.root_val,
14303 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
14304 .ty = elem_ty,
14305 .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
14306 .is_mutable = parent.is_mutable,
14307 };
14323 switch (parent.ty.zigTypeTag()) {
14324 .Array, .Vector => {
14325 const check_len = parent.ty.arrayLenIncludingSentinel();
14326 if (elem_ptr.index >= check_len) {
14327 // TODO have the parent include the decl so we can say "declared here"
14328 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
14329 elem_ptr.index, check_len,
14330 });
14331 }
14332 const elem_ty = parent.ty.childType();
14333 const elem_size = elem_ty.abiSize(target);
14334 return ComptimePtrLoadKit{
14335 .root_val = parent.root_val,
14336 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
14337 .ty = elem_ty,
14338 .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
14339 .is_mutable = parent.is_mutable,
14340 };
14341 },
14342 else => {
14343 if (elem_ptr.index != 0) {
14344 // TODO have the parent include the decl so we can say "declared here"
14345 return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
14346 elem_ptr.index,
14347 });
14348 }
14349 return ComptimePtrLoadKit{
14350 .root_val = parent.root_val,
14351 .val = parent.val,
14352 .ty = parent.ty,
14353 .byte_offset = parent.byte_offset,
14354 .is_mutable = parent.is_mutable,
14355 };
14356 },
14357 }
1430814358 },
1430914359 .field_ptr => {
1431014360 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
test/behavior/array_llvm.zig+12
......@@ -33,3 +33,15 @@ test "read/write through global variable array of struct fields initialized via
3333 };
3434 try S.doTheTest();
3535}
36
37test "implicit cast single-item pointer" {
38 try testImplicitCastSingleItemPtr();
39 comptime try testImplicitCastSingleItemPtr();
40}
41
42fn testImplicitCastSingleItemPtr() !void {
43 var byte: u8 = 100;
44 const slice = @as(*[1]u8, &byte)[0..];
45 slice[0] += 1;
46 try expect(byte == 101);
47}
test/behavior/array_stage1.zig-12
......@@ -4,18 +4,6 @@ const mem = std.mem;
44const expect = testing.expect;
55const expectEqual = testing.expectEqual;
66
7test "implicit cast single-item pointer" {
8 try testImplicitCastSingleItemPtr();
9 comptime try testImplicitCastSingleItemPtr();
10}
11
12fn testImplicitCastSingleItemPtr() !void {
13 var byte: u8 = 100;
14 const slice = @as(*[1]u8, &byte)[0..];
15 slice[0] += 1;
16 try expect(byte == 101);
17}
18
197fn testArrayByValAtComptime(b: [2]u8) u8 {
208 return b[0];
219}