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