blob: beb5e62beb3223fffa07db7601f2aaba377ae381 [file] [log] [blame]
David Tolnayf3a9afa2020-10-08 23:22:12 -07001use crate::derive::DeriveAttribute;
David Tolnaya52602b2020-03-06 10:24:34 -08002use crate::syntax::atom::Atom::{self, *};
David Tolnay05ef6ff2020-08-29 11:27:05 -07003use crate::syntax::file::Module;
David Tolnay0dd85ff2020-05-03 23:43:33 -07004use crate::syntax::report::Errors;
David Tolnay891061b2020-04-19 22:42:33 -07005use crate::syntax::symbol::Symbol;
David Tolnay3caa50a2020-04-19 21:25:34 -07006use crate::syntax::{
David Tolnay8faec772020-11-02 00:18:19 -08007 self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Pair, ResolvableName, Signature,
Adrian Taylorc8713432020-10-21 18:20:55 -07008 Struct, Type, TypeAlias, Types,
David Tolnay3caa50a2020-04-19 21:25:34 -07009};
David Tolnay0531f432020-10-03 23:50:28 -070010use proc_macro2::{Ident, Span, TokenStream};
David Tolnayf9ffb932020-04-20 02:22:57 -070011use quote::{format_ident, quote, quote_spanned, ToTokens};
David Tolnayc94035a2020-11-15 16:49:18 -080012use std::collections::BTreeSet as Set;
David Tolnay05ef6ff2020-08-29 11:27:05 -070013use std::mem;
14use syn::{parse_quote, Result, Token};
David Tolnay7db73692019-10-20 14:51:12 -040015
David Tolnay3c64a4e2020-08-29 14:07:38 -070016pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
David Tolnay0dd85ff2020-05-03 23:43:33 -070017 let ref mut errors = Errors::new();
David Tolnay05ef6ff2020-08-29 11:27:05 -070018 let content = mem::take(&mut ffi.content);
David Tolnay805dca32020-08-29 19:09:55 -070019 let trusted = ffi.unsafety.is_some();
Adrian Taylorc8713432020-10-21 18:20:55 -070020 let namespace = &ffi.namespace;
21 let ref apis = syntax::parse_items(errors, content, trusted, namespace);
David Tolnay52759782020-05-03 23:59:40 -070022 let ref types = Types::collect(errors, apis);
23 errors.propagate()?;
Adrian Taylorc8713432020-10-21 18:20:55 -070024 check::typecheck(errors, apis, types);
David Tolnay0dd85ff2020-05-03 23:43:33 -070025 errors.propagate()?;
David Tolnay7db73692019-10-20 14:51:12 -040026
David Tolnay3c64a4e2020-08-29 14:07:38 -070027 Ok(expand(ffi, apis, types))
David Tolnaycbf3f032020-05-03 23:51:14 -070028}
29
David Tolnay3c64a4e2020-08-29 14:07:38 -070030fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
David Tolnay7db73692019-10-20 14:51:12 -040031 let mut expanded = TokenStream::new();
32 let mut hidden = TokenStream::new();
David Tolnay7db73692019-10-20 14:51:12 -040033
David Tolnay2ec14632020-05-04 00:47:10 -070034 for api in apis {
David Tolnay7db73692019-10-20 14:51:12 -040035 if let Api::RustType(ety) = api {
36 expanded.extend(expand_rust_type(ety));
David Tolnayc8361022020-08-25 21:57:53 -070037 hidden.extend(expand_rust_type_assert_sized(ety));
David Tolnay7db73692019-10-20 14:51:12 -040038 }
39 }
40
David Tolnay2ec14632020-05-04 00:47:10 -070041 for api in apis {
David Tolnay7db73692019-10-20 14:51:12 -040042 match api {
David Tolnay7e69f892020-10-03 22:20:22 -070043 Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
Adrian Taylorc8713432020-10-21 18:20:55 -070044 Api::Struct(strct) => expanded.extend(expand_struct(strct)),
45 Api::Enum(enm) => expanded.extend(expand_enum(enm)),
Joel Galenson905eb2e2020-05-04 14:58:14 -070046 Api::CxxType(ety) => {
David Tolnay17a934c2020-11-02 00:40:04 -080047 let ident = &ety.name.rust;
48 if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) {
Adrian Taylorc8713432020-10-21 18:20:55 -070049 expanded.extend(expand_cxx_type(ety));
David Tolnay9eef6092020-11-15 17:56:41 -080050 hidden.extend(expand_cxx_type_assert_pinned(ety));
Joel Galenson905eb2e2020-05-04 14:58:14 -070051 }
52 }
David Tolnay7db73692019-10-20 14:51:12 -040053 Api::CxxFunction(efn) => {
Adrian Taylorc8713432020-10-21 18:20:55 -070054 expanded.extend(expand_cxx_function_shim(efn, types));
David Tolnay7db73692019-10-20 14:51:12 -040055 }
Adrian Taylorc8713432020-10-21 18:20:55 -070056 Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)),
David Tolnay5f9e8ca2020-05-07 17:21:05 -070057 Api::TypeAlias(alias) => {
58 expanded.extend(expand_type_alias(alias));
Adrian Taylorc8713432020-10-21 18:20:55 -070059 hidden.extend(expand_type_alias_verify(alias, types));
David Tolnay5f9e8ca2020-05-07 17:21:05 -070060 }
David Tolnay7db73692019-10-20 14:51:12 -040061 }
62 }
63
David Tolnayc94035a2020-11-15 16:49:18 -080064 let mut expanded_unique_ptr = Set::new();
David Tolnay7db73692019-10-20 14:51:12 -040065 for ty in types {
David Tolnay0531f432020-10-03 23:50:28 -070066 let explicit_impl = types.explicit_impls.get(ty);
David Tolnay7db73692019-10-20 14:51:12 -040067 if let Type::RustBox(ty) = ty {
68 if let Type::Ident(ident) = &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -070069 if Atom::from(&ident.rust).is_none() {
70 hidden.extend(expand_rust_box(ident, types));
David Tolnay7db73692019-10-20 14:51:12 -040071 }
72 }
Myron Ahneba35cf2020-02-05 19:41:51 +070073 } else if let Type::RustVec(ty) = ty {
74 if let Type::Ident(ident) = &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -070075 if Atom::from(&ident.rust).is_none() {
76 hidden.extend(expand_rust_vec(ident, types));
David Tolnaya006bca2020-04-25 11:28:13 -070077 }
Myron Ahneba35cf2020-02-05 19:41:51 +070078 }
David Tolnay7db73692019-10-20 14:51:12 -040079 } else if let Type::UniquePtr(ptr) = ty {
80 if let Type::Ident(ident) = &ptr.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -070081 if Atom::from(&ident.rust).is_none()
82 && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
David Tolnayc94035a2020-11-15 16:49:18 -080083 && expanded_unique_ptr.insert(&ident.rust)
David Tolnay7e69f892020-10-03 22:20:22 -070084 {
Adrian Taylorc8713432020-10-21 18:20:55 -070085 expanded.extend(expand_unique_ptr(ident, types, explicit_impl));
Myron Ahneba35cf2020-02-05 19:41:51 +070086 }
87 }
David Tolnay4377a9e2020-04-24 15:20:26 -070088 } else if let Type::CxxVector(ptr) = ty {
Myron Ahneba35cf2020-02-05 19:41:51 +070089 if let Type::Ident(ident) = &ptr.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -070090 if Atom::from(&ident.rust).is_none()
91 && (explicit_impl.is_some() || !types.aliases.contains_key(&ident.rust))
David Tolnay7e69f892020-10-03 22:20:22 -070092 {
David Tolnay17631102020-04-24 19:01:30 -070093 // Generate impl for CxxVector<T> if T is a struct or opaque
94 // C++ type. Impl for primitives is already provided by cxx
95 // crate.
Adrian Taylorc8713432020-10-21 18:20:55 -070096 expanded.extend(expand_cxx_vector(ident, explicit_impl, types));
David Tolnay7db73692019-10-20 14:51:12 -040097 }
98 }
99 }
100 }
101
102 // Work around https://github.com/rust-lang/rust/issues/67851.
103 if !hidden.is_empty() {
104 expanded.extend(quote! {
105 #[doc(hidden)]
106 const _: () = {
107 #hidden
108 };
109 });
110 }
111
112 let attrs = ffi
113 .attrs
114 .into_iter()
115 .filter(|attr| attr.path.is_ident("doc"));
116 let vis = &ffi.vis;
David Tolnaycbf3f032020-05-03 23:51:14 -0700117 let ident = &ffi.ident;
David Tolnay7db73692019-10-20 14:51:12 -0400118
David Tolnaycbf3f032020-05-03 23:51:14 -0700119 quote! {
David Tolnay7db73692019-10-20 14:51:12 -0400120 #(#attrs)*
121 #[deny(improper_ctypes)]
122 #[allow(non_snake_case)]
123 #vis mod #ident {
124 #expanded
125 }
David Tolnaycbf3f032020-05-03 23:51:14 -0700126 }
David Tolnay7db73692019-10-20 14:51:12 -0400127}
128
Adrian Taylorc8713432020-10-21 18:20:55 -0700129fn expand_struct(strct: &Struct) -> TokenStream {
David Tolnay17a934c2020-11-02 00:40:04 -0800130 let ident = &strct.name.rust;
David Tolnay7db73692019-10-20 14:51:12 -0400131 let doc = &strct.doc;
David Tolnayf3a9afa2020-10-08 23:22:12 -0700132 let derives = DeriveAttribute(&strct.derives);
David Tolnay17a934c2020-11-02 00:40:04 -0800133 let type_id = type_id(&strct.name);
David Tolnay7db73692019-10-20 14:51:12 -0400134 let fields = strct.fields.iter().map(|field| {
135 // This span on the pub makes "private type in public interface" errors
136 // appear in the right place.
137 let vis = Token![pub](field.ident.span());
138 quote!(#vis #field)
139 });
David Tolnay8684cc52020-10-07 17:09:48 -0700140
David Tolnay7db73692019-10-20 14:51:12 -0400141 quote! {
142 #doc
David Tolnayf3a9afa2020-10-08 23:22:12 -0700143 #derives
David Tolnay7db73692019-10-20 14:51:12 -0400144 #[repr(C)]
145 pub struct #ident {
146 #(#fields,)*
147 }
David Tolnay8684cc52020-10-07 17:09:48 -0700148
149 unsafe impl ::cxx::ExternType for #ident {
150 type Id = #type_id;
151 type Kind = ::cxx::kind::Trivial;
152 }
David Tolnay7db73692019-10-20 14:51:12 -0400153 }
154}
155
Adrian Taylorc8713432020-10-21 18:20:55 -0700156fn expand_enum(enm: &Enum) -> TokenStream {
David Tolnay17a934c2020-11-02 00:40:04 -0800157 let ident = &enm.name.rust;
Joel Galensonc03402a2020-04-23 17:31:09 -0700158 let doc = &enm.doc;
David Tolnayc605e6f2020-05-10 23:37:12 -0700159 let repr = enm.repr;
David Tolnay17a934c2020-11-02 00:40:04 -0800160 let type_id = type_id(&enm.name);
Joel Galenson88547732020-05-05 08:23:42 -0700161 let variants = enm.variants.iter().map(|variant| {
162 let variant_ident = &variant.ident;
163 let discriminant = &variant.discriminant;
164 Some(quote! {
165 pub const #variant_ident: Self = #ident { repr: #discriminant };
166 })
167 });
David Tolnay8684cc52020-10-07 17:09:48 -0700168
Joel Galensonc03402a2020-04-23 17:31:09 -0700169 quote! {
170 #doc
171 #[derive(Copy, Clone, PartialEq, Eq)]
172 #[repr(transparent)]
Joel Galensondb1ec312020-05-01 13:57:32 -0700173 pub struct #ident {
David Tolnayc605e6f2020-05-10 23:37:12 -0700174 pub repr: #repr,
Joel Galensondb1ec312020-05-01 13:57:32 -0700175 }
Joel Galensonc03402a2020-04-23 17:31:09 -0700176
177 #[allow(non_upper_case_globals)]
178 impl #ident {
David Tolnayd7984c22020-04-30 20:09:31 -0700179 #(#variants)*
Joel Galensonc03402a2020-04-23 17:31:09 -0700180 }
David Tolnay8684cc52020-10-07 17:09:48 -0700181
182 unsafe impl ::cxx::ExternType for #ident {
183 type Id = #type_id;
184 type Kind = ::cxx::kind::Trivial;
185 }
Joel Galensonc03402a2020-04-23 17:31:09 -0700186 }
187}
188
Adrian Taylorc8713432020-10-21 18:20:55 -0700189fn expand_cxx_type(ety: &ExternType) -> TokenStream {
David Tolnay17a934c2020-11-02 00:40:04 -0800190 let ident = &ety.name.rust;
David Tolnay7db73692019-10-20 14:51:12 -0400191 let doc = &ety.doc;
David Tolnay17a934c2020-11-02 00:40:04 -0800192 let type_id = type_id(&ety.name);
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700193
David Tolnay7db73692019-10-20 14:51:12 -0400194 quote! {
195 #doc
196 #[repr(C)]
197 pub struct #ident {
198 _private: ::cxx::private::Opaque,
199 }
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700200
201 unsafe impl ::cxx::ExternType for #ident {
202 type Id = #type_id;
David Tolnay38f5ad62020-10-03 18:06:44 -0700203 type Kind = ::cxx::kind::Opaque;
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700204 }
David Tolnay7db73692019-10-20 14:51:12 -0400205 }
206}
207
David Tolnay9eef6092020-11-15 17:56:41 -0800208fn expand_cxx_type_assert_pinned(ety: &ExternType) -> TokenStream {
209 let ident = &ety.name.rust;
210 let infer = Token![_](ident.span());
211
212 quote! {
213 let _ = {
214 // Derived from https://github.com/nvzqz/static-assertions-rs.
215 trait __AmbiguousIfImpl<A> {
216 fn infer() {}
217 }
218
219 impl<T: ?Sized> __AmbiguousIfImpl<()> for T {}
220
221 #[allow(dead_code)]
222 struct __Invalid;
223
224 impl<T: ?Sized + Unpin> __AmbiguousIfImpl<__Invalid> for T {}
225
226 // If there is only one specialized trait impl, type inference with
227 // `_` can be resolved and this can compile. Fails to compile if
228 // user has added a manual Unpin impl for their opaque C++ type as
229 // then `__AmbiguousIfImpl<__Invalid>` also exists.
230 <#ident as __AmbiguousIfImpl<#infer>>::infer
231 };
232 }
233}
234
Adrian Taylorc8713432020-10-21 18:20:55 -0700235fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
David Tolnay18ba92c2020-04-22 16:17:30 -0700236 let receiver = efn.receiver.iter().map(|receiver| {
237 let receiver_type = receiver.ty();
238 quote!(_: #receiver_type)
239 });
David Tolnay39d575f2020-03-03 00:10:56 -0800240 let args = efn.args.iter().map(|arg| {
241 let ident = &arg.ident;
David Tolnay2f090422020-11-02 18:25:35 -0800242 let ty = expand_extern_type(&arg.ty, types, true);
David Tolnaya46a2372020-03-06 10:03:48 -0800243 if arg.ty == RustString {
244 quote!(#ident: *const #ty)
David Tolnay313b10e2020-04-25 16:30:51 -0700245 } else if let Type::RustVec(_) = arg.ty {
246 quote!(#ident: *const #ty)
David Tolnay75dca2e2020-03-25 20:17:52 -0700247 } else if let Type::Fn(_) = arg.ty {
248 quote!(#ident: ::cxx::private::FatFunction)
David Tolnaya46a2372020-03-06 10:03:48 -0800249 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay39d575f2020-03-03 00:10:56 -0800250 quote!(#ident: *mut #ty)
251 } else {
252 quote!(#ident: #ty)
253 }
254 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700255 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700256 let ret = if efn.throws {
257 quote!(-> ::cxx::private::Result)
258 } else {
David Tolnay2f090422020-11-02 18:25:35 -0800259 expand_extern_return_type(&efn.ret, types, true)
David Tolnayebef4a22020-03-17 15:33:47 -0700260 };
David Tolnay7db73692019-10-20 14:51:12 -0400261 let mut outparam = None;
David Tolnay1e548172020-03-16 13:37:09 -0700262 if indirect_return(efn, types) {
David Tolnay2f090422020-11-02 18:25:35 -0800263 let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
David Tolnay7db73692019-10-20 14:51:12 -0400264 outparam = Some(quote!(__return: *mut #ret));
265 }
Adrian Taylorc8713432020-10-21 18:20:55 -0700266 let link_name = mangle::extern_fn(efn, types);
David Tolnay17a934c2020-11-02 00:40:04 -0800267 let local_name = format_ident!("__{}", efn.name.rust);
David Tolnay7db73692019-10-20 14:51:12 -0400268 quote! {
269 #[link_name = #link_name]
Joel Galenson3d4f6122020-04-07 15:54:05 -0700270 fn #local_name(#(#all_args,)* #outparam) #ret;
David Tolnay7db73692019-10-20 14:51:12 -0400271 }
272}
273
Adrian Taylorc8713432020-10-21 18:20:55 -0700274fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
David Tolnay7db73692019-10-20 14:51:12 -0400275 let doc = &efn.doc;
Adrian Taylorc8713432020-10-21 18:20:55 -0700276 let decl = expand_cxx_function_decl(efn, types);
David Tolnayfb6e3862020-04-20 01:33:23 -0700277 let receiver = efn.receiver.iter().map(|receiver| {
David Tolnayf9ffb932020-04-20 02:22:57 -0700278 let var = receiver.var;
David Tolnayc9673842020-11-15 16:26:10 -0800279 if receiver.pinned {
280 let ty = receiver.ty();
281 quote!(#var: #ty)
282 } else {
283 let ampersand = receiver.ampersand;
284 let mutability = receiver.mutability;
285 quote!(#ampersand #mutability #var)
286 }
David Tolnayfb6e3862020-04-20 01:33:23 -0700287 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700288 let args = efn.args.iter().map(|arg| quote!(#arg));
289 let all_args = receiver.chain(args);
David Tolnayebef4a22020-03-17 15:33:47 -0700290 let ret = if efn.throws {
291 let ok = match &efn.ret {
292 Some(ret) => quote!(#ret),
293 None => quote!(()),
294 };
295 quote!(-> ::std::result::Result<#ok, ::cxx::Exception>)
296 } else {
297 expand_return_type(&efn.ret)
298 };
David Tolnay1e548172020-03-16 13:37:09 -0700299 let indirect_return = indirect_return(efn, types);
David Tolnayf9ffb932020-04-20 02:22:57 -0700300 let receiver_var = efn
301 .receiver
302 .iter()
303 .map(|receiver| receiver.var.to_token_stream());
Joel Galenson3d4f6122020-04-07 15:54:05 -0700304 let arg_vars = efn.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400305 let var = &arg.ident;
306 match &arg.ty {
Adrian Taylorc8713432020-10-21 18:20:55 -0700307 Type::Ident(ident) if ident.rust == RustString => {
David Tolnaya46a2372020-03-06 10:03:48 -0800308 quote!(#var.as_mut_ptr() as *const ::cxx::private::RustString)
David Tolnay7db73692019-10-20 14:51:12 -0400309 }
310 Type::RustBox(_) => quote!(::std::boxed::Box::into_raw(#var)),
David Tolnaya4170652020-11-15 17:00:59 -0800311 Type::UniquePtr(ptr) => match ptr.pinned {
312 false => quote!(::cxx::UniquePtr::into_raw(#var)),
313 true => quote!(::cxx::UniquePtr::__pin_into_raw(#var)),
314 },
David Tolnay313b10e2020-04-25 16:30:51 -0700315 Type::RustVec(_) => quote!(#var.as_mut_ptr() as *const ::cxx::private::RustVec<_>),
David Tolnay7db73692019-10-20 14:51:12 -0400316 Type::Ref(ty) => match &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -0700317 Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
David Tolnayf1c7f322020-08-27 00:46:01 -0700318 None => quote!(::cxx::private::RustString::from_ref(#var)),
319 Some(_) => quote!(::cxx::private::RustString::from_mut(#var)),
320 },
David Tolnay33f56ad2020-08-27 17:06:35 -0700321 Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
322 None => quote!(::cxx::private::RustVec::from_ref_vec_string(#var)),
323 Some(_) => quote!(::cxx::private::RustVec::from_mut_vec_string(#var)),
324 },
David Tolnayf1c7f322020-08-27 00:46:01 -0700325 Type::RustVec(_) => match ty.mutability {
326 None => quote!(::cxx::private::RustVec::from_ref(#var)),
327 Some(_) => quote!(::cxx::private::RustVec::from_mut(#var)),
328 },
David Tolnay4cb97672020-11-02 16:13:34 -0800329 inner if types.is_considered_improper_ctype(inner) => match ty.mutability {
330 None => quote!(#var as *const #inner as *const ::std::ffi::c_void),
331 Some(_) => quote!(#var as *mut #inner as *mut ::std::ffi::c_void),
332 },
David Tolnay7db73692019-10-20 14:51:12 -0400333 _ => quote!(#var),
334 },
335 Type::Str(_) => quote!(::cxx::private::RustStr::from(#var)),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700336 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8::from(#var)),
David Tolnay7db73692019-10-20 14:51:12 -0400337 ty if types.needs_indirect_abi(ty) => quote!(#var.as_mut_ptr()),
338 _ => quote!(#var),
339 }
340 });
Joel Galenson3d4f6122020-04-07 15:54:05 -0700341 let vars = receiver_var.chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700342 let trampolines = efn
343 .args
344 .iter()
345 .filter_map(|arg| {
346 if let Type::Fn(f) = &arg.ty {
347 let var = &arg.ident;
Adrian Taylorc8713432020-10-21 18:20:55 -0700348 Some(expand_function_pointer_trampoline(efn, var, f, types))
David Tolnay75dca2e2020-03-25 20:17:52 -0700349 } else {
350 None
351 }
352 })
353 .collect::<TokenStream>();
David Tolnay7db73692019-10-20 14:51:12 -0400354 let mut setup = efn
355 .args
356 .iter()
357 .filter(|arg| types.needs_indirect_abi(&arg.ty))
358 .map(|arg| {
359 let var = &arg.ident;
360 // These are arguments for which C++ has taken ownership of the data
361 // behind the mut reference it received.
362 quote! {
David Tolnaycb4ee4b2020-04-24 11:28:08 -0700363 let mut #var = ::std::mem::MaybeUninit::new(#var);
David Tolnay7db73692019-10-20 14:51:12 -0400364 }
365 })
366 .collect::<TokenStream>();
David Tolnay17a934c2020-11-02 00:40:04 -0800367 let local_name = format_ident!("__{}", efn.name.rust);
David Tolnay7db73692019-10-20 14:51:12 -0400368 let call = if indirect_return {
David Tolnay2f090422020-11-02 18:25:35 -0800369 let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
David Tolnay7db73692019-10-20 14:51:12 -0400370 setup.extend(quote! {
371 let mut __return = ::std::mem::MaybeUninit::<#ret>::uninit();
David Tolnay7db73692019-10-20 14:51:12 -0400372 });
David Tolnay0d524852020-11-02 17:21:50 -0800373 setup.extend(if efn.throws {
374 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700375 #local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
David Tolnay0d524852020-11-02 17:21:50 -0800376 }
David Tolnayebef4a22020-03-17 15:33:47 -0700377 } else {
David Tolnay0d524852020-11-02 17:21:50 -0800378 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700379 #local_name(#(#vars,)* __return.as_mut_ptr());
David Tolnay0d524852020-11-02 17:21:50 -0800380 }
381 });
382 quote!(__return.assume_init())
David Tolnayebef4a22020-03-17 15:33:47 -0700383 } else if efn.throws {
David Tolnay7db73692019-10-20 14:51:12 -0400384 quote! {
David Tolnayebef4a22020-03-17 15:33:47 -0700385 #local_name(#(#vars),*).exception()
David Tolnay7db73692019-10-20 14:51:12 -0400386 }
387 } else {
388 quote! {
389 #local_name(#(#vars),*)
390 }
391 };
David Tolnay2debdb22020-11-02 17:14:53 -0800392 let mut expr;
393 if efn.throws && efn.sig.ret.is_none() {
394 expr = call;
Myron Ahn84849302020-03-25 22:00:58 +0700395 } else {
David Tolnay0d524852020-11-02 17:21:50 -0800396 expr = match &efn.ret {
397 None => call,
398 Some(ret) => match ret {
399 Type::Ident(ident) if ident.rust == RustString => quote!(#call.into_string()),
400 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#call)),
David Tolnaya1f02e52020-11-02 17:10:54 -0800401 Type::RustVec(vec) => {
402 if vec.inner == RustString {
David Tolnay0d524852020-11-02 17:21:50 -0800403 quote!(#call.into_vec_string())
David Tolnaya1f02e52020-11-02 17:10:54 -0800404 } else {
David Tolnay0d524852020-11-02 17:21:50 -0800405 quote!(#call.into_vec())
David Tolnaya1f02e52020-11-02 17:10:54 -0800406 }
David Tolnay33f56ad2020-08-27 17:06:35 -0700407 }
David Tolnaya4170652020-11-15 17:00:59 -0800408 Type::UniquePtr(ptr) => match ptr.pinned {
409 false => quote!(::cxx::UniquePtr::from_raw(#call)),
410 true => quote!(::cxx::UniquePtr::__pin_from_raw(#call)),
411 },
David Tolnaya1f02e52020-11-02 17:10:54 -0800412 Type::Ref(ty) => match &ty.inner {
413 Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
David Tolnay0d524852020-11-02 17:21:50 -0800414 None => quote!(#call.as_string()),
415 Some(_) => quote!(#call.as_mut_string()),
David Tolnaya1f02e52020-11-02 17:10:54 -0800416 },
417 Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
David Tolnay0d524852020-11-02 17:21:50 -0800418 None => quote!(#call.as_vec_string()),
419 Some(_) => quote!(#call.as_mut_vec_string()),
David Tolnaya1f02e52020-11-02 17:10:54 -0800420 },
421 Type::RustVec(_) => match ty.mutability {
David Tolnay0d524852020-11-02 17:21:50 -0800422 None => quote!(#call.as_vec()),
423 Some(_) => quote!(#call.as_mut_vec()),
David Tolnaya1f02e52020-11-02 17:10:54 -0800424 },
David Tolnay4cb97672020-11-02 16:13:34 -0800425 inner if types.is_considered_improper_ctype(inner) => {
426 let mutability = ty.mutability;
427 quote!(&#mutability *#call.cast())
428 }
David Tolnay0d524852020-11-02 17:21:50 -0800429 _ => call,
David Tolnayf1c7f322020-08-27 00:46:01 -0700430 },
David Tolnay0d524852020-11-02 17:21:50 -0800431 Type::Str(_) => quote!(#call.as_str()),
432 Type::SliceRefU8(_) => quote!(#call.as_slice()),
433 _ => call,
434 },
435 };
David Tolnay2debdb22020-11-02 17:14:53 -0800436 if efn.throws {
437 expr = quote!(::std::result::Result::Ok(#expr));
438 }
David Tolnaya1f02e52020-11-02 17:10:54 -0800439 };
David Tolnaye67bcf52020-09-06 23:50:44 -0700440 let mut dispatch = quote!(#setup #expr);
441 let unsafety = &efn.sig.unsafety;
442 if unsafety.is_none() {
443 dispatch = quote!(unsafe { #dispatch });
444 }
David Tolnay17a934c2020-11-02 00:40:04 -0800445 let ident = &efn.name.rust;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700446 let function_shim = quote! {
447 #doc
David Tolnaye67bcf52020-09-06 23:50:44 -0700448 pub #unsafety fn #ident(#(#all_args,)*) #ret {
David Tolnayc66cdbb2020-04-20 01:41:15 -0700449 extern "C" {
450 #decl
David Tolnay7db73692019-10-20 14:51:12 -0400451 }
David Tolnayc66cdbb2020-04-20 01:41:15 -0700452 #trampolines
David Tolnaye67bcf52020-09-06 23:50:44 -0700453 #dispatch
David Tolnayc66cdbb2020-04-20 01:41:15 -0700454 }
455 };
456 match &efn.receiver {
457 None => function_shim,
458 Some(receiver) => {
David Tolnay05e11cc2020-04-20 02:13:56 -0700459 let receiver_type = &receiver.ty;
David Tolnayc66cdbb2020-04-20 01:41:15 -0700460 quote!(impl #receiver_type { #function_shim })
461 }
David Tolnay7db73692019-10-20 14:51:12 -0400462 }
463}
464
David Tolnay75dca2e2020-03-25 20:17:52 -0700465fn expand_function_pointer_trampoline(
David Tolnay75dca2e2020-03-25 20:17:52 -0700466 efn: &ExternFn,
467 var: &Ident,
468 sig: &Signature,
469 types: &Types,
470) -> TokenStream {
Adrian Taylorc8713432020-10-21 18:20:55 -0700471 let c_trampoline = mangle::c_trampoline(efn, var, types);
472 let r_trampoline = mangle::r_trampoline(efn, var, types);
David Tolnay75dca2e2020-03-25 20:17:52 -0700473 let local_name = parse_quote!(__);
David Tolnay17a934c2020-11-02 00:40:04 -0800474 let catch_unwind_label = format!("::{}::{}", efn.name.rust, var);
David Tolnay75dca2e2020-03-25 20:17:52 -0700475 let shim = expand_rust_function_shim_impl(
476 sig,
477 types,
478 &r_trampoline,
479 local_name,
480 catch_unwind_label,
481 None,
482 );
483
484 quote! {
485 let #var = ::cxx::private::FatFunction {
486 trampoline: {
487 extern "C" {
488 #[link_name = #c_trampoline]
489 fn trampoline();
490 }
491 #shim
492 trampoline as usize as *const ()
493 },
494 ptr: #var as usize as *const (),
495 };
496 }
497}
498
David Tolnay7db73692019-10-20 14:51:12 -0400499fn expand_rust_type(ety: &ExternType) -> TokenStream {
David Tolnay17a934c2020-11-02 00:40:04 -0800500 let ident = &ety.name.rust;
David Tolnay7db73692019-10-20 14:51:12 -0400501 quote! {
502 use super::#ident;
503 }
504}
505
David Tolnayc8361022020-08-25 21:57:53 -0700506fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream {
507 // Rustc will render as follows if not sized:
508 //
509 // type TheirType;
510 // -----^^^^^^^^^-
511 // | |
512 // | doesn't have a size known at compile-time
513 // required by this bound in `ffi::_::__AssertSized`
514
David Tolnay17a934c2020-11-02 00:40:04 -0800515 let ident = &ety.name.rust;
David Tolnayc8361022020-08-25 21:57:53 -0700516 let begin_span = Token![::](ety.type_token.span);
517 let sized = quote_spanned! {ety.semi_token.span=>
518 #begin_span std::marker::Sized
519 };
David Tolnay20fa62b2020-11-15 17:34:05 -0800520 let unpin = quote_spanned! {ety.semi_token.span=>
521 #begin_span std::marker::Unpin
522 };
David Tolnay17a934c2020-11-02 00:40:04 -0800523 quote_spanned! {ident.span()=>
David Tolnayc8361022020-08-25 21:57:53 -0700524 let _ = {
525 fn __AssertSized<T: ?#sized + #sized>() {}
David Tolnay20fa62b2020-11-15 17:34:05 -0800526 fn __AssertUnpin<T: #unpin>() {}
527 (__AssertSized::<#ident>, __AssertUnpin::<#ident>)
David Tolnayc8361022020-08-25 21:57:53 -0700528 };
529 }
530}
531
Adrian Taylorc8713432020-10-21 18:20:55 -0700532fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
533 let link_name = mangle::extern_fn(efn, types);
David Tolnay17a934c2020-11-02 00:40:04 -0800534 let local_name = format_ident!("__{}", efn.name.rust);
535 let catch_unwind_label = format!("::{}", efn.name.rust);
536 let invoke = Some(&efn.name.rust);
David Tolnay75dca2e2020-03-25 20:17:52 -0700537 expand_rust_function_shim_impl(
538 efn,
539 types,
540 &link_name,
541 local_name,
542 catch_unwind_label,
543 invoke,
544 )
545}
546
547fn expand_rust_function_shim_impl(
548 sig: &Signature,
549 types: &Types,
David Tolnay891061b2020-04-19 22:42:33 -0700550 link_name: &Symbol,
David Tolnay75dca2e2020-03-25 20:17:52 -0700551 local_name: Ident,
552 catch_unwind_label: String,
553 invoke: Option<&Ident>,
554) -> TokenStream {
David Tolnayf9ffb932020-04-20 02:22:57 -0700555 let receiver_var = sig
556 .receiver
557 .as_ref()
558 .map(|receiver| quote_spanned!(receiver.var.span=> __self));
David Tolnay18ba92c2020-04-22 16:17:30 -0700559 let receiver = sig.receiver.as_ref().map(|receiver| {
560 let receiver_type = receiver.ty();
561 quote!(#receiver_var: #receiver_type)
562 });
David Tolnay75dca2e2020-03-25 20:17:52 -0700563 let args = sig.args.iter().map(|arg| {
David Tolnay39d575f2020-03-03 00:10:56 -0800564 let ident = &arg.ident;
David Tolnay2f090422020-11-02 18:25:35 -0800565 let ty = expand_extern_type(&arg.ty, types, false);
David Tolnay39d575f2020-03-03 00:10:56 -0800566 if types.needs_indirect_abi(&arg.ty) {
567 quote!(#ident: *mut #ty)
568 } else {
569 quote!(#ident: #ty)
570 }
571 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700572 let all_args = receiver.into_iter().chain(args);
David Tolnay75dca2e2020-03-25 20:17:52 -0700573
David Tolnay3a45f2d2020-04-20 01:51:12 -0700574 let arg_vars = sig.args.iter().map(|arg| {
David Tolnay7db73692019-10-20 14:51:12 -0400575 let ident = &arg.ident;
David Tolnay17955e22020-01-20 17:58:24 -0800576 match &arg.ty {
Adrian Taylorc8713432020-10-21 18:20:55 -0700577 Type::Ident(i) if i.rust == RustString => {
David Tolnaycc3767f2020-03-06 10:41:51 -0800578 quote!(::std::mem::take((*#ident).as_mut_string()))
579 }
David Tolnay40226ab2020-03-03 00:05:35 -0800580 Type::RustBox(_) => quote!(::std::boxed::Box::from_raw(#ident)),
David Tolnay33f56ad2020-08-27 17:06:35 -0700581 Type::RustVec(vec) => {
582 if vec.inner == RustString {
583 quote!(::std::mem::take((*#ident).as_mut_vec_string()))
584 } else {
585 quote!(::std::mem::take((*#ident).as_mut_vec()))
586 }
587 }
David Tolnaya4170652020-11-15 17:00:59 -0800588 Type::UniquePtr(ptr) => match ptr.pinned {
589 false => quote!(::cxx::UniquePtr::from_raw(#ident)),
590 true => quote!(::cxx::UniquePtr::__pin_from_raw(#ident)),
591 },
David Tolnay17955e22020-01-20 17:58:24 -0800592 Type::Ref(ty) => match &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -0700593 Type::Ident(i) if i.rust == RustString => match ty.mutability {
David Tolnayf1c7f322020-08-27 00:46:01 -0700594 None => quote!(#ident.as_string()),
595 Some(_) => quote!(#ident.as_mut_string()),
596 },
David Tolnay33f56ad2020-08-27 17:06:35 -0700597 Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
598 None => quote!(#ident.as_vec_string()),
599 Some(_) => quote!(#ident.as_mut_vec_string()),
600 },
David Tolnayf1c7f322020-08-27 00:46:01 -0700601 Type::RustVec(_) => match ty.mutability {
602 None => quote!(#ident.as_vec()),
603 Some(_) => quote!(#ident.as_mut_vec()),
604 },
David Tolnay40226ab2020-03-03 00:05:35 -0800605 _ => quote!(#ident),
David Tolnay17955e22020-01-20 17:58:24 -0800606 },
David Tolnay40226ab2020-03-03 00:05:35 -0800607 Type::Str(_) => quote!(#ident.as_str()),
Adrian Taylorf5dd5522020-04-13 16:50:14 -0700608 Type::SliceRefU8(_) => quote!(#ident.as_slice()),
David Tolnay40226ab2020-03-03 00:05:35 -0800609 ty if types.needs_indirect_abi(ty) => quote!(::std::ptr::read(#ident)),
610 _ => quote!(#ident),
David Tolnay7db73692019-10-20 14:51:12 -0400611 }
612 });
David Tolnayf9ffb932020-04-20 02:22:57 -0700613 let vars = receiver_var.into_iter().chain(arg_vars);
David Tolnay75dca2e2020-03-25 20:17:52 -0700614
David Tolnay26219462020-11-09 19:47:28 -0800615 let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke));
616
David Tolnay75dca2e2020-03-25 20:17:52 -0700617 let mut call = match invoke {
David Tolnay26219462020-11-09 19:47:28 -0800618 Some(_) => quote!(#local_name),
David Tolnayb8ebeb02020-10-31 23:52:06 -0700619 None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)),
David Tolnay75dca2e2020-03-25 20:17:52 -0700620 };
621 call.extend(quote! { (#(#vars),*) });
622
David Tolnay22602b42020-09-21 18:04:05 -0400623 let conversion = sig.ret.as_ref().and_then(|ret| match ret {
Adrian Taylorc8713432020-10-21 18:20:55 -0700624 Type::Ident(ident) if ident.rust == RustString => {
625 Some(quote!(::cxx::private::RustString::from))
626 }
David Tolnay22602b42020-09-21 18:04:05 -0400627 Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)),
628 Type::RustVec(vec) => {
629 if vec.inner == RustString {
630 Some(quote!(::cxx::private::RustVec::from_vec_string))
631 } else {
632 Some(quote!(::cxx::private::RustVec::from))
David Tolnay7db73692019-10-20 14:51:12 -0400633 }
David Tolnay22602b42020-09-21 18:04:05 -0400634 }
David Tolnaya4170652020-11-15 17:00:59 -0800635 Type::UniquePtr(ptr) => match ptr.pinned {
636 false => Some(quote!(::cxx::UniquePtr::into_raw)),
637 true => Some(quote!(::cxx::UniquePtr::__pin_into_raw)),
638 },
David Tolnay22602b42020-09-21 18:04:05 -0400639 Type::Ref(ty) => match &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -0700640 Type::Ident(ident) if ident.rust == RustString => match ty.mutability {
David Tolnay22602b42020-09-21 18:04:05 -0400641 None => Some(quote!(::cxx::private::RustString::from_ref)),
642 Some(_) => Some(quote!(::cxx::private::RustString::from_mut)),
David Tolnay7db73692019-10-20 14:51:12 -0400643 },
David Tolnay22602b42020-09-21 18:04:05 -0400644 Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
645 None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)),
646 Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)),
647 },
648 Type::RustVec(_) => match ty.mutability {
649 None => Some(quote!(::cxx::private::RustVec::from_ref)),
650 Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)),
651 },
David Tolnay7db73692019-10-20 14:51:12 -0400652 _ => None,
David Tolnay22602b42020-09-21 18:04:05 -0400653 },
654 Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
655 Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
656 _ => None,
657 });
658
659 let mut expr = match conversion {
660 None => call,
661 Some(conversion) if !sig.throws => quote!(#conversion(#call)),
662 Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)),
663 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700664
665 let mut outparam = None;
666 let indirect_return = indirect_return(sig, types);
David Tolnay1e548172020-03-16 13:37:09 -0700667 if indirect_return {
David Tolnay2f090422020-11-02 18:25:35 -0800668 let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false);
David Tolnay75dca2e2020-03-25 20:17:52 -0700669 outparam = Some(quote!(__return: *mut #ret,));
David Tolnay1e548172020-03-16 13:37:09 -0700670 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700671 if sig.throws {
672 let out = match sig.ret {
David Tolnaycecada62020-03-17 01:45:58 -0700673 Some(_) => quote!(__return),
674 None => quote!(&mut ()),
675 };
676 expr = quote!(::cxx::private::r#try(#out, #expr));
David Tolnay1e548172020-03-16 13:37:09 -0700677 } else if indirect_return {
David Tolnay7db73692019-10-20 14:51:12 -0400678 expr = quote!(::std::ptr::write(__return, #expr));
679 }
David Tolnay75dca2e2020-03-25 20:17:52 -0700680
David Tolnay1e548172020-03-16 13:37:09 -0700681 expr = quote!(::cxx::private::catch_unwind(__fn, move || #expr));
David Tolnay75dca2e2020-03-25 20:17:52 -0700682
683 let ret = if sig.throws {
David Tolnay486b6ec2020-03-17 01:19:57 -0700684 quote!(-> ::cxx::private::Result)
David Tolnay1e548172020-03-16 13:37:09 -0700685 } else {
David Tolnay2f090422020-11-02 18:25:35 -0800686 expand_extern_return_type(&sig.ret, types, false)
David Tolnay1e548172020-03-16 13:37:09 -0700687 };
David Tolnay75dca2e2020-03-25 20:17:52 -0700688
689 let pointer = match invoke {
David Tolnayb8ebeb02020-10-31 23:52:06 -0700690 None => Some(quote!(__extern: *const ())),
David Tolnay75dca2e2020-03-25 20:17:52 -0700691 Some(_) => None,
692 };
693
David Tolnay7db73692019-10-20 14:51:12 -0400694 quote! {
695 #[doc(hidden)]
696 #[export_name = #link_name]
Joel Galensonc1c4e7a2020-04-15 10:21:00 -0700697 unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
David Tolnay7db73692019-10-20 14:51:12 -0400698 let __fn = concat!(module_path!(), #catch_unwind_label);
David Tolnay26219462020-11-09 19:47:28 -0800699 #wrap_super
David Tolnay7db73692019-10-20 14:51:12 -0400700 #expr
701 }
702 }
703}
704
David Tolnay26219462020-11-09 19:47:28 -0800705// A wrapper like `fn f(x: Arg) { super::f(x) }` just to ensure we have the
706// accurate unsafety declaration and no problematic elided lifetimes.
707fn expand_rust_function_shim_super(
708 sig: &Signature,
709 local_name: &Ident,
710 invoke: &Ident,
711) -> TokenStream {
712 let unsafety = sig.unsafety;
713
714 let receiver_var = sig
715 .receiver
716 .as_ref()
717 .map(|receiver| Ident::new("__self", receiver.var.span));
718 let receiver = sig.receiver.iter().map(|receiver| {
719 let receiver_type = receiver.ty();
720 quote!(#receiver_var: #receiver_type)
721 });
722 let args = sig.args.iter().map(|arg| quote!(#arg));
723 let all_args = receiver.chain(args);
724
David Tolnay5d164672020-11-09 20:23:17 -0800725 let ret = if let Some((result, _langle, rangle)) = sig.throws_tokens {
David Tolnay26219462020-11-09 19:47:28 -0800726 let ok = match &sig.ret {
727 Some(ret) => quote!(#ret),
728 None => quote!(()),
729 };
David Tolnay5d164672020-11-09 20:23:17 -0800730 let impl_trait = quote_spanned!(result.span=> impl);
731 let display = quote_spanned!(rangle.span=> ::std::fmt::Display);
732 quote!(-> ::std::result::Result<#ok, #impl_trait #display>)
David Tolnay26219462020-11-09 19:47:28 -0800733 } else {
734 expand_return_type(&sig.ret)
735 };
736
737 let arg_vars = sig.args.iter().map(|arg| &arg.ident);
738 let vars = receiver_var.iter().chain(arg_vars);
739
740 let span = invoke.span();
741 let call = match &sig.receiver {
742 None => quote_spanned!(span=> super::#invoke),
743 Some(receiver) => {
744 let receiver_type = &receiver.ty;
745 quote_spanned!(span=> #receiver_type::#invoke)
746 }
747 };
748
749 quote_spanned! {span=>
750 #unsafety fn #local_name(#(#all_args,)*) #ret {
751 #call(#(#vars,)*)
752 }
753 }
754}
755
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700756fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
Bryan Henry890083d2020-09-13 10:34:31 -0700757 let doc = &alias.doc;
David Tolnay17a934c2020-11-02 00:40:04 -0800758 let ident = &alias.name.rust;
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700759 let ty = &alias.ty;
760 quote! {
Bryan Henry890083d2020-09-13 10:34:31 -0700761 #doc
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700762 pub type #ident = #ty;
763 }
764}
765
Adrian Taylorc8713432020-10-21 18:20:55 -0700766fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
David Tolnay17a934c2020-11-02 00:40:04 -0800767 let ident = &alias.name.rust;
768 let type_id = type_id(&alias.name);
David Tolnay83fe0f02020-05-07 20:00:15 -0700769 let begin_span = alias.type_token.span;
770 let end_span = alias.semi_token.span;
771 let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
772 let end = quote_spanned!(end_span=> >);
773
David Tolnay63a0e4e2020-10-03 18:52:32 -0700774 let mut verify = quote! {
David Tolnay83fe0f02020-05-07 20:00:15 -0700775 const _: fn() = #begin #ident, #type_id #end;
David Tolnay63a0e4e2020-10-03 18:52:32 -0700776 };
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700777
David Tolnay17a934c2020-11-02 00:40:04 -0800778 if types.required_trivial.contains_key(&alias.name.rust) {
David Tolnay63a0e4e2020-10-03 18:52:32 -0700779 let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
780 verify.extend(quote! {
781 const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
782 });
Adrian Taylorc7043292020-09-25 12:48:36 -0700783 }
David Tolnay63a0e4e2020-10-03 18:52:32 -0700784
785 verify
Adrian Taylorc7043292020-09-25 12:48:36 -0700786}
787
David Tolnay8faec772020-11-02 00:18:19 -0800788fn type_id(name: &Pair) -> TokenStream {
789 let path = name.to_fully_qualified();
David Tolnay5f9e8ca2020-05-07 17:21:05 -0700790 quote! {
791 ::cxx::type_id!(#path)
792 }
793}
794
Adrian Taylorc8713432020-10-21 18:20:55 -0700795fn expand_rust_box(ident: &ResolvableName, types: &Types) -> TokenStream {
796 let link_prefix = format!("cxxbridge05$box${}$", types.resolve(ident).to_symbol());
David Tolnay7db73692019-10-20 14:51:12 -0400797 let link_uninit = format!("{}uninit", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400798 let link_drop = format!("{}drop", link_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400799
Adrian Taylorc8713432020-10-21 18:20:55 -0700800 let local_prefix = format_ident!("{}__box_", &ident.rust);
David Tolnay7db73692019-10-20 14:51:12 -0400801 let local_uninit = format_ident!("{}uninit", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400802 let local_drop = format_ident!("{}drop", local_prefix);
David Tolnay7db73692019-10-20 14:51:12 -0400803
804 let span = ident.span();
805 quote_spanned! {span=>
806 #[doc(hidden)]
807 #[export_name = #link_uninit]
808 unsafe extern "C" fn #local_uninit(
809 this: *mut ::std::boxed::Box<::std::mem::MaybeUninit<#ident>>,
810 ) {
811 ::std::ptr::write(
812 this,
813 ::std::boxed::Box::new(::std::mem::MaybeUninit::uninit()),
814 );
815 }
816 #[doc(hidden)]
David Tolnay7db73692019-10-20 14:51:12 -0400817 #[export_name = #link_drop]
818 unsafe extern "C" fn #local_drop(this: *mut ::std::boxed::Box<#ident>) {
819 ::std::ptr::drop_in_place(this);
820 }
David Tolnay7db73692019-10-20 14:51:12 -0400821 }
822}
823
Adrian Taylorc8713432020-10-21 18:20:55 -0700824fn expand_rust_vec(elem: &ResolvableName, types: &Types) -> TokenStream {
825 let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol(types));
David Tolnayf97c2d52020-04-25 16:37:48 -0700826 let link_new = format!("{}new", link_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700827 let link_drop = format!("{}drop", link_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700828 let link_len = format!("{}len", link_prefix);
David Tolnay219c0792020-04-24 20:31:37 -0700829 let link_data = format!("{}data", link_prefix);
David Tolnayfb6b73c2020-11-10 14:32:16 -0800830 let link_reserve_total = format!("{}reserve_total", link_prefix);
831 let link_set_len = format!("{}set_len", link_prefix);
David Tolnay503d0192020-04-24 22:18:56 -0700832 let link_stride = format!("{}stride", link_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700833
Adrian Taylorc8713432020-10-21 18:20:55 -0700834 let local_prefix = format_ident!("{}__vec_", elem.rust);
David Tolnayf97c2d52020-04-25 16:37:48 -0700835 let local_new = format_ident!("{}new", local_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700836 let local_drop = format_ident!("{}drop", local_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700837 let local_len = format_ident!("{}len", local_prefix);
David Tolnay219c0792020-04-24 20:31:37 -0700838 let local_data = format_ident!("{}data", local_prefix);
David Tolnayfb6b73c2020-11-10 14:32:16 -0800839 let local_reserve_total = format_ident!("{}reserve_total", local_prefix);
840 let local_set_len = format_ident!("{}set_len", local_prefix);
David Tolnay503d0192020-04-24 22:18:56 -0700841 let local_stride = format_ident!("{}stride", local_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700842
David Tolnay83a7ec92020-04-25 12:37:42 -0700843 let span = elem.span();
Myron Ahneba35cf2020-02-05 19:41:51 +0700844 quote_spanned! {span=>
845 #[doc(hidden)]
David Tolnayf97c2d52020-04-25 16:37:48 -0700846 #[export_name = #link_new]
847 unsafe extern "C" fn #local_new(this: *mut ::cxx::private::RustVec<#elem>) {
848 ::std::ptr::write(this, ::cxx::private::RustVec::new());
849 }
850 #[doc(hidden)]
Myron Ahneba35cf2020-02-05 19:41:51 +0700851 #[export_name = #link_drop]
David Tolnay83a7ec92020-04-25 12:37:42 -0700852 unsafe extern "C" fn #local_drop(this: *mut ::cxx::private::RustVec<#elem>) {
David Tolnay4c64afb2020-04-24 11:30:18 -0700853 ::std::ptr::drop_in_place(this);
Myron Ahneba35cf2020-02-05 19:41:51 +0700854 }
David Tolnay85db5a02020-04-25 13:17:27 -0700855 #[doc(hidden)]
Myron Ahneba35cf2020-02-05 19:41:51 +0700856 #[export_name = #link_len]
David Tolnay83a7ec92020-04-25 12:37:42 -0700857 unsafe extern "C" fn #local_len(this: *const ::cxx::private::RustVec<#elem>) -> usize {
David Tolnay2cef5df2020-04-24 11:33:58 -0700858 (*this).len()
Myron Ahneba35cf2020-02-05 19:41:51 +0700859 }
David Tolnay219c0792020-04-24 20:31:37 -0700860 #[doc(hidden)]
861 #[export_name = #link_data]
David Tolnay83a7ec92020-04-25 12:37:42 -0700862 unsafe extern "C" fn #local_data(this: *const ::cxx::private::RustVec<#elem>) -> *const #elem {
David Tolnay219c0792020-04-24 20:31:37 -0700863 (*this).as_ptr()
864 }
David Tolnay503d0192020-04-24 22:18:56 -0700865 #[doc(hidden)]
David Tolnayfb6b73c2020-11-10 14:32:16 -0800866 #[export_name = #link_reserve_total]
867 unsafe extern "C" fn #local_reserve_total(this: *mut ::cxx::private::RustVec<#elem>, cap: usize) {
868 (*this).reserve_total(cap);
869 }
870 #[doc(hidden)]
871 #[export_name = #link_set_len]
872 unsafe extern "C" fn #local_set_len(this: *mut ::cxx::private::RustVec<#elem>, len: usize) {
873 (*this).set_len(len);
874 }
875 #[doc(hidden)]
David Tolnay503d0192020-04-24 22:18:56 -0700876 #[export_name = #link_stride]
877 unsafe extern "C" fn #local_stride() -> usize {
David Tolnay83a7ec92020-04-25 12:37:42 -0700878 ::std::mem::size_of::<#elem>()
David Tolnay503d0192020-04-24 22:18:56 -0700879 }
Myron Ahneba35cf2020-02-05 19:41:51 +0700880 }
881}
882
David Tolnay0531f432020-10-03 23:50:28 -0700883fn expand_unique_ptr(
Adrian Taylorc8713432020-10-21 18:20:55 -0700884 ident: &ResolvableName,
David Tolnay0531f432020-10-03 23:50:28 -0700885 types: &Types,
886 explicit_impl: Option<&Impl>,
887) -> TokenStream {
Adrian Taylorc8713432020-10-21 18:20:55 -0700888 let name = ident.rust.to_string();
889 let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol(types));
David Tolnay7db73692019-10-20 14:51:12 -0400890 let link_null = format!("{}null", prefix);
891 let link_new = format!("{}new", prefix);
892 let link_raw = format!("{}raw", prefix);
893 let link_get = format!("{}get", prefix);
894 let link_release = format!("{}release", prefix);
895 let link_drop = format!("{}drop", prefix);
896
Adrian Taylorc8713432020-10-21 18:20:55 -0700897 let new_method =
898 if types.structs.contains_key(&ident.rust) || types.aliases.contains_key(&ident.rust) {
899 Some(quote! {
900 fn __new(mut value: Self) -> *mut ::std::ffi::c_void {
901 extern "C" {
902 #[link_name = #link_new]
903 fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident);
904 }
905 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
906 unsafe { __new(&mut repr, &mut value) }
907 repr
David Tolnay53838912020-04-09 20:56:44 -0700908 }
Adrian Taylorc8713432020-10-21 18:20:55 -0700909 })
910 } else {
911 None
912 };
David Tolnay53838912020-04-09 20:56:44 -0700913
David Tolnay0531f432020-10-03 23:50:28 -0700914 let begin_span =
915 explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
916 let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
917 let unsafe_token = format_ident!("unsafe", span = begin_span);
918
919 quote_spanned! {end_span=>
920 #unsafe_token impl ::cxx::private::UniquePtrTarget for #ident {
David Tolnay3b40b6f2020-04-24 17:58:24 -0700921 const __NAME: &'static dyn ::std::fmt::Display = &#name;
David Tolnay7db73692019-10-20 14:51:12 -0400922 fn __null() -> *mut ::std::ffi::c_void {
923 extern "C" {
924 #[link_name = #link_null]
925 fn __null(this: *mut *mut ::std::ffi::c_void);
926 }
927 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
928 unsafe { __null(&mut repr) }
929 repr
930 }
David Tolnay53838912020-04-09 20:56:44 -0700931 #new_method
David Tolnay7db73692019-10-20 14:51:12 -0400932 unsafe fn __raw(raw: *mut Self) -> *mut ::std::ffi::c_void {
933 extern "C" {
934 #[link_name = #link_raw]
David Tolnay3b40b6f2020-04-24 17:58:24 -0700935 fn __raw(this: *mut *mut ::std::ffi::c_void, raw: *mut #ident);
David Tolnay7db73692019-10-20 14:51:12 -0400936 }
937 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
938 __raw(&mut repr, raw);
939 repr
940 }
941 unsafe fn __get(repr: *mut ::std::ffi::c_void) -> *const Self {
942 extern "C" {
943 #[link_name = #link_get]
David Tolnay3b40b6f2020-04-24 17:58:24 -0700944 fn __get(this: *const *mut ::std::ffi::c_void) -> *const #ident;
David Tolnay7db73692019-10-20 14:51:12 -0400945 }
946 __get(&repr)
947 }
948 unsafe fn __release(mut repr: *mut ::std::ffi::c_void) -> *mut Self {
949 extern "C" {
950 #[link_name = #link_release]
David Tolnay3b40b6f2020-04-24 17:58:24 -0700951 fn __release(this: *mut *mut ::std::ffi::c_void) -> *mut #ident;
David Tolnay7db73692019-10-20 14:51:12 -0400952 }
953 __release(&mut repr)
954 }
955 unsafe fn __drop(mut repr: *mut ::std::ffi::c_void) {
956 extern "C" {
957 #[link_name = #link_drop]
958 fn __drop(this: *mut *mut ::std::ffi::c_void);
959 }
960 __drop(&mut repr);
961 }
962 }
963 }
964}
965
David Tolnay0531f432020-10-03 23:50:28 -0700966fn expand_cxx_vector(
Adrian Taylorc8713432020-10-21 18:20:55 -0700967 elem: &ResolvableName,
David Tolnay0531f432020-10-03 23:50:28 -0700968 explicit_impl: Option<&Impl>,
Adrian Taylorc8713432020-10-21 18:20:55 -0700969 types: &Types,
David Tolnay0531f432020-10-03 23:50:28 -0700970) -> TokenStream {
971 let _ = explicit_impl;
Adrian Taylorc8713432020-10-21 18:20:55 -0700972 let name = elem.rust.to_string();
973 let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol(types));
David Tolnaya83247c2020-04-24 14:36:10 -0700974 let link_size = format!("{}size", prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700975 let link_get_unchecked = format!("{}get_unchecked", prefix);
Adrian Taylorc8713432020-10-21 18:20:55 -0700976 let unique_ptr_prefix = format!(
977 "cxxbridge05$unique_ptr$std$vector${}$",
978 elem.to_symbol(types)
979 );
David Tolnay3b40b6f2020-04-24 17:58:24 -0700980 let link_unique_ptr_null = format!("{}null", unique_ptr_prefix);
981 let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix);
982 let link_unique_ptr_get = format!("{}get", unique_ptr_prefix);
983 let link_unique_ptr_release = format!("{}release", unique_ptr_prefix);
984 let link_unique_ptr_drop = format!("{}drop", unique_ptr_prefix);
Myron Ahneba35cf2020-02-05 19:41:51 +0700985
David Tolnay0531f432020-10-03 23:50:28 -0700986 let begin_span =
987 explicit_impl.map_or_else(Span::call_site, |explicit| explicit.impl_token.span);
988 let end_span = explicit_impl.map_or_else(Span::call_site, |explicit| explicit.brace_token.span);
989 let unsafe_token = format_ident!("unsafe", span = begin_span);
990
991 quote_spanned! {end_span=>
992 #unsafe_token impl ::cxx::private::VectorElement for #elem {
David Tolnay3b40b6f2020-04-24 17:58:24 -0700993 const __NAME: &'static dyn ::std::fmt::Display = &#name;
David Tolnay17631102020-04-24 19:01:30 -0700994 fn __vector_size(v: &::cxx::CxxVector<Self>) -> usize {
David Tolnayfa2119c2020-04-24 14:42:58 -0700995 extern "C" {
996 #[link_name = #link_size]
David Tolnay17631102020-04-24 19:01:30 -0700997 fn __vector_size(_: &::cxx::CxxVector<#elem>) -> usize;
Myron Ahneba35cf2020-02-05 19:41:51 +0700998 }
David Tolnayfa2119c2020-04-24 14:42:58 -0700999 unsafe { __vector_size(v) }
Myron Ahneba35cf2020-02-05 19:41:51 +07001000 }
David Tolnay93637ca2020-09-24 15:58:20 -04001001 unsafe fn __get_unchecked(v: &::cxx::CxxVector<Self>, pos: usize) -> *const Self {
David Tolnaycc75ad22020-04-24 14:45:16 -07001002 extern "C" {
1003 #[link_name = #link_get_unchecked]
David Tolnay17631102020-04-24 19:01:30 -07001004 fn __get_unchecked(_: &::cxx::CxxVector<#elem>, _: usize) -> *const #elem;
David Tolnaycc75ad22020-04-24 14:45:16 -07001005 }
David Tolnay93637ca2020-09-24 15:58:20 -04001006 __get_unchecked(v, pos)
David Tolnaycc75ad22020-04-24 14:45:16 -07001007 }
David Tolnay3b40b6f2020-04-24 17:58:24 -07001008 fn __unique_ptr_null() -> *mut ::std::ffi::c_void {
1009 extern "C" {
1010 #[link_name = #link_unique_ptr_null]
1011 fn __unique_ptr_null(this: *mut *mut ::std::ffi::c_void);
1012 }
1013 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
1014 unsafe { __unique_ptr_null(&mut repr) }
1015 repr
1016 }
1017 unsafe fn __unique_ptr_raw(raw: *mut ::cxx::CxxVector<Self>) -> *mut ::std::ffi::c_void {
1018 extern "C" {
1019 #[link_name = #link_unique_ptr_raw]
1020 fn __unique_ptr_raw(this: *mut *mut ::std::ffi::c_void, raw: *mut ::cxx::CxxVector<#elem>);
1021 }
1022 let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>();
1023 __unique_ptr_raw(&mut repr, raw);
1024 repr
1025 }
1026 unsafe fn __unique_ptr_get(repr: *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<Self> {
1027 extern "C" {
1028 #[link_name = #link_unique_ptr_get]
1029 fn __unique_ptr_get(this: *const *mut ::std::ffi::c_void) -> *const ::cxx::CxxVector<#elem>;
1030 }
1031 __unique_ptr_get(&repr)
1032 }
1033 unsafe fn __unique_ptr_release(mut repr: *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<Self> {
1034 extern "C" {
1035 #[link_name = #link_unique_ptr_release]
1036 fn __unique_ptr_release(this: *mut *mut ::std::ffi::c_void) -> *mut ::cxx::CxxVector<#elem>;
1037 }
1038 __unique_ptr_release(&mut repr)
1039 }
1040 unsafe fn __unique_ptr_drop(mut repr: *mut ::std::ffi::c_void) {
1041 extern "C" {
1042 #[link_name = #link_unique_ptr_drop]
1043 fn __unique_ptr_drop(this: *mut *mut ::std::ffi::c_void);
1044 }
1045 __unique_ptr_drop(&mut repr);
1046 }
Myron Ahneba35cf2020-02-05 19:41:51 +07001047 }
1048 }
1049}
1050
David Tolnay7db73692019-10-20 14:51:12 -04001051fn expand_return_type(ret: &Option<Type>) -> TokenStream {
1052 match ret {
1053 Some(ret) => quote!(-> #ret),
1054 None => TokenStream::new(),
1055 }
1056}
1057
David Tolnay75dca2e2020-03-25 20:17:52 -07001058fn indirect_return(sig: &Signature, types: &Types) -> bool {
1059 sig.ret
David Tolnay1e548172020-03-16 13:37:09 -07001060 .as_ref()
David Tolnay75dca2e2020-03-25 20:17:52 -07001061 .map_or(false, |ret| sig.throws || types.needs_indirect_abi(ret))
David Tolnay7db73692019-10-20 14:51:12 -04001062}
1063
David Tolnay2f090422020-11-02 18:25:35 -08001064fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream {
David Tolnay7db73692019-10-20 14:51:12 -04001065 match ty {
Adrian Taylorc8713432020-10-21 18:20:55 -07001066 Type::Ident(ident) if ident.rust == RustString => quote!(::cxx::private::RustString),
David Tolnay7db73692019-10-20 14:51:12 -04001067 Type::RustBox(ty) | Type::UniquePtr(ty) => {
David Tolnay2f090422020-11-02 18:25:35 -08001068 let inner = expand_extern_type(&ty.inner, types, proper);
David Tolnay7db73692019-10-20 14:51:12 -04001069 quote!(*mut #inner)
1070 }
David Tolnaye6d50212020-04-25 15:38:23 -07001071 Type::RustVec(ty) => {
David Tolnay2f090422020-11-02 18:25:35 -08001072 let elem = expand_extern_type(&ty.inner, types, proper);
David Tolnaye6d50212020-04-25 15:38:23 -07001073 quote!(::cxx::private::RustVec<#elem>)
1074 }
David Tolnayf1c7f322020-08-27 00:46:01 -07001075 Type::Ref(ty) => {
1076 let mutability = ty.mutability;
1077 match &ty.inner {
Adrian Taylorc8713432020-10-21 18:20:55 -07001078 Type::Ident(ident) if ident.rust == RustString => {
David Tolnayf1c7f322020-08-27 00:46:01 -07001079 quote!(&#mutability ::cxx::private::RustString)
1080 }
1081 Type::RustVec(ty) => {
David Tolnay2f090422020-11-02 18:25:35 -08001082 let inner = expand_extern_type(&ty.inner, types, proper);
David Tolnayf1c7f322020-08-27 00:46:01 -07001083 quote!(&#mutability ::cxx::private::RustVec<#inner>)
1084 }
David Tolnay2f090422020-11-02 18:25:35 -08001085 inner if proper && types.is_considered_improper_ctype(inner) => match mutability {
David Tolnay4cb97672020-11-02 16:13:34 -08001086 None => quote!(*const ::std::ffi::c_void),
1087 Some(_) => quote!(*#mutability ::std::ffi::c_void),
1088 },
David Tolnayf1c7f322020-08-27 00:46:01 -07001089 _ => quote!(#ty),
Myron Ahneba35cf2020-02-05 19:41:51 +07001090 }
David Tolnayf1c7f322020-08-27 00:46:01 -07001091 }
David Tolnay7db73692019-10-20 14:51:12 -04001092 Type::Str(_) => quote!(::cxx::private::RustStr),
Adrian Taylorf5dd5522020-04-13 16:50:14 -07001093 Type::SliceRefU8(_) => quote!(::cxx::private::RustSliceU8),
David Tolnay7db73692019-10-20 14:51:12 -04001094 _ => quote!(#ty),
1095 }
1096}
1097
David Tolnay2f090422020-11-02 18:25:35 -08001098fn expand_extern_return_type(ret: &Option<Type>, types: &Types, proper: bool) -> TokenStream {
David Tolnay7db73692019-10-20 14:51:12 -04001099 let ret = match ret {
1100 Some(ret) if !types.needs_indirect_abi(ret) => ret,
1101 _ => return TokenStream::new(),
1102 };
David Tolnay2f090422020-11-02 18:25:35 -08001103 let ty = expand_extern_type(ret, types, proper);
David Tolnay7db73692019-10-20 14:51:12 -04001104 quote!(-> #ty)
1105}