| ... | ... | @@ -40,6 +40,17 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { |
| 40 | 40 | .Fn => { |
| 41 | 41 | return castInt(DestType, @ptrToInt(&target)); |
| 42 | 42 | }, |
| 43 | .Bool => { |
| 44 | return @boolToInt(target); |
| 45 | }, |
| 46 | else => {}, |
| 47 | } |
| 48 | }, |
| 49 | .Float => { |
| 50 | switch (@typeInfo(SourceType)) { |
| 51 | .Int => return @intToFloat(DestType, target), |
| 52 | .Float => return @floatCast(DestType, target), |
| 53 | .Bool => return @intToFloat(DestType, @boolToInt(target)), |
| 43 | 54 | else => {}, |
| 44 | 55 | } |
| 45 | 56 | }, |
| ... | ... | @@ -446,6 +457,121 @@ pub const Macros = struct { |
| 446 | 457 | } |
| 447 | 458 | }; |
| 448 | 459 | |
| 460 | /// Integer promotion described in C11 6.3.1.1.2 |
| 461 | fn PromotedIntType(comptime T: type) type { |
| 462 | return switch (T) { |
| 463 | bool, u8, i8, c_short => c_int, |
| 464 | c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int, |
| 465 | c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T, |
| 466 | else => if (T == comptime_int) { |
| 467 | @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required"); |
| 468 | } else if (@typeInfo(T) == .Int) { |
| 469 | @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required"); |
| 470 | } else { |
| 471 | @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`"); |
| 472 | }, |
| 473 | }; |
| 474 | } |
| 475 | |
| 476 | /// C11 6.3.1.1.1 |
| 477 | fn integerRank(comptime T: type) u8 { |
| 478 | return switch (T) { |
| 479 | bool => 0, |
| 480 | u8, i8 => 1, |
| 481 | c_short, c_ushort => 2, |
| 482 | c_int, c_uint => 3, |
| 483 | c_long, c_ulong => 4, |
| 484 | c_longlong, c_ulonglong => 5, |
| 485 | else => @compileError("integer rank not supported for `" ++ @typeName(T) ++ "`"), |
| 486 | }; |
| 487 | } |
| 488 | |
| 489 | fn ToUnsigned(comptime T: type) type { |
| 490 | return switch (T) { |
| 491 | c_int => c_uint, |
| 492 | c_long => c_ulong, |
| 493 | c_longlong => c_ulonglong, |
| 494 | else => @compileError("Cannot convert `" ++ @typeName(T) ++ "` to unsigned"), |
| 495 | }; |
| 496 | } |
| 497 | |
| 498 | /// "Usual arithmetic conversions" from C11 standard 6.3.1.8 |
| 499 | fn ArithmeticConversion(comptime A: type, comptime B: type) type { |
| 500 | if (A == c_longdouble or B == c_longdouble) return c_longdouble; |
| 501 | if (A == f80 or B == f80) return f80; |
| 502 | if (A == f64 or B == f64) return f64; |
| 503 | if (A == f32 or B == f32) return f32; |
| 504 | |
| 505 | const A_Promoted = PromotedIntType(A); |
| 506 | const B_Promoted = PromotedIntType(B); |
| 507 | comptime { |
| 508 | std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int)); |
| 509 | std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int)); |
| 510 | } |
| 511 | |
| 512 | if (A_Promoted == B_Promoted) return A_Promoted; |
| 513 | |
| 514 | const a_signed = @typeInfo(A_Promoted).Int.signedness == .signed; |
| 515 | const b_signed = @typeInfo(B_Promoted).Int.signedness == .signed; |
| 516 | |
| 517 | if (a_signed == b_signed) { |
| 518 | return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted; |
| 519 | } |
| 520 | |
| 521 | const SignedType = if (a_signed) A_Promoted else B_Promoted; |
| 522 | const UnsignedType = if (!a_signed) A_Promoted else B_Promoted; |
| 523 | |
| 524 | if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType; |
| 525 | |
| 526 | if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType; |
| 527 | |
| 528 | return ToUnsigned(SignedType); |
| 529 | } |
| 530 | |
| 531 | test "ArithmeticConversion" { |
| 532 | // Promotions not necessarily the same for other platforms |
| 533 | if (builtin.target.cpu.arch != .x86_64 or builtin.target.os.tag != .linux) return error.SkipZigTest; |
| 534 | |
| 535 | const Test = struct { |
| 536 | /// Order of operands should not matter for arithmetic conversions |
| 537 | fn checkPromotion(comptime A: type, comptime B: type, comptime Expected: type) !void { |
| 538 | try std.testing.expect(ArithmeticConversion(A, B) == Expected); |
| 539 | try std.testing.expect(ArithmeticConversion(B, A) == Expected); |
| 540 | } |
| 541 | }; |
| 542 | |
| 543 | try Test.checkPromotion(c_longdouble, c_int, c_longdouble); |
| 544 | try Test.checkPromotion(c_int, f64, f64); |
| 545 | try Test.checkPromotion(f32, bool, f32); |
| 546 | |
| 547 | try Test.checkPromotion(bool, c_short, c_int); |
| 548 | try Test.checkPromotion(c_int, c_int, c_int); |
| 549 | try Test.checkPromotion(c_short, c_int, c_int); |
| 550 | |
| 551 | try Test.checkPromotion(c_int, c_long, c_long); |
| 552 | |
| 553 | try Test.checkPromotion(c_ulonglong, c_uint, c_ulonglong); |
| 554 | |
| 555 | try Test.checkPromotion(c_uint, c_int, c_uint); |
| 556 | |
| 557 | try Test.checkPromotion(c_uint, c_long, c_long); |
| 558 | |
| 559 | try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong); |
| 560 | } |
| 561 | |
| 562 | pub const MacroArithmetic = struct { |
| 563 | pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) { |
| 564 | const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b)); |
| 565 | const a_casted = cast(ResType, a); |
| 566 | const b_casted = cast(ResType, b); |
| 567 | switch (@typeInfo(ResType)) { |
| 568 | .Float => return a_casted / b_casted, |
| 569 | .Int => return @divTrunc(a_casted, b_casted), |
| 570 | else => unreachable, |
| 571 | } |
| 572 | } |
| 573 | }; |
| 574 | |
| 449 | 575 | test "Macro suffix functions" { |
| 450 | 576 | try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32); |
| 451 | 577 | |