diff --git a/cargo/rs_bindings_from_cc/generate_bindings/generate_bindings/Cargo.toml b/cargo/rs_bindings_from_cc/generate_bindings/generate_bindings/Cargo.toml index 0e24f53ea..edb3e969d 100644 --- a/cargo/rs_bindings_from_cc/generate_bindings/generate_bindings/Cargo.toml +++ b/cargo/rs_bindings_from_cc/generate_bindings/generate_bindings/Cargo.toml @@ -25,6 +25,7 @@ generate_function = { path = "../../../../cargo/rs_bindings_from_cc/generate_bin generate_function_thunk = { path = "../../../../cargo/rs_bindings_from_cc/generate_bindings/generate_function_thunk", package = "rs_bindings_from_cc_generate_function_thunk"} generate_struct_and_union = { path = "../../../../cargo/rs_bindings_from_cc/generate_bindings/generate_struct_and_union", package = "rs_bindings_from_cc_generate_struct_and_union"} has_bindings = { path = "../../../../cargo/rs_bindings_from_cc/generate_bindings/has_bindings", package = "rs_bindings_from_cc_has_bindings"} +lifetime_defaults_transform = { path = "../../../../cargo/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform", package = "rs_bindings_from_cc_lifetime_defaults_transform"} rs_type_kind = { path = "../../../../cargo/rs_bindings_from_cc/generate_bindings/rs_type_kind", package = "rs_bindings_from_cc_rs_type_kind"} arc_anyhow = { path = "../../../../cargo/common/arc_anyhow"} code_gen_utils = { path = "../../../../cargo/common/code_gen_utils"} diff --git a/rs_bindings_from_cc/generate_bindings/BUILD b/rs_bindings_from_cc/generate_bindings/BUILD index 6a6f6f66c..8181bc72d 100644 --- a/rs_bindings_from_cc/generate_bindings/BUILD +++ b/rs_bindings_from_cc/generate_bindings/BUILD @@ -43,6 +43,7 @@ rust_library( ":generate_function_thunk", ":generate_struct_and_union", ":has_bindings", + ":lifetime_defaults_transform", ":rs_type_kind", "//common:arc_anyhow", "//common:code_gen_utils", diff --git a/rs_bindings_from_cc/generate_bindings/database/db.rs b/rs_bindings_from_cc/generate_bindings/database/db.rs index 31b532f9e..f428e3032 100644 --- a/rs_bindings_from_cc/generate_bindings/database/db.rs +++ b/rs_bindings_from_cc/generate_bindings/database/db.rs @@ -24,6 +24,7 @@ pub struct CodegenFunctions { pub generate_enum: fn(&BindingsGenerator, Rc) -> Result, pub generate_item: fn(&BindingsGenerator, ir::Item) -> Result, pub generate_record: fn(&BindingsGenerator, Rc) -> Result, + pub decl_lifetime_arity: fn(&BindingsGenerator, ir::ItemId) -> Result, } memoized::query_group! { diff --git a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs index 4254e805e..c9a6c3a33 100644 --- a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs +++ b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs @@ -7,8 +7,8 @@ use crate::code_snippet::{Feature, Visibility}; use crate::BindingsGenerator; use arc_anyhow::{anyhow, Result}; -use code_gen_utils::make_rs_ident; use code_gen_utils::NamespaceQualifier; +use code_gen_utils::{make_rs_ident, make_rs_lifetime_ident}; use crubit_feature::CrubitFeature; use error_report::{bail, ensure}; use flagset::FlagSet; @@ -487,6 +487,7 @@ pub enum RsTypeKind { /// If this record is an instantiation of a `UniformReprTemplateType`, this will be set. uniform_repr_template_type: Option>, owned_ptr_type: Option>, + lifetimes: Vec, }, Enum { enum_: Rc, @@ -845,6 +846,7 @@ impl RsTypeKind { owned_ptr_type: record.owned_ptr_type.clone(), record, crate_path, + lifetimes: lifetimes.to_vec(), }) } @@ -1716,6 +1718,14 @@ impl PrimitiveName { } } +/// Detect whether a `Record` is the projection of `std::string_view`, as we special-case this. +fn record_is_raw_string_view(record: &ir::Record) -> bool { + matches!( + record.template_specialization, + Some(TemplateSpecialization { kind: TemplateSpecializationKind::StdStringView, .. }) + ) && record.rs_name.identifier.as_ref() == "raw_string_view" +} + impl RsTypeKind { pub fn to_token_stream<'a>( &self, @@ -1779,12 +1789,28 @@ impl RsTypeKind { crate_path, uniform_repr_template_type, owned_ptr_type: _, + lifetimes, } => { if let Some(generic_monomorphization) = uniform_repr_template_type { return generic_monomorphization.to_token_stream(&db); } - let ident = make_rs_ident(record.rs_name.identifier.as_ref()); - quote! { #crate_path #ident } + let arity = (db.codegen_functions().decl_lifetime_arity)(&*db, record.id()).expect("RsTypeKind::to_token_stream: can't determine lifetime arity"); + if arity == 0 || lifetimes.len() == arity || record_is_raw_string_view(record) { + // Use the safe projection. + let lts = if lifetimes.is_empty() { + quote! {} + } else { + quote! { <#( #lifetimes ),* > } + }; + let ident = make_rs_ident(record.rs_name.identifier.as_ref()); + quote! { #crate_path #ident #lts } + } else { + // Until we can get unsafe binders, the unsafe projection of a type with + // lifetime parameters is that type instantiated at all 'static. + let statics = std::iter::repeat_n(make_rs_lifetime_ident("static"), arity); + let ident = make_rs_ident(record.rs_name.identifier.as_ref()); + quote! { #crate_path #ident <#( #statics ),* > } + } } RsTypeKind::Enum { enum_, crate_path } => { let ident = make_rs_ident(&enum_.rs_name.identifier); diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs index fcc87af7d..b6cbaa1c8 100644 --- a/rs_bindings_from_cc/generate_bindings/lib.rs +++ b/rs_bindings_from_cc/generate_bindings/lib.rs @@ -389,6 +389,7 @@ pub fn new_database<'db>( generate_enum: generate_enum::generate_enum, generate_item, generate_record: generate_struct_and_union::generate_record, + decl_lifetime_arity: lifetime_defaults_transform::decl_lifetime_arity, }, rs_type_kind_safety, record_field_safety, diff --git a/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs b/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs index 4d26b1b88..be692dd3d 100644 --- a/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs +++ b/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs @@ -28,7 +28,7 @@ pub struct BindingContext { /// All variable names we've seen. pub names: HashSet>, } -fn lifetime_arity(_db: &BindingsGenerator, ty: &CcType) -> Result { +fn lifetime_arity(db: &BindingsGenerator, ty: &CcType) -> Result { // TODO(b/454627672): Support other types. match &ty.variant { CcTypeVariant::Pointer(_) => Ok(1), @@ -36,11 +36,16 @@ fn lifetime_arity(_db: &BindingsGenerator, ty: &CcType) -> Result { CcTypeVariant::FuncPointer { .. } => { bail!("TODO(b/454627672): function pointer returns are unsupported") } - CcTypeVariant::Decl(_id) => bail!("TODO(b/454627672): decl returns are unsupported"), + CcTypeVariant::Decl(id) => decl_lifetime_arity(db, *id), CcTypeVariant::Error(msg) => bail!("encountered error type: {:?}", msg), } } +fn record_lifetime_arity(db: &BindingsGenerator, rc: &Record) -> Result { + // TODO(zarko): Handle the effects of [[lifetimebound]] et al on arity. + Ok(rc.lifetime_inputs.len()) +} + fn decl_lifetime_arity_impl( db: &BindingsGenerator, item_id: ir::ItemId, @@ -63,7 +68,38 @@ fn decl_lifetime_arity_impl( { Ok(1) } - _ => bail!("Unexpected item found in type position: {:?}", item.cc_name_as_str()), + Item::TypeAlias(_ta) => { + bail!("Type aliases unhandled for lifetimes: {:?}", item.cc_name_as_str()) + } + Item::Record(rc) => record_lifetime_arity(db, rc), + Item::IncompleteRecord(_) => { + bail!("Incomplete records unhandled for lifetimes: {:?}", item.cc_name_as_str()) + } + Item::Enum(_ec) => bail!("Enums unhandled for lifetimes: {:?}", item.cc_name_as_str()), + Item::ExistingRustType(_ec) => { + bail!("Existing Rust types unhandled for lifetimes: {:?}", item.cc_name_as_str()) + } + Item::Func(_) => { + bail!("Unexpected function found in type position: {:?}", item.cc_name_as_str()) + } + Item::Comment(_) => { + bail!("Unexpected comment found in type position: {:?}", item.cc_name_as_str()) + } + Item::Namespace(_) => { + bail!("Unexpected namespace found in type position: {:?}", item.cc_name_as_str()) + } + Item::UseMod(_) => { + bail!("Unexpected UseMod found in type position: {:?}", item.cc_name_as_str()) + } + Item::GlobalVar(_) => { + bail!("Unexpected global var found in type position: {:?}", item.cc_name_as_str()) + } + Item::Constant(_) => { + bail!("Unexpected const found in type position: {:?}", item.cc_name_as_str()) + } + Item::UnsupportedItem(_) => { + bail!("Unexpected unsupported item found in type position: {:?}", item.cc_name_as_str()) + } } } @@ -314,6 +350,9 @@ impl<'a, 'db> LifetimeDefaults<'a, 'db> { .get_or_push_new_binding(l, |name| new_bindings.push(name.clone())) }) .collect(); + if new_ty.explicit_lifetimes.iter().any(|l| l.as_ref() == "unknown") { + new_ty.explicit_lifetimes.clear(); + } return Ok(new_ty); } // If there is no viable inferred lifetime, there is nothing to do. diff --git a/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform_test.rs b/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform_test.rs index 251fb593b..c38149af6 100644 --- a/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform_test.rs +++ b/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform_test.rs @@ -285,8 +285,30 @@ fn test_lifetimebound_param_with_decl_type() -> Result<()> { S f(S i1 [[clang::lifetimebound]]); "#), )?; - let dir = lifetime_defaults_transform_ir(&ir); - assert!(dir.is_err()); + let dir = lifetime_defaults_transform_ir(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: "f", + rs_name: "f", ... + return_type: CcType { ... explicit_lifetimes: [] ... }, ... + ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: [] ... }, + identifier: "i1", ... + ... + clang_lifetimebound: true, + ... + } + ], + ... + lifetime_inputs: [], + ... + } + } + ); Ok(()) } @@ -909,7 +931,7 @@ fn test_struct_binds_lifetime_param() -> Result<()> { return_type: CcType { ... explicit_lifetimes: ["a"] ... }, ... lifetime_params: [], ... - lifetime_inputs: ["__this"], + lifetime_inputs: ["__this", "__this_0"], ... } } @@ -947,7 +969,7 @@ fn test_struct_shadows_unknown_lifetime_param() -> Result<()> { return_type: CcType { ... explicit_lifetimes: ["unknown_0"] ... }, ... lifetime_params: [], ... - lifetime_inputs: ["__this"], + lifetime_inputs: ["__this", "__this_0"], ... } } @@ -985,7 +1007,7 @@ fn test_struct_does_not_shadow_unrelated_lifetime_param() -> Result<()> { return_type: CcType { ... explicit_lifetimes: ["a"] ... }, ... lifetime_params: [], ... - lifetime_inputs: ["__this", "a"], + lifetime_inputs: ["__this", "__this_0", "a"], ... } } @@ -1023,7 +1045,7 @@ fn test_struct_renames_shadowed_lifetime_param_in_function() -> Result<()> { return_type: CcType { ... explicit_lifetimes: ["a_0"] ... }, ... lifetime_params: [], ... - lifetime_inputs: ["a_0", "__this"], + lifetime_inputs: ["a_0", "__this", "__this_0"], ... } } diff --git a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.cc b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.cc index 8093ec4b9..4c04d2adf 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.cc +++ b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.cc @@ -3,3 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h" + +SV sv_ident(SV s) { return s; } +SV sv_make_raw() { return SV{}; } diff --git a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h index 3dbada0bc..9cb958aa3 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h +++ b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h @@ -9,4 +9,10 @@ struct LIFETIME_PARAMS("a") SV {}; +// TODO(zarko): We should mark 'unknowns (or equivalent) as unsafe. +SV sv_ident(SV s); +SV $unknown sv_ident_unknown(SV $unknown s); +SV sv_ident_unknown_elided(SV $unknown s); +SV sv_make_raw(); + #endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_ASSUME_LIFETIMES_SIMPLE_STRING_VIEW_H_ diff --git a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_api_impl.cc b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_api_impl.cc index 9f64153c2..92d74d52d 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_api_impl.cc +++ b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_api_impl.cc @@ -26,4 +26,31 @@ extern "C" void __rust_thunk___ZN2SVC1Ev(struct SV* __this) { crubit::construct_at(__this); } +extern "C" void __rust_thunk___Z8sv_ident2SV(struct SV* __return, + struct SV* s) { + new (__return) auto(sv_ident(std::move(*s))); +} + +static_assert((struct SV (*)(struct SV)) & ::sv_ident); + +extern "C" void __rust_thunk___Z16sv_ident_unknown2SV(struct SV* __return, + struct SV* s) { + new (__return) auto(sv_ident_unknown(std::move(*s))); +} + +static_assert((struct SV (*)(struct SV)) & ::sv_ident_unknown); + +extern "C" void __rust_thunk___Z23sv_ident_unknown_elided2SV( + struct SV* __return, struct SV* s) { + new (__return) auto(sv_ident_unknown_elided(std::move(*s))); +} + +static_assert((struct SV (*)(struct SV)) & ::sv_ident_unknown_elided); + +extern "C" void __rust_thunk___Z11sv_make_rawv(struct SV* __return) { + new (__return) auto(sv_make_raw()); +} + +static_assert((struct SV (*)()) & ::sv_make_raw); + #pragma clang diagnostic pop diff --git a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_rs_api.rs b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_rs_api.rs index ca0b3ff40..77f887a1c 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_rs_api.rs +++ b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_rs_api.rs @@ -42,17 +42,83 @@ impl<'a> Default for SV<'a> { } } +/// TODO(zarko): We should mark 'unknowns (or equivalent) as unsafe. +/// +/// Generated from: rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h;l=13 +#[inline(always)] +pub fn sv_ident<'s>(mut s: crate::SV<'s>) -> crate::SV<'s> { + unsafe { + let mut __return = ::core::mem::MaybeUninit::>::uninit(); + crate::detail::__rust_thunk___Z8sv_ident2SV( + &raw mut __return as *mut ::core::ffi::c_void, + &mut s, + ); + __return.assume_init() + } +} + +/// Generated from: rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h;l=14 +#[inline(always)] +pub fn sv_ident_unknown(mut s: crate::SV<'static>) -> crate::SV<'static> { + unsafe { + let mut __return = ::core::mem::MaybeUninit::>::uninit(); + crate::detail::__rust_thunk___Z16sv_ident_unknown2SV( + &raw mut __return as *mut ::core::ffi::c_void, + &mut s, + ); + __return.assume_init() + } +} + +/// Generated from: rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h;l=15 +#[inline(always)] +pub fn sv_ident_unknown_elided(mut s: crate::SV<'static>) -> crate::SV<'static> { + unsafe { + let mut __return = ::core::mem::MaybeUninit::>::uninit(); + crate::detail::__rust_thunk___Z23sv_ident_unknown_elided2SV( + &raw mut __return as *mut ::core::ffi::c_void, + &mut s, + ); + __return.assume_init() + } +} + +/// Generated from: rs_bindings_from_cc/test/assume_lifetimes/simple_string_view.h;l=16 +#[inline(always)] +pub fn sv_make_raw() -> crate::SV<'static> { + unsafe { + let mut __return = ::core::mem::MaybeUninit::>::uninit(); + crate::detail::__rust_thunk___Z11sv_make_rawv( + &raw mut __return as *mut ::core::ffi::c_void, + ); + __return.assume_init() + } +} + mod detail { #[allow(unused_imports)] use super::*; unsafe extern "C" { pub(crate) unsafe fn __rust_thunk___ZN2SVC1Ev(__this: *mut ::core::ffi::c_void); + pub(crate) unsafe fn __rust_thunk___Z8sv_ident2SV<'s>( + __return: *mut ::core::ffi::c_void, + s: &mut crate::SV<'s>, + ); + pub(crate) unsafe fn __rust_thunk___Z16sv_ident_unknown2SV( + __return: *mut ::core::ffi::c_void, + s: &mut crate::SV<'static>, + ); + pub(crate) unsafe fn __rust_thunk___Z23sv_ident_unknown_elided2SV( + __return: *mut ::core::ffi::c_void, + s: &mut crate::SV<'static>, + ); + pub(crate) unsafe fn __rust_thunk___Z11sv_make_rawv(__return: *mut ::core::ffi::c_void); } } const _: () = { assert!(::core::mem::size_of::() == 1); assert!(::core::mem::align_of::() == 1); - static_assertions::assert_impl_all!(crate::SV: Copy,Clone); - static_assertions::assert_not_impl_any!(crate::SV: Drop); + static_assertions::assert_impl_all!(crate::SV<'static>: Copy,Clone); + static_assertions::assert_not_impl_any!(crate::SV<'static>: Drop); }; diff --git a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_test.rs b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_test.rs index 0af984ea4..91f5c7746 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_test.rs +++ b/rs_bindings_from_cc/test/assume_lifetimes/simple_string_view_test.rs @@ -7,4 +7,8 @@ use googletest::prelude::*; #[gtest] fn my_test() { let sv: simple_string_view::SV<'_> = simple_string_view::SV::default(); + let _sv_id = simple_string_view::sv_ident(sv); + + let sv_raw = simple_string_view::sv_make_raw(); + let _sv_raw_id = simple_string_view::sv_ident(sv_raw); } diff --git a/rs_bindings_from_cc/test/assume_lifetimes/test_annotations.h b/rs_bindings_from_cc/test/assume_lifetimes/test_annotations.h index b4dd4187f..d2a439380 100644 --- a/rs_bindings_from_cc/test/assume_lifetimes/test_annotations.h +++ b/rs_bindings_from_cc/test/assume_lifetimes/test_annotations.h @@ -11,5 +11,6 @@ #define $b $(b) #define $c $(c) #define $static $(static) +#define $unknown $(unknown) #endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_ASSUME_LIFETIMES_TEST_ANNOTATIONS_H_