diff --git a/src/value.zig b/src/value.zig index 47f69b2099..4c6a64eefc 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1300,16 +1300,16 @@ pub const Value = extern union { for (fields) |field, i| { const field_val = field_vals[i]; const field_bigint_const = switch (field.ty.zigTypeTag()) { - .Float => switch (field.ty.floatBits(target)) { - 16 => bitcastFloatToBigInt(f16, field_val.toFloat(f16), &field_buf), - 32 => bitcastFloatToBigInt(f32, field_val.toFloat(f32), &field_buf), - 64 => bitcastFloatToBigInt(f64, field_val.toFloat(f64), &field_buf), - 80 => bitcastFloatToBigInt(f80, field_val.toFloat(f80), &field_buf), - 128 => bitcastFloatToBigInt(f128, field_val.toFloat(f128), &field_buf), - else => unreachable, + .Void => continue, + .Float => floatToBigInt(field_val, field.ty, target, &field_buf), + .Int, .Bool => intOrBoolToBigInt(field_val, field.ty, target, &field_buf, &field_space), + .Struct => switch (field.ty.containerLayout()) { + .Auto, .Extern => unreachable, // Sema should have error'd before this. + .Packed => packedStructToInt(field_val, field.ty, target, &field_buf), }, - .Int, .Bool => field_val.toBigInt(&field_space, target), - .Struct => packedStructToInt(field_val, field.ty, target, &field_buf), + .Vector => vectorToBigInt(field_val, field.ty, target, &field_buf), + .Enum => enumToBigInt(field_val, field.ty, target, &field_space), + .Union => unreachable, // TODO: packed structs support packed unions else => unreachable, }; var field_bigint = BigIntMutable.init(&field_buf2, 0); @@ -1320,6 +1320,61 @@ pub const Value = extern union { return bigint.toConst(); } + fn intOrBoolToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb, space: *BigIntSpace) BigIntConst { + const big_int_const = val.toBigInt(space, target); + if (big_int_const.positive) return big_int_const; + + var big_int = BigIntMutable.init(buf, 0); + big_int.bitNotWrap(big_int_const.negate(), .unsigned, @intCast(u32, ty.bitSize(target))); + big_int.addScalar(big_int.toConst(), 1); + return big_int.toConst(); + } + + fn vectorToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst { + const endian = target.cpu.arch.endian(); + var vec_bitint = BigIntMutable.init(buf, 0); + const vec_len = @intCast(usize, ty.arrayLen()); + const elem_ty = ty.childType(); + const elem_size = @intCast(usize, elem_ty.bitSize(target)); + + var elem_buf: [16]std.math.big.Limb = undefined; + var elem_space: BigIntSpace = undefined; + var elem_buf2: [16]std.math.big.Limb = undefined; + + var elem_i: usize = 0; + while (elem_i < vec_len) : (elem_i += 1) { + const elem_i_target = if (endian == .Big) vec_len - elem_i - 1 else elem_i; + const elem_val = val.indexVectorlike(elem_i_target); + const elem_bigint_const = switch (elem_ty.zigTypeTag()) { + .Int, .Bool => intOrBoolToBigInt(elem_val, elem_ty, target, &elem_buf, &elem_space), + .Float => floatToBigInt(elem_val, elem_ty, target, &elem_buf), + .Pointer => unreachable, // TODO + else => unreachable, // Sema should not let this happen + }; + var elem_bitint = BigIntMutable.init(&elem_buf2, 0); + elem_bitint.shiftLeft(elem_bigint_const, elem_size * elem_i); + vec_bitint.bitOr(vec_bitint.toConst(), elem_bitint.toConst()); + } + return vec_bitint.toConst(); + } + + fn enumToBigInt(val: Value, ty: Type, target: Target, space: *BigIntSpace) BigIntConst { + var enum_buf: Payload.U64 = undefined; + const int_val = val.enumToInt(ty, &enum_buf); + return int_val.toBigInt(space, target); + } + + fn floatToBigInt(val: Value, ty: Type, target: Target, buf: []std.math.big.Limb) BigIntConst { + return switch (ty.floatBits(target)) { + 16 => bitcastFloatToBigInt(f16, val.toFloat(f16), buf), + 32 => bitcastFloatToBigInt(f32, val.toFloat(f32), buf), + 64 => bitcastFloatToBigInt(f64, val.toFloat(f64), buf), + 80 => bitcastFloatToBigInt(f80, val.toFloat(f80), buf), + 128 => bitcastFloatToBigInt(f128, val.toFloat(f128), buf), + else => unreachable, + }; + } + fn bitcastFloatToBigInt(comptime F: type, f: F, buf: []std.math.big.Limb) BigIntConst { const Int = @Type(.{ .Int = .{ .signedness = .unsigned, diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 3a7719191d..c629a1a34b 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -282,3 +282,28 @@ test "@bitCast packed struct of floats" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "comptime @bitCast packed struct to int" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + + const S = packed struct { + @"void": void = {}, + uint: u8 = 13, + uint_bit_aligned: u3 = 2, + iint_pos: i4 = 1, + iint_neg4: i3 = -4, + iint_neg2: i3 = -2, + float: f32 = 3.14, + @"enum": enum(u2) { A, B = 1, C, D } = .B, + vectorb: @Vector(3, bool) = .{ true, false, true }, + vectori: @Vector(2, u8) = .{ 127, 42 }, + vectorf: @Vector(2, f16) = .{ 3.14, 2.71 }, + }; + const Int = @typeInfo(S).Struct.backing_integer.?; + var s: S = .{}; + try expectEqual(@bitCast(Int, s), comptime @bitCast(Int, S{})); +}