authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-06-04 13:12:55+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-06 13:11:50-07:00
logcb5d2b691aadde5665cefc54542e3e0651ebc2fa
tree75c7dba7e75a9c1ed3abdb5471b49fbf4258b39a
parenta040ccb42f1b34ba612c975b7030ccdcbb3f8086

Sema: validate equality on store to comptime field


5 files changed, 133 insertions(+), 11 deletions(-)

src/Sema.zig+38-9
...@@ -3616,7 +3616,7 @@ fn zirValidateArrayInit(...@@ -3616,7 +3616,7 @@ fn zirValidateArrayInit(
3616 const air_tags = sema.air_instructions.items(.tag);3616 const air_tags = sema.air_instructions.items(.tag);
3617 const air_datas = sema.air_instructions.items(.data);3617 const air_datas = sema.air_instructions.items(.data);
36183618
3619 for (instrs) |elem_ptr, i| {3619 outer: for (instrs) |elem_ptr, i| {
3620 const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node;3620 const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node;
3621 const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node };3621 const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node };
36223622
...@@ -3630,6 +3630,10 @@ fn zirValidateArrayInit(...@@ -3630,6 +3630,10 @@ fn zirValidateArrayInit(
3630 // of the for loop.3630 // of the for loop.
3631 var block_index = block.instructions.items.len - 1;3631 var block_index = block.instructions.items.len - 1;
3632 while (block.instructions.items[block_index] != elem_ptr_air_inst) {3632 while (block.instructions.items[block_index] != elem_ptr_air_inst) {
3633 if (block_index == 0) {
3634 array_is_comptime = true;
3635 continue :outer;
3636 }
3633 block_index -= 1;3637 block_index -= 1;
3634 }3638 }
3635 first_block_index = @minimum(first_block_index, block_index);3639 first_block_index = @minimum(first_block_index, block_index);
...@@ -3672,6 +3676,13 @@ fn zirValidateArrayInit(...@@ -3672,6 +3676,13 @@ fn zirValidateArrayInit(
3672 }3676 }
36733677
3674 if (array_is_comptime) {3678 if (array_is_comptime) {
3679 if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
3680 if (ptr_val.tag() == .comptime_field_ptr) {
3681 // This store was validated by the individual elem ptrs.
3682 return;
3683 }
3684 }
3685
3675 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert3686 // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
3676 // instead a single `store` to the array_ptr with a comptime struct value.3687 // instead a single `store` to the array_ptr with a comptime struct value.
3677 // Also to populate the sentinel value, if any.3688 // Also to populate the sentinel value, if any.
...@@ -18462,14 +18473,11 @@ fn structFieldPtrByIndex(...@@ -18462,14 +18473,11 @@ fn structFieldPtrByIndex(
18462 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);18473 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
1846318474
18464 if (field.is_comptime) {18475 if (field.is_comptime) {
18465 var anon_decl = try block.startAnonDecl(field_src);18476 const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{
18466 defer anon_decl.deinit();18477 .field_ty = try field.ty.copy(sema.arena),
18467 const decl = try anon_decl.finish(18478 .field_val = try field.default_val.copy(sema.arena),
18468 try field.ty.copy(anon_decl.arena()),18479 });
18469 try field.default_val.copy(anon_decl.arena()),18480 return sema.addConstant(ptr_field_ty, val);
18470 ptr_ty_data.@"align",
18471 );
18472 return sema.analyzeDeclRef(decl);
18473 }18481 }
1847418482
18475 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {18483 if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
...@@ -20247,6 +20255,14 @@ fn storePtrVal(...@@ -20247,6 +20255,14 @@ fn storePtrVal(
2024720255
20248 const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0);20256 const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0);
2024920257
20258 if (mut_kit.decl_ref_mut.runtime_index == std.math.maxInt(u32)) {
20259 // Special case for comptime field ptr.
20260 if (!mut_kit.val.eql(bitcasted_val, mut_kit.ty, sema.mod)) {
20261 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
20262 }
20263 return;
20264 }
20265
20250 const arena = mut_kit.beginArena(sema.mod);20266 const arena = mut_kit.beginArena(sema.mod);
20251 defer mut_kit.finishArena(sema.mod);20267 defer mut_kit.finishArena(sema.mod);
2025220268
...@@ -20296,6 +20312,19 @@ fn beginComptimePtrMutation(...@@ -20296,6 +20312,19 @@ fn beginComptimePtrMutation(
20296 .ty = decl.ty,20312 .ty = decl.ty,
20297 };20313 };
20298 },20314 },
20315 .comptime_field_ptr => {
20316 const payload = ptr_val.castTag(.comptime_field_ptr).?.data;
20317 const duped = try sema.arena.create(Value);
20318 duped.* = payload.field_val;
20319 return ComptimePtrMutationKit{
20320 .decl_ref_mut = .{
20321 .decl_index = @intToEnum(Module.Decl.Index, 0),
20322 .runtime_index = std.math.maxInt(u32),
20323 },
20324 .val = duped,
20325 .ty = payload.field_ty,
20326 };
20327 },
20299 .elem_ptr => {20328 .elem_ptr => {
20300 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;20329 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
20301 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);20330 var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
src/TypedValue.zig+10
...@@ -264,6 +264,16 @@ pub fn print(...@@ -264,6 +264,16 @@ pub fn print(
264 .val = decl.val,264 .val = decl.val,
265 }, writer, level - 1, mod);265 }, writer, level - 1, mod);
266 },266 },
267 .comptime_field_ptr => {
268 const payload = val.castTag(.comptime_field_ptr).?.data;
269 if (level == 0) {
270 return writer.writeAll("(comptime field ptr)");
271 }
272 return print(.{
273 .ty = payload.field_ty,
274 .val = payload.field_val,
275 }, writer, level - 1, mod);
276 },
267 .elem_ptr => {277 .elem_ptr => {
268 const elem_ptr = val.castTag(.elem_ptr).?.data;278 const elem_ptr = val.castTag(.elem_ptr).?.data;
269 try writer.writeAll("&");279 try writer.writeAll("&");
src/value.zig+43-2
...@@ -120,6 +120,8 @@ pub const Value = extern union {...@@ -120,6 +120,8 @@ pub const Value = extern union {
120 /// This Tag will never be seen by machine codegen backends. It is changed into a120 /// This Tag will never be seen by machine codegen backends. It is changed into a
121 /// `decl_ref` when a comptime variable goes out of scope.121 /// `decl_ref` when a comptime variable goes out of scope.
122 decl_ref_mut,122 decl_ref_mut,
123 /// Behaves like `decl_ref_mut` but validates that the stored value matches the field value.
124 comptime_field_ptr,
123 /// Pointer to a specific element of an array, vector or slice.125 /// Pointer to a specific element of an array, vector or slice.
124 elem_ptr,126 elem_ptr,
125 /// Pointer to a specific field of a struct or union.127 /// Pointer to a specific field of a struct or union.
...@@ -316,6 +318,7 @@ pub const Value = extern union {...@@ -316,6 +318,7 @@ pub const Value = extern union {
316 .aggregate => Payload.Aggregate,318 .aggregate => Payload.Aggregate,
317 .@"union" => Payload.Union,319 .@"union" => Payload.Union,
318 .bound_fn => Payload.BoundFn,320 .bound_fn => Payload.BoundFn,
321 .comptime_field_ptr => Payload.ComptimeFieldPtr,
319 };322 };
320 }323 }
321324
...@@ -506,6 +509,18 @@ pub const Value = extern union {...@@ -506,6 +509,18 @@ pub const Value = extern union {
506 };509 };
507 return Value{ .ptr_otherwise = &new_payload.base };510 return Value{ .ptr_otherwise = &new_payload.base };
508 },511 },
512 .comptime_field_ptr => {
513 const payload = self.cast(Payload.ComptimeFieldPtr).?;
514 const new_payload = try arena.create(Payload.ComptimeFieldPtr);
515 new_payload.* = .{
516 .base = payload.base,
517 .data = .{
518 .field_val = try payload.data.field_val.copy(arena),
519 .field_ty = try payload.data.field_ty.copy(arena),
520 },
521 };
522 return Value{ .ptr_otherwise = &new_payload.base };
523 },
509 .elem_ptr => {524 .elem_ptr => {
510 const payload = self.castTag(.elem_ptr).?;525 const payload = self.castTag(.elem_ptr).?;
511 const new_payload = try arena.create(Payload.ElemPtr);526 const new_payload = try arena.create(Payload.ElemPtr);
...@@ -754,6 +769,9 @@ pub const Value = extern union {...@@ -754,6 +769,9 @@ pub const Value = extern union {
754 const decl_index = val.castTag(.decl_ref).?.data;769 const decl_index = val.castTag(.decl_ref).?.data;
755 return out_stream.print("(decl_ref {d})", .{decl_index});770 return out_stream.print("(decl_ref {d})", .{decl_index});
756 },771 },
772 .comptime_field_ptr => {
773 return out_stream.writeAll("(comptime_field_ptr)");
774 },
757 .elem_ptr => {775 .elem_ptr => {
758 const elem_ptr = val.castTag(.elem_ptr).?.data;776 const elem_ptr = val.castTag(.elem_ptr).?.data;
759 try out_stream.print("&[{}] ", .{elem_ptr.index});777 try out_stream.print("&[{}] ", .{elem_ptr.index});
...@@ -1706,6 +1724,7 @@ pub const Value = extern union {...@@ -1706,6 +1724,7 @@ pub const Value = extern union {
1706 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),1724 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),
17071725
1708 .decl_ref_mut,1726 .decl_ref_mut,
1727 .comptime_field_ptr,
1709 .extern_fn,1728 .extern_fn,
1710 .decl_ref,1729 .decl_ref,
1711 .function,1730 .function,
...@@ -1770,6 +1789,7 @@ pub const Value = extern union {...@@ -1770,6 +1789,7 @@ pub const Value = extern union {
1770 .bool_true,1789 .bool_true,
1771 .decl_ref,1790 .decl_ref,
1772 .decl_ref_mut,1791 .decl_ref_mut,
1792 .comptime_field_ptr,
1773 .extern_fn,1793 .extern_fn,
1774 .function,1794 .function,
1775 .variable,1795 .variable,
...@@ -2362,7 +2382,7 @@ pub const Value = extern union {...@@ -2362,7 +2382,7 @@ pub const Value = extern union {
23622382
2363 pub fn isComptimeMutablePtr(val: Value) bool {2383 pub fn isComptimeMutablePtr(val: Value) bool {
2364 return switch (val.tag()) {2384 return switch (val.tag()) {
2365 .decl_ref_mut => true,2385 .decl_ref_mut, .comptime_field_ptr => true,
2366 .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr),2386 .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr),
2367 .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr),2387 .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr),
2368 .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr),2388 .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr),
...@@ -2426,6 +2446,9 @@ pub const Value = extern union {...@@ -2426,6 +2446,9 @@ pub const Value = extern union {
2426 const decl: Module.Decl.Index = ptr_val.pointerDecl().?;2446 const decl: Module.Decl.Index = ptr_val.pointerDecl().?;
2427 std.hash.autoHash(hasher, decl);2447 std.hash.autoHash(hasher, decl);
2428 },2448 },
2449 .comptime_field_ptr => {
2450 std.hash.autoHash(hasher, Value.Tag.comptime_field_ptr);
2451 },
24292452
2430 .elem_ptr => {2453 .elem_ptr => {
2431 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;2454 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
...@@ -2471,7 +2494,7 @@ pub const Value = extern union {...@@ -2471,7 +2494,7 @@ pub const Value = extern union {
2471 return switch (val.tag()) {2494 return switch (val.tag()) {
2472 .slice => val.castTag(.slice).?.data.ptr,2495 .slice => val.castTag(.slice).?.data.ptr,
2473 // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc.2496 // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc.
2474 .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr => val,2497 .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr, .comptime_field_ptr => val,
2475 else => unreachable,2498 else => unreachable,
2476 };2499 };
2477 }2500 }
...@@ -2497,6 +2520,14 @@ pub const Value = extern union {...@@ -2497,6 +2520,14 @@ pub const Value = extern union {
2497 return 1;2520 return 1;
2498 }2521 }
2499 },2522 },
2523 .comptime_field_ptr => {
2524 const payload = val.castTag(.comptime_field_ptr).?.data;
2525 if (payload.field_ty.zigTypeTag() == .Array) {
2526 return payload.field_ty.arrayLen();
2527 } else {
2528 return 1;
2529 }
2530 },
2500 else => unreachable,2531 else => unreachable,
2501 };2532 };
2502 }2533 }
...@@ -2587,6 +2618,7 @@ pub const Value = extern union {...@@ -2587,6 +2618,7 @@ pub const Value = extern union {
25872618
2588 .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer),2619 .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer),
2589 .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer),2620 .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer),
2621 .comptime_field_ptr => return val.castTag(.comptime_field_ptr).?.data.field_val.elemValueAdvanced(mod, index, arena, buffer),
2590 .elem_ptr => {2622 .elem_ptr => {
2591 const data = val.castTag(.elem_ptr).?.data;2623 const data = val.castTag(.elem_ptr).?.data;
2592 return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer);2624 return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer);
...@@ -2623,6 +2655,7 @@ pub const Value = extern union {...@@ -2623,6 +2655,7 @@ pub const Value = extern union {
26232655
2624 .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end),2656 .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end),
2625 .decl_ref_mut => sliceArray(mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val, mod, arena, start, end),2657 .decl_ref_mut => sliceArray(mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val, mod, arena, start, end),
2658 .comptime_field_ptr => sliceArray(val.castTag(.comptime_field_ptr).?.data.field_val, mod, arena, start, end),
2626 .elem_ptr => blk: {2659 .elem_ptr => blk: {
2627 const elem_ptr = val.castTag(.elem_ptr).?.data;2660 const elem_ptr = val.castTag(.elem_ptr).?.data;
2628 break :blk sliceArray(elem_ptr.array_ptr, mod, arena, start + elem_ptr.index, end + elem_ptr.index);2661 break :blk sliceArray(elem_ptr.array_ptr, mod, arena, start + elem_ptr.index, end + elem_ptr.index);
...@@ -4742,6 +4775,14 @@ pub const Value = extern union {...@@ -4742,6 +4775,14 @@ pub const Value = extern union {
4742 },4775 },
4743 };4776 };
47444777
4778 pub const ComptimeFieldPtr = struct {
4779 base: Payload,
4780 data: struct {
4781 field_val: Value,
4782 field_ty: Type,
4783 },
4784 };
4785
4745 pub const ElemPtr = struct {4786 pub const ElemPtr = struct {
4746 pub const base_tag = Tag.elem_ptr;4787 pub const base_tag = Tag.elem_ptr;
47474788
test/behavior/struct.zig+22
...@@ -1336,3 +1336,25 @@ test "packed struct field access via pointer" {...@@ -1336,3 +1336,25 @@ test "packed struct field access via pointer" {
1336 try S.doTheTest();1336 try S.doTheTest();
1337 comptime try S.doTheTest();1337 comptime try S.doTheTest();
1338}1338}
1339
1340test "store to comptime field" {
1341 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1342
1343 {
1344 const S = struct {
1345 comptime a: [2]u32 = [2]u32{ 1, 2 },
1346 };
1347 var s: S = .{};
1348 s.a = [2]u32{ 1, 2 };
1349 s.a[0] = 1;
1350 }
1351 {
1352 const T = struct { a: u32, b: u32 };
1353 const S = struct {
1354 comptime a: T = T{ .a = 1, .b = 2 },
1355 };
1356 var s: S = .{};
1357 s.a = T{ .a = 1, .b = 2 };
1358 s.a.a = 1;
1359 }
1360}
test/cases/compile_errors/invalid_store_to_comptime_field.zig created+20
...@@ -0,0 +1,20 @@
1pub export fn entry() void {
2 const S = struct {
3 comptime a: [2]u32 = [2]u32{ 1, 2 },
4 };
5 var s: S = .{};
6 s.a = [2]u32{ 2, 2 };
7}
8pub export fn entry1() void {
9 const T = struct { a: u32, b: u32 };
10 const S = struct {
11 comptime a: T = T{ .a = 1, .b = 2 },
12 };
13 var s: S = .{};
14 s.a = T{ .a = 2, .b = 2 };
15}
16// error
17// backend=stage2,llvm
18//
19// :6:19: error: value stored in comptime field does not match the default value of the field
20// :14:19: error: value stored in comptime field does not match the default value of the field