From a25f94ddb26d218af2ecbf7cdf943ac755a355a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Wed, 11 Mar 2026 22:17:26 +0900 Subject: [PATCH] Sema: handle generic poison in rounding builtins --- src/Sema.zig | 42 +++++++++++++++++++++++++----------------- test/behavior/cast.zig | 17 +++++++++++++++++ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 90a9533171..8bce6432ee 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20850,30 +20850,36 @@ fn zirRoundCast( const src = block.nodeOffset(extra.node); const operand_src = block.builtinCallArgSrc(extra.node, 0); - const builtin_name = switch (mode) { - .round => "@round", - .floor => "@floor", - .ceil => "@ceil", - .truncate => "@trunc", - .exact => unreachable, - }; + const dest_ty_or_poison = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse Type.generic_poison; + var dest_ty = dest_ty_or_poison; + if (!dest_ty.isGenericPoison()) { + if (dest_ty.zigTypeTag(zcu) == .error_union) { + dest_ty = dest_ty.errorUnionPayload(zcu); + } + if (dest_ty.zigTypeTag(zcu) == .optional) { + dest_ty = dest_ty.childType(zcu); + } + } - const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, builtin_name); const operand = sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); - try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); - const dest_scalar_ty = dest_ty.scalarType(zcu); + const is_poison = dest_ty.isGenericPoison(); + if (!is_poison) { + try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); + } + const dest_scalar_ty = if (is_poison) dest_ty else dest_ty.scalarType(zcu); const operand_scalar_ty = operand_ty.scalarType(zcu); - if (dest_scalar_ty.zigTypeTag(zcu) == .float or dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { + if (is_poison or dest_scalar_ty.zigTypeTag(zcu) == .float or dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { const coerced_operand = try sema.coerce(block, dest_ty, operand, operand_src); + const math_ty = if (is_poison) operand_ty else dest_ty; const result_ref = switch (mode) { - .round => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.round), - .floor => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.floor), - .ceil => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.ceil), - .truncate => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.trunc), + .round => try sema.maybeConstantUnaryMath(coerced_operand, math_ty, Value.round), + .floor => try sema.maybeConstantUnaryMath(coerced_operand, math_ty, Value.floor), + .ceil => try sema.maybeConstantUnaryMath(coerced_operand, math_ty, Value.ceil), + .truncate => try sema.maybeConstantUnaryMath(coerced_operand, math_ty, Value.trunc), else => unreachable, }; @@ -25122,9 +25128,11 @@ fn zirRoundOpType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDa const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const operand_src = block.builtinCallArgSrc(extra.node, 0); - const dest_ty = try sema.resolveDestType(block, operand_src, extra.operand, .remove_eu_opt, "type hint"); + const dest_ty_or_poison = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse Type.generic_poison; - const float_ty = dest_ty.optEuBaseType(zcu); + if (dest_ty_or_poison.isGenericPoison()) return .generic_poison_type; + + const float_ty = dest_ty_or_poison.optEuBaseType(zcu); switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) { .float, .comptime_float => return .fromType(float_ty), else => return .comptime_float_type, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index da54201ab8..04c5ce3848 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -253,6 +253,23 @@ fn testIntFromFloats() !void { try expectTruncCast(f32, -128.2, i8, -128); } +test "rounding builtins with anytype and context propagation" { + const S = struct { + const x: i32 = 10; + fn check(expected: anytype, actual: anytype) !void { + try expectEqual(expected, actual); + } + }; + try expectEqual(@as(f32, 1.0), @round(@as(f32, 1.4))); + try S.check(@as(f32, 1.0), @round(@as(f32, 1.4))); + + const y: f64 = @floor(@floatFromInt(S.x)); + try expect(y == 10.0); + + try expectEqual(1.0, @round(1.4)); + try S.check(1.0, @round(1.4)); +} + fn expectIntFromFloat(comptime F: type, f: F, comptime I: type, i: I) !void { try expect(@as(I, @intFromFloat(f)) == i); }