| author | |
| committer | |
| log | cb5d2b691aadde5665cefc54542e3e0651ebc2fa |
| tree | 75c7dba7e75a9c1ed3abdb5471b49fbf4258b39a |
| parent | a040ccb42f1b34ba612c975b7030ccdcbb3f8086 |
5 files changed, 133 insertions(+), 11 deletions(-)
src/Sema.zig+38-9| ... | ... | @@ -3616,7 +3616,7 @@ fn zirValidateArrayInit( |
| 3616 | 3616 | const air_tags = sema.air_instructions.items(.tag); |
| 3617 | 3617 | const air_datas = sema.air_instructions.items(.data); |
| 3618 | 3618 | |
| 3619 | for (instrs) |elem_ptr, i| { | |
| 3619 | outer: for (instrs) |elem_ptr, i| { | |
| 3620 | 3620 | const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node; |
| 3621 | 3621 | const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node }; |
| 3622 | 3622 | |
| ... | ... | @@ -3630,6 +3630,10 @@ fn zirValidateArrayInit( |
| 3630 | 3630 | // of the for loop. |
| 3631 | 3631 | var block_index = block.instructions.items.len - 1; |
| 3632 | 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 | 3637 | block_index -= 1; |
| 3634 | 3638 | } |
| 3635 | 3639 | first_block_index = @minimum(first_block_index, block_index); |
| ... | ... | @@ -3672,6 +3676,13 @@ fn zirValidateArrayInit( |
| 3672 | 3676 | } |
| 3673 | 3677 | |
| 3674 | 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 | 3686 | // Our task is to delete all the `elem_ptr` and `store` instructions, and insert |
| 3676 | 3687 | // instead a single `store` to the array_ptr with a comptime struct value. |
| 3677 | 3688 | // Also to populate the sentinel value, if any. |
| ... | ... | @@ -18462,14 +18473,11 @@ fn structFieldPtrByIndex( |
| 18462 | 18473 | const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); |
| 18463 | 18474 | |
| 18464 | 18475 | if (field.is_comptime) { |
| 18465 | var anon_decl = try block.startAnonDecl(field_src); | |
| 18466 | defer anon_decl.deinit(); | |
| 18467 | const decl = try anon_decl.finish( | |
| 18468 | try field.ty.copy(anon_decl.arena()), | |
| 18469 | try field.default_val.copy(anon_decl.arena()), | |
| 18470 | ptr_ty_data.@"align", | |
| 18471 | ); | |
| 18472 | return sema.analyzeDeclRef(decl); | |
| 18476 | const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{ | |
| 18477 | .field_ty = try field.ty.copy(sema.arena), | |
| 18478 | .field_val = try field.default_val.copy(sema.arena), | |
| 18479 | }); | |
| 18480 | return sema.addConstant(ptr_field_ty, val); | |
| 18473 | 18481 | } |
| 18474 | 18482 | |
| 18475 | 18483 | if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { |
| ... | ... | @@ -20247,6 +20255,14 @@ fn storePtrVal( |
| 20247 | 20255 | |
| 20248 | 20256 | const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0); |
| 20249 | 20257 | |
| 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 | 20266 | const arena = mut_kit.beginArena(sema.mod); |
| 20251 | 20267 | defer mut_kit.finishArena(sema.mod); |
| 20252 | 20268 | |
| ... | ... | @@ -20296,6 +20312,19 @@ fn beginComptimePtrMutation( |
| 20296 | 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 | 20328 | .elem_ptr => { |
| 20300 | 20329 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; |
| 20301 | 20330 | var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr); |
src/TypedValue.zig+10| ... | ... | @@ -264,6 +264,16 @@ pub fn print( |
| 264 | 264 | .val = decl.val, |
| 265 | 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 | 277 | .elem_ptr => { |
| 268 | 278 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| 269 | 279 | try writer.writeAll("&"); |
src/value.zig+43-2| ... | ... | @@ -120,6 +120,8 @@ pub const Value = extern union { |
| 120 | 120 | /// This Tag will never be seen by machine codegen backends. It is changed into a |
| 121 | 121 | /// `decl_ref` when a comptime variable goes out of scope. |
| 122 | 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 | 125 | /// Pointer to a specific element of an array, vector or slice. |
| 124 | 126 | elem_ptr, |
| 125 | 127 | /// Pointer to a specific field of a struct or union. |
| ... | ... | @@ -316,6 +318,7 @@ pub const Value = extern union { |
| 316 | 318 | .aggregate => Payload.Aggregate, |
| 317 | 319 | .@"union" => Payload.Union, |
| 318 | 320 | .bound_fn => Payload.BoundFn, |
| 321 | .comptime_field_ptr => Payload.ComptimeFieldPtr, | |
| 319 | 322 | }; |
| 320 | 323 | } |
| 321 | 324 | |
| ... | ... | @@ -506,6 +509,18 @@ pub const Value = extern union { |
| 506 | 509 | }; |
| 507 | 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 | 524 | .elem_ptr => { |
| 510 | 525 | const payload = self.castTag(.elem_ptr).?; |
| 511 | 526 | const new_payload = try arena.create(Payload.ElemPtr); |
| ... | ... | @@ -754,6 +769,9 @@ pub const Value = extern union { |
| 754 | 769 | const decl_index = val.castTag(.decl_ref).?.data; |
| 755 | 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 | 775 | .elem_ptr => { |
| 758 | 776 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| 759 | 777 | try out_stream.print("&[{}] ", .{elem_ptr.index}); |
| ... | ... | @@ -1706,6 +1724,7 @@ pub const Value = extern union { |
| 1706 | 1724 | .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(), |
| 1707 | 1725 | |
| 1708 | 1726 | .decl_ref_mut, |
| 1727 | .comptime_field_ptr, | |
| 1709 | 1728 | .extern_fn, |
| 1710 | 1729 | .decl_ref, |
| 1711 | 1730 | .function, |
| ... | ... | @@ -1770,6 +1789,7 @@ pub const Value = extern union { |
| 1770 | 1789 | .bool_true, |
| 1771 | 1790 | .decl_ref, |
| 1772 | 1791 | .decl_ref_mut, |
| 1792 | .comptime_field_ptr, | |
| 1773 | 1793 | .extern_fn, |
| 1774 | 1794 | .function, |
| 1775 | 1795 | .variable, |
| ... | ... | @@ -2362,7 +2382,7 @@ pub const Value = extern union { |
| 2362 | 2382 | |
| 2363 | 2383 | pub fn isComptimeMutablePtr(val: Value) bool { |
| 2364 | 2384 | return switch (val.tag()) { |
| 2365 | .decl_ref_mut => true, | |
| 2385 | .decl_ref_mut, .comptime_field_ptr => true, | |
| 2366 | 2386 | .elem_ptr => isComptimeMutablePtr(val.castTag(.elem_ptr).?.data.array_ptr), |
| 2367 | 2387 | .field_ptr => isComptimeMutablePtr(val.castTag(.field_ptr).?.data.container_ptr), |
| 2368 | 2388 | .eu_payload_ptr => isComptimeMutablePtr(val.castTag(.eu_payload_ptr).?.data.container_ptr), |
| ... | ... | @@ -2426,6 +2446,9 @@ pub const Value = extern union { |
| 2426 | 2446 | const decl: Module.Decl.Index = ptr_val.pointerDecl().?; |
| 2427 | 2447 | std.hash.autoHash(hasher, decl); |
| 2428 | 2448 | }, |
| 2449 | .comptime_field_ptr => { | |
| 2450 | std.hash.autoHash(hasher, Value.Tag.comptime_field_ptr); | |
| 2451 | }, | |
| 2429 | 2452 | |
| 2430 | 2453 | .elem_ptr => { |
| 2431 | 2454 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; |
| ... | ... | @@ -2471,7 +2494,7 @@ pub const Value = extern union { |
| 2471 | 2494 | return switch (val.tag()) { |
| 2472 | 2495 | .slice => val.castTag(.slice).?.data.ptr, |
| 2473 | 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 | 2498 | else => unreachable, |
| 2476 | 2499 | }; |
| 2477 | 2500 | } |
| ... | ... | @@ -2497,6 +2520,14 @@ pub const Value = extern union { |
| 2497 | 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 | 2531 | else => unreachable, |
| 2501 | 2532 | }; |
| 2502 | 2533 | } |
| ... | ... | @@ -2587,6 +2618,7 @@ pub const Value = extern union { |
| 2587 | 2618 | |
| 2588 | 2619 | .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer), |
| 2589 | 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 | 2622 | .elem_ptr => { |
| 2591 | 2623 | const data = val.castTag(.elem_ptr).?.data; |
| 2592 | 2624 | return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer); |
| ... | ... | @@ -2623,6 +2655,7 @@ pub const Value = extern union { |
| 2623 | 2655 | |
| 2624 | 2656 | .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end), |
| 2625 | 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 | 2659 | .elem_ptr => blk: { |
| 2627 | 2660 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| 2628 | 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 | 4775 | }, |
| 4743 | 4776 | }; |
| 4744 | 4777 | |
| 4778 | pub const ComptimeFieldPtr = struct { | |
| 4779 | base: Payload, | |
| 4780 | data: struct { | |
| 4781 | field_val: Value, | |
| 4782 | field_ty: Type, | |
| 4783 | }, | |
| 4784 | }; | |
| 4785 | ||
| 4745 | 4786 | pub const ElemPtr = struct { |
| 4746 | 4787 | pub const base_tag = Tag.elem_ptr; |
| 4747 | 4788 |
test/behavior/struct.zig+22| ... | ... | @@ -1336,3 +1336,25 @@ test "packed struct field access via pointer" { |
| 1336 | 1336 | try S.doTheTest(); |
| 1337 | 1337 | comptime try S.doTheTest(); |
| 1338 | 1338 | } |
| 1339 | ||
| 1340 | test "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 @@ |
| 1 | pub 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 | } | |
| 8 | pub 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 |