/* * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "cppstdlib/limits.hpp" #include "cppstdlib/type_traits.hpp" #include "utilities/integerCast.hpp" #include "utilities/globalDefinitions.hpp" #include "unittest.hpp" // Enable gcc warnings to verify we don't get any of these. // Eventually we plan to have these globally enabled, but not there yet. #ifdef __GNUC__ #pragma GCC diagnostic warning "-Wconversion" #pragma GCC diagnostic warning "-Wsign-conversion" #endif // Tautology tests for signed -> signed types. static_assert(is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); static_assert(is_always_integer_convertible()); static_assert(is_always_integer_convertible()); // Tautology tests for unsigned -> unsigned types. static_assert(is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); static_assert(is_always_integer_convertible()); static_assert(is_always_integer_convertible()); // Tautology tests for signed -> unsigned types. static_assert(!is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); // Tautology tests for unsigned -> signed types. static_assert(!is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); static_assert(is_always_integer_convertible()); static_assert(!is_always_integer_convertible()); template struct TestIntegerCastValues { static TestIntegerCastValues values; T minus_one = static_cast(-1); T zero = static_cast(0); T one = static_cast(1); T min = std::numeric_limits::min(); T max = std::numeric_limits::max(); }; template TestIntegerCastValues TestIntegerCastValues::values{}; template struct TestIntegerCastPairedValues { static TestIntegerCastPairedValues values; From min = static_cast(std::numeric_limits::min()); From max = static_cast(std::numeric_limits::max()); }; template TestIntegerCastPairedValues TestIntegerCastPairedValues::values{}; ////////////////////////////////////////////////////////////////////////////// // Integer casts between integral types of different sizes. // Test narrowing to verify checking. // Test widening to verify no compiler warnings for tautological comparisons. template struct TestIntegerCastIntegerValues { static TestIntegerCastIntegerValues values; TestIntegerCastValues to; TestIntegerCastValues from; TestIntegerCastPairedValues to_as_from; }; template TestIntegerCastIntegerValues TestIntegerCastIntegerValues::values{}; template static void good_integer_conversion(From from) { ASSERT_TRUE(is_integer_convertible(from)); EXPECT_EQ(static_cast(from), integer_cast(from)); } template static void bad_integer_conversion(From from) { EXPECT_FALSE(is_integer_convertible(from)); } // signed -> signed is tautological unless From is wider than To. TEST(TestIntegerCast, wide_signed_to_narrow_signed_integers) { using To = int32_t; using From = int64_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; good_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); bad_integer_conversion(values.from.min); bad_integer_conversion(values.from.max); good_integer_conversion(values.to_as_from.min); good_integer_conversion(values.to_as_from.max); bad_integer_conversion(values.to_as_from.min - 1); bad_integer_conversion(values.to_as_from.max + 1); } // unsigned -> unsigned is tautological unless From is wider than To. TEST(TestIntegerCast, wide_unsigned_to_narrow_unsigned_integers) { using To = uint32_t; using From = uint64_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; bad_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); good_integer_conversion(values.from.min); bad_integer_conversion(values.from.max); good_integer_conversion(values.to_as_from.min); good_integer_conversion(values.to_as_from.min); bad_integer_conversion(values.to_as_from.min - 1); bad_integer_conversion(values.to_as_from.max + 1); } TEST(TestIntegerCast, unsigned_to_signed_same_size_integers) { using To = int32_t; using From = uint32_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); good_integer_conversion(values.from.min); bad_integer_conversion(values.from.max); bad_integer_conversion(values.to_as_from.min); good_integer_conversion(values.to_as_from.max); bad_integer_conversion(values.to_as_from.max + 1); } // Narrow unsigned to wide signed is tautological. TEST(TestIntegerCast, wide_unsigned_to_narrow_signed_integers) { using To = int32_t; using From = uint64_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; bad_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); good_integer_conversion(values.from.min); bad_integer_conversion(values.from.max); bad_integer_conversion(values.to_as_from.min); good_integer_conversion(values.to_as_from.max); bad_integer_conversion(values.to_as_from.min - 1); bad_integer_conversion(values.to_as_from.max + 1); } TEST(TestIntegerCast, signed_to_unsigned_same_size_integers) { using To = uint32_t; using From = int32_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; bad_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); bad_integer_conversion(values.from.min); good_integer_conversion(values.from.max); good_integer_conversion(values.to_as_from.min); bad_integer_conversion(values.to_as_from.max); } TEST(TestIntegerCast, narrow_signed_to_wide_unsigned_integers) { using To = uint64_t; using From = int32_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; bad_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); bad_integer_conversion(values.from.min); good_integer_conversion(values.from.max); good_integer_conversion(values.to_as_from.min); bad_integer_conversion(values.to_as_from.max); } TEST(TestIntegerCast, wide_signed_to_narrow_unsigned_integers) { using To = uint32_t; using From = int64_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; bad_integer_conversion(values.from.minus_one); good_integer_conversion(values.from.zero); good_integer_conversion(values.from.one); bad_integer_conversion(values.from.min); bad_integer_conversion(values.from.max); good_integer_conversion(values.to_as_from.min); good_integer_conversion(values.to_as_from.max); } TEST(TestIntegerCast, permit_tautology) { using From = uint32_t; using To = int64_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; static_assert(is_always_integer_convertible()); EXPECT_EQ(static_cast(values.from.min), (integer_cast(values.from.min))); EXPECT_EQ(static_cast(values.from.min), (integer_cast_permit_tautology(values.from.min))); EXPECT_EQ(static_cast(values.from.max), (integer_cast(values.from.max))); EXPECT_EQ(static_cast(values.from.max), integer_cast_permit_tautology(values.from.max)); } TEST(TestIntegerCast, check_constexpr) { using From = int64_t; using To = int32_t; constexpr From value = std::numeric_limits::max(); constexpr To converted = integer_cast(value); EXPECT_EQ(static_cast(value), converted); } #ifdef ASSERT TEST_VM_ASSERT(TestIntegerCast, cast_failure_signed_range) { using From = int64_t; using To = int32_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; From value = values.from.max; To expected = static_cast(value); // Narrowing conversion. EXPECT_FALSE(is_integer_convertible(value)); // Should assert. If it doesn't, then shuld be equal, so fail. EXPECT_NE(static_cast(value), integer_cast(value)); } TEST_VM_ASSERT(TestIntegerCast, cast_failure_unsigned_range) { using From = uint64_t; using To = uint32_t; using Values = TestIntegerCastIntegerValues; const Values& values = Values::values; From value = values.from.max; To expected = static_cast(value); // Narrowing conversion. EXPECT_FALSE(is_integer_convertible(value)); // Should assert. If it doesn't, then should be equal, so fail. EXPECT_NE(static_cast(value), integer_cast(value)); } #endif // ASSERT