Skip to content

Commit 0988d44

Browse files
authored
Merge pull request #1428 from google/lifetime-prob
Add lifetimes to functions returning `CppRef<'a, T>`
2 parents 0dd3f63 + 0df10dc commit 0988d44

File tree

6 files changed

+64
-71
lines changed

6 files changed

+64
-71
lines changed

engine/src/conversion/api.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -719,10 +719,3 @@ impl<T: AnalysisPhase> Api<T> {
719719
Ok(Box::new(std::iter::once(Api::Enum { name, item })))
720720
}
721721
}
722-
723-
/// Whether a type is a pointer of some kind.
724-
pub(crate) enum Pointerness {
725-
Not,
726-
ConstPtr,
727-
MutPtr,
728-
}

engine/src/conversion/codegen_cpp/function_wrapper_cpp.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use syn::{Type, TypePtr};
1010

1111
use crate::conversion::{
1212
analysis::fun::function_wrapper::{CppConversionType, TypeConversionPolicy},
13-
api::Pointerness,
1413
ConvertErrorFromCpp,
1514
};
1615

@@ -63,17 +62,6 @@ impl TypeConversionPolicy {
6362
cpp_name_map.type_to_cpp(self.cxxbridge_type())
6463
}
6564

66-
pub(crate) fn is_a_pointer(&self) -> Pointerness {
67-
match self.cxxbridge_type() {
68-
Type::Ptr(TypePtr {
69-
mutability: Some(_),
70-
..
71-
}) => Pointerness::MutPtr,
72-
Type::Ptr(_) => Pointerness::ConstPtr,
73-
_ => Pointerness::Not,
74-
}
75-
}
76-
7765
fn unique_ptr_wrapped_type(
7866
&self,
7967
original_name_map: &CppNameMap,

engine/src/conversion/codegen_rs/fun_codegen.rs

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
88

9-
use autocxx_parser::IncludeCppConfig;
109
use indexmap::set::IndexSet as HashSet;
1110
use std::borrow::Cow;
1211

@@ -24,15 +23,15 @@ use super::{
2423
function_wrapper_rs::RustParamConversion,
2524
maybe_unsafes_to_tokens,
2625
unqualify::{unqualify_params, unqualify_ret_type},
27-
ImplBlockDetails, ImplBlockKey, MaybeUnsafeStmt, RsCodegenResult, TraitImplBlockDetails, Use,
26+
ImplBlockDetails, MaybeUnsafeStmt, RsCodegenResult, TraitImplBlockDetails, Use,
2827
};
2928
use crate::{
3029
conversion::{
3130
analysis::fun::{
3231
function_wrapper::TypeConversionPolicy, ArgumentAnalysis, FnAnalysis, FnKind,
3332
MethodKind, RustRenameStrategy, TraitMethodDetails,
3433
},
35-
api::{Pointerness, UnsafetyNeeded},
34+
api::UnsafetyNeeded,
3635
},
3736
minisyn::minisynize_vec,
3837
types::{Namespace, QualifiedName},
@@ -91,7 +90,6 @@ pub(super) fn gen_function(
9190
analysis: FnAnalysis,
9291
cpp_call_name: String,
9392
non_pod_types: &HashSet<QualifiedName>,
94-
config: &IncludeCppConfig,
9593
) -> RsCodegenResult {
9694
if analysis.ignore_reason.is_err() || !analysis.externally_callable {
9795
return RsCodegenResult::default();
@@ -125,14 +123,14 @@ pub(super) fn gen_function(
125123
non_pod_types,
126124
ret_type: &ret_type,
127125
ret_conversion: &ret_conversion,
128-
reference_wrappers: config.unsafe_policy.requires_cpprefs(),
129126
};
130127
// In rare occasions, we might need to give an explicit lifetime.
131128
let (lifetime_tokens, params, ret_type) = add_explicit_lifetime_if_necessary(
132129
&param_details,
133130
params,
134131
Cow::Borrowed(&ret_type),
135132
non_pod_types,
133+
&ret_conversion,
136134
);
137135

138136
if analysis.rust_wrapper_needed {
@@ -238,7 +236,6 @@ struct FnGenerator<'a> {
238236
always_unsafe_due_to_trait_definition: bool,
239237
doc_attrs: &'a Vec<Attribute>,
240238
non_pod_types: &'a HashSet<QualifiedName>,
241-
reference_wrappers: bool,
242239
}
243240

244241
impl<'a> FnGenerator<'a> {
@@ -308,6 +305,7 @@ impl<'a> FnGenerator<'a> {
308305
wrapper_params,
309306
ret_type,
310307
self.non_pod_types,
308+
self.ret_conversion,
311309
);
312310

313311
let cxxbridge_name = self.cxxbridge_name;
@@ -394,39 +392,15 @@ impl<'a> FnGenerator<'a> {
394392
let rust_name = make_ident(self.rust_name);
395393
let unsafety = self.unsafety.wrapper_token();
396394
let doc_attrs = self.doc_attrs;
397-
let receiver_pointerness = self
398-
.param_details
399-
.iter()
400-
.next()
401-
.map(|pd| pd.conversion.is_a_pointer())
402-
.unwrap_or(Pointerness::Not);
403395
let ty = impl_block_type_name.get_final_ident();
404-
let ty = match receiver_pointerness {
405-
Pointerness::MutPtr if self.reference_wrappers => ImplBlockKey {
406-
ty: parse_quote! {
407-
#ty
408-
},
409-
lifetime: Some(parse_quote! { 'a }),
410-
},
411-
Pointerness::ConstPtr if self.reference_wrappers => ImplBlockKey {
412-
ty: parse_quote! {
413-
#ty
414-
},
415-
lifetime: Some(parse_quote! { 'a }),
416-
},
417-
_ => ImplBlockKey {
418-
ty: parse_quote! { # ty },
419-
lifetime: None,
420-
},
421-
};
422396
Box::new(ImplBlockDetails {
423397
item: ImplItem::Fn(parse_quote! {
424398
#(#doc_attrs)*
425399
pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
426400
#call_body
427401
}
428402
}),
429-
ty,
403+
ty: parse_quote! { # ty },
430404
})
431405
}
432406

@@ -469,7 +443,7 @@ impl<'a> FnGenerator<'a> {
469443
};
470444
Box::new(ImplBlockDetails {
471445
item: ImplItem::Fn(parse_quote! { #stuff }),
472-
ty: ImplBlockKey { ty, lifetime: None },
446+
ty,
473447
})
474448
}
475449

engine/src/conversion/codegen_rs/lifetime.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
// except according to those terms.
88
use crate::{
99
conversion::analysis::fun::{
10-
function_wrapper::RustConversionType, ArgumentAnalysis, ReceiverMutability,
10+
function_wrapper::{RustConversionType, TypeConversionPolicy},
11+
ArgumentAnalysis, ReceiverMutability,
1112
},
1213
types::QualifiedName,
1314
};
@@ -22,7 +23,7 @@ use syn::{
2223

2324
/// Function which can add explicit lifetime parameters to function signatures
2425
/// where necessary, based on analysis of parameters and return types.
25-
/// This is necessary in three cases:
26+
/// This is necessary in four cases:
2627
/// 1) where the parameter is a Pin<&mut T>
2728
/// and the return type is some kind of reference - because lifetime elision
2829
/// is not smart enough to see inside a Pin.
@@ -31,11 +32,13 @@ use syn::{
3132
/// built-in type
3233
/// 3) Any parameter is any form of reference, and we're returning an `impl New`
3334
/// 3a) an 'impl ValueParam' counts as a reference.
35+
/// 4) If we're using CppRef<'a, T> as a param or return type
3436
pub(crate) fn add_explicit_lifetime_if_necessary<'r>(
3537
param_details: &[ArgumentAnalysis],
3638
mut params: Punctuated<FnArg, Comma>,
3739
ret_type: Cow<'r, ReturnType>,
3840
non_pod_types: &HashSet<QualifiedName>,
41+
ret_conversion: &Option<TypeConversionPolicy>,
3942
) -> (
4043
Option<TokenStream>,
4144
Punctuated<FnArg, Comma>,
@@ -53,12 +56,31 @@ pub(crate) fn add_explicit_lifetime_if_necessary<'r>(
5356
RustConversionType::FromValueParamToPtr
5457
)
5558
});
59+
60+
let any_param_is_cppref = param_details.iter().any(|pd| {
61+
matches!(
62+
pd.conversion.rust_conversion,
63+
RustConversionType::FromReferenceWrapperToPointer
64+
)
65+
});
5666
let return_type_is_impl = return_type_is_impl(&ret_type);
67+
let return_type_is_cppref = matches!(
68+
ret_conversion,
69+
Some(TypeConversionPolicy {
70+
rust_conversion: RustConversionType::FromPointerToReferenceWrapper,
71+
..
72+
})
73+
);
5774
let non_pod_ref_param = reference_parameter_is_non_pod_reference(&params, non_pod_types);
5875
let ret_type_pod = return_type_is_pod_or_known_type_reference(&ret_type, non_pod_types);
5976
let returning_impl_with_a_reference_param = return_type_is_impl && any_param_is_reference;
6077
let hits_1024_bug = non_pod_ref_param && ret_type_pod;
61-
if !(has_mutable_receiver || hits_1024_bug || returning_impl_with_a_reference_param) {
78+
if !(has_mutable_receiver
79+
|| hits_1024_bug
80+
|| returning_impl_with_a_reference_param
81+
|| return_type_is_cppref
82+
|| any_param_is_cppref)
83+
{
6284
return (None, params, ret_type);
6385
}
6486
let new_return_type = match ret_type.as_ref() {
@@ -89,6 +111,9 @@ pub(crate) fn add_explicit_lifetime_if_necessary<'r>(
89111
};
90112

91113
match new_return_type {
114+
None if return_type_is_cppref || any_param_is_cppref => {
115+
(Some(quote! { <'a> }), params, ret_type)
116+
}
92117
None => (None, params, ret_type),
93118
Some(new_return_type) => {
94119
for FnArg::Typed(PatType { ty, .. }) | FnArg::Receiver(syn::Receiver { ty, .. }) in

engine/src/conversion/codegen_rs/mod.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ use itertools::Itertools;
2323
use proc_macro2::{Span, TokenStream};
2424
use syn::{
2525
parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
26-
ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Lifetime, TraitItem, Type,
27-
TypePath,
26+
ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, TraitItem, Type, TypePath,
2827
};
2928

3029
use crate::{
@@ -59,16 +58,10 @@ use super::{
5958
use super::{convert_error::ErrorContext, ConvertErrorFromCpp};
6059
use quote::quote;
6160

62-
#[derive(Clone, Hash, PartialEq, Eq)]
63-
struct ImplBlockKey {
64-
ty: Type,
65-
lifetime: Option<Lifetime>,
66-
}
67-
6861
/// An entry which needs to go into an `impl` block for a given type.
6962
struct ImplBlockDetails {
7063
item: ImplItem,
71-
ty: ImplBlockKey,
64+
ty: Type,
7265
}
7366

7467
struct TraitImplBlockDetails {
@@ -412,10 +405,8 @@ impl<'a> RsCodeGenerator<'a> {
412405
}
413406
}
414407
for (ty, entries) in impl_entries_by_type.into_iter() {
415-
let lt = ty.lifetime.map(|lt| quote! { < #lt > });
416-
let ty = ty.ty;
417408
output_items.push(Item::Impl(parse_quote! {
418-
impl #lt #ty {
409+
impl #ty {
419410
#(#entries)*
420411
}
421412
}))
@@ -494,7 +485,6 @@ impl<'a> RsCodeGenerator<'a> {
494485
analysis,
495486
cpp_call_name,
496487
non_pod_types,
497-
self.config,
498488
),
499489
Api::Const { const_item, .. } => RsCodegenResult {
500490
bindgen_mod_items: vec![Item::Const(const_item.into())],
@@ -1095,10 +1085,7 @@ impl<'a> RsCodeGenerator<'a> {
10951085
fn #method(_uhoh: autocxx::BindingGenerationFailure) {
10961086
}
10971087
},
1098-
ty: ImplBlockKey {
1099-
ty: parse_quote! { #self_ty },
1100-
lifetime: None,
1101-
},
1088+
ty: parse_quote! { #self_ty },
11021089
})),
11031090
None,
11041091
None,

integration-tests/tests/cpprefs_test.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use autocxx_integration_tests::{directives_from_lists, do_run_test};
1212
use indoc::indoc;
1313
use proc_macro2::TokenStream;
1414
use quote::quote;
15+
use test_log::test;
1516

1617
const fn arbitrary_self_types_supported() -> bool {
1718
rustversion::cfg!(nightly)
@@ -107,3 +108,28 @@ fn test_method_call_const() {
107108
&[],
108109
)
109110
}
111+
112+
#[test]
113+
fn test_return_reference_cpprefs() {
114+
let cxx = indoc! {"
115+
const Bob& give_bob(const Bob& input_bob) {
116+
return input_bob;
117+
}
118+
"};
119+
let hdr = indoc! {"
120+
#include <cstdint>
121+
struct Bob {
122+
uint32_t a;
123+
uint32_t b;
124+
};
125+
const Bob& give_bob(const Bob& input_bob);
126+
"};
127+
let rs = quote! {
128+
let b = CppPin::new(ffi::Bob { a: 3, b: 4 });
129+
let b_ref = b.as_cpp_ref();
130+
let bob = ffi::give_bob(&b_ref);
131+
let val = unsafe { bob.as_ref() };
132+
assert_eq!(val.b, 4);
133+
};
134+
run_cpprefs_test(cxx, hdr, rs, &["give_bob"], &["Bob"]);
135+
}

0 commit comments

Comments
 (0)