use crate::lang::functions::overload::{ArgTransform, FnTransforms, Overload, OverloadKind, RvalTransform};
use crate::lang::functions::{Argument, Function, FunctionKind, Signature};
use crate::lang::meta::Emission;
use crate::lang::types::OverloadFamily;
use crate::lang::types::kind::task::Task;
use crate::lang::types::kind::{DelegateKind, Primitive, TypeKind, TypePattern};
use crate::lang::types::{Decorators, Type};
use crate::lang::{FunctionId, TypeId};
use crate::pass::Outcome::Unchanged;
use crate::pass::model::rust::fns::overload::{IntPtrEligibility, derive_overload_id, intptr_eligibility, is_eligible_intptr};
use crate::pass::{ModelResult, PassInfo, model};
use std::collections::HashSet;
#[derive(Default)]
pub struct Config {}
pub struct Pass {
info: PassInfo,
processed: HashSet<FunctionId>,
}
impl Pass {
#[must_use]
pub fn new(_: Config) -> Self {
Self { info: PassInfo { name: file!() }, processed: HashSet::default() }
}
#[allow(clippy::too_many_arguments)]
pub fn process(
&mut self,
_pass_meta: &mut crate::pass::PassMeta,
fns_all: &mut model::common::fns::all::Pass,
kinds: &mut model::common::types::kind::Pass,
names: &mut model::common::types::names::Pass,
types: &mut model::common::types::all::Pass,
type_overloads: &model::rust::types::overload::all::Pass,
) -> ModelResult {
let mut outcome = Unchanged;
let mut originals: Vec<_> = fns_all.originals().map(|(&id, f)| (id, f.clone())).collect();
originals.sort_by_key(|(id, _)| *id);
for &(original_id, ref original_fn) in &originals {
if self.processed.contains(&original_id) {
continue;
}
let args = &original_fn.signature.arguments;
let async_result_ty = detect_async_callback(args, types);
let transformable_args = match async_result_ty {
Some(_) => &args[..args.len() - 1],
None => &args[..],
};
let has_delegate = transformable_args.iter().any(|a| is_delegate_class(a.ty, types));
let has_any_unknown = transformable_args
.iter()
.any(|a| matches!(intptr_eligibility(a.ty, types), IntPtrEligibility::Unknown));
if has_any_unknown {
continue;
}
if async_result_ty.is_none() && !has_delegate {
self.processed.insert(original_id);
continue;
}
if !all_families_ready(transformable_args, types, type_overloads) {
continue;
}
if let Some(result_ty_id) = async_result_ty {
let Some(result_ty) = types.get(result_ty_id) else { continue };
if !matches!(&result_ty.kind, TypeKind::TypePattern(TypePattern::Result(_, _, _))) {
self.processed.insert(original_id);
continue;
}
}
let (overload_args, arg_transforms) = build_arg_transforms(transformable_args, types, type_overloads);
if has_delegate && async_result_ty.is_none() {
let sig = Signature { arguments: overload_args.clone(), rval: original_fn.signature.rval };
let id = derive_overload_id(original_id, &sig);
let transforms = FnTransforms { rval: RvalTransform::PassThrough, args: arg_transforms.clone() };
let func = Function {
emission: original_fn.emission.clone(),
name: original_fn.name.clone(),
signature: sig,
kind: FunctionKind::Overload(Overload { kind: OverloadKind::Body(transforms), base: original_id }),
};
fns_all.register(id, func);
outcome.changed();
}
if let Some(result_ty_id) = async_result_ty {
let task_ty_id = resolve_or_create_task_type(result_ty_id, types, kinds, names);
let sig = Signature { arguments: overload_args, rval: task_ty_id };
let id = derive_overload_id(original_id, &sig);
let transforms = FnTransforms { rval: RvalTransform::AsyncTask(result_ty_id), args: arg_transforms };
let func = Function {
emission: original_fn.emission.clone(),
name: original_fn.name.clone(),
signature: sig,
kind: FunctionKind::Overload(Overload { kind: OverloadKind::Async(transforms), base: original_id }),
};
fns_all.register(id, func);
outcome.changed();
}
self.processed.insert(original_id);
}
Ok(outcome)
}
}
fn resolve_or_create_task_type(
result_ty_id: TypeId,
types: &mut model::common::types::all::Pass,
kinds: &mut model::common::types::kind::Pass,
names: &mut model::common::types::names::Pass,
) -> TypeId {
let result_ty = types.get(result_ty_id);
let (inner, task_name) = match result_ty.map(|t| &t.kind) {
Some(TypeKind::TypePattern(TypePattern::Result(ok_ty, _, _))) => {
let ok_is_void = matches!(types.get(*ok_ty).map(|t| &t.kind), Some(TypeKind::Primitive(Primitive::Void)));
if ok_is_void {
(None, "Task".to_string())
} else {
let ok_name = types.get(*ok_ty).map_or_else(|| "void".to_string(), |t| t.name.clone());
(Some(*ok_ty), format!("Task<{ok_name}>"))
}
}
_ => (None, "Task".to_string()),
};
let task_ty_id = TypeId::from_id(result_ty_id.id().derive(0x_7461_736B_5F74_7970));
if types.get(task_ty_id).is_none() {
let kind = TypeKind::Task(Task { inner });
kinds.set(task_ty_id, kind.clone());
names.set(task_ty_id, task_name.clone());
types.set(task_ty_id, Type { emission: Emission::Builtin, name: task_name, kind, decorators: Decorators::default() });
}
task_ty_id
}
fn detect_async_callback(args: &[Argument], types: &model::common::types::all::Pass) -> Option<TypeId> {
let last = args.last()?;
let ty = types.get(last.ty)?;
match &ty.kind {
TypeKind::TypePattern(TypePattern::AsyncCallback(t)) => Some(*t),
_ => None,
}
}
fn all_families_ready(args: &[Argument], types: &model::common::types::all::Pass, type_overloads: &model::rust::types::overload::all::Pass) -> bool {
args.iter().all(|arg| {
if is_delegate_class(arg.ty, types) {
matches!(type_overloads.get(arg.ty), Some(OverloadFamily::Delegate(_)))
} else if is_eligible_intptr(arg.ty, types) {
matches!(type_overloads.get(arg.ty), Some(OverloadFamily::Pointer(_)))
} else {
true
}
})
}
fn build_arg_transforms(
args: &[Argument],
types: &model::common::types::all::Pass,
type_overloads: &model::rust::types::overload::all::Pass,
) -> (Vec<Argument>, Vec<ArgTransform>) {
let mut overload_args = Vec::new();
let mut transforms = Vec::new();
for arg in args {
if let Some(OverloadFamily::Delegate(family)) = is_delegate_class(arg.ty, types).then(|| type_overloads.get(arg.ty)).flatten() {
overload_args.push(Argument { name: arg.name.clone(), ty: family.signature });
transforms.push(ArgTransform::WrapDelegate);
} else if let Some(OverloadFamily::Pointer(family)) = is_eligible_intptr(arg.ty, types).then(|| type_overloads.get(arg.ty)).flatten() {
overload_args.push(Argument { name: arg.name.clone(), ty: family.by_ref });
transforms.push(ArgTransform::Ref);
} else {
overload_args.push(Argument { name: arg.name.clone(), ty: arg.ty });
transforms.push(ArgTransform::PassThrough);
}
}
(overload_args, transforms)
}
fn is_delegate_class(ty: TypeId, types: &model::common::types::all::Pass) -> bool {
matches!(types.get(ty).map(|t| &t.kind), Some(TypeKind::Delegate(d)) if d.kind == DelegateKind::Class)
}