1use std::collections::{HashMap, HashSet};
2
3use proc_macro2::Span;
4use syn::parse;
5
6use crate::ast::App;
7
8pub fn app(app: &App) -> parse::Result<()> {
9 let mut owners = HashMap::new();
12 for (core, _, name, access) in app.resource_accesses() {
13 if app.resource(name).is_none() {
14 return Err(parse::Error::new(
15 name.span(),
16 "this resource has NOT been declared",
17 ));
18 }
19
20 if access.is_exclusive() {
21 if let Some(owner) = owners.get(name) {
22 if core != *owner {
23 return Err(parse::Error::new(
24 name.span(),
25 "resources can NOT be exclusively accessed (`&mut-`) from different cores",
26 ));
27 }
28 } else {
29 owners.insert(name, core);
30 }
31 }
32 }
33
34 let exclusive_accesses = app
39 .resource_accesses()
40 .filter_map(|(_, priority, name, access)| {
41 if priority.is_some() && access.is_exclusive() {
42 Some(name)
43 } else {
44 None
45 }
46 })
47 .collect::<HashSet<_>>();
48 for (_, _, name, access) in app.resource_accesses() {
49 if access.is_shared() && exclusive_accesses.contains(name) {
50 return Err(parse::Error::new(
51 name.span(),
52 "this implementation doesn't support shared (`&-`) - exclusive (`&mut-`) locks; use `x` instead of `&x`",
53 ));
54 }
55 }
56
57 for (name, access) in app.inits.values().flat_map(|init| &init.args.resources) {
60 if app.late_resources.contains_key(name) {
61 return Err(parse::Error::new(
62 name.span(),
63 "late resources can NOT be assigned to `init`",
64 ));
65 }
66
67 if access.is_shared() {
68 return Err(parse::Error::new(
69 name.span(),
70 "`init` has direct exclusive access to resources; use `x` instead of `&x` ",
71 ));
72 }
73 }
74
75 let cores = app.args.cores;
77 let mut late_resources_set = app.late_resources.keys().collect::<HashSet<_>>();
78 if late_resources_set.is_empty() {
79 for init in app.inits.values() {
80 if init.returns_late_resources {
81 return Err(parse::Error::new(
82 init.name.span(),
83 "no late resources exist so this function must NOT return `LateResources`",
84 ));
85 }
86 }
87 } else {
88 if cores == 1 {
89 if let Some(init) = app.inits.get(&0) {
91 if !init.returns_late_resources {
92 return Err(parse::Error::new(
93 init.name.span(),
94 "late resources exist so `#[init]` must return `init::LateResources`",
95 ));
96 }
97 } else {
98 return Err(parse::Error::new(
99 Span::call_site(),
100 "late resources exist so a `#[init]` function must be defined",
101 ));
102 }
103 } else {
104 let mut rest = None;
106
107 let mut initialized = HashMap::new();
108 for (core, init) in &app.inits {
109 if !init.returns_late_resources {
110 continue;
111 }
112
113 if late_resources_set.is_empty() {
114 return Err(parse::Error::new(
115 init.name.span(),
116 "no more late resources to initialize; \
117 this function must NOT return `LateResources`",
118 ));
119 }
120
121 if !init.args.late.is_empty() {
122 for res in &init.args.late {
123 if !app.late_resources.contains_key(res) {
124 return Err(parse::Error::new(
125 res.span(),
126 "this is not a late resource",
127 ));
128 }
129
130 if let Some(other) = initialized.get(res) {
131 return Err(parse::Error::new(
132 res.span(),
133 &format!("this resource is initialized by core {}", other),
134 ));
135 } else {
136 late_resources_set.remove(res);
137 initialized.insert(res, core);
138 }
139 }
140 } else if let Some(rest) = rest {
141 return Err(parse::Error::new(
142 init.name.span(),
143 &format!(
144 "unclear how initialization of late resources is split between \
145 cores {} and {}",
146 rest, core,
147 ),
148 ));
149 } else {
150 rest = Some(core);
151 }
152 }
153
154 if let Some(res) = late_resources_set.iter().next() {
155 if rest.is_none() {
156 return Err(parse::Error::new(
157 res.span(),
158 "this resource is not being initialized",
159 ));
160 }
161 }
162 }
163 }
164
165 for task in app.hardware_tasks.values() {
167 let binds = &task.args.binds;
168
169 if let Some(extern_interrupts) = app.extern_interrupts.get(&task.args.core) {
170 if extern_interrupts.contains_key(binds) {
171 return Err(parse::Error::new(
172 binds.span(),
173 "`extern` interrupts can't be used as hardware tasks",
174 ));
175 }
176 }
177 }
178
179 for task in app.task_references() {
181 if app.hardware_tasks.contains_key(task) {
182 return Err(parse::Error::new(
183 task.span(),
184 "hardware tasks can NOT be spawned",
185 ));
186 } else if !app.software_tasks.contains_key(task) {
187 return Err(parse::Error::new(
188 task.span(),
189 "this software task has NOT been declared",
190 ));
191 }
192 }
193
194 Ok(())
195}