use crate::diagnostics::SubtypeFailureReason;
use crate::instantiate::{TypeSubstitution, instantiate_type};
use crate::subtype::SubtypeChecker;
use crate::type_resolver::TypeResolver;
use crate::types::{
FunctionShape, IntrinsicKind, LiteralValue, ObjectShape, ObjectShapeId, PropertyInfo,
TupleElement, TypeId, Visibility,
};
use crate::utils;
use crate::visitor::{
array_element_type, callable_shape_id, function_shape_id, intrinsic_kind, literal_value,
object_shape_id, object_with_index_shape_id, tuple_list_id, union_list_id,
};
impl<'a, R: TypeResolver> SubtypeChecker<'a, R> {
pub fn explain_failure(
&mut self,
source: TypeId,
target: TypeId,
) -> Option<SubtypeFailureReason> {
if source == target {
return None;
}
if !self.strict_null_checks && source.is_nullish() {
return None;
}
if source.is_any() || target.is_any_or_unknown() {
return None;
}
if source.is_never() {
return None;
}
if source.is_error() || target.is_error() {
return Some(SubtypeFailureReason::ErrorType {
source_type: source,
target_type: target,
});
}
self.explain_failure_inner(source, target)
}
fn explain_failure_inner(
&mut self,
source: TypeId,
target: TypeId,
) -> Option<SubtypeFailureReason> {
let mut resolved_source = self.resolve_ref_type(source);
let mut resolved_target = self.resolve_ref_type(target);
if let Some(app_id) = crate::visitor::application_id(self.interner, resolved_source)
&& let Some(expanded) = self.try_expand_application(app_id)
{
resolved_source = self.resolve_ref_type(expanded);
}
if let Some(app_id) = crate::visitor::application_id(self.interner, resolved_target)
&& let Some(expanded) = self.try_expand_application(app_id)
{
resolved_target = self.resolve_ref_type(expanded);
}
if let Some(shape) = self.apparent_primitive_shape_for_type(resolved_source) {
if let Some(t_shape_id) = object_shape_id(self.interner, resolved_target) {
let t_shape = self.interner.object_shape(t_shape_id);
return self.explain_object_failure(
source,
target,
&shape.properties,
None,
&t_shape.properties,
);
}
if let Some(t_shape_id) = object_with_index_shape_id(self.interner, resolved_target) {
let t_shape = self.interner.object_shape(t_shape_id);
return self.explain_indexed_object_failure(source, target, &shape, None, &t_shape);
}
}
if let (Some(s_shape_id), Some(t_shape_id)) = (
object_shape_id(self.interner, resolved_source),
object_shape_id(self.interner, resolved_target),
) {
let s_shape = self.interner.object_shape(s_shape_id);
let t_shape = self.interner.object_shape(t_shape_id);
return self.explain_object_failure(
source,
target,
&s_shape.properties,
Some(s_shape_id),
&t_shape.properties,
);
}
if let (Some(s_shape_id), Some(t_shape_id)) = (
object_with_index_shape_id(self.interner, resolved_source),
object_with_index_shape_id(self.interner, resolved_target),
) {
let s_shape = self.interner.object_shape(s_shape_id);
let t_shape = self.interner.object_shape(t_shape_id);
return self.explain_indexed_object_failure(
source,
target,
&s_shape,
Some(s_shape_id),
&t_shape,
);
}
if let (Some(s_shape_id), Some(t_shape_id)) = (
object_with_index_shape_id(self.interner, resolved_source),
object_shape_id(self.interner, resolved_target),
) {
let s_shape = self.interner.object_shape(s_shape_id);
let t_shape = self.interner.object_shape(t_shape_id);
return self.explain_object_with_index_to_object_failure(
source,
target,
&s_shape,
s_shape_id,
&t_shape.properties,
);
}
if let (Some(s_shape_id), Some(t_shape_id)) = (
object_shape_id(self.interner, resolved_source),
object_with_index_shape_id(self.interner, resolved_target),
) {
let s_shape = self.interner.object_shape(s_shape_id);
let t_shape = self.interner.object_shape(t_shape_id);
if let Some(reason) = self.explain_object_failure(
source,
target,
&s_shape.properties,
Some(s_shape_id),
&t_shape.properties,
) {
return Some(reason);
}
if let Some(ref number_idx) = t_shape.number_index {
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "number",
source_value_type: TypeId::ANY,
target_value_type: number_idx.value_type,
});
}
if let Some(ref string_idx) = t_shape.string_index {
for prop in &s_shape.properties {
let prop_type = self.optional_property_type(prop);
if !self
.check_subtype(prop_type, string_idx.value_type)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "string",
source_value_type: prop_type,
target_value_type: string_idx.value_type,
});
}
}
}
return None;
}
if let Some(t_elem) = array_element_type(self.interner, resolved_target) {
let s_shape_id = object_shape_id(self.interner, resolved_source)
.or_else(|| object_with_index_shape_id(self.interner, resolved_source));
if let Some(s_sid) = s_shape_id
&& let Some(array_base) = self.resolver.get_array_base_type()
{
let params = self.resolver.get_array_base_type_params();
let instantiated = if params.is_empty() {
array_base
} else {
let subst = TypeSubstitution::from_args(self.interner, params, &[t_elem]);
instantiate_type(self.interner, array_base, &subst)
};
let resolved_inst = self.resolve_ref_type(instantiated);
let s_shape = self.interner.object_shape(s_sid);
if let Some(t_obj_sid) = object_shape_id(self.interner, resolved_inst)
.or_else(|| object_with_index_shape_id(self.interner, resolved_inst))
{
let t_shape = self.interner.object_shape(t_obj_sid);
return self.explain_object_failure(
source,
target,
&s_shape.properties,
Some(s_sid),
&t_shape.properties,
);
}
if let Some(callable_sid) = callable_shape_id(self.interner, resolved_inst) {
let callable = self.interner.callable_shape(callable_sid);
if !callable.properties.is_empty() {
return self.explain_object_failure(
source,
target,
&s_shape.properties,
Some(s_sid),
&callable.properties,
);
}
}
}
}
if let Some(s_elem) = array_element_type(self.interner, resolved_source) {
let t_shape_id = object_shape_id(self.interner, resolved_target)
.or_else(|| object_with_index_shape_id(self.interner, resolved_target));
if let Some(t_sid) = t_shape_id
&& let Some(array_base) = self.resolver.get_array_base_type()
{
let params = self.resolver.get_array_base_type_params();
let instantiated = if params.is_empty() {
array_base
} else {
let subst = TypeSubstitution::from_args(self.interner, params, &[s_elem]);
instantiate_type(self.interner, array_base, &subst)
};
let resolved_inst = self.resolve_ref_type(instantiated);
let t_shape = self.interner.object_shape(t_sid);
if let Some(s_obj_sid) = object_shape_id(self.interner, resolved_inst)
.or_else(|| object_with_index_shape_id(self.interner, resolved_inst))
{
let s_shape = self.interner.object_shape(s_obj_sid);
return self.explain_object_failure(
source,
target,
&s_shape.properties,
Some(s_obj_sid),
&t_shape.properties,
);
}
if let Some(callable_sid) = callable_shape_id(self.interner, resolved_inst) {
let callable = self.interner.callable_shape(callable_sid);
if !callable.properties.is_empty() {
return self.explain_object_failure(
source,
target,
&callable.properties,
None,
&t_shape.properties,
);
}
}
}
}
if let (Some(s_fn_id), Some(t_fn_id)) = (
function_shape_id(self.interner, source),
function_shape_id(self.interner, target),
) {
let s_fn = self.interner.function_shape(s_fn_id);
let t_fn = self.interner.function_shape(t_fn_id);
return self.explain_function_failure(&s_fn, &t_fn);
}
if let (Some(s_elem), Some(t_elem)) = (
array_element_type(self.interner, source),
array_element_type(self.interner, target),
) {
if !self.check_subtype(s_elem, t_elem).is_true() {
return Some(SubtypeFailureReason::ArrayElementMismatch {
source_element: s_elem,
target_element: t_elem,
});
}
return None;
}
if let (Some(s_elems), Some(t_elems)) = (
tuple_list_id(self.interner, source),
tuple_list_id(self.interner, target),
) {
let s_elems = self.interner.tuple_list(s_elems);
let t_elems = self.interner.tuple_list(t_elems);
return self.explain_tuple_failure(&s_elems, &t_elems);
}
if let Some(members) = union_list_id(self.interner, target) {
let members = self.interner.type_list(members);
return Some(SubtypeFailureReason::NoUnionMemberMatches {
source_type: source,
target_union_members: members.as_ref().to_vec(),
});
}
if let (Some(s_kind), Some(t_kind)) = (
intrinsic_kind(self.interner, source),
intrinsic_kind(self.interner, target),
) {
if s_kind != t_kind {
return Some(SubtypeFailureReason::IntrinsicTypeMismatch {
source_type: source,
target_type: target,
});
}
return None;
}
if literal_value(self.interner, source).is_some()
&& literal_value(self.interner, target).is_some()
{
return Some(SubtypeFailureReason::LiteralTypeMismatch {
source_type: source,
target_type: target,
});
}
if let (Some(lit), Some(t_kind)) = (
literal_value(self.interner, source),
intrinsic_kind(self.interner, target),
) {
let compatible = match lit {
LiteralValue::String(_) => t_kind == IntrinsicKind::String,
LiteralValue::Number(_) => t_kind == IntrinsicKind::Number,
LiteralValue::BigInt(_) => t_kind == IntrinsicKind::Bigint,
LiteralValue::Boolean(_) => t_kind == IntrinsicKind::Boolean,
};
if !compatible {
return Some(SubtypeFailureReason::LiteralTypeMismatch {
source_type: source,
target_type: target,
});
}
return None;
}
if intrinsic_kind(self.interner, source).is_some()
&& literal_value(self.interner, target).is_some()
{
return Some(SubtypeFailureReason::TypeMismatch {
source_type: source,
target_type: target,
});
}
Some(SubtypeFailureReason::TypeMismatch {
source_type: source,
target_type: target,
})
}
fn explain_object_failure(
&mut self,
source: TypeId,
target: TypeId,
source_props: &[PropertyInfo],
source_shape_id: Option<ObjectShapeId>,
target_props: &[PropertyInfo],
) -> Option<SubtypeFailureReason> {
let mut missing_props: Vec<tsz_common::interner::Atom> = Vec::new();
for t_prop in target_props {
if !t_prop.optional {
let s_prop = self.lookup_property(source_props, source_shape_id, t_prop.name);
if s_prop.is_none() {
missing_props.push(t_prop.name);
}
}
}
if missing_props.len() > 1 {
return Some(SubtypeFailureReason::MissingProperties {
property_names: missing_props,
source_type: source,
target_type: target,
});
}
if missing_props.len() == 1 {
return Some(SubtypeFailureReason::MissingProperty {
property_name: missing_props[0],
source_type: source,
target_type: target,
});
}
for t_prop in target_props {
let s_prop = self.lookup_property(source_props, source_shape_id, t_prop.name);
if let Some(sp) = s_prop {
if t_prop.visibility != Visibility::Public {
if sp.parent_id != t_prop.parent_id {
return Some(SubtypeFailureReason::PropertyNominalMismatch {
property_name: t_prop.name,
});
}
}
else if sp.visibility != Visibility::Public {
return Some(SubtypeFailureReason::PropertyVisibilityMismatch {
property_name: t_prop.name,
source_visibility: sp.visibility,
target_visibility: t_prop.visibility,
});
}
if sp.optional && !t_prop.optional {
return Some(SubtypeFailureReason::OptionalPropertyRequired {
property_name: t_prop.name,
});
}
let source_type = self.optional_property_type(sp);
let target_type = self.optional_property_type(t_prop);
let allow_bivariant = sp.is_method || t_prop.is_method;
if !self
.check_subtype_with_method_variance(source_type, target_type, allow_bivariant)
.is_true()
{
let nested = self.explain_failure_with_method_variance(
source_type,
target_type,
allow_bivariant,
);
return Some(SubtypeFailureReason::PropertyTypeMismatch {
property_name: t_prop.name,
source_property_type: source_type,
target_property_type: target_type,
nested_reason: nested.map(Box::new),
});
}
if !t_prop.readonly
&& (sp.write_type != sp.type_id || t_prop.write_type != t_prop.type_id)
{
let source_write = self.optional_property_write_type(sp);
let target_write = self.optional_property_write_type(t_prop);
if !self
.check_subtype_with_method_variance(
target_write,
source_write,
allow_bivariant,
)
.is_true()
{
let nested = self.explain_failure_with_method_variance(
target_write,
source_write,
allow_bivariant,
);
return Some(SubtypeFailureReason::PropertyTypeMismatch {
property_name: t_prop.name,
source_property_type: source_write,
target_property_type: target_write,
nested_reason: nested.map(Box::new),
});
}
}
}
}
None
}
fn explain_indexed_object_failure(
&mut self,
source: TypeId,
target: TypeId,
source_shape: &ObjectShape,
source_shape_id: Option<ObjectShapeId>,
target_shape: &ObjectShape,
) -> Option<SubtypeFailureReason> {
if let Some(reason) = self.explain_object_failure(
source,
target,
&source_shape.properties,
source_shape_id,
&target_shape.properties,
) {
return Some(reason);
}
if let Some(ref t_string_idx) = target_shape.string_index {
match &source_shape.string_index {
Some(s_string_idx) => {
if s_string_idx.readonly && !t_string_idx.readonly {
return Some(SubtypeFailureReason::TypeMismatch {
source_type: source,
target_type: target,
});
}
if !self
.check_subtype(s_string_idx.value_type, t_string_idx.value_type)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "string",
source_value_type: s_string_idx.value_type,
target_value_type: t_string_idx.value_type,
});
}
}
None => {
for prop in &source_shape.properties {
let prop_type = self.optional_property_type(prop);
if !self
.check_subtype(prop_type, t_string_idx.value_type)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "string",
source_value_type: prop_type,
target_value_type: t_string_idx.value_type,
});
}
}
}
}
}
if let Some(ref t_number_idx) = target_shape.number_index
&& let Some(ref s_number_idx) = source_shape.number_index
{
if s_number_idx.readonly && !t_number_idx.readonly {
return Some(SubtypeFailureReason::TypeMismatch {
source_type: source,
target_type: target,
});
}
if !self
.check_subtype(s_number_idx.value_type, t_number_idx.value_type)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "number",
source_value_type: s_number_idx.value_type,
target_value_type: t_number_idx.value_type,
});
}
}
if let Some(reason) =
self.explain_properties_against_index_signatures(&source_shape.properties, target_shape)
{
return Some(reason);
}
None
}
fn explain_object_with_index_to_object_failure(
&mut self,
source: TypeId,
target: TypeId,
source_shape: &ObjectShape,
source_shape_id: ObjectShapeId,
target_props: &[PropertyInfo],
) -> Option<SubtypeFailureReason> {
for t_prop in target_props {
if let Some(sp) =
self.lookup_property(&source_shape.properties, Some(source_shape_id), t_prop.name)
{
if t_prop.visibility != Visibility::Public {
if sp.parent_id != t_prop.parent_id {
return Some(SubtypeFailureReason::PropertyNominalMismatch {
property_name: t_prop.name,
});
}
}
else if sp.visibility != Visibility::Public {
return Some(SubtypeFailureReason::PropertyVisibilityMismatch {
property_name: t_prop.name,
source_visibility: sp.visibility,
target_visibility: t_prop.visibility,
});
}
if sp.optional && !t_prop.optional {
return Some(SubtypeFailureReason::OptionalPropertyRequired {
property_name: t_prop.name,
});
}
let source_type = self.optional_property_type(sp);
let target_type = self.optional_property_type(t_prop);
let allow_bivariant = sp.is_method || t_prop.is_method;
if !self
.check_subtype_with_method_variance(source_type, target_type, allow_bivariant)
.is_true()
{
let nested = self.explain_failure_with_method_variance(
source_type,
target_type,
allow_bivariant,
);
return Some(SubtypeFailureReason::PropertyTypeMismatch {
property_name: t_prop.name,
source_property_type: source_type,
target_property_type: target_type,
nested_reason: nested.map(Box::new),
});
}
if !t_prop.readonly
&& (sp.write_type != sp.type_id || t_prop.write_type != t_prop.type_id)
{
let source_write = self.optional_property_write_type(sp);
let target_write = self.optional_property_write_type(t_prop);
if !self
.check_subtype_with_method_variance(
target_write,
source_write,
allow_bivariant,
)
.is_true()
{
let nested = self.explain_failure_with_method_variance(
target_write,
source_write,
allow_bivariant,
);
return Some(SubtypeFailureReason::PropertyTypeMismatch {
property_name: t_prop.name,
source_property_type: source_write,
target_property_type: target_write,
nested_reason: nested.map(Box::new),
});
}
}
continue;
}
let mut checked = false;
let target_type = self.optional_property_type(t_prop);
if utils::is_numeric_property_name(self.interner, t_prop.name)
&& let Some(number_idx) = &source_shape.number_index
{
checked = true;
if number_idx.readonly && !t_prop.readonly {
return Some(SubtypeFailureReason::ReadonlyPropertyMismatch {
property_name: t_prop.name,
});
}
if !self
.check_subtype_with_method_variance(
number_idx.value_type,
target_type,
t_prop.is_method,
)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "number",
source_value_type: number_idx.value_type,
target_value_type: target_type,
});
}
}
if let Some(string_idx) = &source_shape.string_index {
checked = true;
if string_idx.readonly && !t_prop.readonly {
return Some(SubtypeFailureReason::ReadonlyPropertyMismatch {
property_name: t_prop.name,
});
}
if !self
.check_subtype_with_method_variance(
string_idx.value_type,
target_type,
t_prop.is_method,
)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "string",
source_value_type: string_idx.value_type,
target_value_type: target_type,
});
}
}
if !checked && !t_prop.optional {
return Some(SubtypeFailureReason::MissingProperty {
property_name: t_prop.name,
source_type: source,
target_type: target,
});
}
}
None
}
fn explain_properties_against_index_signatures(
&mut self,
source: &[PropertyInfo],
target: &ObjectShape,
) -> Option<SubtypeFailureReason> {
let string_index = target.string_index.as_ref();
let number_index = target.number_index.as_ref();
if string_index.is_none() && number_index.is_none() {
return None;
}
for prop in source {
let prop_type = self.optional_property_type(prop);
let allow_bivariant = prop.is_method;
if let Some(number_idx) = number_index {
let is_numeric = utils::is_numeric_property_name(self.interner, prop.name);
if is_numeric {
if !number_idx.readonly && prop.readonly {
return Some(SubtypeFailureReason::ReadonlyPropertyMismatch {
property_name: prop.name,
});
}
if !self
.check_subtype_with_method_variance(
prop_type,
number_idx.value_type,
allow_bivariant,
)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "number",
source_value_type: prop_type,
target_value_type: number_idx.value_type,
});
}
}
}
if let Some(string_idx) = string_index {
if !string_idx.readonly && prop.readonly {
return Some(SubtypeFailureReason::ReadonlyPropertyMismatch {
property_name: prop.name,
});
}
if !self
.check_subtype_with_method_variance(
prop_type,
string_idx.value_type,
allow_bivariant,
)
.is_true()
{
return Some(SubtypeFailureReason::IndexSignatureMismatch {
index_kind: "string",
source_value_type: prop_type,
target_value_type: string_idx.value_type,
});
}
}
}
None
}
fn explain_function_failure(
&mut self,
source: &FunctionShape,
target: &FunctionShape,
) -> Option<SubtypeFailureReason> {
if !(self
.check_subtype(source.return_type, target.return_type)
.is_true()
|| self.allow_void_return && target.return_type == TypeId::VOID)
{
let nested = self.explain_failure(source.return_type, target.return_type);
return Some(SubtypeFailureReason::ReturnTypeMismatch {
source_return: source.return_type,
target_return: target.return_type,
nested_reason: nested.map(Box::new),
});
}
let target_has_rest = target.params.last().is_some_and(|p| p.rest);
let rest_elem_type = if target_has_rest {
target
.params
.last()
.map(|param| self.get_array_element_type(param.type_id))
} else {
None
};
let rest_is_top = self.allow_bivariant_rest
&& matches!(rest_elem_type, Some(TypeId::ANY | TypeId::UNKNOWN));
let source_required = self.required_param_count(&source.params);
let target_required = self.required_param_count(&target.params);
let too_many_params = !self.allow_bivariant_param_count
&& !rest_is_top
&& !target_has_rest
&& source_required > target_required;
if !target_has_rest && too_many_params {
return Some(SubtypeFailureReason::TooManyParameters {
source_count: source_required,
target_count: target_required,
});
}
let source_has_rest = source.params.last().is_some_and(|p| p.rest);
let target_fixed_count = if target_has_rest {
target.params.len().saturating_sub(1)
} else {
target.params.len()
};
let source_fixed_count = if source_has_rest {
source.params.len().saturating_sub(1)
} else {
source.params.len()
};
let fixed_compare_count = std::cmp::min(source_fixed_count, target_fixed_count);
let is_method_or_ctor =
source.is_method || target.is_method || source.is_constructor || target.is_constructor;
for i in 0..fixed_compare_count {
let s_param = &source.params[i];
let t_param = &target.params[i];
if !self.are_parameters_compatible_impl(
s_param.type_id,
t_param.type_id,
is_method_or_ctor,
) {
return Some(SubtypeFailureReason::ParameterTypeMismatch {
param_index: i,
source_param: s_param.type_id,
target_param: t_param.type_id,
});
}
}
if target_has_rest {
let Some(rest_elem_type) = rest_elem_type else {
return None; };
if rest_is_top {
return None;
}
for i in target_fixed_count..source_fixed_count {
let s_param = &source.params[i];
if !self.are_parameters_compatible_impl(
s_param.type_id,
rest_elem_type,
is_method_or_ctor,
) {
return Some(SubtypeFailureReason::ParameterTypeMismatch {
param_index: i,
source_param: s_param.type_id,
target_param: rest_elem_type,
});
}
}
if source_has_rest {
let s_rest_param = source.params.last()?;
let s_rest_elem = self.get_array_element_type(s_rest_param.type_id);
if !self.are_parameters_compatible_impl(
s_rest_elem,
rest_elem_type,
is_method_or_ctor,
) {
return Some(SubtypeFailureReason::ParameterTypeMismatch {
param_index: source_fixed_count,
source_param: s_rest_elem,
target_param: rest_elem_type,
});
}
}
}
if source_has_rest {
let rest_param = source.params.last()?;
let rest_elem_type = self.get_array_element_type(rest_param.type_id);
let rest_is_top = self.allow_bivariant_rest && rest_elem_type.is_any_or_unknown();
if !rest_is_top {
for i in source_fixed_count..target_fixed_count {
let t_param = &target.params[i];
if !self.are_parameters_compatible(rest_elem_type, t_param.type_id) {
return Some(SubtypeFailureReason::ParameterTypeMismatch {
param_index: i,
source_param: rest_elem_type,
target_param: t_param.type_id,
});
}
}
}
}
None
}
fn explain_tuple_failure(
&mut self,
source: &[TupleElement],
target: &[TupleElement],
) -> Option<SubtypeFailureReason> {
let source_required = source.iter().filter(|e| !e.optional && !e.rest).count();
let target_required = target.iter().filter(|e| !e.optional && !e.rest).count();
if source_required < target_required {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
for (i, t_elem) in target.iter().enumerate() {
if t_elem.rest {
let expansion = self.expand_tuple_rest(t_elem.type_id);
let outer_tail = &target[i + 1..];
let combined_suffix: Vec<_> = expansion
.tail
.iter()
.chain(outer_tail.iter())
.cloned()
.collect();
let mut source_end = source.len();
for tail_elem in combined_suffix.iter().rev() {
if source_end <= i {
if !tail_elem.optional {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
break;
}
let s_elem = &source[source_end - 1];
if s_elem.rest {
if !tail_elem.optional {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
break;
}
let assignable = self
.check_subtype(s_elem.type_id, tail_elem.type_id)
.is_true();
if tail_elem.optional && !assignable {
break;
}
if !assignable {
return Some(SubtypeFailureReason::TupleElementTypeMismatch {
index: source_end - 1,
source_element: s_elem.type_id,
target_element: tail_elem.type_id,
});
}
source_end -= 1;
}
let mut source_iter = source.iter().enumerate().take(source_end).skip(i);
for t_fixed in &expansion.fixed {
match source_iter.next() {
Some((j, s_elem)) => {
if s_elem.rest {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
if !self
.check_subtype(s_elem.type_id, t_fixed.type_id)
.is_true()
{
return Some(SubtypeFailureReason::TupleElementTypeMismatch {
index: j,
source_element: s_elem.type_id,
target_element: t_fixed.type_id,
});
}
}
None => {
if !t_fixed.optional {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
}
}
}
if let Some(variadic) = expansion.variadic {
let variadic_array = self.interner.array(variadic);
for (j, s_elem) in source_iter {
let target_type = if s_elem.rest {
variadic_array
} else {
variadic
};
if !self.check_subtype(s_elem.type_id, target_type).is_true() {
return Some(SubtypeFailureReason::TupleElementTypeMismatch {
index: j,
source_element: s_elem.type_id,
target_element: target_type,
});
}
}
return None;
}
if source_iter.next().is_some() {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
return None;
}
if let Some(s_elem) = source.get(i) {
if s_elem.rest {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(), target_count: target.len(),
});
}
if !self.check_subtype(s_elem.type_id, t_elem.type_id).is_true() {
return Some(SubtypeFailureReason::TupleElementTypeMismatch {
index: i,
source_element: s_elem.type_id,
target_element: t_elem.type_id,
});
}
} else if !t_elem.optional {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
}
if source.len() > target.len() {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(),
target_count: target.len(),
});
}
for s_elem in source {
if s_elem.rest {
return Some(SubtypeFailureReason::TupleElementMismatch {
source_count: source.len(), target_count: target.len(),
});
}
}
None
}
pub fn are_types_structurally_identical(&self, a: TypeId, b: TypeId) -> bool {
if a == b {
return true;
}
if let Some(db) = self.query_db {
return db.canonical_id(a) == db.canonical_id(b);
}
let mut canonicalizer =
crate::canonicalize::Canonicalizer::new(self.interner, self.resolver);
let canon_a = canonicalizer.canonicalize(a);
let canon_b = canonicalizer.canonicalize(b);
canon_a == canon_b
}
}