machine_check_machine/
lib.rs1#![doc = include_str!("../README.md")]
2
3use std::path::PathBuf;
4
5use proc_macro2::{Ident, Span};
6use quote::quote;
7use support::rules::NoRuleMatch;
8use syn::spanned::Spanned;
9use syn::visit_mut::{self, VisitMut};
10use syn::{parse_quote, Attribute, Item, ItemFn, ItemMod, Meta, MetaList, PathSegment};
11use syn_path::path;
12
13use crate::util::create_item_mod;
14
15mod abstr;
16mod concr;
17mod refin;
18mod ssa;
19mod support;
20mod util;
21
22#[derive(Clone)]
23pub struct MachineDescription {
24 pub items: Vec<Item>,
25 pub panic_messages: Vec<String>,
26}
27
28pub fn process_file(mut file: syn::File) -> Result<syn::File, MachineError> {
29 process_items(&mut file.items)?;
30 Ok(file)
31}
32
33pub fn process_module(mut module: ItemMod) -> Result<ItemMod, MachineError> {
34 let Some((_, items)) = &mut module.content else {
35 return Err(MachineError::new(
36 ErrorType::ModuleWithoutContent,
37 module.span(),
38 ));
39 };
40 process_items(items)?;
41 Ok(module)
42}
43
44pub fn default_main() -> Item {
45 let main_fn: ItemFn = parse_quote!(
46 fn main() {
47 ::machine_check_exec::run::<refin::Input, refin::State, refin::Machine>()
48 }
49 );
50 Item::Fn(main_fn)
51}
52
53#[allow(dead_code)]
54fn out_dir() -> Option<PathBuf> {
55 let mut args = std::env::args();
57
58 let mut out_dir = None;
59 while let Some(arg) = args.next() {
60 if arg == "--out-dir" {
61 out_dir = args.next();
62 }
63 }
64 let out_dir = out_dir?;
65 let mut out_dir = PathBuf::from(out_dir);
67 while !out_dir.ends_with("target") {
68 if !out_dir.pop() {
69 return None;
70 }
71 }
72
73 Some(out_dir)
74}
75
76#[allow(dead_code)]
77fn unparse(machine: &MachineDescription) -> String {
78 prettyplease::unparse(&syn::File {
79 shebang: None,
80 attrs: vec![],
81 items: machine.items.clone(),
82 })
83}
84
85fn process_items(items: &mut Vec<Item>) -> Result<(), MachineError> {
86 let ssa_machine = ssa::create_ssa_machine(items.clone())?;
91 let mut abstract_machine = abstr::create_abstract_machine(&ssa_machine)?;
97
98 let refinement_machine = refin::create_refinement_machine(&abstract_machine)?;
104
105 let refinement_module = create_machine_module("__mck_mod_refin", refinement_machine);
107 abstract_machine.items.push(refinement_module);
108
109 support::strip_machine::strip_machine(&mut abstract_machine)?;
110
111 concr::process_items(items, &ssa_machine.panic_messages)?;
112
113 let abstract_module = create_machine_module("__mck_mod_abstr", abstract_machine);
114 items.push(abstract_module);
115
116 redirect_mck(items)?;
117
118 Ok(())
132}
133
134fn redirect_mck(items: &mut [Item]) -> Result<(), MachineError> {
135 let mut redirect_visitor = RedirectVisitor;
136 for item in items.iter_mut() {
137 redirect_visitor.visit_item_mut(item);
138 }
139
140 Ok(())
141}
142
143struct RedirectVisitor;
144
145impl VisitMut for RedirectVisitor {
146 fn visit_path_mut(&mut self, path: &mut syn::Path) {
147 if path.leading_colon.is_some() {
148 let first_segment = path
149 .segments
150 .first()
151 .expect("Path should have first segment");
152 if first_segment.ident == "mck" {
153 let span = first_segment.span();
154 path.segments.insert(
156 0,
157 PathSegment {
158 ident: Ident::new("machine_check", span),
159 arguments: syn::PathArguments::None,
160 },
161 );
162 }
163 }
164 visit_mut::visit_path_mut(self, path);
165 }
166}
167
168fn create_machine_module(name: &str, machine: MachineDescription) -> Item {
169 let mut module = create_item_mod(
170 syn::Visibility::Public(Default::default()),
171 Ident::new(name, Span::call_site()),
172 machine.items,
173 );
174 module.attrs.push(Attribute {
177 pound_token: Default::default(),
178 style: syn::AttrStyle::Outer,
179 bracket_token: Default::default(),
180 meta: Meta::List(MetaList {
181 path: path!(allow),
182 delimiter: syn::MacroDelimiter::Paren(Default::default()),
183 tokens: quote!(
184 non_snake_case, clippy::all, ),
187 }),
188 });
189
190 Item::Mod(module)
191}
192
193#[derive(thiserror::Error, Debug, Clone)]
194#[error("{0}")]
195pub enum BackwardErrorType {
196 #[error("Unable to convert")]
197 NoRuleMatch,
198 #[error("Identifier type discovery failed")]
199 IdentTypeDiscovery,
200 #[error("Unsupported construct: {0}")]
201 UnsupportedConstruct(String),
202}
203
204#[derive(thiserror::Error, Debug, Clone)]
205#[error("{ty}")]
206pub struct BackwardError {
207 pub ty: BackwardErrorType,
208 pub span: Span,
209}
210
211impl From<NoRuleMatch> for BackwardError {
212 fn from(error: NoRuleMatch) -> Self {
213 BackwardError::new(BackwardErrorType::NoRuleMatch, error.0)
214 }
215}
216
217impl BackwardError {
218 fn new(ty: BackwardErrorType, span: Span) -> Self {
219 Self { ty, span }
220 }
221}
222
223#[derive(thiserror::Error, Debug, Clone)]
224pub enum ErrorType {
225 #[error("machine-check: Cannot parse module without content")]
226 ModuleWithoutContent,
227
228 #[error("machine-check error: Unknown macro")]
229 UnknownMacro,
230 #[error("{0}")]
231 MacroError(String),
232 #[error("{0}")]
233 MacroParseError(syn::Error),
234 #[error("machine-check: {0}")]
235 UnsupportedConstruct(String),
236 #[error("machine-check: {0}")]
237 IllegalConstruct(String),
238 #[error("machine-check: Could not infer variable type")]
239 InferenceFailure,
240 #[error("machine-check (concrete conversion): {0}")]
241 ConcreteConversionError(String),
242 #[error("machine-check (forward conversion): {0}")]
243 ForwardConversionError(String),
244 #[error("machine-check (backward conversion): {0}")]
245 BackwardConversionError(String),
246
247 #[error("machine-check internal error (SSA translation): {0}")]
248 SsaInternal(String),
249 #[error("machine-check internal error (forward translation): {0}")]
250 ForwardInternal(String),
251 #[error("machine-check internal error (backward translation): {0}")]
252 BackwardInternal(String),
253 #[error("machine-check internal error (rules): {0}")]
254 RulesInternal(String),
255}
256
257impl From<BackwardError> for MachineError {
258 fn from(error: BackwardError) -> Self {
259 MachineError {
260 ty: ErrorType::BackwardConversionError(format!("{}", error)),
261 span: error.span,
262 }
263 }
264}
265
266#[derive(thiserror::Error, Debug, Clone)]
267#[error("{span:?}: {ty}")]
268pub struct MachineError {
269 pub ty: ErrorType,
270 pub span: Span,
271}
272
273impl MachineError {
274 fn new(ty: ErrorType, span: Span) -> Self {
275 Self { ty, span }
276 }
277}