mod macros;
use std::{collections::HashMap, hash::Hash};
use machine_check_common::PropertyMacros;
use proc_macro2::Span;
use quote::ToTokens;
use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::{Brace, Paren},
visit_mut::VisitMut,
Block, Expr, Generics, Ident, ItemFn, Path, PathArguments, PathSegment, Signature, Stmt, Token,
};
use syn_path::path;
use crate::{
into_wir::{
conversion::{
convert_indexing, convert_to_ssa, convert_total, convert_types, expand_macros,
infer_types, resolve_use,
},
from_syn, Errors,
},
util::{create_type_path, path_matches_global_names},
wir::{
WBasicType, WIdent, WProperty, WSubproperty, WSubpropertyFixedPoint, WSubpropertyFunc,
WSubpropertyNext, YConverted, YTac,
},
};
#[derive(Clone, Debug, Hash)]
pub struct ExprSubpropertyFunc {
pub parent: Option<usize>,
pub expr: Expr,
pub dependencies: Vec<usize>,
pub display: Option<String>,
}
#[derive(Clone, Debug, Hash)]
enum ExprSubproperty {
Expr(ExprSubpropertyFunc),
Next(WSubpropertyNext),
FixedPoint(WSubpropertyFixedPoint),
}
impl ExprSubproperty {
fn parent(&self) -> Option<usize> {
match self {
ExprSubproperty::Expr(func) => func.parent,
ExprSubproperty::Next(next) => next.parent,
ExprSubproperty::FixedPoint(fixed_point) => fixed_point.parent,
}
}
}
#[derive(Clone, Debug, Hash)]
struct ExprProperty {
subproperties: Vec<ExprSubproperty>,
}
impl ExprProperty {
fn resolve_use(&mut self, use_map: &HashMap<Ident, Path>) -> Result<(), Errors> {
for subproperty in &mut self.subproperties {
if let ExprSubproperty::Expr(subproperty_func) = subproperty {
resolve_use::resolve_use_expr(&mut subproperty_func.expr, use_map)?;
}
}
Ok(())
}
}
pub fn create_from_syn<D>(
expr: syn::Expr,
global_ident_types: &HashMap<WIdent, WBasicType>,
property_macros: &PropertyMacros<D>,
) -> Result<(WProperty<YConverted>, Vec<String>), Errors> {
let span = expr.span();
let use_map = property_use_map(span);
let display = expr.to_token_stream().to_string();
let mut property = ExprProperty {
subproperties: vec![ExprSubproperty::Expr(ExprSubpropertyFunc {
parent: None,
expr,
dependencies: Vec::new(),
display: Some(display),
})],
};
loop {
let mut expanded_some_macro = false;
property.resolve_use(&use_map)?;
expanded_some_macro |= macros::expand_property_macros(&mut property, property_macros)?;
property.resolve_use(&use_map)?;
for subproperty in &mut property.subproperties {
if let ExprSubproperty::Expr(subproperty_func) = subproperty {
expanded_some_macro |= expand_macros::expand_in_expr(&mut subproperty_func.expr)?;
}
}
if !expanded_some_macro {
break;
}
}
struct Visitor;
impl VisitMut for Visitor {
fn visit_expr_call_mut(&mut self, expr_call: &mut syn::ExprCall) {
if let Expr::Path(expr_path) = &mut *expr_call.func {
let is_as_unsigned =
path_matches_global_names(&expr_path.path, &["machine_check", "as_unsigned"]);
let is_as_signed =
path_matches_global_names(&expr_path.path, &["machine_check", "as_signed"]);
if is_as_unsigned || is_as_signed {
expr_path.path = if is_as_unsigned {
syn::parse_quote!(::std::convert::Into::<::machine_check::Unsigned>::into)
} else {
syn::parse_quote!(::std::convert::Into::<::machine_check::Signed>::into)
};
}
}
}
}
for subproperty in &mut property.subproperties {
if let ExprSubproperty::Expr(subproperty_func) = subproperty {
Visitor.visit_expr_mut(&mut subproperty_func.expr)
}
}
let property = property_from_exprs(property)?;
let property = convert_indexing::convert_property(property);
let (property, panic_messages) = convert_total::convert_property(property);
let property = convert_to_ssa::convert_property(property, global_ident_types)?;
let property = infer_types::infer_property(property)?;
let property = convert_types::convert_property(property)?;
Ok((property, panic_messages))
}
fn property_from_exprs(property: ExprProperty) -> Result<WProperty<YTac>, Errors> {
let mut subproperties = Vec::new();
for (index, subproperty) in property.subproperties.into_iter().enumerate() {
let subproperty = match subproperty {
ExprSubproperty::Expr(subproperty_func) => {
let span = subproperty_func.expr.span();
let signature = Signature {
constness: None,
asyncness: None,
unsafety: None,
abi: None,
fn_token: Token,
ident: Ident::new(&format!("__mck_subfn_{}", index), span),
generics: Generics::default(),
paren_token: Paren::default(),
inputs: Punctuated::default(),
variadic: None,
output: syn::ReturnType::Type(
Token,
Box::new(create_type_path(path!(bool))),
),
};
let func = ItemFn {
attrs: Vec::new(),
vis: syn::Visibility::Inherited,
sig: signature,
block: Box::new(Block {
brace_token: Brace::default(),
stmts: vec![Stmt::Expr(subproperty_func.expr, None)],
}),
};
let func = from_syn::fold_item_fn(func)?;
WSubproperty::Func(WSubpropertyFunc {
parent: subproperty_func.parent,
func,
children: subproperty_func.dependencies,
display: subproperty_func.display,
})
}
ExprSubproperty::Next(next_operator) => WSubproperty::Next(next_operator),
ExprSubproperty::FixedPoint(fixed_point_operator) => {
WSubproperty::FixedPoint(fixed_point_operator)
}
};
subproperties.push(subproperty);
}
Ok(WProperty { subproperties })
}
fn property_use_map(span: Span) -> HashMap<Ident, Path> {
let machine_check_ident = Ident::new("machine_check", span);
let mut use_map = HashMap::new();
for use_name in PROPERTY_USE_MACHINE_CHECK {
let path = Path {
leading_colon: Some(Token),
segments: Punctuated::from_iter([
PathSegment {
ident: machine_check_ident.clone(),
arguments: PathArguments::None,
},
PathSegment {
ident: Ident::new(use_name, span),
arguments: PathArguments::None,
},
]),
};
use_map.insert(Ident::new(use_name, span), path);
}
use_map
}
const PROPERTY_USE_MACHINE_CHECK: [&str; 17] = [
"Bitvector",
"Unsigned",
"Signed",
"lfp",
"gfp",
"AX",
"AG",
"AF",
"AR",
"AU",
"EX",
"EG",
"EF",
"ER",
"EU",
"as_signed",
"as_unsigned",
];