diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b57e1896cc..170272f287 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2063,9 +2063,8 @@ pub const Const = struct { // This is the inverse of calcDivLimbsBufferLen const available_len = (limbs.len / 3) - 2; - // TODO https://github.com/ziglang/zig/issues/11439 - const biggest = comptime Const{ - .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len), + const biggest: Const = .{ + .limbs = &([1]Limb{comptime math.maxInt(Limb)} ** available_len), .positive = false, }; var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined; diff --git a/src/Sema.zig b/src/Sema.zig index a70f372d8a..88847ced0a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2711,17 +2711,72 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const ptr = try sema.resolveInst(inst_data.operand); - const ptr_ty = sema.typeOf(ptr); - var ptr_info = ptr_ty.ptrInfo().data; + const src = inst_data.src(); + const alloc = try sema.resolveInst(inst_data.operand); + const alloc_ty = sema.typeOf(alloc); + + var ptr_info = alloc_ty.ptrInfo().data; + const elem_ty = ptr_info.pointee_type; + + // Detect if all stores to an `.alloc` were comptime known. + ct: { + var search_index: usize = block.instructions.items.len; + const air_tags = sema.air_instructions.items(.tag); + const air_datas = sema.air_instructions.items(.data); + + const store_inst = while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .store => break candidate, + else => break :ct, + } + } else unreachable; // TODO shouldn't need this + + while (true) { + if (search_index == 0) break :ct; + search_index -= 1; + + const candidate = block.instructions.items[search_index]; + switch (air_tags[candidate]) { + .dbg_stmt => continue, + .alloc => { + if (Air.indexToRef(candidate) != alloc) break :ct; + break; + }, + else => break :ct, + } + } + + const store_op = air_datas[store_inst].bin_op; + const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct; + if (store_op.lhs != alloc) break :ct; + + // Remove all the unnecessary runtime instructions. + block.instructions.shrinkRetainingCapacity(search_index); + + var anon_decl = try block.startAnonDecl(src); + defer anon_decl.deinit(); + return sema.analyzeDeclRef(try anon_decl.finish( + try elem_ty.copy(anon_decl.arena()), + try store_val.copy(anon_decl.arena()), + ptr_info.@"align", + )); + } + ptr_info.mutable = false; const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); - if (try sema.resolveMaybeUndefVal(block, inst_data.src(), ptr)) |val| { + // Detect if a comptime value simply needs to have its type changed. + if (try sema.resolveMaybeUndefVal(block, inst_data.src(), alloc)) |val| { return sema.addConstant(const_ptr_ty, val); } - try sema.requireRuntimeBlock(block, inst_data.src()); - return block.addBitCast(const_ptr_ty, ptr); + + try sema.requireRuntimeBlock(block, src); + return block.addBitCast(const_ptr_ty, alloc); } fn zirAllocInferredComptime( @@ -3160,7 +3215,9 @@ fn validateUnionInit( return sema.failWithOwnedErrorMsg(block, msg); } - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null) + { // In this case, comptime machinery already did everything. No work to do here. return; } @@ -3206,7 +3263,18 @@ fn validateUnionInit( if (store_inst == field_ptr_air_inst) break; if (air_tags[store_inst] != .store) continue; const bin_op = air_datas[store_inst].bin_op; - if (bin_op.lhs != field_ptr_air_ref) continue; + var lhs = bin_op.lhs; + if (Air.refToIndex(lhs)) |lhs_index| { + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != field_ptr_air_ref) continue; + while (block_index > 0) : (block_index -= 1) { + const block_inst = block.instructions.items[block_index - 1]; + if (air_tags[block_inst] != .dbg_stmt) break; + } if (block_index > 0 and field_ptr_air_inst == block.instructions.items[block_index - 1]) { @@ -3285,7 +3353,9 @@ fn validateStructInit( const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); const struct_ty = sema.typeOf(struct_ptr).childType(); - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null) + { // In this case the only thing we need to do is evaluate the implicit // store instructions for default field values, and report any missing fields. // Avoid the cost of the extra machinery for detecting a comptime struct init value. @@ -3387,7 +3457,19 @@ fn validateStructInit( } if (air_tags[store_inst] != .store) continue; const bin_op = air_datas[store_inst].bin_op; - if (bin_op.lhs != field_ptr_air_ref) continue; + var lhs = bin_op.lhs; + { + const lhs_index = Air.refToIndex(lhs) orelse continue; + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != field_ptr_air_ref) continue; + while (block_index > 0) : (block_index -= 1) { + const block_inst = block.instructions.items[block_index - 1]; + if (air_tags[block_inst] != .dbg_stmt) break; + } if (block_index > 0 and field_ptr_air_inst == block.instructions.items[block_index - 1]) { @@ -3489,7 +3571,9 @@ fn zirValidateArrayInit( }); } - if (is_comptime or block.is_comptime) { + if ((is_comptime or block.is_comptime) and + (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null) + { // In this case the comptime machinery will have evaluated the store instructions // at comptime so we have almost nothing to do here. However, in case of a // sentinel-terminated array, the sentinel will not have been populated by @@ -3544,7 +3628,14 @@ fn zirValidateArrayInit( switch (air_tags[next_air_inst]) { .store => { const bin_op = air_datas[next_air_inst].bin_op; - if (bin_op.lhs != elem_ptr_air_ref) { + var lhs = bin_op.lhs; + if (Air.refToIndex(lhs)) |lhs_index| { + if (air_tags[lhs_index] == .bitcast) { + lhs = air_datas[lhs_index].ty_op.operand; + block_index -= 1; + } + } + if (lhs != elem_ptr_air_ref) { array_is_comptime = false; continue; } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 32df664bae..3129091091 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1005,3 +1005,51 @@ test "generic function uses return type of other generic function" { }; try std.testing.expect(S.call(S.func, .{@as(u8, 1)}) == 1); } + +test "const alloc with comptime known initializer is made comptime known" { + const S = struct { + a: bool, + b: [2]u8, + }; + { + const s: S = .{ + .a = false, + .b = .{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const s: S = .{ + .a = false, + .b = [2]u8{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const s: S = comptime .{ + .a = false, + .b = .{ 1, 2 }, + }; + if (s.a) @compileError("bad"); + } + { + const Const = struct { + limbs: []const usize, + positive: bool, + }; + const biggest: Const = .{ + .limbs = &([1]usize{comptime std.math.maxInt(usize)} ** 128), + .positive = false, + }; + if (biggest.positive) @compileError("bad"); + } + { + const U = union(enum) { + a: usize, + }; + const u: U = .{ + .a = comptime std.math.maxInt(usize), + }; + if (u.a == 0) @compileError("bad"); + } +} diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig index f4cc2101c4..b91ac8e2fb 100644 --- a/test/behavior/bugs/1741.zig +++ b/test/behavior/bugs/1741.zig @@ -2,9 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); test "fixed" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const x: f32 align(128) = 12.34; try std.testing.expect(@ptrToInt(&x) % 128 == 0); }