use std::sync::Arc;
use super::{Compiler, derives_default, idx16};
use crate::interpreter::bytecode::{DefaultIr, EnumVariant, NO_CONV, Op, Reg, StructShape};
use crate::interpreter::numeric::IntWidth;
use crate::interpreter::resolver::Res;
pub(super) const DEFAULT_DEPTH: usize = 8;
impl Compiler<'_> {
pub(super) fn default_ir(&mut self, ty: &syn::Type) -> Option<DefaultIr> {
self.default_ir_at(ty, 0)
}
pub(super) fn default_ir_at(&mut self, ty: &syn::Type, depth: usize) -> Option<DefaultIr> {
if depth > DEFAULT_DEPTH {
return None;
}
match ty {
syn::Type::Tuple(t) if t.elems.is_empty() => Some(DefaultIr::Unit),
syn::Type::Tuple(t) => {
let items = t
.elems
.iter()
.map(|elem| self.default_ir_at(elem, depth + 1))
.collect::<Option<Vec<_>>>()?;
Some(DefaultIr::Tuple(items))
}
syn::Type::Paren(p) => self.default_ir_at(&p.elem, depth),
syn::Type::Group(g) => self.default_ir_at(&g.elem, depth),
syn::Type::Path(p) => self.default_ir_path(&p.path, depth),
_ => None,
}
}
pub(super) fn default_ir_path(&mut self, path: &syn::Path, depth: usize) -> Option<DefaultIr> {
let last = path.segments.last()?.ident.to_string();
if let Some(width) = IntWidth::parse(&last) {
return Some(DefaultIr::Int(width));
}
let builtin = match last.as_str() {
"f32" => Some(DefaultIr::F32),
"f64" => Some(DefaultIr::F64),
"bool" => Some(DefaultIr::Bool),
"char" => Some(DefaultIr::Char),
"String" | "str" => Some(DefaultIr::Str),
"Vec" | "VecDeque" => Some(DefaultIr::Vec),
"HashMap" | "BTreeMap" => Some(DefaultIr::Map),
"HashSet" | "BTreeSet" => Some(DefaultIr::Set),
"Option" => Some(DefaultIr::Opt),
_ => None,
};
if builtin.is_some() {
return builtin;
}
let mut segs: Vec<String> = path.segments.iter().map(|s| s.ident.to_string()).collect();
if segs.len() == 1
&& segs[0] == "Self"
&& let Some(ty) = self.ctx.impl_type
{
segs[0] = ty.to_string();
}
match self.resolve_path_res(&segs).ok()? {
Res::Struct(canon) => self.default_ir_struct(&canon, depth),
Res::Enum(canon) => self.default_ir_enum(&canon),
Res::Alias(m, target) => match &*target {
syn::Type::Path(p) => {
let canon = self.ctx.resolver.resolve_struct_key(m, &p.path)?;
self.default_ir_struct(&canon, depth)
}
_ => None,
},
_ => None,
}
}
pub(super) fn default_ir_struct(
&mut self,
canon: &Arc<str>,
depth: usize,
) -> Option<DefaultIr> {
let def = self.ctx.resolver.structs.get(canon)?;
if !derives_default(&def.ast.attrs) {
return None;
}
let ast = def.ast.clone();
let mut names = Vec::new();
let mut renames = Vec::new();
let mut skip_none = Vec::new();
let mut fields = Vec::new();
for (index, field) in ast.fields.iter().enumerate() {
let name = field
.ident
.as_ref()
.map_or_else(|| index.to_string(), ToString::to_string);
names.push(Arc::<str>::from(name));
renames
.push(crate::interpreter::serde_attrs::serde_rename(field).map(Arc::<str>::from));
skip_none.push(crate::interpreter::serde_attrs::serde_skip_none(field));
fields.push(self.default_ir_at(&field.ty, depth + 1)?);
}
let shape = self.shape_for(canon, names, renames, skip_none);
Some(DefaultIr::Struct { shape, fields })
}
pub(super) fn default_ir_enum(&mut self, canon: &Arc<str>) -> Option<DefaultIr> {
let ast = self.ctx.resolver.enums.get(canon)?.clone();
if !derives_default(&ast.attrs) {
return None;
}
let variant = ast.variants.iter().find(|variant| {
variant
.attrs
.iter()
.any(|attr| attr.path().is_ident("default"))
})?;
let name = variant.ident.to_string();
let def = self.ctx.resolver.enum_defs.get(canon)?;
Some(DefaultIr::Enum(EnumVariant {
def: def.clone(),
variant: def.variant_index(&name)?,
}))
}
pub(super) fn shape_for(
&mut self,
name: &Arc<str>,
fields: Vec<Arc<str>>,
renames: Vec<Option<Arc<str>>>,
skip_none: Vec<bool>,
) -> Arc<StructShape> {
if let Some(known) = self.shapes.iter().find(|s| {
s.name == *name
&& s.fields == fields
&& s.renames == renames
&& s.skip_none == skip_none
}) {
return known.clone();
}
let type_id = self.ctx.resolver.type_id_of(name);
let built = StructShape::typed(name.clone(), type_id, fields, renames, skip_none);
self.shapes.push(built.clone());
built
}
pub(super) fn default_ir_path_pub(&mut self, path: &syn::Path) -> Option<DefaultIr> {
self.default_ir_path(path, 0)
}
pub(super) fn install_return_error(&mut self, output: &syn::ReturnType) {
let syn::ReturnType::Type(_, ty) = output else {
return;
};
if let Some(canon) = self.result_error_type(ty) {
self.cur().ret_error = Some(canon);
}
}
pub(super) fn result_error_type(&self, ty: &syn::Type) -> Option<Arc<str>> {
let syn::Type::Path(p) = ty else { return None };
let last = p.path.segments.last()?;
if last.ident != "Result" {
return None;
}
let syn::PathArguments::AngleBracketed(args) = &last.arguments else {
return None;
};
let err = args
.args
.iter()
.filter_map(|arg| match arg {
syn::GenericArgument::Type(t) => Some(t),
_ => None,
})
.nth(1)?;
let syn::Type::Path(err_path) = err else {
return None;
};
let segs: Vec<String> = err_path
.path
.segments
.iter()
.map(|s| s.ident.to_string())
.collect();
match self.resolve_path_res(&segs).ok()? {
Res::Struct(canon) | Res::Enum(canon) => Some(canon),
_ => None,
}
}
pub(super) fn try_conv(&mut self) -> u16 {
let Some(target) = self.cur().ret_error.clone() else {
return NO_CONV;
};
let table = &mut self.cur().try_targets;
if let Some(index) = table.iter().position(|known| *known == target) {
return idx16(index);
}
table.push(target);
idx16(table.len() - 1)
}
pub(super) fn emit_default(&mut self, dst: Reg, ir: DefaultIr) {
let table = &mut self.cur().defaults;
table.push(ir);
let index = idx16(table.len() - 1);
self.emit(Op::BuildDefault { dst, ir: index });
}
}