use crate::{
scope::ScopePointer,
r#type::Type,
type_expr::{ErasedScopePortal, ScopedTypeExpr, TypeExpr},
};
use std::fmt::Debug;
#[cfg(feature = "json-schema")]
use schemars::JsonSchema;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "tsify")]
use tsify::Tsify;
#[derive(Debug)]
pub enum SupertypeResult<Diagnostics> {
Supertype,
Unrelated(Diagnostics),
Unknown,
}
impl PartialEq for SupertypeResult<NoSupertypeDiagnostics> {
fn eq(&self, other: &Self) -> bool {
matches!(
(self, other),
(SupertypeResult::Supertype, SupertypeResult::Supertype)
| (SupertypeResult::Unrelated(_), SupertypeResult::Unrelated(_))
| (SupertypeResult::Unknown, SupertypeResult::Unknown)
)
}
}
impl Eq for SupertypeResult<NoSupertypeDiagnostics> {}
#[derive(Debug)]
enum NoSupertypeReason<Diagnostics> {
Unrelated(Diagnostics),
Unknown,
}
impl<D> PartialEq for NoSupertypeReason<D> {
fn eq(&self, other: &Self) -> bool {
matches!((self, other), (Self::Unrelated(_), Self::Unrelated(_)) | (Self::Unknown, Self::Unknown))
}
}
impl<D> SupertypeResult<D> {
pub fn is_supertype(&self) -> bool {
matches!(self, SupertypeResult::Supertype)
}
}
impl<D> From<Result<(), NoSupertypeReason<D>>> for SupertypeResult<D> {
fn from(result: Result<(), NoSupertypeReason<D>>) -> Self {
match result {
Ok(()) => SupertypeResult::Supertype,
Err(NoSupertypeReason::Unrelated(d)) => SupertypeResult::Unrelated(d),
Err(NoSupertypeReason::Unknown) => SupertypeResult::Unknown,
}
}
}
impl<Diagnostics> NoSupertypeReason<Diagnostics> {
pub fn map_unrelated(self, mapper: impl FnOnce(Diagnostics) -> Diagnostics) -> Self {
match self {
Self::Unrelated(d) => Self::Unrelated(mapper(d)),
other => other,
}
}
}
pub trait SupertypeDiagnostics<T: Type>: Debug {
fn new(parent: &ScopedTypeExpr<T>, child: &ScopedTypeExpr<T>, reason: Option<NoSupertypeLayerReason>) -> Self;
fn new_empty() -> Self;
fn add_layer(
self,
parent: &ScopedTypeExpr<T>,
child: &ScopedTypeExpr<T>,
reason: Option<NoSupertypeLayerReason>,
) -> Self;
}
#[derive(Debug)]
pub struct NoSupertypeDiagnostics;
impl<T: Type> SupertypeDiagnostics<T> for NoSupertypeDiagnostics {
fn new(_parent: &ScopedTypeExpr<T>, _child: &ScopedTypeExpr<T>, _reason: Option<NoSupertypeLayerReason>) -> Self {
NoSupertypeDiagnostics
}
fn new_empty() -> Self {
Self
}
fn add_layer(
self,
_parent: &ScopedTypeExpr<T>,
_child: &ScopedTypeExpr<T>,
_reason: Option<NoSupertypeLayerReason>,
) -> Self {
self
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[derive(Debug, Clone, PartialEq)]
pub enum NoSupertypeLayerReason {
UnknownTypeParam,
NodeSignatureInputsVarg,
NodeSignatureInputs,
NodeSignatureOutputs,
NodeSignatureTags,
NodeSignatureRequiredTags,
PortTypesVarg,
PortTypesArity,
PortTypesPort,
Index,
ConstructorArity,
ConstructorParam,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(
rename_all = "camelCase",
bound(
serialize = "T: Serialize, T::Operator: Serialize",
deserialize = "T: Deserialize<'de>, T::Operator: Deserialize<'de>"
)
)
)]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[cfg_attr(feature = "json-schema", schemars(bound = "T: JsonSchema, T::Operator: JsonSchema"))]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[derive(Debug, Clone, PartialEq)]
pub struct NoSupertypeLayer<T: Type> {
pub parent: TypeExpr<T, ErasedScopePortal>,
pub child: TypeExpr<T, ErasedScopePortal>,
pub reason: Option<NoSupertypeLayerReason>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde",
serde(
rename_all = "camelCase",
bound(
serialize = "T: Serialize, T::Operator: Serialize",
deserialize = "T: Deserialize<'de>, T::Operator: Deserialize<'de>"
)
)
)]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[cfg_attr(feature = "json-schema", schemars(bound = "T: JsonSchema, T::Operator: JsonSchema"))]
#[cfg_attr(feature = "tsify", derive(Tsify))]
#[derive(Debug, Clone, PartialEq)]
pub struct DetailedSupertypeDiagnostics<T: Type> {
layers: Vec<NoSupertypeLayer<T>>,
}
impl<T: Type> SupertypeDiagnostics<T> for DetailedSupertypeDiagnostics<T> {
fn new(parent: &ScopedTypeExpr<T>, child: &ScopedTypeExpr<T>, reason: Option<NoSupertypeLayerReason>) -> Self {
Self { layers: vec![NoSupertypeLayer { parent: parent.clone().into(), child: child.clone().into(), reason }] }
}
fn new_empty() -> Self {
Self { layers: vec![] }
}
fn add_layer(
mut self,
parent: &ScopedTypeExpr<T>,
child: &ScopedTypeExpr<T>,
reason: Option<NoSupertypeLayerReason>,
) -> Self {
self.layers.push(NoSupertypeLayer { parent: parent.clone().into(), child: child.clone().into(), reason });
self
}
}
impl<T: Type> ScopedTypeExpr<T> {
pub fn supertype_of_naive(&self, child: &ScopedTypeExpr<T>) -> SupertypeResult<NoSupertypeDiagnostics> {
let scope = ScopePointer::new_root();
self.supertype_of_impl::<NoSupertypeDiagnostics>(child, &scope, &scope).into()
}
pub fn supertype_of(
&self,
child: &ScopedTypeExpr<T>,
parent_scope: &ScopePointer<T>,
child_scope: &ScopePointer<T>,
) -> SupertypeResult<NoSupertypeDiagnostics> {
self.supertype_of_impl::<NoSupertypeDiagnostics>(child, parent_scope, child_scope).into()
}
pub fn supertype_of_detailed(
&self,
child: &ScopedTypeExpr<T>,
parent_scope: &ScopePointer<T>,
child_scope: &ScopePointer<T>,
) -> SupertypeResult<DetailedSupertypeDiagnostics<T>> {
self.supertype_of_impl::<DetailedSupertypeDiagnostics<T>>(child, parent_scope, child_scope).into()
}
fn supertype_of_impl<D: SupertypeDiagnostics<T>>(
&self,
child: &Self,
parent_scope: &ScopePointer<T>,
child_scope: &ScopePointer<T>,
) -> Result<(), NoSupertypeReason<D>> {
use NoSupertypeReason::*;
let (parent, parent_scope) = self.build_uninferred_child_scope(parent_scope);
let (child, child_scope) = child.build_inferred_child_scope(parent.as_ref(), child_scope, &parent_scope);
match (parent.as_ref(), child.as_ref()) {
(Self::Any, _) => Ok(()),
(parent, Self::Any) => match parent.is_any(&parent_scope) {
None => Err(Unknown),
Some(true) => Ok(()),
Some(false) => Err(Unrelated(D::new(parent, child.as_ref(), None))),
},
(_, Self::Never) => Ok(()),
(parent @ Self::Never, child) => match child.is_never(&child_scope) {
None => Err(Unknown),
Some(true) => Ok(()),
Some(false) => Err(Unrelated(D::new(parent, child, None))),
},
(
parent @ Self::TypeParameter(parent_param, _infer1),
child @ Self::TypeParameter(child_param, _infer2),
) => {
let Some((parent_registered, parent_param_scope)) = parent_scope.lookup(parent_param) else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::UnknownTypeParam))));
};
let Some((child_registered, child_param_scope)) = child_scope.lookup(child_param) else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::UnknownTypeParam))));
};
if parent_param_scope == child_param_scope && parent_param == child_param {
return Ok(());
}
let (child_boundary, child_boundary_scope) = child_registered.get_boundary(child_param_scope);
if child_boundary.is_never(&child_boundary_scope).unwrap_or(false) {
return Ok(());
}
match (parent_registered.inferred(), child_registered.inferred()) {
(Some((parent_inferred, parent_inferred_scope)), Some((child_inferred, child_inferred_scope))) => {
parent_inferred.supertype_of_impl(
&child_inferred,
&parent_inferred_scope,
&child_inferred_scope,
)
}
(Some((parent_inferred, parent_inferred_scope)), None) => {
let (child_boundary, child_boundary_scope) = child_registered.get_boundary(child_param_scope);
if parent_inferred
.supertype_of(child_boundary.as_ref(), &parent_inferred_scope, &child_boundary_scope)
.is_supertype()
{
return Ok(());
}
parent_inferred.supertype_of_impl::<D>(child, &parent_inferred_scope, &child_scope)
}
(None, Some((child_inferred, child_inferred_scope))) => {
parent.supertype_of_impl::<D>(&child_inferred, &parent_scope, &child_inferred_scope)
}
(None, None) => Err(Unknown),
}
}
(parent @ Self::TypeParameter(parent_param, _infer), child) => {
let Some((parent_registered, _parent_param_scope)) = parent_scope.lookup(parent_param) else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::UnknownTypeParam))));
};
if let Some((parent_inferred, parent_inferred_scope)) = parent_registered.inferred() {
parent_inferred
.supertype_of_impl::<D>(child, &parent_inferred_scope, &child_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(parent, child, None)))
} else {
Err(Unknown)
}
}
(parent, child @ Self::TypeParameter(child_param, _infer)) => {
let Some((child_registered, child_param_scope)) = child_scope.lookup(child_param) else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::UnknownTypeParam))));
};
let (child_boundary, child_boundary_scope) = child_registered.get_boundary(child_param_scope);
if child_boundary.is_never(&child_boundary_scope).unwrap_or(false) {
return Ok(());
}
if let Some((child_inferred, child_inferred_scope)) = child_registered.inferred() {
parent
.supertype_of_impl::<D>(&child_inferred, &parent_scope, &child_inferred_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(parent, child, None)))
} else {
let (child_boundary, child_boundary_scope) = child_registered.get_boundary(child_param_scope);
parent
.supertype_of_impl::<D>(child_boundary.as_ref(), &parent_scope, &child_boundary_scope)
.map_err(|_| Unknown)
}
}
(parent, Self::ScopePortal { expr, scope }) => {
parent.supertype_of_impl::<D>(expr, &parent_scope, &scope.portal)
}
(Self::ScopePortal { expr, scope }, child) => {
expr.supertype_of_impl::<D>(child, &scope.portal, &child_scope)
}
(Self::Operation { a, b, operator }, child) => {
let a_normalized = a.normalize(&parent_scope);
let b_normalized = b.normalize(&parent_scope);
T::operation(&a_normalized, operator, &b_normalized).supertype_of_impl::<D>(
child,
&parent_scope,
&child_scope,
)
}
(parent, Self::Operation { a, b, operator }) => {
let a_normalized = a.normalize(&child_scope);
let b_normalized = b.normalize(&child_scope);
parent.supertype_of_impl::<D>(
&T::operation(&a_normalized, operator, &b_normalized),
&parent_scope,
&child_scope,
)
}
(Self::KeyOf(parent_expr), child) => {
let (keyof, keyof_scope) = parent_expr.keyof(&parent_scope).ok_or(Unknown)?;
keyof.supertype_of_impl::<D>(child, &keyof_scope, &child_scope)
}
(parent, child @ Self::KeyOf(child_expr)) => {
let (keyof, keyof_scope) = child_expr.keyof(&child_scope).ok_or(Unknown)?;
parent.supertype_of_impl::<D>(&keyof, &parent_scope, &keyof_scope).map_err(|e| {
e.map_unrelated(|d| d.add_layer(parent, child, Some(NoSupertypeLayerReason::UnknownTypeParam)))
})
}
(parent @ Self::Union(_, _), Self::Union(child_a, child_b)) => {
match (
parent.supertype_of_impl::<D>(child_a, &parent_scope, &child_scope),
parent.supertype_of_impl::<D>(child_b, &parent_scope, &child_scope),
) {
(Ok(()), Ok(())) => Ok(()),
(_, Err(Unknown)) | (Err(Unknown), _) => Err(Unknown),
(Err(Unrelated(e)), _) | (_, Err(Unrelated(e))) => Err(Unrelated(e)),
}
}
(parent @ Self::Union(parent_a, parent_b), child) => {
match (
parent_a.supertype_of_impl::<D>(child, &parent_scope, &child_scope),
parent_b.supertype_of_impl::<D>(child, &parent_scope, &child_scope),
) {
(Ok(()), _) => Ok(()),
(_, Ok(())) => Ok(()),
(Err(Unknown), _) | (_, Err(Unknown)) => Err(Unknown),
(Err(Unrelated(e)), _) => Err(Unrelated(e.add_layer(parent, child, None))),
}
}
(parent, child @ Self::Union(child_a, child_b)) => {
match (
parent.supertype_of_impl::<D>(child_a, &parent_scope, &child_scope),
parent.supertype_of_impl::<D>(child_b, &parent_scope, &child_scope),
) {
(Ok(()), Ok(())) => Ok(()),
(_, Err(Unknown)) | (Err(Unknown), _) => Err(Unknown),
(_, Err(Unrelated(e))) | (Err(Unrelated(e)), _) => Err(Unrelated(e.add_layer(parent, child, None))),
}
}
(Self::Intersection(parent_a, parent_b), child) => {
let (intersection, intersection_scope) =
Self::intersection(parent_a, parent_b, &parent_scope, &parent_scope).ok_or(Unknown)?;
intersection
.supertype_of_impl::<D>(child, &intersection_scope, &child_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(self, child, None)))
}
(parent, Self::Intersection(child_a, child_b)) => {
let (intersection, intersection_scope) =
Self::intersection(child_a, child_b, &child_scope, &child_scope).ok_or(Unknown)?;
parent
.supertype_of_impl::<D>(&intersection, &parent_scope, &intersection_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(parent, &child, None)))
}
(Self::Conditional(conditional), child) => conditional
.distribute(&parent_scope)
.ok_or(Unknown)?
.supertype_of_impl::<D>(child, &parent_scope, &child_scope),
(parent, Self::Conditional(conditional)) => parent.supertype_of_impl::<D>(
&conditional.distribute(&child_scope).ok_or(Unknown)?,
&parent_scope,
&child_scope,
),
(parent @ Self::NodeSignature(parent_sig), child @ Self::NodeSignature(child_sig)) => {
if let (Self::PortTypes(parent_in), Self::PortTypes(child_in)) = (&parent_sig.inputs, &child_sig.inputs)
&& parent_in.varg.is_some()
&& child_in.varg.is_none()
&& child_in.ports.len() > parent_in.ports.len()
{
return Err(Unrelated(D::new(
parent,
child,
Some(NoSupertypeLayerReason::NodeSignatureInputsVarg),
)));
}
child_sig.inputs.supertype_of_impl::<D>(&parent_sig.inputs, &child_scope, &parent_scope).map_err(
|e| {
e.map_unrelated(|d| {
d.add_layer(parent, child, Some(NoSupertypeLayerReason::NodeSignatureInputs))
})
},
)?;
parent_sig.outputs.supertype_of_impl::<D>(&child_sig.outputs, &parent_scope, &child_scope).map_err(
|e| {
e.map_unrelated(|d| {
d.add_layer(parent, child, Some(NoSupertypeLayerReason::NodeSignatureOutputs))
})
},
)?;
if let Some(parent_tags) = &parent_sig.tags {
if let Some(child_tags) = &child_sig.tags {
if !parent_tags.is_superset(child_tags) {
return Err(Unrelated(D::new(
parent,
child,
Some(NoSupertypeLayerReason::NodeSignatureTags),
)));
}
} else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::NodeSignatureTags))));
}
}
if !child_sig.required_tags.is_superset(&parent_sig.required_tags) {
return Err(Unrelated(D::new(
parent,
child,
Some(NoSupertypeLayerReason::NodeSignatureRequiredTags),
)));
}
Ok(())
}
(parent @ Self::PortTypes(parent_ports), child @ Self::PortTypes(child_ports)) => {
if parent_ports.varg.is_some() && child_ports.varg.is_none() {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::PortTypesVarg))));
}
let max_arg_count = parent_ports.ports.len().max(child_ports.ports.len()) + 1;
for i in 0..max_arg_count {
let Some(parent_arg) = parent_ports.get_port_type(i) else {
break;
};
let Some(child_arg) = child_ports.get_port_type(i) else {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::PortTypesArity))));
};
parent_arg.supertype_of_impl::<D>(child_arg, &parent_scope, &child_scope).map_err(|e| {
e.map_unrelated(|d| d.add_layer(parent, child, Some(NoSupertypeLayerReason::PortTypesPort)))
})?;
}
Ok(())
}
(Self::Index { expr, index }, child) => {
let (index_type, index_scope) = expr.index(index, &parent_scope, &parent_scope).ok_or(Unknown)?;
index_type
.supertype_of_impl::<D>(child, &index_scope, &child_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(self, child, Some(NoSupertypeLayerReason::Index))))
}
(parent, Self::Index { expr, index }) => {
let (index_type, index_scope) = expr.index(index, &child_scope, &child_scope).ok_or(Unknown)?;
parent
.supertype_of_impl::<D>(&index_type, &parent_scope, &index_scope)
.map_err(|e| e.map_unrelated(|d| d.add_layer(parent, &child, Some(NoSupertypeLayerReason::Index))))
}
(parent @ Self::Type(inst_parent), child @ Self::Type(inst_child)) => {
if inst_parent.supertype_of(inst_child) { Ok(()) } else { Err(Unrelated(D::new(parent, child, None))) }
}
(parent @ Self::Type(inst_parent), child @ Self::Constructor { inner: inst_child, .. }) => {
if inst_parent.supertype_of(inst_child) { Ok(()) } else { Err(Unrelated(D::new(parent, child, None))) }
}
(parent @ Self::Constructor { inner: inst_parent, parameters }, child @ Self::Type(inst_child)) => {
if !parameters.is_empty() {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::ConstructorArity))));
}
if inst_parent.supertype_of(inst_child) { Ok(()) } else { Err(Unrelated(D::new(parent, child, None))) }
}
(
parent @ Self::Constructor { inner: parent_inner, parameters: parent_parameters },
child @ Self::Constructor { inner: child_inner, parameters: child_parameters },
) => {
if !parent_inner.supertype_of(child_inner) {
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::ConstructorParam))));
}
for (ident, parent_param) in parent_parameters {
let Some(child_param) = child_parameters.get(ident) else {
if parent_param.is_optional_in_constructor(&parent_scope) {
continue;
}
return Err(Unrelated(D::new(parent, child, Some(NoSupertypeLayerReason::ConstructorArity))));
};
parent_param.supertype_of_impl::<D>(child_param, &parent_scope, &child_scope).map_err(|e| {
e.map_unrelated(|d| d.add_layer(parent, child, Some(NoSupertypeLayerReason::ConstructorParam)))
})?;
}
Ok(())
}
(Self::NodeSignature(_), _) | (_, Self::NodeSignature(_)) => {
Err(Unrelated(D::new(parent.as_ref(), child.as_ref(), None)))
}
(Self::PortTypes { .. }, _) | (_, Self::PortTypes { .. }) => {
Err(Unrelated(D::new(parent.as_ref(), child.as_ref(), None)))
}
}
}
pub fn is_optional_in_constructor(&self, scope: &ScopePointer<T>) -> bool {
let mut is_optional = false;
self.traverse_union(scope, &mut |traversal_expr, traversal_scope| {
if let Some(t) = traversal_expr.normalize_to_type(traversal_scope)
&& t.optional_in_constructor()
{
is_optional = true;
}
});
is_optional
}
}