authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-01 18:59:13-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-01 18:59:13-04:00
loga4cdb49a58f62776b73ad873b25243e65ac29266
tree1a648141781d2d46bb869996742f69022f5c4986
parentb82cccc9e9b2230097f81fecec12ac0fdae97518
parent94624893d88526d7a0f88f048e2a26abea6454c2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11763 from Vexu/stage2-alloc-const

Stage2: detect when initializer of const variable is comptime known

4 files changed, 154 insertions(+), 18 deletions(-)

lib/std/math/big/int.zig+2-3
...@@ -2063,9 +2063,8 @@ pub const Const = struct {...@@ -2063,9 +2063,8 @@ pub const Const = struct {
2063 // This is the inverse of calcDivLimbsBufferLen2063 // This is the inverse of calcDivLimbsBufferLen
2064 const available_len = (limbs.len / 3) - 2;2064 const available_len = (limbs.len / 3) - 2;
20652065
2066 // TODO https://github.com/ziglang/zig/issues/114392066 const biggest: Const = .{
2067 const biggest = comptime Const{2067 .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len),
2068 .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len),
2069 .positive = false,2068 .positive = false,
2070 };2069 };
2071 var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined;2070 var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined;
src/Sema.zig+103-12
...@@ -2711,17 +2711,72 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -2711,17 +2711,72 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
27112711
2712fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2712fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2713 const inst_data = sema.code.instructions.items(.data)[inst].un_node;2713 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
2714 const ptr = try sema.resolveInst(inst_data.operand);2714 const src = inst_data.src();
2715 const ptr_ty = sema.typeOf(ptr);2715 const alloc = try sema.resolveInst(inst_data.operand);
2716 var ptr_info = ptr_ty.ptrInfo().data;2716 const alloc_ty = sema.typeOf(alloc);
2717
2718 var ptr_info = alloc_ty.ptrInfo().data;
2719 const elem_ty = ptr_info.pointee_type;
2720
2721 // Detect if all stores to an `.alloc` were comptime known.
2722 ct: {
2723 var search_index: usize = block.instructions.items.len;
2724 const air_tags = sema.air_instructions.items(.tag);
2725 const air_datas = sema.air_instructions.items(.data);
2726
2727 const store_inst = while (true) {
2728 if (search_index == 0) break :ct;
2729 search_index -= 1;
2730
2731 const candidate = block.instructions.items[search_index];
2732 switch (air_tags[candidate]) {
2733 .dbg_stmt => continue,
2734 .store => break candidate,
2735 else => break :ct,
2736 }
2737 } else unreachable; // TODO shouldn't need this
2738
2739 while (true) {
2740 if (search_index == 0) break :ct;
2741 search_index -= 1;
2742
2743 const candidate = block.instructions.items[search_index];
2744 switch (air_tags[candidate]) {
2745 .dbg_stmt => continue,
2746 .alloc => {
2747 if (Air.indexToRef(candidate) != alloc) break :ct;
2748 break;
2749 },
2750 else => break :ct,
2751 }
2752 }
2753
2754 const store_op = air_datas[store_inst].bin_op;
2755 const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct;
2756 if (store_op.lhs != alloc) break :ct;
2757
2758 // Remove all the unnecessary runtime instructions.
2759 block.instructions.shrinkRetainingCapacity(search_index);
2760
2761 var anon_decl = try block.startAnonDecl(src);
2762 defer anon_decl.deinit();
2763 return sema.analyzeDeclRef(try anon_decl.finish(
2764 try elem_ty.copy(anon_decl.arena()),
2765 try store_val.copy(anon_decl.arena()),
2766 ptr_info.@"align",
2767 ));
2768 }
2769
2717 ptr_info.mutable = false;2770 ptr_info.mutable = false;
2718 const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);2771 const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
27192772
2720 if (try sema.resolveMaybeUndefVal(block, inst_data.src(), ptr)) |val| {2773 // Detect if a comptime value simply needs to have its type changed.
2774 if (try sema.resolveMaybeUndefVal(block, inst_data.src(), alloc)) |val| {
2721 return sema.addConstant(const_ptr_ty, val);2775 return sema.addConstant(const_ptr_ty, val);
2722 }2776 }
2723 try sema.requireRuntimeBlock(block, inst_data.src());2777
2724 return block.addBitCast(const_ptr_ty, ptr);2778 try sema.requireRuntimeBlock(block, src);
2779 return block.addBitCast(const_ptr_ty, alloc);
2725}2780}
27262781
2727fn zirAllocInferredComptime(2782fn zirAllocInferredComptime(
...@@ -3160,7 +3215,9 @@ fn validateUnionInit(...@@ -3160,7 +3215,9 @@ fn validateUnionInit(
3160 return sema.failWithOwnedErrorMsg(block, msg);3215 return sema.failWithOwnedErrorMsg(block, msg);
3161 }3216 }
31623217
3163 if (is_comptime or block.is_comptime) {3218 if ((is_comptime or block.is_comptime) and
3219 (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
3220 {
3164 // In this case, comptime machinery already did everything. No work to do here.3221 // In this case, comptime machinery already did everything. No work to do here.
3165 return;3222 return;
3166 }3223 }
...@@ -3206,7 +3263,18 @@ fn validateUnionInit(...@@ -3206,7 +3263,18 @@ fn validateUnionInit(
3206 if (store_inst == field_ptr_air_inst) break;3263 if (store_inst == field_ptr_air_inst) break;
3207 if (air_tags[store_inst] != .store) continue;3264 if (air_tags[store_inst] != .store) continue;
3208 const bin_op = air_datas[store_inst].bin_op;3265 const bin_op = air_datas[store_inst].bin_op;
3209 if (bin_op.lhs != field_ptr_air_ref) continue;3266 var lhs = bin_op.lhs;
3267 if (Air.refToIndex(lhs)) |lhs_index| {
3268 if (air_tags[lhs_index] == .bitcast) {
3269 lhs = air_datas[lhs_index].ty_op.operand;
3270 block_index -= 1;
3271 }
3272 }
3273 if (lhs != field_ptr_air_ref) continue;
3274 while (block_index > 0) : (block_index -= 1) {
3275 const block_inst = block.instructions.items[block_index - 1];
3276 if (air_tags[block_inst] != .dbg_stmt) break;
3277 }
3210 if (block_index > 0 and3278 if (block_index > 0 and
3211 field_ptr_air_inst == block.instructions.items[block_index - 1])3279 field_ptr_air_inst == block.instructions.items[block_index - 1])
3212 {3280 {
...@@ -3285,7 +3353,9 @@ fn validateStructInit(...@@ -3285,7 +3353,9 @@ fn validateStructInit(
3285 const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);3353 const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
3286 const struct_ty = sema.typeOf(struct_ptr).childType();3354 const struct_ty = sema.typeOf(struct_ptr).childType();
32873355
3288 if (is_comptime or block.is_comptime) {3356 if ((is_comptime or block.is_comptime) and
3357 (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
3358 {
3289 // In this case the only thing we need to do is evaluate the implicit3359 // In this case the only thing we need to do is evaluate the implicit
3290 // store instructions for default field values, and report any missing fields.3360 // store instructions for default field values, and report any missing fields.
3291 // Avoid the cost of the extra machinery for detecting a comptime struct init value.3361 // Avoid the cost of the extra machinery for detecting a comptime struct init value.
...@@ -3387,7 +3457,19 @@ fn validateStructInit(...@@ -3387,7 +3457,19 @@ fn validateStructInit(
3387 }3457 }
3388 if (air_tags[store_inst] != .store) continue;3458 if (air_tags[store_inst] != .store) continue;
3389 const bin_op = air_datas[store_inst].bin_op;3459 const bin_op = air_datas[store_inst].bin_op;
3390 if (bin_op.lhs != field_ptr_air_ref) continue;3460 var lhs = bin_op.lhs;
3461 {
3462 const lhs_index = Air.refToIndex(lhs) orelse continue;
3463 if (air_tags[lhs_index] == .bitcast) {
3464 lhs = air_datas[lhs_index].ty_op.operand;
3465 block_index -= 1;
3466 }
3467 }
3468 if (lhs != field_ptr_air_ref) continue;
3469 while (block_index > 0) : (block_index -= 1) {
3470 const block_inst = block.instructions.items[block_index - 1];
3471 if (air_tags[block_inst] != .dbg_stmt) break;
3472 }
3391 if (block_index > 0 and3473 if (block_index > 0 and
3392 field_ptr_air_inst == block.instructions.items[block_index - 1])3474 field_ptr_air_inst == block.instructions.items[block_index - 1])
3393 {3475 {
...@@ -3489,7 +3571,9 @@ fn zirValidateArrayInit(...@@ -3489,7 +3571,9 @@ fn zirValidateArrayInit(
3489 });3571 });
3490 }3572 }
34913573
3492 if (is_comptime or block.is_comptime) {3574 if ((is_comptime or block.is_comptime) and
3575 (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
3576 {
3493 // In this case the comptime machinery will have evaluated the store instructions3577 // In this case the comptime machinery will have evaluated the store instructions
3494 // at comptime so we have almost nothing to do here. However, in case of a3578 // at comptime so we have almost nothing to do here. However, in case of a
3495 // sentinel-terminated array, the sentinel will not have been populated by3579 // sentinel-terminated array, the sentinel will not have been populated by
...@@ -3544,7 +3628,14 @@ fn zirValidateArrayInit(...@@ -3544,7 +3628,14 @@ fn zirValidateArrayInit(
3544 switch (air_tags[next_air_inst]) {3628 switch (air_tags[next_air_inst]) {
3545 .store => {3629 .store => {
3546 const bin_op = air_datas[next_air_inst].bin_op;3630 const bin_op = air_datas[next_air_inst].bin_op;
3547 if (bin_op.lhs != elem_ptr_air_ref) {3631 var lhs = bin_op.lhs;
3632 if (Air.refToIndex(lhs)) |lhs_index| {
3633 if (air_tags[lhs_index] == .bitcast) {
3634 lhs = air_datas[lhs_index].ty_op.operand;
3635 block_index -= 1;
3636 }
3637 }
3638 if (lhs != elem_ptr_air_ref) {
3548 array_is_comptime = false;3639 array_is_comptime = false;
3549 continue;3640 continue;
3550 }3641 }
test/behavior/basic.zig+48
...@@ -1005,3 +1005,51 @@ test "generic function uses return type of other generic function" {...@@ -1005,3 +1005,51 @@ test "generic function uses return type of other generic function" {
1005 };1005 };
1006 try std.testing.expect(S.call(S.func, .{@as(u8, 1)}) == 1);1006 try std.testing.expect(S.call(S.func, .{@as(u8, 1)}) == 1);
1007}1007}
1008
1009test "const alloc with comptime known initializer is made comptime known" {
1010 const S = struct {
1011 a: bool,
1012 b: [2]u8,
1013 };
1014 {
1015 const s: S = .{
1016 .a = false,
1017 .b = .{ 1, 2 },
1018 };
1019 if (s.a) @compileError("bad");
1020 }
1021 {
1022 const s: S = .{
1023 .a = false,
1024 .b = [2]u8{ 1, 2 },
1025 };
1026 if (s.a) @compileError("bad");
1027 }
1028 {
1029 const s: S = comptime .{
1030 .a = false,
1031 .b = .{ 1, 2 },
1032 };
1033 if (s.a) @compileError("bad");
1034 }
1035 {
1036 const Const = struct {
1037 limbs: []const usize,
1038 positive: bool,
1039 };
1040 const biggest: Const = .{
1041 .limbs = &([1]usize{comptime std.math.maxInt(usize)} ** 128),
1042 .positive = false,
1043 };
1044 if (biggest.positive) @compileError("bad");
1045 }
1046 {
1047 const U = union(enum) {
1048 a: usize,
1049 };
1050 const u: U = .{
1051 .a = comptime std.math.maxInt(usize),
1052 };
1053 if (u.a == 0) @compileError("bad");
1054 }
1055}
test/behavior/bugs/1741.zig+1-3
...@@ -2,9 +2,7 @@ const std = @import("std");...@@ -2,9 +2,7 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4test "fixed" {4test "fixed" {
5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;5 if (builtin.zig_backend != .stage1) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8 const x: f32 align(128) = 12.34;6 const x: f32 align(128) = 12.34;
9 try std.testing.expect(@ptrToInt(&x) % 128 == 0);7 try std.testing.expect(@ptrToInt(&x) % 128 == 0);
10}8}