use crate::enums::polarity::Polarity;
use crate::enums::table_state::TableState;
use crate::functions::extend_type_pack::extend_type_pack;
use crate::functions::find_metatable_entry::find_metatable_entry;
use crate::functions::follow_type::follow_type_id;
use crate::functions::fresh_type::fresh_type;
use crate::functions::get_mutable_type::getMutable;
use crate::functions::get_type_alt_j::get_type_id;
use crate::functions::instantiate::instantiate;
use crate::functions::track_interior_free_type::track_interior_free_type;
use crate::records::any_type::AnyType;
use crate::records::constraint::Constraint;
use crate::records::constraint_solver::ConstraintSolver;
use crate::records::free_type::FreeType;
use crate::records::function_type::FunctionType;
use crate::records::iterable_constraint::IterableConstraint;
use crate::records::metatable_type::MetatableType;
use crate::records::never_type::NeverType;
use crate::records::primitive_type::PrimitiveType;
use crate::records::reduce_constraint::ReduceConstraint;
use crate::records::table_indexer::TableIndexer;
use crate::records::table_type::TableType;
use crate::records::type_check_limits::TypeCheckLimits;
use crate::records::type_level::TypeLevel;
use crate::records::unification_too_complex::UnificationTooComplex;
use crate::type_aliases::constraint_v::ConstraintV;
use crate::type_aliases::props_type::Props;
use crate::type_aliases::type_error_data::TypeErrorData;
use crate::type_aliases::type_id::TypeId;
use core::ptr::NonNull;
use luaur_ast::records::location::Location;
use luaur_common::FFlag;
impl ConstraintSolver {
pub fn try_dispatch_iterable_table(
&mut self,
iterator_ty: TypeId,
c: &IterableConstraint,
constraint: *const Constraint,
force: bool,
) -> bool {
let iterator_ty = unsafe { follow_type_id(iterator_ty) };
if unsafe { !get_type_id::<FreeType>(iterator_ty).is_null() } {
let scope = unsafe { (*constraint).scope };
let key_ty = unsafe {
fresh_type(
&mut *self.arena,
&*self.builtin_types,
scope,
Polarity::Mixed,
)
};
let value_ty = unsafe {
fresh_type(
&mut *self.arena,
&*self.builtin_types,
scope,
Polarity::Mixed,
)
};
track_interior_free_type(scope, key_ty);
track_interior_free_type(scope, value_ty);
let props = Props::default();
let table_ty = unsafe {
(*self.arena).add_type(
TableType::table_type_props_optional_table_indexer_type_level_scope_table_state(
&props,
Some(TableIndexer {
index_type: key_ty,
index_result_type: value_ty,
is_read_only: false,
}),
TypeLevel::default(),
scope,
TableState::Sealed,
),
)
};
self.constraint_solver_unify(constraint, iterator_ty, table_ty);
let mut it = c.variables.iter();
if let Some(ty) = it.next() {
self.bind_not_null_constraint_type_id_type_id(constraint, *ty, key_ty);
}
if let Some(ty) = it.next() {
self.bind_not_null_constraint_type_id_type_id(constraint, *ty, value_ty);
}
return true;
}
if unsafe { !get_type_id::<AnyType>(iterator_ty).is_null() } {
self.unpack_iterable_variables(constraint, c, unsafe { (*self.builtin_types).anyType });
return true;
}
if unsafe { !get_type_id::<NeverType>(iterator_ty).is_null() } {
self.unpack_iterable_variables(constraint, c, unsafe {
(*self.builtin_types).neverType
});
return true;
}
if let Some(iterator_table) = unsafe { get_type_id::<TableType>(iterator_ty).as_ref() } {
if iterator_table.state == TableState::Free && !force {
return self.block_type_id_not_null_constraint(iterator_ty, constraint);
}
if let Some(indexer) = iterator_table.indexer {
let value_type = if FFlag::LuauRefineNilFromTableIndexerResultType.get() {
let intersection_with_not_nil = unsafe {
(*self.arena).add_type_function_type_function_initializer_list_type_id(
&(*self.builtin_types).typeFunctions.intersect_func,
&[indexer.index_result_type, (*self.builtin_types).notNilType],
)
};
unsafe {
self.push_constraint(
NonNull::new((*constraint).scope).unwrap(),
(*constraint).location,
ConstraintV::Reduce(ReduceConstraint {
ty: intersection_with_not_nil,
}),
);
}
intersection_with_not_nil
} else {
indexer.index_result_type
};
let mut expected_variables = alloc::vec![indexer.index_type, value_type];
while expected_variables.len() < c.variables.len() {
expected_variables.push(unsafe { (*self.builtin_types).errorType });
}
for (variable, expected) in c.variables.iter().zip(expected_variables.iter()) {
self.constraint_solver_unify(constraint, *variable, *expected);
self.bind_not_null_constraint_type_id_type_id(constraint, *variable, *expected);
}
} else {
self.unpack_iterable_variables(constraint, c, unsafe {
(*self.builtin_types).errorType
});
}
return true;
}
let iter_fn = find_metatable_entry(
self.builtin_types,
&mut self.errors,
iterator_ty,
"__iter",
Location::default(),
);
if let Some(iter_fn) = iter_fn {
if self.is_blocked_type_id(iter_fn) {
return self.block_type_id_not_null_constraint(iter_fn, constraint);
}
let scope = unsafe { (*constraint).scope };
let instantiated_iter_fn = instantiate(
self.builtin_types,
self.arena,
&mut self.limits as *mut TypeCheckLimits,
scope,
iter_fn,
);
if let Some(instantiated_iter_fn) = instantiated_iter_fn {
let iter_ftv = unsafe { get_type_id::<FunctionType>(instantiated_iter_fn) };
if !iter_ftv.is_null() {
let iter_ftv = unsafe { &*iter_ftv };
let expected_iter_args = unsafe {
(*self.arena).add_type_pack_initializer_list_type_id(&[iterator_ty])
};
self.constraint_solver_unify(
constraint,
iter_ftv.arg_types,
expected_iter_args,
);
let iter_rets = extend_type_pack(
unsafe { &mut *self.arena },
self.builtin_types,
iter_ftv.ret_types,
2,
alloc::vec::Vec::new(),
);
if iter_rets.head.len() < 1 {
return true;
}
let next_fn_ty = iter_rets.head[0];
let instantiated_next_fn = instantiate(
self.builtin_types,
self.arena,
&mut self.limits as *mut TypeCheckLimits,
scope,
next_fn_ty,
);
if let Some(instantiated_next_fn) = instantiated_next_fn {
let next_fn = unsafe { get_type_id::<FunctionType>(instantiated_next_fn) };
if !next_fn.is_null() {
let ret_types = unsafe { (*next_fn).ret_types };
self.unpack_and_assign(
c.variables.clone(),
ret_types,
NonNull::new(constraint as *mut Constraint).unwrap(),
);
}
return true;
} else {
let location = unsafe { (*constraint).location };
self.report_error_type_error_data_location(
TypeErrorData::UnificationTooComplex(UnificationTooComplex::default()),
&location,
);
}
} else {
}
} else {
let location = unsafe { (*constraint).location };
self.report_error_type_error_data_location(
TypeErrorData::UnificationTooComplex(UnificationTooComplex::default()),
&location,
);
}
return true;
}
if let Some(iterator_metatable) =
unsafe { get_type_id::<MetatableType>(iterator_ty).as_ref() }
{
return self.try_dispatch_iterable_table(
iterator_metatable.table,
c,
constraint,
force,
);
}
if let Some(primitive_ty) = unsafe { get_type_id::<PrimitiveType>(iterator_ty).as_ref() } {
if primitive_ty.r#type == PrimitiveType::Table {
self.unpack_iterable_variables(constraint, c, unsafe {
(*self.builtin_types).unknownType
});
return true;
}
}
self.unpack_iterable_variables(constraint, c, unsafe { (*self.builtin_types).errorType });
true
}
fn unpack_iterable_variables(
&mut self,
constraint: *const Constraint,
c: &IterableConstraint,
ty: TypeId,
) {
for var_ty in &c.variables {
self.bind_not_null_constraint_type_id_type_id(constraint, *var_ty, ty);
}
}
}