From 80842905f2d8a60ed7961e8ea6abac908574a10a Mon Sep 17 00:00:00 2001 From: kkHAIKE Date: Thu, 15 Sep 2022 14:17:40 +0800 Subject: [PATCH] Value: add Enum and eegative int support to bitCast packed struct --- src/value.zig | 38 ++++++++++++++++++++++++++++----- test/behavior/bitcast.zig | 44 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/src/value.zig b/src/value.zig index 7a0636dda028..04d899ba7918 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1309,11 +1309,27 @@ pub const Value = extern union { else => unreachable, }, .Int, .Bool => field_val.toBigInt(&field_space, target), + .Enum => blk: { + var enum_buffer: Payload.U64 = undefined; + const int_val = field_val.enumToInt(field.ty, &enum_buffer); + break :blk int_val.toBigInt(&field_space, target); + }, .Struct => packedStructToInt(field_val, field.ty, target, &field_buf), else => unreachable, }; var field_bigint = BigIntMutable.init(&field_buf2, 0); - field_bigint.shiftLeft(field_bigint_const, bits); + // need to cast negative to unsigned + // r = --a + // = ~(-a - 1) + if (!field_bigint_const.positive) { + // can use 'field_buf' now, cuz this case use 'field_space' in const. + var field_bigint_mul = BigIntMutable.init(&field_buf, 0); + field_bigint_mul.addScalar(field_bigint_const.negate(), -1); + field_bigint_mul.bitNotWrap(field_bigint_mul.toConst(), .unsigned, @intCast(usize, field.ty.bitSize(target))); + field_bigint.shiftLeft(field_bigint_mul.toConst(), bits); + } else { + field_bigint.shiftLeft(field_bigint_const, bits); + } bits += @intCast(u16, field.ty.bitSize(target)); bigint.bitOr(bigint.toConst(), field_bigint.toConst()); } @@ -1443,10 +1459,22 @@ pub const Value = extern union { else => unreachable, }, .Bool => makeBool(!field_bigint.eqZero()), - .Int => try Tag.int_big_positive.create( - arena, - try arena.dupe(std.math.big.Limb, field_bigint.limbs), - ), + .Int, .Enum => blk: { + // check if it shoudle be negative + const int_info = field.ty.intInfo(target); + if (int_info.signedness == .signed and !field_bigint.fitsInTwosComp(.signed, int_info.bits)) { + bigint_mut.bitNotWrap(field_bigint, .unsigned, int_info.bits); + bigint_mut.addScalar(bigint_mut.toConst(), 1); + break :blk try Tag.int_big_negative.create( + arena, + try arena.dupe(std.math.big.Limb, bigint_mut.limbs), + ); + } + break :blk try Tag.int_big_positive.create( + arena, + try arena.dupe(std.math.big.Limb, field_bigint.limbs), + ); + }, .Struct => try intToPackedStruct(field.ty, target, field_bigint, arena), else => unreachable, }; diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 3a7719191d9b..2b2bb1a93a2c 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -175,15 +175,17 @@ test "bitcast packed struct to integer and back" { const LevelUpMove = packed struct { move_id: u9, + negint: i8, level: u7, }; const S = struct { fn doTheTest() !void { - var move = LevelUpMove{ .move_id = 1, .level = 2 }; - var v = @bitCast(u16, move); + var move = LevelUpMove{ .move_id = 1, .level = 2, .negint = -3 }; + var v = @bitCast(u24, move); var back_to_a_move = @bitCast(LevelUpMove, v); try expect(back_to_a_move.move_id == 1); try expect(back_to_a_move.level == 2); + try expect(back_to_a_move.negint == -3); } }; try S.doTheTest(); @@ -282,3 +284,41 @@ test "@bitCast packed struct of floats" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "@bitCast packed struct of enum" { + 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 EnumA = enum(u2) { + zero, + one, + }; + + const EnumB = enum(i4) { + one = 1, + negone = -1, + _, + }; + + const Foo = packed struct { + a: EnumA, + b: EnumB, + c: EnumB, + }; + + const S = struct { + fn doTheTest() !void { + var foo = Foo{ .a = .one, .b = .negone, .c = @intToEnum(EnumB, 2) }; + var v = @bitCast(u10, foo); + var back = @bitCast(Foo, v); + try expect(back.a == .one); + try expect(back.b == .negone); + try expect(@enumToInt(back.c) == 2); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +}