• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Methods for types and navigating lifetimes within methods.
2 
3 use std::collections::BTreeSet;
4 use std::ops::Deref;
5 
6 use super::{
7     Attrs, Docs, Ident, IdentBuf, InputOnly, OutType, OutputOnly, SelfType, TraitPath, Type,
8     TypeContext,
9 };
10 
11 use super::lifetimes::{Lifetime, LifetimeEnv, Lifetimes, MaybeStatic};
12 
13 use borrowing_field::BorrowingFieldVisitor;
14 use borrowing_param::BorrowingParamVisitor;
15 
16 pub mod borrowing_field;
17 pub mod borrowing_param;
18 
19 /// A method exposed to Diplomat.
20 #[derive(Debug)]
21 #[non_exhaustive]
22 pub struct Method {
23     /// Documentation specified on the method
24     pub docs: Docs,
25     /// The name of the method as initially declared.
26     pub name: IdentBuf,
27     /// The name of the generated `extern "C"` function
28     pub abi_name: IdentBuf,
29     /// The lifetimes introduced in this method and surrounding impl block.
30     pub lifetime_env: LifetimeEnv,
31 
32     /// An &self, &mut self, or Self parameter
33     pub param_self: Option<ParamSelf>,
34     /// The parameters of the method
35     pub params: Vec<Param>,
36     /// The output type, including whether it returns a Result/Option/Writeable/etc
37     pub output: ReturnType,
38     /// Resolved (and inherited) diplomat::attr attributes on this method
39     pub attrs: Attrs,
40 }
41 
42 pub trait CallbackInstantiationFunctionality {
43     #[allow(clippy::result_unit_err)]
get_inputs(&self) -> Result<&[CallbackParam], ()>44     fn get_inputs(&self) -> Result<&[CallbackParam], ()>; // the types of the parameters
45     #[allow(clippy::result_unit_err)]
get_output_type(&self) -> Result<&Option<Type>, ()>46     fn get_output_type(&self) -> Result<&Option<Type>, ()>;
47 }
48 
49 #[derive(Debug)]
50 #[non_exhaustive]
51 // Note: we do not support borrowing across callbacks
52 pub struct Callback {
53     pub param_self: Option<TraitParamSelf>, // this is None for callbacks as method arguments
54     pub params: Vec<CallbackParam>,
55     pub output: Box<Option<Type>>, // this will be used in Rust (note: can technically be a callback, or void)
56     pub name: Option<IdentBuf>,
57     pub attrs: Option<Attrs>,
58     pub docs: Option<Docs>,
59 }
60 
61 // uninstantiatable; represents no callback allowed
62 #[derive(Debug, Clone)]
63 #[non_exhaustive]
64 pub enum NoCallback {}
65 
66 impl CallbackInstantiationFunctionality for Callback {
get_inputs(&self) -> Result<&[CallbackParam], ()>67     fn get_inputs(&self) -> Result<&[CallbackParam], ()> {
68         Ok(&self.params)
69     }
get_output_type(&self) -> Result<&Option<Type>, ()>70     fn get_output_type(&self) -> Result<&Option<Type>, ()> {
71         Ok(&self.output)
72     }
73 }
74 
75 impl CallbackInstantiationFunctionality for NoCallback {
get_inputs(&self) -> Result<&[CallbackParam], ()>76     fn get_inputs(&self) -> Result<&[CallbackParam], ()> {
77         Err(())
78     }
get_output_type(&self) -> Result<&Option<Type>, ()>79     fn get_output_type(&self) -> Result<&Option<Type>, ()> {
80         Err(())
81     }
82 }
83 
84 /// Type that the method returns.
85 #[derive(Debug, Clone)]
86 #[non_exhaustive]
87 pub enum SuccessType {
88     /// Conceptually returns a string, which gets written to the `write: DiplomatWrite` argument
89     Write,
90     /// A Diplomat type. Some types can be outputs, but not inputs, which is expressed by the `OutType` parameter.
91     OutType(OutType),
92     /// A `()` type in Rust.
93     Unit,
94 }
95 
96 /// Whether or not the method returns a value or a result.
97 #[derive(Debug)]
98 #[allow(clippy::exhaustive_enums)] // this only exists for fallible/infallible, breaking changes for more complex returns are ok
99 pub enum ReturnType {
100     Infallible(SuccessType),
101     Fallible(SuccessType, Option<OutType>),
102     Nullable(SuccessType),
103 }
104 
105 /// The `self` parameter of a method.
106 #[derive(Debug)]
107 #[non_exhaustive]
108 pub struct ParamSelf {
109     pub ty: SelfType,
110     pub attrs: Attrs,
111 }
112 
113 #[derive(Debug, Clone)]
114 #[non_exhaustive]
115 pub struct TraitParamSelf {
116     pub trait_path: TraitPath,
117 }
118 
119 /// A parameter in a method.
120 #[derive(Debug)]
121 #[non_exhaustive]
122 pub struct Param {
123     pub name: IdentBuf,
124     pub ty: Type<InputOnly>,
125     pub attrs: Attrs,
126 }
127 
128 /// A parameter in a callback
129 /// No name, since all we get is the callback type signature
130 #[derive(Debug)]
131 #[non_exhaustive]
132 pub struct CallbackParam {
133     pub ty: Type<OutputOnly>,
134     pub name: Option<IdentBuf>,
135 }
136 
137 impl SuccessType {
138     /// Returns whether the variant is `Write`.
is_write(&self) -> bool139     pub fn is_write(&self) -> bool {
140         matches!(self, SuccessType::Write)
141     }
142 
143     /// Returns whether the variant is `Unit`.
is_unit(&self) -> bool144     pub fn is_unit(&self) -> bool {
145         matches!(self, SuccessType::Unit)
146     }
147 
as_type(&self) -> Option<&OutType>148     pub fn as_type(&self) -> Option<&OutType> {
149         match self {
150             SuccessType::OutType(ty) => Some(ty),
151             _ => None,
152         }
153     }
154 }
155 
156 impl Deref for ReturnType {
157     type Target = SuccessType;
158 
deref(&self) -> &Self::Target159     fn deref(&self) -> &Self::Target {
160         match self {
161             ReturnType::Infallible(ret) | ReturnType::Fallible(ret, _) | Self::Nullable(ret) => ret,
162         }
163     }
164 }
165 
166 impl ReturnType {
167     /// Returns `true` if the FFI function returns `void`. Not that this is different from `is_unit`,
168     /// which will be true for `DiplomatResult<(), E>` and false for infallible write.
is_ffi_unit(&self) -> bool169     pub fn is_ffi_unit(&self) -> bool {
170         matches!(
171             self,
172             ReturnType::Infallible(SuccessType::Unit | SuccessType::Write)
173         )
174     }
175 
176     /// The "main" return type of this function: the Ok, Some, or regular type
success_type(&self) -> &SuccessType177     pub fn success_type(&self) -> &SuccessType {
178         match &self {
179             Self::Infallible(s) => s,
180             Self::Fallible(s, _) => s,
181             Self::Nullable(s) => s,
182         }
183     }
184 
185     /// Get the list of method lifetimes actually used by the method return type
186     ///
187     /// Most input lifetimes aren't actually used. An input lifetime is generated
188     /// for each borrowing parameter but is only important if we use it in the return.
used_method_lifetimes(&self) -> BTreeSet<Lifetime>189     pub fn used_method_lifetimes(&self) -> BTreeSet<Lifetime> {
190         let mut set = BTreeSet::new();
191 
192         let mut add_to_set = |ty: &OutType| {
193             for lt in ty.lifetimes() {
194                 if let MaybeStatic::NonStatic(lt) = lt {
195                     set.insert(lt);
196                 }
197             }
198         };
199 
200         match self {
201             ReturnType::Infallible(SuccessType::OutType(ref ty))
202             | ReturnType::Nullable(SuccessType::OutType(ref ty)) => add_to_set(ty),
203             ReturnType::Fallible(ref ok, ref err) => {
204                 if let SuccessType::OutType(ref ty) = ok {
205                     add_to_set(ty)
206                 }
207                 if let Some(ref ty) = err {
208                     add_to_set(ty)
209                 }
210             }
211             _ => (),
212         }
213 
214         set
215     }
216 
with_contained_types(&self, mut f: impl FnMut(&OutType))217     pub fn with_contained_types(&self, mut f: impl FnMut(&OutType)) {
218         match self {
219             Self::Infallible(SuccessType::OutType(o))
220             | Self::Nullable(SuccessType::OutType(o))
221             | Self::Fallible(SuccessType::OutType(o), None) => f(o),
222             Self::Fallible(SuccessType::OutType(o), Some(o2)) => {
223                 f(o);
224                 f(o2)
225             }
226             Self::Fallible(_, Some(o)) => f(o),
227             _ => (),
228         }
229     }
230 }
231 
232 impl ParamSelf {
new(ty: SelfType, attrs: Attrs) -> Self233     pub(super) fn new(ty: SelfType, attrs: Attrs) -> Self {
234         Self { ty, attrs }
235     }
236 
237     /// Return the number of fields and leaves that will show up in the [`BorrowingFieldVisitor`].
238     ///
239     /// This method is used to calculate how much space to allocate upfront.
field_leaf_lifetime_counts(&self, tcx: &TypeContext) -> (usize, usize)240     fn field_leaf_lifetime_counts(&self, tcx: &TypeContext) -> (usize, usize) {
241         match self.ty {
242             SelfType::Opaque(_) => (1, 1),
243             SelfType::Struct(ref ty) => ty.resolve(tcx).fields.iter().fold((1, 0), |acc, field| {
244                 let inner = field.ty.field_leaf_lifetime_counts(tcx);
245                 (acc.0 + inner.0, acc.1 + inner.1)
246             }),
247             SelfType::Enum(_) => (0, 0),
248         }
249     }
250 }
251 
252 impl TraitParamSelf {
new(trait_path: TraitPath) -> Self253     pub(super) fn new(trait_path: TraitPath) -> Self {
254         Self { trait_path }
255     }
256 }
257 
258 impl Param {
new(name: IdentBuf, ty: Type<InputOnly>, attrs: Attrs) -> Self259     pub(super) fn new(name: IdentBuf, ty: Type<InputOnly>, attrs: Attrs) -> Self {
260         Self { name, ty, attrs }
261     }
262 }
263 
264 impl Method {
265     /// Returns a fresh [`Lifetimes`] corresponding to `self`.
method_lifetimes(&self) -> Lifetimes266     pub fn method_lifetimes(&self) -> Lifetimes {
267         self.lifetime_env.lifetimes()
268     }
269 
270     /// Returns a new [`BorrowingParamVisitor`], which can *shallowly* link output lifetimes
271     /// to the parameters they borrow from.
272     ///
273     /// This is useful for backends which wish to have lifetime codegen for methods only handle the local
274     /// method lifetime, and delegate to generated code on structs for handling the internals of struct lifetimes.
borrowing_param_visitor<'tcx>( &'tcx self, tcx: &'tcx TypeContext, ) -> BorrowingParamVisitor<'tcx>275     pub fn borrowing_param_visitor<'tcx>(
276         &'tcx self,
277         tcx: &'tcx TypeContext,
278     ) -> BorrowingParamVisitor<'tcx> {
279         BorrowingParamVisitor::new(self, tcx)
280     }
281 
282     /// Returns a new [`BorrowingFieldVisitor`], which allocates memory to
283     /// efficiently represent all fields (and their paths!) of the inputs that
284     /// have a lifetime.
285     ///
286     /// This is useful for backends which wish to "splat out" lifetime edge codegen for methods,
287     /// linking each borrowed input param/field (however deep it may be in a struct) to a borrowed output param/field.
288     ///
289     /// ```ignore
290     /// # use std::collections::BTreeMap;
291     /// let visitor = method.borrowing_field_visitor(&tcx, "this".ck().unwrap());
292     /// let mut map = BTreeMap::new();
293     /// visitor.visit_borrowing_fields(|lifetime, field| {
294     ///     map.entry(lifetime).or_default().push(field);
295     /// })
296     /// ```
borrowing_field_visitor<'m>( &'m self, tcx: &'m TypeContext, self_name: &'m Ident, ) -> BorrowingFieldVisitor<'m>297     pub fn borrowing_field_visitor<'m>(
298         &'m self,
299         tcx: &'m TypeContext,
300         self_name: &'m Ident,
301     ) -> BorrowingFieldVisitor<'m> {
302         BorrowingFieldVisitor::new(self, tcx, self_name)
303     }
304 }
305