summaryrefslogtreecommitdiff
path: root/src/info_structures.rs
blob: 05dc734c309a436b6ba6bce6e8e8fe1775e9643c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
use crate::utils::{make_generic_arguments, make_generic_consumers, replace_this_with_lifetime};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, ToTokens};
use syn::{
    punctuated::Punctuated, token::Comma, Attribute, ConstParam, Error, GenericParam, Generics,
    LifetimeDef, Type, TypeParam, Visibility,
};

#[derive(Clone, Copy)]
pub struct Options {
    pub do_no_doc: bool,
    pub do_pub_extras: bool,
}

#[derive(Clone, Copy, PartialEq)]
pub enum FieldType {
    /// Not borrowed by other parts of the struct.
    Tail,
    /// Immutably borrowed by at least one other field.
    Borrowed,
    /// Mutably borrowed by one other field.
    BorrowedMut,
}

impl FieldType {
    pub fn is_tail(self) -> bool {
        self == Self::Tail
    }
}

#[derive(Clone)]
pub struct BorrowRequest {
    pub index: usize,
    pub mutable: bool,
}

#[derive(Clone)]
pub enum Derive {
    Debug,
    PartialEq,
    Eq,
}

#[derive(Copy, Clone)]
pub enum BuilderType {
    Sync,
    Async,
    AsyncSend,
}

impl BuilderType {
    pub fn is_async(&self) -> bool {
        match self {
            BuilderType::Sync => false,
            _ => true,
        }
    }
}

#[derive(Clone)]
pub struct StructInfo {
    pub derives: Vec<Derive>,
    pub ident: Ident,
    pub generics: Generics,
    pub vis: Visibility,
    pub fields: Vec<StructFieldInfo>,
    pub first_lifetime: Ident,
    pub attributes: Vec<Attribute>,
}

impl StructInfo {
    // The lifetime to use in place of 'this for internal implementations,
    // should never be exposed to the user.
    pub fn fake_lifetime(&self) -> Ident {
        return self.first_lifetime.clone();
    }

    pub fn generic_params(&self) -> &Punctuated<GenericParam, Comma> {
        &self.generics.params
    }

    /// Same as generic_params but with 'this and 'outer_borrow prepended.
    pub fn borrowed_generic_params(&self) -> TokenStream {
        if self.generic_params().is_empty() {
            quote! { <'outer_borrow, 'this> }
        } else {
            let mut new_generic_params = self.generic_params().clone();
            new_generic_params.insert(0, syn::parse_quote! { 'this });
            new_generic_params.insert(0, syn::parse_quote! { 'outer_borrow });
            quote! { <#new_generic_params> }
        }
    }

    /// Same as generic_params but without bounds and with '_ prepended twice.
    pub fn borrowed_generic_params_inferred(&self) -> TokenStream {
        use GenericParam::*;
        let params = self.generic_params().iter().map(|p| match p {
            Type(TypeParam { ident, .. }) | Const(ConstParam { ident, .. }) => {
                ident.to_token_stream()
            }
            Lifetime(LifetimeDef { lifetime, .. }) => lifetime.to_token_stream(),
        });
        quote! { <'_, '_, #(#params,)*> }
    }

    pub fn generic_arguments(&self) -> Vec<TokenStream> {
        make_generic_arguments(&self.generics)
    }

    /// Same as generic_arguments but with 'outer_borrow and 'this prepended.
    pub fn borrowed_generic_arguments(&self) -> Vec<TokenStream> {
        let mut args = self.generic_arguments();
        args.insert(0, quote! { 'this });
        args.insert(0, quote! { 'outer_borrow });
        args
    }

    pub fn generic_consumers(&self) -> impl Iterator<Item = (TokenStream, Ident)> {
        make_generic_consumers(&self.generics)
    }
}

#[derive(Clone)]
pub struct StructFieldInfo {
    pub name: Ident,
    pub typ: Type,
    pub field_type: FieldType,
    pub vis: Visibility,
    pub borrows: Vec<BorrowRequest>,
    /// If this is true and borrows is empty, the struct will borrow from self in the future but
    /// does not require a builder to be initialized. It should not be able to be removed from the
    /// struct with into_heads.
    pub self_referencing: bool,
    /// If it is None, the user has not specified whether or not the field is covariant. If this is
    /// Some(false), we should avoid making borrow_* or borrow_*_mut functions as they will not
    /// be able to compile.
    pub covariant: Option<bool>,
}

#[derive(Clone)]
pub enum ArgType {
    /// Used when the initial value of a field can be passed directly into the constructor.
    Plain(TokenStream),
    /// Used when a field requires self references and thus requires something that implements
    /// a builder function trait instead of a simple plain type.
    TraitBound(TokenStream),
}

impl StructFieldInfo {
    pub fn builder_name(&self) -> Ident {
        format_ident!("{}_builder", self.name)
    }

    pub fn illegal_ref_name(&self) -> Ident {
        format_ident!("{}_illegal_static_reference", self.name)
    }

    pub fn is_borrowed(&self) -> bool {
        self.field_type != FieldType::Tail
    }

    pub fn is_mutably_borrowed(&self) -> bool {
        self.field_type == FieldType::BorrowedMut
    }

    pub fn boxed(&self) -> TokenStream {
        let name = &self.name;
        quote! { ::ouroboros::macro_help::aliasable_boxed(#name) }
    }

    pub fn stored_type(&self) -> TokenStream {
        let t = &self.typ;
        if self.is_borrowed() {
            quote! { ::ouroboros::macro_help::AliasableBox<#t> }
        } else {
            quote! { #t }
        }
    }

    /// Returns code which takes a variable with the same name and type as this field and turns it
    /// into a static reference to its dereffed contents.
    pub fn make_illegal_static_reference(&self) -> TokenStream {
        let field_name = &self.name;
        let ref_name = self.illegal_ref_name();
        quote! {
            let #ref_name = unsafe {
                ::ouroboros::macro_help::change_lifetime(&*#field_name)
            };
        }
    }

    /// Like make_illegal_static_reference, but provides a mutable reference instead.
    pub fn make_illegal_static_mut_reference(&self) -> TokenStream {
        let field_name = &self.name;
        let ref_name = self.illegal_ref_name();
        quote! {
            let #ref_name = unsafe {
                ::ouroboros::macro_help::change_lifetime_mut(&mut *#field_name)
            };
        }
    }

    /// Generates an error requesting that the user explicitly specify whether or not the
    /// field's type is covariant.
    pub fn covariance_error(&self) {
        let error = concat!(
            "Ouroboros cannot automatically determine if this type is covariant.\n\n",
            "If it is covariant, it should be legal to convert any instance of that type to an ",
            "instance of that type where all usages of 'this are replaced with a smaller ",
            "lifetime. For example, Box<&'this i32> is covariant because it is legal to use it as ",
            "a Box<&'a i32> where 'this: 'a. In contrast, Fn(&'this i32) cannot be used as ",
            "Fn(&'a i32).\n\n",
            "To resolve this error, add #[covariant] or #[not_covariant] to the field.\n",
        );
        proc_macro_error::emit_error!(self.typ, error);
    }

    pub fn make_constructor_arg_type_impl(
        &self,
        info: &StructInfo,
        make_builder_return_type: impl FnOnce() -> TokenStream,
    ) -> Result<ArgType, Error> {
        let field_type = &self.typ;
        let fake_lifetime = info.fake_lifetime();
        if self.borrows.is_empty() {
            // Even if self_referencing is true, as long as borrows is empty, we don't need to use a
            // builder to construct it.
            let field_type =
                replace_this_with_lifetime(field_type.into_token_stream(), fake_lifetime.clone());
            Ok(ArgType::Plain(quote! { #field_type }))
        } else {
            let mut field_builder_params = Vec::new();
            for borrow in &self.borrows {
                if borrow.mutable {
                    let field = &info.fields[borrow.index];
                    let field_type = &field.typ;
                    field_builder_params.push(quote! {
                        &'this mut #field_type
                    });
                } else {
                    let field = &info.fields[borrow.index];
                    let field_type = &field.typ;
                    field_builder_params.push(quote! {
                        &'this #field_type
                    });
                }
            }
            let return_type = make_builder_return_type();
            let bound = quote! { for<'this> ::core::ops::FnOnce(#(#field_builder_params),*) -> #return_type };
            Ok(ArgType::TraitBound(bound))
        }
    }

    /// Returns a trait bound if `for_field` refers to any other fields, and a plain type if not. This
    /// is the type used in the constructor to initialize the value of `for_field`.
    pub fn make_constructor_arg_type(
        &self,
        info: &StructInfo,
        builder_type: BuilderType,
    ) -> Result<ArgType, Error> {
        let field_type = &self.typ;
        let return_ty_constructor = || match builder_type {
            BuilderType::AsyncSend => {
                quote! {
                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
                        dyn ::core::future::Future<Output=#field_type> + ::core::marker::Send + 'this>>
                }
            }
            BuilderType::Async => {
                quote! { ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
                dyn ::core::future::Future<Output=#field_type> + 'this>> }
            }
            BuilderType::Sync => quote! { #field_type },
        };
        self.make_constructor_arg_type_impl(info, return_ty_constructor)
    }

    /// Like make_constructor_arg_type, but used for the try_new constructor.
    pub fn make_try_constructor_arg_type(
        &self,
        info: &StructInfo,
        builder_type: BuilderType,
    ) -> Result<ArgType, Error> {
        let field_type = &self.typ;
        let return_ty_constructor = || match builder_type {
            BuilderType::AsyncSend => {
                quote! {
                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
                        dyn ::core::future::Future<Output=::core::result::Result<#field_type, Error_>>
                            + ::core::marker::Send + 'this>>
                }
            }
            BuilderType::Async => {
                quote! {
                    ::core::pin::Pin<::ouroboros::macro_help::alloc::boxed::Box<
                        dyn ::core::future::Future<Output=::core::result::Result<#field_type, Error_>>
                            + 'this>>
                }
            }
            BuilderType::Sync => quote! { ::core::result::Result<#field_type, Error_> },
        };
        self.make_constructor_arg_type_impl(info, return_ty_constructor)
    }
}