use formalang::ir::{
EnumId, GenericBase, IrEnum, IrGenericParam, IrModule, ResolvedType, StructId,
};
#[derive(Debug)]
pub(crate) enum Compound<'a> {
Optional(&'a ResolvedType),
Array(&'a ResolvedType),
Range(&'a ResolvedType),
Dictionary {
key: &'a ResolvedType,
value: &'a ResolvedType,
},
None,
}
impl<'a> Compound<'a> {
pub(crate) fn of(ty: &'a ResolvedType, module: &IrModule) -> Self {
let ResolvedType::Generic { base, args } = ty else {
return Self::None;
};
match base {
GenericBase::Enum(eid) if Some(*eid) == module.prelude_optional_id() => {
args.first().map_or(Self::None, Self::Optional)
}
GenericBase::Struct(sid) if Some(*sid) == module.prelude_array_id() => {
args.first().map_or(Self::None, Self::Array)
}
GenericBase::Struct(sid) if Some(*sid) == module.prelude_range_id() => {
args.first().map_or(Self::None, Self::Range)
}
GenericBase::Struct(sid) if Some(*sid) == module.prelude_dictionary_id() => {
if let [k, v] = args.as_slice() {
Self::Dictionary { key: k, value: v }
} else {
Self::None
}
}
GenericBase::Enum(_) | GenericBase::Struct(_) | GenericBase::Trait(_) => Self::None,
}
}
}
pub(crate) fn optional_inner<'a>(
ty: &'a ResolvedType,
module: &IrModule,
) -> Option<&'a ResolvedType> {
match Compound::of(ty, module) {
Compound::Optional(t) => Some(t),
Compound::Array(_) | Compound::Range(_) | Compound::Dictionary { .. } | Compound::None => {
None
}
}
}
pub(crate) fn array_elem<'a>(ty: &'a ResolvedType, module: &IrModule) -> Option<&'a ResolvedType> {
match Compound::of(ty, module) {
Compound::Array(t) => Some(t),
Compound::Optional(_)
| Compound::Range(_)
| Compound::Dictionary { .. }
| Compound::None => None,
}
}
pub(crate) fn range_bound<'a>(ty: &'a ResolvedType, module: &IrModule) -> Option<&'a ResolvedType> {
match Compound::of(ty, module) {
Compound::Range(t) => Some(t),
Compound::Optional(_)
| Compound::Array(_)
| Compound::Dictionary { .. }
| Compound::None => None,
}
}
pub(crate) const fn enum_id_of(ty: &ResolvedType) -> Option<EnumId> {
match ty {
ResolvedType::Enum(id)
| ResolvedType::Generic {
base: GenericBase::Enum(id),
..
} => Some(*id),
ResolvedType::Primitive(_)
| ResolvedType::Struct(_)
| ResolvedType::Trait(_)
| ResolvedType::Tuple(_)
| ResolvedType::Generic { .. }
| ResolvedType::TypeParam(_)
| ResolvedType::External { .. }
| ResolvedType::Closure { .. }
| ResolvedType::Error => None,
}
}
pub(crate) fn substitute_type_params(
ty: &ResolvedType,
generic_params: &[IrGenericParam],
type_args: &[ResolvedType],
) -> ResolvedType {
match ty {
ResolvedType::TypeParam(name) => {
if let Some(idx) = generic_params.iter().position(|p| &p.name == name)
&& let Some(arg) = type_args.get(idx)
{
return arg.clone();
}
ty.clone()
}
ResolvedType::Generic { base, args } => ResolvedType::Generic {
base: *base,
args: args
.iter()
.map(|a| substitute_type_params(a, generic_params, type_args))
.collect(),
},
ResolvedType::Tuple(fields) => ResolvedType::Tuple(
fields
.iter()
.map(|(n, t)| {
(
n.clone(),
substitute_type_params(t, generic_params, type_args),
)
})
.collect(),
),
ResolvedType::Closure {
param_tys,
return_ty,
} => ResolvedType::Closure {
param_tys: param_tys
.iter()
.map(|(c, t)| (*c, substitute_type_params(t, generic_params, type_args)))
.collect(),
return_ty: Box::new(substitute_type_params(return_ty, generic_params, type_args)),
},
ResolvedType::Primitive(_)
| ResolvedType::Struct(_)
| ResolvedType::Trait(_)
| ResolvedType::Enum(_)
| ResolvedType::External { .. }
| ResolvedType::Error => ty.clone(),
}
}
pub(crate) fn substitute_enum(
e: &IrEnum,
generic_params: &[IrGenericParam],
type_args: &[ResolvedType],
) -> IrEnum {
if generic_params.is_empty() || type_args.is_empty() {
return e.clone();
}
let mut clone = e.clone();
for variant in &mut clone.variants {
for field in &mut variant.fields {
field.ty = substitute_type_params(&field.ty, generic_params, type_args);
}
}
clone.generic_params.clear();
clone
}
pub(crate) fn generic_args_for_enum(ty: &ResolvedType, expected_id: EnumId) -> &[ResolvedType] {
if let ResolvedType::Generic {
base: GenericBase::Enum(id),
args,
} = ty
&& *id == expected_id
{
args.as_slice()
} else {
&[]
}
}
pub(crate) const fn struct_id_of(ty: &ResolvedType) -> Option<StructId> {
match ty {
ResolvedType::Struct(id)
| ResolvedType::Generic {
base: GenericBase::Struct(id),
..
} => Some(*id),
ResolvedType::Primitive(_)
| ResolvedType::Enum(_)
| ResolvedType::Trait(_)
| ResolvedType::Tuple(_)
| ResolvedType::Generic { .. }
| ResolvedType::TypeParam(_)
| ResolvedType::External { .. }
| ResolvedType::Closure { .. }
| ResolvedType::Error => None,
}
}
pub(crate) fn dictionary_kv<'a>(
ty: &'a ResolvedType,
module: &IrModule,
) -> Option<(&'a ResolvedType, &'a ResolvedType)> {
match Compound::of(ty, module) {
Compound::Dictionary { key, value } => Some((key, value)),
Compound::Optional(_) | Compound::Array(_) | Compound::Range(_) | Compound::None => None,
}
}