use std::collections::btree_map::Entry;
use super::{CustomConcrete, ExtensionBuildError};
use super::{Extension, ExtensionId, SignatureError};
use crate::types::{least_upper_bound, CustomType, TypeName};
use crate::types::type_param::{check_type_args, TypeArg};
use crate::types::type_param::TypeParam;
use crate::types::TypeBound;
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(tag = "b")]
#[allow(missing_docs)]
pub enum TypeDefBound {
Explicit { bound: TypeBound },
FromParams { indices: Vec<usize> },
}
impl From<TypeBound> for TypeDefBound {
fn from(bound: TypeBound) -> Self {
Self::Explicit { bound }
}
}
impl TypeDefBound {
pub fn any() -> Self {
TypeDefBound::Explicit {
bound: TypeBound::Any,
}
}
pub fn copyable() -> Self {
TypeDefBound::Explicit {
bound: TypeBound::Copyable,
}
}
pub fn from_params(indices: Vec<usize>) -> Self {
TypeDefBound::FromParams { indices }
}
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct TypeDef {
extension: ExtensionId,
name: TypeName,
params: Vec<TypeParam>,
description: String,
bound: TypeDefBound,
}
impl TypeDef {
pub fn check_args(&self, args: &[TypeArg]) -> Result<(), SignatureError> {
check_type_args(args, &self.params).map_err(SignatureError::TypeArgMismatch)
}
pub fn check_custom(&self, custom: &CustomType) -> Result<(), SignatureError> {
if self.extension() != custom.parent_extension() {
return Err(SignatureError::ExtensionMismatch(
self.extension().clone(),
custom.parent_extension().clone(),
));
}
if self.name() != custom.def_name() {
return Err(SignatureError::NameMismatch(
self.name().clone(),
custom.def_name().clone(),
));
}
check_type_args(custom.type_args(), &self.params)?;
let calc_bound = self.bound(custom.args());
if calc_bound == custom.bound() {
Ok(())
} else {
Err(SignatureError::WrongBound {
expected: calc_bound,
actual: custom.bound(),
})
}
}
pub fn instantiate(&self, args: impl Into<Vec<TypeArg>>) -> Result<CustomType, SignatureError> {
let args = args.into();
check_type_args(&args, &self.params)?;
let bound = self.bound(&args);
Ok(CustomType::new(
self.name().clone(),
args,
self.extension().clone(),
bound,
))
}
pub fn bound(&self, args: &[TypeArg]) -> TypeBound {
match &self.bound {
TypeDefBound::Explicit { bound } => *bound,
TypeDefBound::FromParams { indices } => {
let args: Vec<_> = args.iter().collect();
if indices.is_empty() {
return TypeBound::Any;
}
least_upper_bound(indices.iter().map(|i| {
let ta = args.get(*i);
match ta {
Some(TypeArg::Type { ty: s }) => s.least_upper_bound(),
_ => panic!("TypeArg index does not refer to a type."),
}
}))
}
}
}
pub fn params(&self) -> &[TypeParam] {
&self.params
}
fn name(&self) -> &TypeName {
&self.name
}
fn extension(&self) -> &ExtensionId {
&self.extension
}
}
impl Extension {
pub fn add_type(
&mut self,
name: TypeName,
params: Vec<TypeParam>,
description: String,
bound: TypeDefBound,
) -> Result<&TypeDef, ExtensionBuildError> {
let ty = TypeDef {
extension: self.name.clone(),
name,
params,
description,
bound,
};
match self.types.entry(ty.name.clone()) {
Entry::Occupied(_) => Err(ExtensionBuildError::TypeDefExists(ty.name)),
Entry::Vacant(ve) => Ok(ve.insert(ty)),
}
}
}
#[cfg(test)]
mod test {
use crate::extension::prelude::{QB_T, USIZE_T};
use crate::extension::SignatureError;
use crate::std_extensions::arithmetic::float_types::FLOAT64_TYPE;
use crate::types::type_param::{TypeArg, TypeArgError, TypeParam};
use crate::types::{Signature, Type, TypeBound};
use super::{TypeDef, TypeDefBound};
#[test]
fn test_instantiate_typedef() {
let def = TypeDef {
name: "MyType".into(),
params: vec![TypeParam::Type {
b: TypeBound::Copyable,
}],
extension: "MyRsrc".try_into().unwrap(),
description: "Some parametrised type".into(),
bound: TypeDefBound::FromParams { indices: vec![0] },
};
let typ = Type::new_extension(
def.instantiate(vec![TypeArg::Type {
ty: Type::new_function(Signature::new(vec![], vec![])),
}])
.unwrap(),
);
assert_eq!(typ.least_upper_bound(), TypeBound::Copyable);
let typ2 = Type::new_extension(def.instantiate([USIZE_T.into()]).unwrap());
assert_eq!(typ2.least_upper_bound(), TypeBound::Copyable);
assert_eq!(
def.instantiate([TypeArg::Type { ty: QB_T }]),
Err(SignatureError::TypeArgMismatch(
TypeArgError::TypeMismatch {
arg: TypeArg::Type { ty: QB_T },
param: TypeBound::Copyable.into()
}
))
);
assert_eq!(
def.instantiate([]).unwrap_err(),
SignatureError::TypeArgMismatch(TypeArgError::WrongNumberArgs(0, 1))
);
assert_eq!(
def.instantiate([
TypeArg::Type { ty: FLOAT64_TYPE },
TypeArg::Type { ty: FLOAT64_TYPE },
])
.unwrap_err(),
SignatureError::TypeArgMismatch(TypeArgError::WrongNumberArgs(2, 1))
);
}
}