From 35150fbefc5aaf2e8709e7e49f9897a6b1943ab6 Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Wed, 22 Jul 2026 09:53:55 -0700 Subject: [PATCH] Add C++ rs_std::usize and rs_std::isize wrappers in int.h These will be used by template specializations to disambiguate between a specialization of `usize` and `u64`/`u32` (or `isize and `i64`/`i32`) when those might be the same type in C++. `rs_std::usize` and `rs_std::isize` will always be considered a new type in C++ and will never overlap with fundamental types. PiperOrigin-RevId: 952180393 --- support/rs_std/BUILD | 27 ++++++++ support/rs_std/BUILD.gn | 18 ++++++ support/rs_std/CMakeLists.txt | 13 ++++ support/rs_std/int.h | 115 ++++++++++++++++++++++++++++++++++ support/rs_std/int_test.cc | 72 +++++++++++++++++++++ 5 files changed, 245 insertions(+) create mode 100644 support/rs_std/int.h create mode 100644 support/rs_std/int_test.cc diff --git a/support/rs_std/BUILD b/support/rs_std/BUILD index cca6e03a6..665346a2e 100644 --- a/support/rs_std/BUILD +++ b/support/rs_std/BUILD @@ -36,6 +36,33 @@ cc_library( ], ) +cc_library( + name = "int", + hdrs = ["int.h"], + aspect_hints = ["//features:experimental"], + compatible_with = ["//buildenv/target:non_prod"], + tags = [ + "alt_dep=//support/public:int", + "avoid_dep", + ], + visibility = [ + "//visibility:public", + ], + deps = [ + "//support:annotations", + ], +) + +crubit_cc_test( + name = "int_test", + srcs = ["int_test.cc"], + deps = [ + ":int", + "@fuzztest//:fuzztest_gtest_main", + "@googletest//:gmock", + ], +) + alias( name = "rs_char", actual = "//support/rs_std:char", diff --git a/support/rs_std/BUILD.gn b/support/rs_std/BUILD.gn index d385c68a0..00324f45a 100644 --- a/support/rs_std/BUILD.gn +++ b/support/rs_std/BUILD.gn @@ -65,6 +65,23 @@ source_set("dyn_erased_future_cc") { ] } +source_set("int") { + public_configs = [ "$crubit_src_dir:config" ] + if (defined(crubit_gn_configs_to_remove)) { + configs -= crubit_gn_configs_to_remove + } + if (defined(crubit_gn_support_configs_to_add)) { + configs += crubit_gn_support_configs_to_add + } + + public = [ + "int.h", + ] + deps = [ + "$crubit_src_dir/support:annotations", + ] +} + source_set("iterator_adapter") { public_configs = [ "$crubit_src_dir:config" ] if (defined(crubit_gn_configs_to_remove)) { @@ -330,6 +347,7 @@ group("rs_std_cpp") { ":char", ":dyn_callable", ":dyn_erased_future_cc", + ":int", ":iterator_adapter", ":lossy_formatter_for_bindings", ":option", diff --git a/support/rs_std/CMakeLists.txt b/support/rs_std/CMakeLists.txt index 88e029317..03f3916eb 100755 --- a/support/rs_std/CMakeLists.txt +++ b/support/rs_std/CMakeLists.txt @@ -49,6 +49,19 @@ target_link_libraries(crubit_support_rs_std_dyn_erased_future_cc INTERFACE crubit_support_rs_std_waker ) +add_library(crubit_support_rs_std_int INTERFACE +"int.h" +) +set_target_properties(crubit_support_rs_std_int PROPERTIES LINKER_LANGUAGE CXX) + +target_include_directories(crubit_support_rs_std_int INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/../.." +) + +target_link_libraries(crubit_support_rs_std_int INTERFACE +crubit_support_annotations +) + add_library(crubit_support_rs_std_iterator_adapter INTERFACE "iterator_adapter.h" ) diff --git a/support/rs_std/int.h b/support/rs_std/int.h new file mode 100644 index 000000000..d890b4286 --- /dev/null +++ b/support/rs_std/int.h @@ -0,0 +1,115 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// IWYU pragma: private, include "support/rs_std/int.h" + +#ifndef CRUBIT_SUPPORT_RS_STD_INT_H_ +#define CRUBIT_SUPPORT_RS_STD_INT_H_ + +#include +#include +#include + +#include "support/annotations.h" + +namespace rs_std { + +// `rs_std::usize` is a C++ representation of the `usize` type from Rust when +// used as a template argument inside template specializations (such as +// `rs_std::Option`). This prevents C++ explicit template specialization +// redefinition errors or overload collisions between `usize`, `u64`, and `u32`. +class CRUBIT_INTERNAL_RUST_TYPE("usize") CRUBIT_INTERNAL_SAME_ABI usize final { + public: + constexpr usize() noexcept = default; + + // Implicit constructing conversion from standard integer types. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr usize(::std::uintptr_t value) noexcept : value_(value) {} + + constexpr usize(const usize&) noexcept = default; + constexpr usize& operator=(const usize&) noexcept = default; + + // Implicit conversion back to `::std::uintptr_t` for seamless indexing and + // arithmetic in C++. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr operator ::std::uintptr_t() const noexcept { return value_; } + + friend constexpr bool operator==(usize lhs, usize rhs) noexcept = default; + friend constexpr std::strong_ordering operator<=>( + usize lhs, usize rhs) noexcept = default; + + friend constexpr usize operator+(usize lhs, usize rhs) noexcept { + return usize(lhs.value_ + rhs.value_); + } + friend constexpr usize operator-(usize lhs, usize rhs) noexcept { + return usize(lhs.value_ - rhs.value_); + } + constexpr usize& operator+=(usize rhs) noexcept { + value_ += rhs.value_; + return *this; + } + constexpr usize& operator-=(usize rhs) noexcept { + value_ -= rhs.value_; + return *this; + } + + private: + ::std::uintptr_t value_ = 0; +}; + +static_assert(sizeof(usize) == sizeof(::std::uintptr_t)); +static_assert(alignof(usize) == alignof(::std::uintptr_t)); +static_assert(::std::is_trivially_copyable_v); +static_assert(::std::is_trivially_destructible_v); + +// `rs_std::isize` is a C++ representation of the `isize` type from Rust when +// used as a template argument inside template specializations (such as +// `rs_std::Option`). This prevents C++ explicit template specialization +// redefinition errors or overload collisions between `isize`, `i64`, and `i32`. +class CRUBIT_INTERNAL_RUST_TYPE("isize") CRUBIT_INTERNAL_SAME_ABI isize final { + public: + constexpr isize() noexcept = default; + + // Implicit constructing conversion from standard integer types. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr isize(::std::intptr_t value) noexcept : value_(value) {} + + constexpr isize(const isize&) noexcept = default; + constexpr isize& operator=(const isize&) noexcept = default; + + // Implicit conversion back to `::std::intptr_t` for seamless arithmetic and + // conversion in C++. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr operator ::std::intptr_t() const noexcept { return value_; } + + friend constexpr bool operator==(isize lhs, isize rhs) noexcept = default; + friend constexpr auto operator<=>(isize lhs, isize rhs) noexcept = default; + + friend constexpr isize operator+(isize lhs, isize rhs) noexcept { + return isize(lhs.value_ + rhs.value_); + } + friend constexpr isize operator-(isize lhs, isize rhs) noexcept { + return isize(lhs.value_ - rhs.value_); + } + constexpr isize& operator+=(isize rhs) noexcept { + value_ += rhs.value_; + return *this; + } + constexpr isize& operator-=(isize rhs) noexcept { + value_ -= rhs.value_; + return *this; + } + + private: + ::std::intptr_t value_ = 0; +}; + +static_assert(sizeof(isize) == sizeof(::std::intptr_t)); +static_assert(alignof(isize) == alignof(::std::intptr_t)); +static_assert(::std::is_trivially_copyable_v); +static_assert(::std::is_trivially_destructible_v); + +} // namespace rs_std + +#endif // CRUBIT_SUPPORT_RS_STD_INT_H_ diff --git a/support/rs_std/int_test.cc b/support/rs_std/int_test.cc new file mode 100644 index 000000000..6d320a01b --- /dev/null +++ b/support/rs_std/int_test.cc @@ -0,0 +1,72 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "support/rs_std/int.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace { + +static_assert(std::is_nothrow_default_constructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_copyable_v); +static_assert(std::is_trivially_copy_constructible_v); +static_assert(std::is_trivially_copy_assignable_v); +static_assert(std::is_trivially_move_constructible_v); +static_assert(std::is_trivially_move_assignable_v); +static_assert(sizeof(rs_std::usize) == sizeof(std::uintptr_t)); +static_assert(alignof(rs_std::usize) == alignof(std::uintptr_t)); + +static_assert(std::is_nothrow_default_constructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_copyable_v); +static_assert(std::is_trivially_copy_constructible_v); +static_assert(std::is_trivially_copy_assignable_v); +static_assert(std::is_trivially_move_constructible_v); +static_assert(std::is_trivially_move_assignable_v); +static_assert(sizeof(rs_std::isize) == sizeof(std::intptr_t)); +static_assert(alignof(rs_std::isize) == alignof(std::intptr_t)); + +TEST(UsizeTest, DefaultConstructor) { + rs_std::usize value; + EXPECT_EQ(value, rs_std::usize(0)); +} + +TEST(UsizeTest, ImplicitConversions) { + rs_std::usize val = 42; + std::uintptr_t raw = val; + EXPECT_EQ(raw, 42); +} + +TEST(UsizeTest, ArithmeticAndComparisons) { + rs_std::usize a = 10; + rs_std::usize b = 20; + EXPECT_LT(a, b); + EXPECT_EQ(a + b, rs_std::usize(30)); + EXPECT_EQ(b - a, rs_std::usize(10)); +} + +TEST(IsizeTest, DefaultConstructor) { + rs_std::isize value; + EXPECT_EQ(value, rs_std::isize(0)); +} + +TEST(IsizeTest, ImplicitConversions) { + rs_std::isize val = -42; + std::intptr_t raw = val; + EXPECT_EQ(raw, -42); +} + +TEST(IsizeTest, ArithmeticAndComparisons) { + rs_std::isize a = -10; + rs_std::isize b = 20; + EXPECT_LT(a, b); + EXPECT_EQ(a + b, rs_std::isize(10)); + EXPECT_EQ(b - a, rs_std::isize(30)); +} + +} // namespace