Skip to content

Commit dbed762

Browse files
Googlercopybara-github
authored andcommitted
Support accepting non-Rust movable C++ types as constructors in Rust functions.
PiperOrigin-RevId: 910223861
1 parent f7fe9fd commit dbed762

10 files changed

Lines changed: 363 additions & 24 deletions

File tree

cc_bindings_from_rs/generate_bindings/format_type.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,16 @@ pub fn format_ty_for_cc<'tcx>(
145145
location: TypeLocation,
146146
) -> Result<CcSnippet<'tcx>> {
147147
let tcx = db.tcx();
148+
149+
// Normalize the type to resolve projections (associated types).
150+
let ty = query_compiler::try_normalize(
151+
tcx,
152+
ty::PseudoCanonicalInput {
153+
typing_env: rustc_middle::ty::TypingEnv::fully_monomorphized(),
154+
value: ty,
155+
},
156+
)
157+
.unwrap_or(ty);
148158
fn cstdint<'tcx>(tokens: TokenStream) -> CcSnippet<'tcx> {
149159
CcSnippet::with_include(tokens, CcInclude::cstdint())
150160
}
@@ -270,6 +280,15 @@ pub fn format_ty_for_cc<'tcx>(
270280
}
271281

272282
ty::TyKind::Adt(adt, substs) => {
283+
if matches_qualified_name(db, adt.did(), &["ctor", "RvalueReference"]) {
284+
let referent = substs[1].expect_ty();
285+
return format_pointer_or_reference_ty_for_cc(
286+
db,
287+
referent,
288+
Mutability::Mut,
289+
quote! { && },
290+
);
291+
}
273292
let def_id = adt.did();
274293
let mut prereqs = CcPrerequisites::default();
275294

@@ -766,8 +785,9 @@ pub fn format_ty_for_rs<'tcx>(db: &BindingsGenerator<'tcx>, ty: Ty<'tcx>) -> Res
766785
let has_composable_bridging =
767786
matches!(is_bridged_type(db, ty)?, Some(BridgedType::Composable(_)));
768787
// We support generics if they're for `std::option::Option` or `std::result::Result`.
769-
let is_supported_generic_type =
770-
BridgedBuiltin::new(db, adt).is_some() || !has_non_lifetime_substs(substs);
788+
let is_supported_generic_type = BridgedBuiltin::new(db, adt).is_some()
789+
|| !has_non_lifetime_substs(substs)
790+
|| matches_qualified_name(db, adt.did(), &["ctor", "RvalueReference"]);
771791
ensure!(
772792
has_cpp_type || is_supported_generic_type || has_composable_bridging,
773793
"Generic types without composable bridging are not supported yet (b/259749095)"

cc_bindings_from_rs/generate_bindings/generate_bindings_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2146,7 +2146,7 @@ fn test_format_item_unsupported_type_without_direct_existence() {
21462146
"#;
21472147
test_format_item(test_src, "EvilAlias", |result| {
21482148
let err = result.unwrap_err();
2149-
assert_eq!(err, "The following Rust type is not supported yet: <i64 as Evil>::Type");
2149+
assert_eq!(err, "Not a public or a supported reexported type (b/262052635).");
21502150
});
21512151
}
21522152

cc_bindings_from_rs/generate_bindings/generate_function.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,16 @@ fn cc_param_to_c_abi<'tcx>(
185185
}
186186
}
187187
} else if is_c_abi_compatible_by_value(tcx, ty) {
188-
quote! { #cc_ident }
188+
if let ty::TyKind::Adt(adt, _) = ty.kind() {
189+
if crate::matches_qualified_name(db, adt.did(), &["ctor", "RvalueReference"]) {
190+
includes.insert(code_gen_utils::CcInclude::utility());
191+
quote! { ::std::move(#cc_ident) }
192+
} else {
193+
quote! { #cc_ident }
194+
}
195+
} else {
196+
quote! { #cc_ident }
197+
}
189198
} else if let ty::TyKind::Tuple(tuple_tys) = ty.kind() {
190199
let n = tuple_tys.len();
191200
let c_abi_names = ident_for_each(&format!("{cc_ident}_cabi"), n);

cc_bindings_from_rs/generate_bindings/generate_function_test.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,9 +1032,7 @@ fn test_format_item_unsupported_fn_async() {
10321032
let err = result.unwrap_err();
10331033
assert_eq!(
10341034
err,
1035-
"Error formatting function return type `impl std::future::Future<Output = ()>`: \
1036-
The following Rust type is not supported yet: \
1037-
impl std::future::Future<Output = ()>"
1035+
"Error formatting function return type `impl std::future::Future<Output = ()>`: The following Rust type is not supported yet: {async fn body of async_function()}"
10381036
);
10391037
});
10401038
}

cc_bindings_from_rs/generate_bindings/get_generic_args.rs

Lines changed: 105 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
// Exceptions. See /LICENSE for license information.
33
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44

5-
use crate::liberate_and_deanonymize_late_bound_regions;
5+
use crate::{liberate_and_deanonymize_late_bound_regions, matches_qualified_name};
66
use arc_anyhow::{anyhow, bail, ensure, Result};
77
use database::BindingsGenerator;
88
use rustc_infer::infer::{InferCtxt, RegionVariableOrigin};
99
use rustc_infer::traits::{Obligation, ObligationCause};
1010
use rustc_middle::ty::{self, Ty, TyCtxt};
1111
use rustc_span::def_id::DefId;
12-
use rustc_span::symbol::sym;
12+
use rustc_span::symbol::{sym, Symbol};
1313
use rustc_trait_selection::infer::canonical::ir::TypingMode;
1414
use rustc_trait_selection::infer::TyCtxtInferExt;
1515
use rustc_trait_selection::traits::ObligationCtxt;
@@ -36,6 +36,15 @@ pub fn get_generic_args<'tcx>(
3636
finder.generic_param_indices
3737
};
3838

39+
let params_used_in_return_type = {
40+
let mut finder = GenericParamsFinder::default();
41+
let fn_sig = tcx.fn_sig(fn_def_id).instantiate_identity();
42+
let fn_sig = liberate_and_deanonymize_late_bound_regions(tcx, fn_sig, fn_def_id);
43+
use rustc_type_ir::TypeVisitable;
44+
fn_sig.output().visit_with(&mut finder);
45+
finder.generic_param_indices
46+
};
47+
3948
let replacements: HashMap<usize, ty::GenericArg<'tcx>> = (0..generics.count())
4049
.map(|idx| {
4150
let param_def = generics.param_at(idx, tcx);
@@ -50,14 +59,20 @@ pub fn get_generic_args<'tcx>(
5059
"No support for replacing an _unused_ generic type param: `{}`",
5160
param_def.name,
5261
);
53-
get_replacement_for_generic_type_param(tcx, fn_def_id, predicates, param_def)
54-
.map(|ty| ty.into())
55-
.ok_or_else(|| {
56-
anyhow!(
57-
"No valid non-generic replacement for generic type param `{}`",
58-
param_def.name,
59-
)
60-
})?
62+
get_replacement_for_generic_type_param(
63+
db,
64+
fn_def_id,
65+
predicates,
66+
param_def,
67+
params_used_in_return_type.contains(&param_def.index),
68+
)
69+
.map(|ty| ty.into())
70+
.ok_or_else(|| {
71+
anyhow!(
72+
"No valid non-generic replacement for generic type param `{}`",
73+
param_def.name,
74+
)
75+
})?
6176
}
6277
};
6378
Ok((idx, replacement))
@@ -79,32 +94,97 @@ pub fn get_generic_args<'tcx>(
7994
/// If the returned type needs to use a new anonymous lifetime, then it will be generated
8095
/// using the given `def_id` as its scope.
8196
fn get_replacement_for_trait_predicate<'tcx>(
82-
tcx: TyCtxt<'tcx>,
97+
db: &BindingsGenerator<'tcx>,
8398
trait_predicate: ty::TraitPredicate<'tcx>,
99+
predicates: ty::GenericPredicates<'tcx>,
84100
new_anon_lifetime: impl Fn() -> ty::Region<'tcx>,
101+
is_used_in_return_type: bool,
85102
) -> Option<Ty<'tcx>> {
103+
let tcx = db.tcx();
86104
if trait_predicate.polarity != ty::PredicatePolarity::Positive {
87105
return None;
88106
}
89107
let trait_ref = trait_predicate.trait_ref;
90108

91109
// `args[0]` is `Self` / `T`. And when working with `Into<U>`, `AsRef<U>`, etc.
92110
// we typically want the first and only other generic argument - `U`.
93-
let ty1 = trait_ref.args.get(1).and_then(|generic_arg| generic_arg.as_type())?;
111+
let ty1 = trait_ref.args.get(1).and_then(|generic_arg| generic_arg.as_type());
94112

95113
// `T: Into<U>` => `U`
96114
if tcx.is_diagnostic_item(sym::Into, trait_ref.def_id) {
97-
return Some(ty1);
115+
return ty1;
98116
}
99117

100118
// `T: AsRef<U>` => `&U`
101119
if tcx.is_diagnostic_item(sym::AsRef, trait_ref.def_id) {
102-
return Some(Ty::new_imm_ref(tcx, new_anon_lifetime(), ty1));
120+
return Some(Ty::new_imm_ref(tcx, new_anon_lifetime(), ty1?));
103121
}
104122

105123
// `T: AsMut<U>` => `&mut U`
106124
if tcx.is_diagnostic_item(sym::AsMut, trait_ref.def_id) {
107-
return Some(Ty::new_mut_ref(tcx, new_anon_lifetime(), ty1));
125+
return Some(Ty::new_mut_ref(tcx, new_anon_lifetime(), ty1?));
126+
}
127+
128+
// Support for Ctor trait (b/489315162)
129+
if matches_qualified_name(db, trait_ref.def_id, &["ctor", "Ctor"]) {
130+
if is_used_in_return_type {
131+
return None;
132+
}
133+
// 1. Find the `DefId` of the `Output` associated type in the `Ctor` trait.
134+
let ctor_output_def_id = tcx
135+
.associated_items(trait_ref.def_id)
136+
.in_definition_order()
137+
.find(|item| {
138+
item.name() == sym::Output && matches!(item.kind, ty::AssocKind::Type { .. })
139+
})
140+
.map(|item| item.def_id)?;
141+
142+
// 2. Iterate over the predicates and look for projections.
143+
let output_ty = predicates
144+
.predicates
145+
.iter()
146+
.filter_map(|(clause, _)| {
147+
if let ty::ClauseKind::Projection(projection_predicate) =
148+
clause.kind().skip_binder()
149+
{
150+
let projection_term = projection_predicate.projection_term;
151+
if projection_term.def_id == ctor_output_def_id
152+
&& projection_term.self_ty() == trait_ref.self_ty()
153+
{
154+
return projection_predicate.term.as_type();
155+
}
156+
}
157+
None
158+
})
159+
.next()?;
160+
161+
// 3. Find DefIds for CtorNew and RvalueReference
162+
let ctor_crate = trait_ref.def_id.krate;
163+
let public_paths = db.public_paths_by_def_id(ctor_crate);
164+
let find_by_name = |name: &str| {
165+
let target_symbol = Symbol::intern(name);
166+
public_paths.iter().find_map(|(def_id, paths)| {
167+
if paths.canonical().name == target_symbol {
168+
Some(*def_id)
169+
} else {
170+
None
171+
}
172+
})
173+
};
174+
let _ctor_new_def_id = find_by_name("CtorNew")?;
175+
let rvalue_ref_def_id = find_by_name("RvalueReference")?;
176+
177+
// 4. Construct RvalueReference<'_, Output>
178+
let anon_region = new_anon_lifetime();
179+
let rvalue_ref_ty = Ty::new_adt(
180+
tcx,
181+
tcx.adt_def(rvalue_ref_def_id),
182+
tcx.mk_args(&[anon_region.into(), output_ty.into()]),
183+
);
184+
185+
// TODO(b/489315162): Verify Output: CtorNew<RvalueReference<'_, Output>>
186+
187+
return Some(rvalue_ref_ty);
108188
}
109189

110190
// TODO(b/281542952): Implement other replacements as needed.
@@ -146,11 +226,13 @@ fn is_valid_replacement_for_generic_type_param<'tcx>(
146226
/// that may be constraining `T`. When multiple answers are possible, returns
147227
/// the first one.
148228
fn get_replacement_for_generic_type_param<'tcx>(
149-
tcx: TyCtxt<'tcx>,
229+
db: &BindingsGenerator<'tcx>,
150230
def_id: DefId,
151231
predicates: ty::GenericPredicates<'tcx>,
152232
generic_type_param: &ty::GenericParamDef,
233+
is_used_in_return_type: bool,
153234
) -> Option<Ty<'tcx>> {
235+
let tcx = db.tcx();
154236
// Look only at trait predicates involving this param (e.g. `T: SomeTrait`).
155237
let trait_predicates_for_this_generic_param = predicates
156238
.predicates
@@ -171,7 +253,13 @@ fn get_replacement_for_generic_type_param<'tcx>(
171253
// Find the first replacement that fits all the constraints.
172254
trait_predicates_for_this_generic_param
173255
.filter_map(|trait_predicate| {
174-
get_replacement_for_trait_predicate(tcx, trait_predicate, new_anon_lifetime)
256+
get_replacement_for_trait_predicate(
257+
db,
258+
trait_predicate,
259+
predicates,
260+
new_anon_lifetime,
261+
is_used_in_return_type,
262+
)
175263
})
176264
.find(|new_ty| {
177265
is_valid_replacement_for_generic_type_param(

cc_bindings_from_rs/test/functions/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ rust_library(
2424
aspect_hints = [
2525
"//features:experimental",
2626
],
27+
deps = [
28+
"//support:ctor",
29+
],
2730
)
2831

2932
cc_bindings_from_rust(

cc_bindings_from_rs/test/functions/functions.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,4 +274,34 @@ pub mod generic_fn_tests {
274274
}
275275
}
276276
}
277+
278+
pub mod ctor_trait_tests {
279+
use ctor::Ctor;
280+
use std::marker::PhantomPinned;
281+
282+
pub struct NonMovable {
283+
pub value: i32,
284+
_pinned: PhantomPinned,
285+
}
286+
287+
impl NonMovable {
288+
pub fn new(value: i32) -> Self {
289+
NonMovable { value, _pinned: PhantomPinned }
290+
}
291+
}
292+
293+
impl<'a> ::ctor::CtorNew<::ctor::RvalueReference<'a, Self>> for NonMovable {
294+
type CtorType = ::ctor::RvalueReference<'a, Self>;
295+
type Error = i32;
296+
fn ctor_new(
297+
args: ::ctor::RvalueReference<'a, Self>,
298+
) -> ::ctor::RvalueReference<'a, Self> {
299+
args
300+
}
301+
}
302+
303+
pub fn accept_ctor(_c: impl Ctor<Output = NonMovable>) -> i32 {
304+
42
305+
}
306+
}
277307
}

0 commit comments

Comments
 (0)