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 75d1502fe..d00b4a617 100644 --- a/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs +++ b/rs_bindings_from_cc/generate_bindings/lifetime_defaults_transform.rs @@ -282,11 +282,12 @@ impl LifetimeDefaults { // TODO(b/475407556): Support lifetime_capture_by. let mut return_lifetime: Vec> = func.return_type.explicit_lifetimes.clone(); let mut has_lifetimebound = false; + let is_constructor = func.cc_name == ir::UnqualifiedIdentifier::Constructor; // First, check to see if there are any existing lifetime annotations that we need to // respect. - for param in func.params.iter() { - if param.clang_lifetimebound { - has_lifetimebound = true; + for (ix, param) in func.params.iter().enumerate() { + if param.clang_lifetimebound || (is_constructor && ix == 0) { + has_lifetimebound |= param.clang_lifetimebound; if return_lifetime.is_empty() { // If a [[lifetimebound]] parameter already has a lifetime annotation and we // don't have a lifetime for the return value yet, use the parameter's @@ -316,17 +317,24 @@ impl LifetimeDefaults { // annotations, we need to create new lifetime variables for the return value. // Use a reserved name for these so we don't conflict with lifetimes embedded in // types or on non-[[lifetimebound]] parameters. - let arity = self.get_lifetime_arity(&func.return_type)?; + let arity = self.get_lifetime_arity(if is_constructor { + &func.params[0].type_ + } else { + &func.return_type + })?; for _ in 0..arity { - return_lifetime.push(self.bindings.fresh_name_for(&Rc::from("__rv"))) + let name = if is_constructor { &Rc::from("__this") } else { &Rc::from("__rv") }; + return_lifetime.push(self.bindings.fresh_name_for(name)) } } - for param in func.params.iter_mut() { - if param.clang_lifetimebound { + for (ix, param) in func.params.iter_mut().enumerate() { + if param.clang_lifetimebound || (is_constructor && ix == 0) { param.type_.explicit_lifetimes = return_lifetime.clone(); } } - func.return_type.explicit_lifetimes = return_lifetime; + if !is_constructor { + func.return_type.explicit_lifetimes = return_lifetime; + } Ok(()) } 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 bc09e36cf..c0d159626 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 @@ -590,6 +590,186 @@ fn test_this_lifetime_returned_for_member_function_with_reference_param() -> Res Ok(()) } +#[gtest] +fn test_this_lifetime_applied_for_constructor() -> Result<()> { + let ir = ir_from_assumed_lifetimes_cc( + &(with_full_lifetime_macros() + + r#" + struct S { S(); S(const S& o) = delete; S(const S&& o) = delete; }; + "#), + )?; + let dir = lifetime_defaults_transform(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: Constructor, + rs_name: Constructor, ... + return_type: CcType { ... variant: Primitive(Void) ... }, ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: ["__this"] ... }, + identifier: "__this", ... + } + ], + lifetime_params: [], + ... + lifetime_inputs: ["__this"], + ... + } + } + ); + Ok(()) +} + +#[gtest] +fn test_this_lifetime_annotation_applied_for_constructor() -> Result<()> { + let ir = ir_from_assumed_lifetimes_cc( + &(with_full_lifetime_macros() + + r#" + struct S { S() $a; S(const S& o) = delete; S(const S&& o) = delete; }; + "#), + )?; + let dir = lifetime_defaults_transform(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: Constructor, + rs_name: Constructor, ... + return_type: CcType { ... variant: Primitive(Void) ... }, ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: ["a"] ... }, + identifier: "__this", ... + } + ], + lifetime_params: [], + ... + lifetime_inputs: ["a"], + ... + } + } + ); + Ok(()) +} + +#[gtest] +fn test_param_lifetime_inferred_for_constructor() -> Result<()> { + let ir = ir_from_assumed_lifetimes_cc( + &(with_full_lifetime_macros() + + r#" + struct S { S(int& i1); S(const S& o) = delete; S(const S&& o) = delete; }; + "#), + )?; + let dir = lifetime_defaults_transform(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: Constructor, + rs_name: Constructor, ... + return_type: CcType { ... variant: Primitive(Void) ... }, ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: ["__this"] ... }, + identifier: "__this", ... + }, + FuncParam { + type_: CcType { ... explicit_lifetimes: ["i1"] ... }, + identifier: "i1", ... + }, + ], + lifetime_params: [], + ... + lifetime_inputs: ["__this", "i1"], + ... + } + } + ); + Ok(()) +} + +#[gtest] +fn test_param_lifetimebound_to_this_in_constructor() -> Result<()> { + let ir = ir_from_assumed_lifetimes_cc( + &(with_full_lifetime_macros() + + r#" + struct S { + S(int& i1 [[clang::lifetimebound]]); + S(const S& o) = delete; + S(const S&& o) = delete; + }; + "#), + )?; + let dir = lifetime_defaults_transform(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: Constructor, + rs_name: Constructor, ... + return_type: CcType { ... variant: Primitive(Void) ... }, ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: ["__this"] ... }, + identifier: "__this", ... + }, + FuncParam { + type_: CcType { ... explicit_lifetimes: ["__this"] ... }, + identifier: "i1", ... + }, + ], + lifetime_params: [], + ... + lifetime_inputs: ["__this"], + ... + } + } + ); + Ok(()) +} + +#[gtest] +fn test_param_lifetimebound_to_this_in_constructor_explicit_lifetime() -> Result<()> { + let ir = ir_from_assumed_lifetimes_cc( + &(with_full_lifetime_macros() + + r#" + struct S { + S(int& i1 [[clang::lifetimebound]]) $a; + S(const S& o) = delete; + S(const S&& o) = delete; + }; + "#), + )?; + let dir = lifetime_defaults_transform(&ir)?; + assert_ir_matches!( + dir, + quote! { + Func { + cc_name: Constructor, + rs_name: Constructor, ... + return_type: CcType { ... variant: Primitive(Void) ... }, ... + params: [ + FuncParam { + type_: CcType { ... explicit_lifetimes: ["a"] ... }, + identifier: "__this", ... + }, + FuncParam { + type_: CcType { ... explicit_lifetimes: ["a"] ... }, + identifier: "i1", ... + }, + ], + lifetime_params: [], + ... + lifetime_inputs: ["a"], + ... + } + } + ); + Ok(()) +} + #[gtest] fn test_binding_context_has_static() -> Result<()> { let mut ctx = BindingContext::new();