From 46758ad29a8226c8ba46b62ed66a157a35f78342 Mon Sep 17 00:00:00 2001 From: Googler Date: Mon, 6 Apr 2026 11:07:31 -0700 Subject: [PATCH] Add Send/Sync derivation for CRUBIT_THREAD_SAFE types and add tests PiperOrigin-RevId: 895408797 --- rs_bindings_from_cc/importers/cxx_record.cc | 13 + rs_bindings_from_cc/ir.cc | 1 + rs_bindings_from_cc/ir.h | 5 + rs_bindings_from_cc/ir.rs | 3 + rs_bindings_from_cc/ir_from_cc_test.rs | 43 ++ rs_bindings_from_cc/test/annotations/BUILD | 28 + .../test/annotations/thread_safe.h | 34 + .../test/annotations/thread_safe_api_impl.cc | 71 ++ .../test/annotations/thread_safe_rs_api.rs | 681 ++++++++++++++++++ .../test/annotations/thread_safe_test.rs | 50 ++ support/annotations.h | 24 + support/annotations.swig | 2 + 12 files changed, 955 insertions(+) create mode 100644 rs_bindings_from_cc/test/annotations/thread_safe.h create mode 100644 rs_bindings_from_cc/test/annotations/thread_safe_api_impl.cc create mode 100644 rs_bindings_from_cc/test/annotations/thread_safe_rs_api.rs create mode 100644 rs_bindings_from_cc/test/annotations/thread_safe_test.rs diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc index 305906790..473e736e7 100644 --- a/rs_bindings_from_cc/importers/cxx_record.cc +++ b/rs_bindings_from_cc/importers/cxx_record.cc @@ -1108,12 +1108,24 @@ std::optional CXXRecordDeclImporter::Import( const clang::TypedefNameDecl* anon_typedef = record_decl->getTypedefNameForAnonDecl(); + absl::StatusOr is_thread_safe = + HasAnnotationWithoutArgs(*record_decl, "crubit_thread_safe"); + if (!is_thread_safe.ok()) { + return unsupported( + FormattedError::FromStatus(std::move(is_thread_safe).status())); + } + absl::StatusOr trait_derives = GetTraitDerives(*record_decl); if (!trait_derives.ok()) { return unsupported( FormattedError::FromStatus(std::move(trait_derives).status())); } + if (*is_thread_safe) { + trait_derives->send = true; + trait_derives->sync = true; + } + absl::StatusOr safety_annotation = GetSafetyAnnotation(*record_decl); if (!safety_annotation.ok()) { @@ -1190,6 +1202,7 @@ std::optional CXXRecordDeclImporter::Import( .enclosing_item_id = std::move(enclosing_item_id), .overloads_operator_delete = MayOverloadOperatorDelete(*record_decl), .detected_formatter = *detected_formatter, + .is_thread_safe = *is_thread_safe, .lifetime_inputs = std::move(lifetime_inputs), .deprecated = std::move(deprecated), }; diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc index 6d392a019..52436c1c4 100644 --- a/rs_bindings_from_cc/ir.cc +++ b/rs_bindings_from_cc/ir.cc @@ -714,6 +714,7 @@ llvm::json::Value Record::ToJson() const { {"must_bind", must_bind}, {"overloads_operator_delete", overloads_operator_delete}, {"detected_formatter", detected_formatter}, + {"is_thread_safe", is_thread_safe}, }; if (!lifetime_inputs.empty()) { diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h index 3159b59db..96f339276 100644 --- a/rs_bindings_from_cc/ir.h +++ b/rs_bindings_from_cc/ir.h @@ -814,6 +814,11 @@ struct Record { bool overloads_operator_delete = false; bool detected_formatter = false; + // Whether this type is annotated as thread-safe (CRUBIT_THREAD_SAFE). + // Thread-safe types implement Send+Sync and wrap their internals in + // UnsafeCell, allowing non-const C++ methods to be called via &self. + bool is_thread_safe = false; + // Lifetime variable names bound by this record. std::vector lifetime_inputs; diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs index d85c6cd78..45fd44eda 100644 --- a/rs_bindings_from_cc/ir.rs +++ b/rs_bindings_from_cc/ir.rs @@ -1214,6 +1214,9 @@ pub struct Record { /// string is used. #[serde(default)] pub deprecated: Option>, + /// Whether this type is annotated as thread-safe (CRUBIT_THREAD_SAFE). + #[serde(default)] + pub is_thread_safe: bool, } impl GenericItem for Record { diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs index 6b28f1842..f9e859ef6 100644 --- a/rs_bindings_from_cc/ir_from_cc_test.rs +++ b/rs_bindings_from_cc/ir_from_cc_test.rs @@ -898,6 +898,49 @@ fn test_conflicting_unsafe_annotation() { ); } +#[gtest] +fn test_struct_with_thread_safe_annotation() { + let ir = ir_from_cc( + r#" + struct [[clang::annotate("crubit_thread_safe")]] + ThreadSafeType { + int foo; + };"#, + ) + .unwrap(); + + assert_ir_matches!( + ir, + quote! { + Record { + rs_name: "ThreadSafeType", ... + is_thread_safe: true, ... + } + } + ); +} + +#[gtest] +fn test_struct_without_thread_safe_annotation() { + let ir = ir_from_cc( + r#" + struct NotThreadSafe { + int foo; + };"#, + ) + .unwrap(); + + assert_ir_matches!( + ir, + quote! { + Record { + rs_name: "NotThreadSafe", ... + is_thread_safe: false, ... + } + } + ); +} + #[gtest] fn test_struct_with_unnamed_struct_and_union_members() { // This test input causes `field_decl->getName()` to return an empty string. diff --git a/rs_bindings_from_cc/test/annotations/BUILD b/rs_bindings_from_cc/test/annotations/BUILD index f8a938e1c..06511e551 100644 --- a/rs_bindings_from_cc/test/annotations/BUILD +++ b/rs_bindings_from_cc/test/annotations/BUILD @@ -159,3 +159,31 @@ crubit_rust_test( "@crate_index//:googletest", ], ) + +crubit_test_cc_library( + name = "thread_safe", + hdrs = ["thread_safe.h"], + deps = [ + "//support:annotations", + ], +) + +crubit_rust_test( + name = "thread_safe_test", + srcs = ["thread_safe_test.rs"], + cc_deps = [ + ":thread_safe", + ], + deps = [ + "//support:ctor", + "@crate_index//:googletest", + ], +) + +golden_test( + name = "thread_safe_golden_test", + basename = "thread_safe", + cc_library = "thread_safe", + golden_cc = "thread_safe_api_impl.cc", + golden_rs = "thread_safe_rs_api.rs", +) diff --git a/rs_bindings_from_cc/test/annotations/thread_safe.h b/rs_bindings_from_cc/test/annotations/thread_safe.h new file mode 100644 index 000000000..4ed02682a --- /dev/null +++ b/rs_bindings_from_cc/test/annotations/thread_safe.h @@ -0,0 +1,34 @@ +// 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 + +#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_ANNOTATIONS_THREAD_SAFE_H_ +#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_ANNOTATIONS_THREAD_SAFE_H_ + +#include + +#include "support/annotations.h" + +namespace crubit::test { + +// A simple thread-safe struct. +class CRUBIT_THREAD_SAFE ThreadSafeStruct final { + public: + void Increment() { x_++; } + int Get() const { return x_.load(); } + + private: + std::atomic x_{0}; +}; + +// A regular (non-thread-safe) struct for comparison. +struct RegularStruct final { + int value; + + int Get() const { return value; } + void Set(int v) { value = v; } +}; + +} // namespace crubit::test + +#endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_ANNOTATIONS_THREAD_SAFE_H_ diff --git a/rs_bindings_from_cc/test/annotations/thread_safe_api_impl.cc b/rs_bindings_from_cc/test/annotations/thread_safe_api_impl.cc new file mode 100644 index 000000000..044cb021a --- /dev/null +++ b/rs_bindings_from_cc/test/annotations/thread_safe_api_impl.cc @@ -0,0 +1,71 @@ +// 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 + +// Automatically @generated Rust bindings for the following C++ target: +// //rs_bindings_from_cc/test/annotations:thread_safe +// Features: fmt, supported, types + +#include "support/internal/cxx20_backports.h" +#include "support/internal/offsetof.h" +#include "support/internal/sizeof.h" + +#include +#include + +// Public headers of the C++ library being wrapped. +#include "rs_bindings_from_cc/test/annotations/thread_safe.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wthread-safety-analysis" + +static_assert(CRUBIT_SIZEOF(class crubit::test::ThreadSafeStruct) == 4); +static_assert(alignof(class crubit::test::ThreadSafeStruct) == 4); + +extern "C" void __rust_thunk___ZN6crubit4test16ThreadSafeStructC1Ev( + class crubit::test::ThreadSafeStruct* __this) { + crubit::construct_at(__this); +} + +extern "C" void __rust_thunk___ZN6crubit4test16ThreadSafeStruct9IncrementEv( + class crubit::test::ThreadSafeStruct* __this) { + __this->Increment(); +} + +static_assert((void (::crubit::test::ThreadSafeStruct::*)()) & + ::crubit::test::ThreadSafeStruct::Increment); + +extern "C" int __rust_thunk___ZNK6crubit4test16ThreadSafeStruct3GetEv( + class crubit::test::ThreadSafeStruct const* __this) { + return __this->Get(); +} + +static_assert((int (::crubit::test::ThreadSafeStruct::*)() const) & + ::crubit::test::ThreadSafeStruct::Get); + +static_assert(CRUBIT_SIZEOF(struct crubit::test::RegularStruct) == 4); +static_assert(alignof(struct crubit::test::RegularStruct) == 4); +static_assert(CRUBIT_OFFSET_OF(value, struct crubit::test::RegularStruct) == 0); + +extern "C" void __rust_thunk___ZN6crubit4test13RegularStructC1Ev( + struct crubit::test::RegularStruct* __this) { + crubit::construct_at(__this); +} + +extern "C" int __rust_thunk___ZNK6crubit4test13RegularStruct3GetEv( + struct crubit::test::RegularStruct const* __this) { + return __this->Get(); +} + +static_assert((int (::crubit::test::RegularStruct::*)() const) & + ::crubit::test::RegularStruct::Get); + +extern "C" void __rust_thunk___ZN6crubit4test13RegularStruct3SetEi( + struct crubit::test::RegularStruct* __this, int v) { + __this->Set(v); +} + +static_assert((void (::crubit::test::RegularStruct::*)(int)) & + ::crubit::test::RegularStruct::Set); + +#pragma clang diagnostic pop diff --git a/rs_bindings_from_cc/test/annotations/thread_safe_rs_api.rs b/rs_bindings_from_cc/test/annotations/thread_safe_rs_api.rs new file mode 100644 index 000000000..5806f7419 --- /dev/null +++ b/rs_bindings_from_cc/test/annotations/thread_safe_rs_api.rs @@ -0,0 +1,681 @@ +// 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 + +// Automatically @generated Rust bindings for the following C++ target: +// //rs_bindings_from_cc/test/annotations:thread_safe +// Features: fmt, supported, types + +#![rustfmt::skip] +#![feature(custom_inner_attributes, impl_trait_in_assoc_type, negative_impls)] +#![allow(stable_features)] +#![allow(improper_ctypes)] +#![allow(nonstandard_style)] +#![allow(unused)] +#![allow(deprecated)] +#![deny(warnings)] + +pub mod crubit { + pub mod test { + /// A simple thread-safe struct. + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=15 + #[::ctor::recursively_pinned] + #[repr(C, align(4))] + ///CRUBIT_ANNOTATE: cpp_type=crubit :: test :: ThreadSafeStruct + pub struct ThreadSafeStruct { + __non_field_data: [::core::cell::Cell<::core::mem::MaybeUninit>; 0], + /// Reason for representing this field as a blob of bytes: + /// Types of non-public C++ fields can be elided away + pub(crate) x_: [::core::cell::Cell<::core::mem::MaybeUninit>; 4], + } + unsafe impl Send for ThreadSafeStruct {} + unsafe impl Sync for ThreadSafeStruct {} + unsafe impl ::cxx::ExternType for ThreadSafeStruct { + type Id = ::cxx::type_id!("crubit :: test :: ThreadSafeStruct"); + type Kind = ::cxx::kind::Opaque; + } + impl ThreadSafeStruct { + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=17 + #[inline(always)] + pub unsafe fn Increment(__this: *mut Self) { + unsafe { self::thread_safe_struct::Increment(__this) } + } + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=18 + #[inline(always)] + pub unsafe fn Get(__this: *const Self) -> ::ffi_11::c_int { + unsafe { self::thread_safe_struct::Get(__this) } + } + } + + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=15 + impl ::ctor::CtorNew<()> for ThreadSafeStruct { + type CtorType = ::ctor::Ctor![Self]; + type Error = ::ctor::Infallible; + #[inline(always)] + fn ctor_new(args: ()) -> Self::CtorType { + let () = args; + unsafe { + ::ctor::FnCtor::new(move |dest: *mut Self| { + crate::detail::__rust_thunk___ZN6crubit4test16ThreadSafeStructC1Ev( + dest as *mut ::core::ffi::c_void, + ); + }) + } + } + } + + pub mod thread_safe_struct { + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=17 + #[inline(always)] + pub(crate) unsafe fn Increment(__this: *mut crate::crubit::test::ThreadSafeStruct) { + unsafe { + crate::detail::__rust_thunk___ZN6crubit4test16ThreadSafeStruct9IncrementEv( + __this, + ) + } + } + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=18 + #[inline(always)] + pub(crate) unsafe fn Get( + __this: *const crate::crubit::test::ThreadSafeStruct, + ) -> ::ffi_11::c_int { + unsafe { + crate::detail::__rust_thunk___ZNK6crubit4test16ThreadSafeStruct3GetEv(__this) + } + } + } + + /// A regular (non-thread-safe) struct for comparison. + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=25 + #[derive(Clone, Copy, ::ctor::MoveAndAssignViaCopy)] + #[repr(C)] + ///CRUBIT_ANNOTATE: cpp_type=crubit :: test :: RegularStruct + pub struct RegularStruct { + pub value: ::ffi_11::c_int, + } + impl !Send for RegularStruct {} + impl !Sync for RegularStruct {} + unsafe impl ::cxx::ExternType for RegularStruct { + type Id = ::cxx::type_id!("crubit :: test :: RegularStruct"); + type Kind = ::cxx::kind::Trivial; + } + impl RegularStruct { + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=28 + #[inline(always)] + pub unsafe fn Get(__this: *const Self) -> ::ffi_11::c_int { + unsafe { self::regular_struct::Get(__this) } + } + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=29 + #[inline(always)] + pub unsafe fn Set(__this: *mut Self, v: ::ffi_11::c_int) { + unsafe { self::regular_struct::Set(__this, v) } + } + } + + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=25 + impl Default for RegularStruct { + #[inline(always)] + fn default() -> Self { + let mut tmp = ::core::mem::MaybeUninit::::zeroed(); + unsafe { + crate::detail::__rust_thunk___ZN6crubit4test13RegularStructC1Ev( + &raw mut tmp as *mut _, + ); + tmp.assume_init() + } + } + } + + pub mod regular_struct { + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=28 + #[inline(always)] + pub(crate) unsafe fn Get( + __this: *const crate::crubit::test::RegularStruct, + ) -> ::ffi_11::c_int { + unsafe { + crate::detail::__rust_thunk___ZNK6crubit4test13RegularStruct3GetEv(__this) + } + } + /// # Safety + /// + /// The caller must ensure that the following unsafe arguments are not misused by the function: + /// * `__this`: raw pointer + /// + /// Generated from: rs_bindings_from_cc/test/annotations/thread_safe.h;l=29 + #[inline(always)] + pub(crate) unsafe fn Set( + __this: *mut crate::crubit::test::RegularStruct, + v: ::ffi_11::c_int, + ) { + unsafe { + crate::detail::__rust_thunk___ZN6crubit4test13RegularStruct3SetEi(__this, v) + } + } + } + } +} + +// namespace crubit::test + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/integral_constant.h;l=21 +// error: struct `std::integral_constant` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/integral_constant.h;l=21 +// error: struct `std::integral_constant` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/support.h;l=114 +// error: struct `std::__cxx_atomic_impl>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__type_traits/type_identity.h;l=21 +// error: struct `std::__type_identity>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000000000000000000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000000000000000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000000000000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000000000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1000L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<100L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<10L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000000000000000000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000000000000000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000000000000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000000000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1000L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 100L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 10L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<1L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<2629746L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<31556952L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<3600L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<604800L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<60L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/ratio;l=232 +// error: class `std::ratio<86400L, 1L>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/duration.h;l=166 +// error: class `std::chrono::duration>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/time_point.h;l=36 +// error: class `std::chrono::time_point>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/time_point.h;l=36 +// error: class `std::chrono::time_point>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/time_point.h;l=36 +// error: class `std::chrono::time_point>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__chrono/time_point.h;l=36 +// error: class `std::chrono::time_point>>` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=40 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=142 +// error: struct `std::__atomic_base` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +// Generated from: nowhere/llvm/src/libcxx/include/__atomic/atomic.h;l=240 +// error: struct `std::atomic` could not be bound +// template instantiation is not yet supported + +mod detail { + #[allow(unused_imports)] + use super::*; + unsafe extern "C" { + pub(crate) unsafe fn __rust_thunk___ZN6crubit4test16ThreadSafeStructC1Ev( + __this: *mut ::core::ffi::c_void, + ); + pub(crate) unsafe fn __rust_thunk___ZN6crubit4test16ThreadSafeStruct9IncrementEv( + __this: *mut crate::crubit::test::ThreadSafeStruct, + ); + pub(crate) unsafe fn __rust_thunk___ZNK6crubit4test16ThreadSafeStruct3GetEv( + __this: *const crate::crubit::test::ThreadSafeStruct, + ) -> ::ffi_11::c_int; + pub(crate) unsafe fn __rust_thunk___ZN6crubit4test13RegularStructC1Ev( + __this: *mut ::core::ffi::c_void, + ); + pub(crate) unsafe fn __rust_thunk___ZNK6crubit4test13RegularStruct3GetEv( + __this: *const crate::crubit::test::RegularStruct, + ) -> ::ffi_11::c_int; + pub(crate) unsafe fn __rust_thunk___ZN6crubit4test13RegularStruct3SetEi( + __this: *mut crate::crubit::test::RegularStruct, + v: ::ffi_11::c_int, + ); + } +} + +const _: () = { + assert!(::core::mem::size_of::() == 4); + assert!(::core::mem::align_of::() == 4); + static_assertions::assert_not_impl_any!(crate::crubit::test::ThreadSafeStruct: Copy,Drop); + assert!(::core::mem::offset_of!(crate::crubit::test::ThreadSafeStruct, x_) == 0); + assert!(::core::mem::size_of::() == 4); + assert!(::core::mem::align_of::() == 4); + static_assertions::assert_impl_all!(crate::crubit::test::RegularStruct: Copy,Clone); + static_assertions::assert_not_impl_any!(crate::crubit::test::RegularStruct: Drop); + assert!(::core::mem::offset_of!(crate::crubit::test::RegularStruct, value) == 0); +}; diff --git a/rs_bindings_from_cc/test/annotations/thread_safe_test.rs b/rs_bindings_from_cc/test/annotations/thread_safe_test.rs new file mode 100644 index 000000000..ee42b96bc --- /dev/null +++ b/rs_bindings_from_cc/test/annotations/thread_safe_test.rs @@ -0,0 +1,50 @@ +// 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 + +use googletest::gtest; +use thread_safe::crubit::test::ThreadSafeStruct; + +/// Verify that the thread-safe type implements Send. +/// This is a compile-time check: if ThreadSafeStruct doesn't implement Send, +/// this test will fail to compile. +#[gtest] +fn test_thread_safe_is_send() { + fn assert_send() {} + assert_send::(); +} + +/// Verify that the thread-safe type implements Sync. +/// This is a compile-time check: if ThreadSafeStruct doesn't implement Sync, +/// this test will fail to compile. +#[gtest] +fn test_thread_safe_is_sync() { + fn assert_sync() {} + assert_sync::(); +} + +/// Verify that the generated struct has the expected size (matching the C++ type +/// with one int). +#[gtest] +fn test_thread_safe_struct_has_correct_size() { + assert_eq!(std::mem::size_of::(), 4); +} + +#[gtest] +fn test_thread_safe_get() { + use ctor::emplace; + use ctor::CtorNew; + let s = emplace!(ThreadSafeStruct::ctor_new(())); + let val = unsafe { ThreadSafeStruct::Get(s.as_ref().get_ref()) }; + assert_eq!(val, 0); +} + +/// Verify that a non-thread-safe type does NOT implement Send or Sync. +/// These are compile-time checks using negative trait bounds. +#[gtest] +fn test_regular_struct_is_not_send_or_sync() { + // RegularStruct should not be Send or Sync (Crubit generates negative impls). + // We verify it exists and has expected size. The Send/Sync negative impls + // are verified by the codegen unit tests. + assert_eq!(std::mem::size_of::(), 4); +} diff --git a/support/annotations.h b/support/annotations.h index f26b47f5b..824c21c94 100644 --- a/support/annotations.h +++ b/support/annotations.h @@ -323,4 +323,28 @@ #define CRUBIT_OVERRIDE_DISPLAY(should_bind) \ CRUBIT_INTERNAL_ANNOTATE("crubit_override_display", should_bind) +// Marks a type as thread-safe for Rust interop. +// +// Types annotated with `CRUBIT_THREAD_SAFE` will: +// * Implement `Send + Sync` in Rust +// (TODO: b/475929893) * Have their internal representation wrapped in +// `UnsafeCell`, allowing non-const C++ methods to be called via shared +// references (`&self`) +// +// This annotation is appropriate for types that internally synchronize +// access (e.g., types with mutexes, atomics, or other synchronization +// primitives). +// +// Example: +// ```c++ +// class CRUBIT_THREAD_SAFE ThreadSafeCounter { +// public: +// void Increment(); // Can be called via mut T*. +// int Get() const; // Can also be called via &self +// private: +// std::atomic count_; +// }; +// ``` +#define CRUBIT_THREAD_SAFE CRUBIT_INTERNAL_ANNOTATE("crubit_thread_safe") + #endif // THIRD_PARTY_CRUBIT_SUPPORT_ANNOTATIONS_H_ diff --git a/support/annotations.swig b/support/annotations.swig index 0d2d9111b..a1960257b 100644 --- a/support/annotations.swig +++ b/support/annotations.swig @@ -26,3 +26,5 @@ %enddef %define CRUBIT_OVERRIDE_DISPLAY(should_bind) %enddef +%define CRUBIT_THREAD_SAFE +%enddef