cortex_m_rtic_macros/codegen/
local_resources_struct.rs

1use proc_macro2::TokenStream as TokenStream2;
2use quote::quote;
3use rtic_syntax::{
4    ast::{App, TaskLocal},
5    Context,
6};
7
8use crate::codegen::util;
9
10/// Generates local resources structs
11pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) {
12    let mut lt = None;
13
14    let resources = match ctxt {
15        Context::Init => &app.init.args.local_resources,
16        Context::Idle => &app.idle.as_ref().unwrap().args.local_resources,
17        Context::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources,
18        Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources,
19    };
20
21    let task_name = util::get_task_name(ctxt, app);
22
23    let mut fields = vec![];
24    let mut values = vec![];
25    let mut has_cfgs = false;
26
27    for (name, task_local) in resources {
28        let (cfgs, ty, is_declared) = match task_local {
29            TaskLocal::External => {
30                let r = app.local_resources.get(name).expect("UNREACHABLE");
31                (&r.cfgs, &r.ty, false)
32            }
33            TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true),
34            _ => unreachable!(),
35        };
36
37        has_cfgs |= !cfgs.is_empty();
38
39        let lt = if ctxt.runs_once() {
40            quote!('static)
41        } else {
42            lt = Some(quote!('a));
43            quote!('a)
44        };
45
46        let mangled_name = if matches!(task_local, TaskLocal::External) {
47            util::static_local_resource_ident(name)
48        } else {
49            util::declared_static_local_resource_ident(name, &task_name)
50        };
51
52        let local_resource_doc = format!(" Local resource `{name}`");
53        fields.push(quote!(
54            #[doc = #local_resource_doc]
55            #(#cfgs)*
56            pub #name: &#lt mut #ty
57        ));
58
59        let expr = if is_declared {
60            // If the local resources is already initialized, we only need to access its value and
61            // not go through an `MaybeUninit`
62            quote!(&mut *#mangled_name.get_mut())
63        } else {
64            quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
65        };
66
67        values.push(quote!(
68            #(#cfgs)*
69            #name: #expr
70        ));
71    }
72
73    if lt.is_some() {
74        *needs_lt = true;
75
76        // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused
77        if has_cfgs {
78            fields.push(quote!(
79                #[doc(hidden)]
80                pub __marker__: core::marker::PhantomData<&'a ()>
81            ));
82
83            values.push(quote!(__marker__: core::marker::PhantomData));
84        }
85    }
86
87    let doc = format!(" Local resources `{}` has access to", ctxt.ident(app));
88    let ident = util::local_resources_ident(ctxt, app);
89    let item = quote!(
90        #[allow(non_snake_case)]
91        #[allow(non_camel_case_types)]
92        #[doc = #doc]
93        pub struct #ident<#lt> {
94            #(#fields,)*
95        }
96    );
97
98    let constructor = quote!(
99        impl<#lt> #ident<#lt> {
100            #[inline(always)]
101            #[doc(hidden)]
102            pub unsafe fn new() -> Self {
103                #ident {
104                    #(#values,)*
105                }
106            }
107        }
108    );
109
110    (item, constructor)
111}