diff --git a/src/Sema.zig b/src/Sema.zig index 4c2f72034e..931e06724b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1827,6 +1827,22 @@ fn resolveMaybeUndefValAllowVariables( block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref, +) CompileError!?Value { + var make_runtime = false; + if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, src, inst, &make_runtime)) |val| { + if (make_runtime) return null; + return val; + } + return null; +} + +/// Returns all Value tags including `variable`, `undef` and `runtime_value`. +fn resolveMaybeUndefValAllowVariablesMaybeRuntime( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + inst: Air.Inst.Ref, + make_runtime: *bool, ) CompileError!?Value { // First section of indexes correspond to a set number of constant values. var i: usize = @enumToInt(inst); @@ -1843,7 +1859,7 @@ fn resolveMaybeUndefValAllowVariables( .constant => { const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; const val = sema.air_values.items[ty_pl.payload]; - if (val.tag() == .runtime_int) return null; + if (val.tag() == .runtime_value) make_runtime.* = true; return val; }, .const_ty => { @@ -3896,6 +3912,7 @@ fn validateUnionInit( var first_block_index = block.instructions.items.len; var block_index = block.instructions.items.len - 1; var init_val: ?Value = null; + var make_runtime = false; while (block_index > 0) : (block_index -= 1) { const store_inst = block.instructions.items[block_index]; if (store_inst == field_ptr_air_inst) break; @@ -3920,7 +3937,7 @@ fn validateUnionInit( } else { first_block_index = @min(first_block_index, block_index); } - init_val = try sema.resolveMaybeUndefValAllowVariables(block, init_src, bin_op.rhs); + init_val = try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, init_src, bin_op.rhs, &make_runtime); break; } @@ -3933,10 +3950,11 @@ fn validateUnionInit( // instead a single `store` to the result ptr with a comptime union value. block.instructions.shrinkRetainingCapacity(first_block_index); - const union_val = try Value.Tag.@"union".create(sema.arena, .{ + var union_val = try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val, }); + if (make_runtime) union_val = try Value.Tag.runtime_value.create(sema.arena, union_val); const union_init = try sema.addConstant(union_ty, union_val); try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store); return; @@ -4054,6 +4072,7 @@ fn validateStructInit( var struct_is_comptime = true; var first_block_index = block.instructions.items.len; + var make_runtime = false; const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); @@ -4130,7 +4149,7 @@ fn validateStructInit( } else { first_block_index = @min(first_block_index, block_index); } - if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| { + if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, field_src, bin_op.rhs, &make_runtime)) |val| { field_values[i] = val; } else { struct_is_comptime = false; @@ -4185,7 +4204,8 @@ fn validateStructInit( // instead a single `store` to the struct_ptr with a comptime struct value. block.instructions.shrinkRetainingCapacity(first_block_index); - const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values); + var struct_val = try Value.Tag.aggregate.create(sema.arena, field_values); + if (make_runtime) struct_val = try Value.Tag.runtime_value.create(sema.arena, struct_val); const struct_init = try sema.addConstant(struct_ty, struct_val); try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store); return; @@ -4265,6 +4285,7 @@ fn zirValidateArrayInit( var array_is_comptime = true; var first_block_index = block.instructions.items.len; + var make_runtime = false; // Collect the comptime element values in case the array literal ends up // being comptime-known. @@ -4326,7 +4347,7 @@ fn zirValidateArrayInit( array_is_comptime = false; continue; } - if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| { + if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| { element_vals[i] = val; } else { array_is_comptime = false; @@ -4352,7 +4373,7 @@ fn zirValidateArrayInit( array_is_comptime = false; continue; } - if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| { + if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| { element_vals[i] = val; } else { array_is_comptime = false; @@ -4383,7 +4404,8 @@ fn zirValidateArrayInit( block.instructions.shrinkRetainingCapacity(first_block_index); - const array_val = try Value.Tag.aggregate.create(sema.arena, element_vals); + var array_val = try Value.Tag.aggregate.create(sema.arena, element_vals); + if (make_runtime) array_val = try Value.Tag.runtime_value.create(sema.arena, array_val); const array_init = try sema.addConstant(array_ty, array_val); try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store); } @@ -6635,20 +6657,14 @@ fn analyzeInlineCallArg( .ty = param_ty, .val = arg_val, }; - } else if (((try sema.resolveMaybeUndefVal(arg_block, arg_src, casted_arg)) == null) or - try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_comptime) - { + } else if (zir_tags[inst] == .param_comptime or try sema.typeRequiresComptime(param_ty)) { try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); - } else { + } else if (try sema.resolveMaybeUndefVal(arg_block, arg_src, casted_arg)) |val| { // We have a comptime value but we need a runtime value to preserve inlining semantics, - const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = param_ty, - .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), - }); - const alloc = try arg_block.addTy(.alloc, ptr_type); - _ = try arg_block.addBinOp(.store, alloc, casted_arg); - const loaded = try arg_block.addTyOp(.load, param_ty, alloc); - try sema.inst_map.putNoClobber(sema.gpa, inst, loaded); + const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); + try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped); + } else { + try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); } arg_i.* += 1; @@ -6685,20 +6701,14 @@ fn analyzeInlineCallArg( .ty = sema.typeOf(uncasted_arg), .val = arg_val, }; - } else if ((try sema.resolveMaybeUndefVal(arg_block, arg_src, uncasted_arg)) == null or - try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_anytype_comptime) - { + } else if (zir_tags[inst] == .param_anytype_comptime or try sema.typeRequiresComptime(param_ty)) { try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); - } else { + } else if (try sema.resolveMaybeUndefVal(arg_block, arg_src, uncasted_arg)) |val| { // We have a comptime value but we need a runtime value to preserve inlining semantics, - const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = param_ty, - .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), - }); - const alloc = try arg_block.addTy(.alloc, ptr_type); - _ = try arg_block.addBinOp(.store, alloc, uncasted_arg); - const loaded = try arg_block.addTyOp(.load, param_ty, alloc); - try sema.inst_map.putNoClobber(sema.gpa, inst, loaded); + const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); + try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped); + } else { + try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); } arg_i.* += 1; @@ -14826,7 +14836,7 @@ fn zirBuiltinSrc( // fn_name: [:0]const u8, field_values[1] = func_name_val; // line: u32 - field_values[2] = try Value.Tag.runtime_int.create(sema.arena, extra.line + 1); + field_values[2] = try Value.Tag.runtime_value.create(sema.arena, try Value.Tag.int_u64.create(sema.arena, extra.line + 1)); // column: u32, field_values[3] = try Value.Tag.int_u64.create(sema.arena, extra.column + 1); diff --git a/src/TypedValue.zig b/src/TypedValue.zig index ba32e55f1e..619fb003f9 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -477,6 +477,6 @@ pub fn print( }, .generic_poison_type => return writer.writeAll("(generic poison type)"), .generic_poison => return writer.writeAll("(generic poison)"), - .runtime_int => return writer.writeAll("[runtime value]"), + .runtime_value => return writer.writeAll("[runtime value]"), }; } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index eb8ca8e8f1..3bb5bbe0d3 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -5401,7 +5401,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } } -fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { +fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { + var typed_value = arg_tv; + if (typed_value.val.castTag(.runtime_value)) |rt| { + typed_value.val = rt.data; + } log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 1ebc348fc2..67cf899dc3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -6047,7 +6047,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } } -fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { +fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { + var typed_value = arg_tv; + if (typed_value.val.castTag(.runtime_value)) |rt| { + typed_value.val = rt.data; + } log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 538fcb13c1..69d5e38f65 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2582,7 +2582,11 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo( return @intCast(WantedT, result); } -fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { +fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { + var val = arg_val; + if (val.castTag(.runtime_value)) |rt| { + val = rt.data; + } if (val.isUndefDeep()) return func.emitUndefined(ty); if (val.castTag(.decl_ref)) |decl_ref| { const decl_index = decl_ref.data; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5f793aaeb9..965a34251c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6960,7 +6960,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { } } -fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { +fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { + var typed_value = arg_tv; + if (typed_value.val.castTag(.runtime_value)) |rt| { + typed_value.val = rt.data; + } log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() }); if (typed_value.val.isUndef()) return MCValue{ .undef = {} }; diff --git a/src/codegen.zig b/src/codegen.zig index e7f927a2d6..757bd23b38 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -149,7 +149,7 @@ fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian pub fn generateSymbol( bin_file: *link.File, src_loc: Module.SrcLoc, - typed_value: TypedValue, + arg_tv: TypedValue, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, @@ -157,6 +157,11 @@ pub fn generateSymbol( const tracy = trace(@src()); defer tracy.end(); + var typed_value = arg_tv; + if (arg_tv.val.castTag(.runtime_value)) |rt| { + typed_value.val = rt.data; + } + const target = bin_file.options.target; const endian = target.cpu.arch.endian(); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d6584d75ae..d0f76f0390 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -555,9 +555,13 @@ pub const DeclGen = struct { dg: *DeclGen, writer: anytype, ty: Type, - val: Value, + arg_val: Value, location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { + var val = arg_val; + if (val.castTag(.runtime_value)) |rt| { + val = rt.data; + } const target = dg.module.getTarget(); if (val.isUndefDeep()) { switch (ty.zigTypeTag()) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index ffc19cb6f6..938770629f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3187,7 +3187,11 @@ pub const DeclGen = struct { return llvm_elem_ty; } - fn lowerValue(dg: *DeclGen, tv: TypedValue) Error!*llvm.Value { + fn lowerValue(dg: *DeclGen, arg_tv: TypedValue) Error!*llvm.Value { + var tv = arg_tv; + if (tv.val.castTag(.runtime_value)) |rt| { + tv.val = rt.data; + } if (tv.val.isUndef()) { const llvm_type = try dg.lowerType(tv.ty); return llvm_type.getUndef(); diff --git a/src/value.zig b/src/value.zig index d24c5a1c17..28601c1723 100644 --- a/src/value.zig +++ b/src/value.zig @@ -111,10 +111,12 @@ pub const Value = extern union { int_i64, int_big_positive, int_big_negative, - runtime_int, function, extern_fn, variable, + /// A wrapper for values which are comptime-known but should + /// semantically be runtime-known. + runtime_value, /// Represents a pointer to a Decl. /// When machine codegen backend sees this, it must set the Decl's `alive` field to true. decl_ref, @@ -282,6 +284,7 @@ pub const Value = extern union { .eu_payload, .opt_payload, .empty_array_sentinel, + .runtime_value, => Payload.SubValue, .eu_payload_ptr, @@ -305,7 +308,6 @@ pub const Value = extern union { .int_type => Payload.IntType, .int_u64 => Payload.U64, .int_i64 => Payload.I64, - .runtime_int => Payload.U64, .function => Payload.Function, .variable => Payload.Variable, .decl_ref_mut => Payload.DeclRefMut, @@ -485,7 +487,6 @@ pub const Value = extern union { }, .int_type => return self.copyPayloadShallow(arena, Payload.IntType), .int_u64 => return self.copyPayloadShallow(arena, Payload.U64), - .runtime_int => return self.copyPayloadShallow(arena, Payload.U64), .int_i64 => return self.copyPayloadShallow(arena, Payload.I64), .int_big_positive, .int_big_negative => { const old_payload = self.cast(Payload.BigInt).?; @@ -567,6 +568,7 @@ pub const Value = extern union { .eu_payload, .opt_payload, .empty_array_sentinel, + .runtime_value, => { const payload = self.cast(Payload.SubValue).?; const new_payload = try arena.create(Payload.SubValue); @@ -765,7 +767,7 @@ pub const Value = extern union { .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream), .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}), .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}), - .runtime_int => return out_stream.writeAll("[runtime value]"), + .runtime_value => return out_stream.writeAll("[runtime value]"), .function => return out_stream.print("(function decl={d})", .{val.castTag(.function).?.data.owner_decl}), .extern_fn => return out_stream.writeAll("(extern function)"), .variable => return out_stream.writeAll("(variable)"), @@ -1081,8 +1083,6 @@ pub const Value = extern union { .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(), .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt(), - .runtime_int => return BigIntMutable.init(&space.limbs, val.castTag(.runtime_int).?.data).toConst(), - .undef => unreachable, .lazy_align => { @@ -1138,8 +1138,6 @@ pub const Value = extern union { .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null, .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null, - .runtime_int => return val.castTag(.runtime_int).?.data, - .undef => unreachable, .lazy_align => { @@ -2357,6 +2355,8 @@ pub const Value = extern union { const zig_ty_tag = ty.zigTypeTag(); std.hash.autoHash(hasher, zig_ty_tag); if (val.isUndef()) return; + // The value is runtime-known and shouldn't affect the hash. + if (val.tag() == .runtime_value) return; switch (zig_ty_tag) { .BoundFn => unreachable, // TODO remove this from the language @@ -2632,9 +2632,6 @@ pub const Value = extern union { .lazy_size, => return hashInt(ptr_val, hasher, target), - // The value is runtime-known and shouldn't affect the hash. - .runtime_int => {}, - else => unreachable, } } diff --git a/test/behavior/bugs/13164.zig b/test/behavior/bugs/13164.zig index ee9fe1c120..37f5bdf805 100644 --- a/test/behavior/bugs/13164.zig +++ b/test/behavior/bugs/13164.zig @@ -10,6 +10,7 @@ inline fn setLimits(min: ?u32, max: ?u32) !void { test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var x: u32 = 42; try setLimits(x, null); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 80fa2021d8..22e12d0808 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1135,3 +1135,40 @@ test "array of vectors is copied" { points2[0..points.len].* = points; try std.testing.expectEqual(points2[6], Vec3{ -345, -311, 381 }); } + +test "byte vector initialized in inline function" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + inline fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) { + return .{ e0, e1, e2, e3 }; + } + + fn all(vb: @Vector(4, bool)) bool { + return @reduce(.And, vb); + } + }; + + try expect(S.all(S.boolx4(true, true, true, true))); +} + +test "byte vector initialized in inline function" { + // TODO https://github.com/ziglang/zig/issues/13279 + if (true) return error.SkipZigTest; + + const S = struct { + fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) { + return .{ e0, e1, e2, e3 }; + } + + fn all(vb: @Vector(4, bool)) bool { + return @reduce(.And, vb); + } + }; + + try expect(S.all(S.boolx4(true, true, true, true))); +}