pub mod calling;
pub mod casts;
pub mod classes;
pub mod functions;
pub mod generics;
pub mod others;
pub mod printing;
pub mod properties;
pub mod store;
pub mod subtyping;
mod terms;
use derive_debug_extras::DebugExtras;
pub(crate) use generics::substitution::*;
pub(crate) use casts::*;
use source_map::SpanWithSource;
pub use store::TypeStore;
pub use terms::Constant;
use crate::{
context::information::InformationChain,
events::RootReference,
features::{
objects::SpecialObjects,
operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise, PureUnary},
},
Decidable, FunctionId,
};
pub use self::functions::*;
use self::{generics::generic_type_arguments::GenericArguments, properties::PropertyKey};
pub type ExplicitTypeArgument = (TypeId, SpanWithSource);
pub type TypeArguments = crate::Map<TypeId, TypeId>;
pub type TypeRestrictions = crate::Map<TypeId, ExplicitTypeArgument>;
pub type LookUpGenericMap = crate::Map<TypeId, LookUpGeneric>;
#[derive(PartialEq, Eq, Clone, Copy, DebugExtras, Hash)]
pub struct TypeId(pub(crate) u16);
impl TypeId {
pub const ERROR_TYPE: Self = Self(0);
pub const UNIMPLEMENTED_ERROR_TYPE: TypeId = TypeId::ERROR_TYPE;
pub const NEVER_TYPE: Self = Self(1);
pub const ANY_TYPE: Self = Self(2);
pub const BOOLEAN_TYPE: Self = Self(3);
pub const NUMBER_TYPE: Self = Self(4);
pub const STRING_TYPE: Self = Self(5);
pub const UNDEFINED_TYPE: Self = Self(6);
pub const NULL_TYPE: Self = Self(7);
pub const VOID_TYPE: Self = Self(8);
pub const ARRAY_TYPE: Self = Self(9);
pub const PROMISE_TYPE: Self = Self(10);
pub const T_TYPE: Self = Self(11);
pub const OBJECT_TYPE: Self = Self(12);
pub const FUNCTION_TYPE: Self = Self(13);
pub const REGEXP_TYPE: Self = Self(14);
pub const SYMBOL_TYPE: Self = Self(15);
pub const TRUE: Self = Self(16);
pub const FALSE: Self = Self(17);
pub const ZERO: Self = Self(18);
pub const ONE: Self = Self(19);
pub const NAN_TYPE: Self = Self(20);
pub const ANY_INFERRED_FREE_THIS: Self = Self(21);
pub const NEW_TARGET_ARG: Self = Self(22);
pub const SYMBOL_TO_PRIMITIVE: Self = Self(23);
pub const LITERAL_RESTRICTION: Self = Self(24);
pub const READONLY_RESTRICTION: Self = Self(25);
pub const IMPORT_META: Self = Self(26);
pub(crate) const INTERNAL_TYPE_COUNT: usize = 27;
}
#[derive(Debug, binary_serialize_derive::BinarySerializable)]
pub enum Type {
AliasTo {
to: TypeId,
name: String,
parameters: Option<Vec<TypeId>>,
},
And(TypeId, TypeId),
Or(TypeId, TypeId),
RootPolyType(PolyNature),
Constructor(Constructor),
PartiallyAppliedGenerics(PartiallyAppliedGenerics),
Interface {
name: String,
nominal: bool,
parameters: Option<Vec<TypeId>>,
},
Class {
name: String,
parameters: Option<Vec<TypeId>>,
},
Constant(crate::Constant),
FunctionReference(FunctionId),
Object(ObjectNature),
SpecialObject(SpecialObjects),
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum PolyNature {
Parameter { fixed_to: TypeId },
StructureGeneric { name: String, constrained: bool },
InferGeneric { name: String },
FunctionGeneric { name: String, eager_fixed: TypeId },
MappedGeneric { name: String, eager_fixed: TypeId },
Error(TypeId),
Open(TypeId),
FreeVariable { reference: RootReference, based_on: TypeId },
RecursiveFunction(FunctionId, TypeId),
CatchVariable(TypeId),
}
impl PolyNature {
#[must_use]
pub fn is_substitutable(&self) -> bool {
matches!(
self,
Self::Parameter { .. }
| Self::StructureGeneric { .. }
| Self::FunctionGeneric { .. }
| Self::InferGeneric { .. }
)
}
#[must_use]
pub fn is_inferrable(&self) -> bool {
matches!(
self,
Self::Parameter { fixed_to: to } | Self::FreeVariable { based_on: to, .. } | Self::CatchVariable(to)
if matches!(*to, TypeId::ANY_TYPE)
)
}
#[must_use]
pub fn try_get_constraint(&self) -> Option<TypeId> {
match self {
PolyNature::Parameter { fixed_to: to }
| PolyNature::FunctionGeneric { eager_fixed: to, .. }
| PolyNature::MappedGeneric { eager_fixed: to, .. }
| PolyNature::FreeVariable { based_on: to, .. }
| PolyNature::RecursiveFunction(_, to)
| PolyNature::CatchVariable(to)
| PolyNature::Open(to)
| PolyNature::Error(to) => Some(*to),
PolyNature::StructureGeneric { .. } | PolyNature::InferGeneric { .. } => None,
}
}
}
#[must_use]
pub fn is_primitive(ty: TypeId, _types: &TypeStore) -> bool {
if matches!(ty, TypeId::BOOLEAN_TYPE | TypeId::NUMBER_TYPE | TypeId::STRING_TYPE) {
return true;
}
false
}
#[must_use]
pub fn is_type_constant(ty: TypeId, types: &TypeStore) -> bool {
matches!(ty, TypeId::UNDEFINED_TYPE | TypeId::NULL_TYPE)
|| matches!(
types.get_type_by_id(ty),
Type::Constant(..) | Type::Object(ObjectNature::RealDeal) | Type::SpecialObject(..)
)
}
#[derive(Copy, Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum ObjectNature {
RealDeal,
AnonymousTypeAnnotation,
}
impl Type {
pub(crate) fn get_parameters(&self) -> Option<Vec<TypeId>> {
if let Type::Class { parameters, .. }
| Type::Interface { parameters, .. }
| Type::AliasTo { parameters, .. } = self
{
parameters.clone()
} else {
None
}
}
pub(crate) fn is_dependent(&self) -> bool {
#[allow(clippy::match_same_arms)]
match self {
Type::PartiallyAppliedGenerics(..) => false,
Type::Constructor(_) | Type::RootPolyType(_) => true,
Type::And(_, _) | Type::Or(_, _) => false,
Type::AliasTo { .. } => false,
Type::Interface { .. } | Type::Class { .. } => false,
Type::Constant(_)
| Type::SpecialObject(_)
| Type::FunctionReference(..)
| Type::Object(_) => false,
}
}
pub(crate) fn is_operator(&self) -> bool {
matches!(self, Self::And(..) | Self::Or(..))
}
fn is_nominal(&self) -> bool {
matches!(self, Self::Class { .. } | Self::Interface { nominal: true, .. })
}
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum Constructor {
BinaryOperator {
lhs: TypeId,
operator: MathematicalAndBitwise,
rhs: TypeId,
},
CanonicalRelationOperator {
lhs: TypeId,
operator: CanonicalEqualityAndInequality,
rhs: TypeId,
},
UnaryOperator {
operator: PureUnary,
operand: TypeId,
},
TypeOperator(TypeOperator),
TypeRelationOperator(TypeRelationOperator),
ConditionalResult {
condition: TypeId,
truthy_result: TypeId,
otherwise_result: TypeId,
result_union: TypeId,
},
Image {
on: TypeId,
with: Box<[SynthesisedArgument]>,
result: TypeId,
},
Property {
on: TypeId,
under: PropertyKey<'static>,
result: TypeId,
bind_this: bool,
},
Awaited {
on: TypeId,
result: TypeId,
},
KeyOf(TypeId),
}
impl Constructor {
fn get_base(&self) -> Option<TypeId> {
match self {
Constructor::ConditionalResult { result_union: result, .. }
| Constructor::Awaited { result, .. }
| Constructor::Property { result, .. }
| Constructor::Image { result, .. } => Some(*result),
Constructor::BinaryOperator { .. }
| Constructor::CanonicalRelationOperator { .. }
| Constructor::UnaryOperator { .. }
| Constructor::TypeRelationOperator(_)
| Constructor::TypeOperator(_) => None,
Constructor::KeyOf(_) => Some(TypeId::STRING_TYPE),
}
}
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub struct PartiallyAppliedGenerics {
pub on: TypeId,
pub arguments: GenericArguments,
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum TypeOperator {
PrototypeOf(TypeId),
TypeOf(TypeId),
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum TypeRelationOperator {
Extends { item: TypeId, extends: TypeId },
}
pub(crate) fn new_logical_or_type(lhs: TypeId, rhs: TypeId, types: &mut TypeStore) -> TypeId {
types.new_conditional_type(lhs, lhs, rhs)
}
#[must_use]
pub fn is_type_truthy_falsy(id: TypeId, types: &TypeStore) -> Decidable<bool> {
if id == TypeId::TRUE || id == TypeId::FALSE {
Decidable::Known(id == TypeId::TRUE)
} else if id == TypeId::NULL_TYPE || id == TypeId::UNDEFINED_TYPE {
Decidable::Known(false)
} else {
let ty = types.get_type_by_id(id);
match ty {
Type::AliasTo { .. }
| Type::And(_, _)
| Type::Or(_, _)
| Type::RootPolyType(_)
| Type::Constructor(_)
| Type::Interface { .. }
| Type::PartiallyAppliedGenerics(..)
| Type::Class { .. } => {
Decidable::Unknown(id)
}
Type::FunctionReference(..) | Type::SpecialObject(_) | Type::Object(_) => {
Decidable::Known(true)
}
Type::Constant(cst) => {
Decidable::Known(cast_as_boolean(cst, false).unwrap())
}
}
}
}
pub enum ArgumentOrLookup {
Argument(TypeId),
LookUpGeneric(LookUpGeneric),
}
pub type GenericChain<'a> = Option<GenericChainLink<'a>>;
pub type GenericChainParent<'a> = Option<&'a GenericChainLink<'a>>;
#[derive(Clone, Copy, Debug)]
pub enum GenericChainLink<'a> {
Link {
from: TypeId,
parent_link: GenericChainParent<'a>,
value: &'a GenericArguments,
},
FunctionRoot {
parent_link: Option<&'a GenericArguments>,
call_site_type_arguments: Option<&'a crate::Map<TypeId, (TypeId, SpanWithSource)>>,
type_arguments: &'a crate::Map<TypeId, TypeId>,
},
}
impl<'a> GenericChainLink<'a> {
fn get_value(self) -> Option<&'a GenericArguments> {
if let Self::Link { value, .. } = self {
Some(value)
} else {
None
}
}
#[allow(unused)]
pub(crate) fn get_argument(
&self,
on: TypeId,
info: &impl InformationChain,
types: &TypeStore,
) -> Option<Vec<TypeId>> {
match self {
GenericChainLink::Link { parent_link: parent, value, from: _ } => value
.get_argument_as_list(on, info, types)
.or_else(|| parent.and_then(|parent| parent.get_argument(on, info, types))),
GenericChainLink::FunctionRoot {
parent_link: parent,
call_site_type_arguments,
type_arguments,
} => parent
.and_then(|parent| parent.get_argument_as_list(on, info, types))
.or_else(|| {
call_site_type_arguments.and_then(|ta1| ta1.get(&on).map(|(arg, _)| vec![*arg]))
})
.or_else(|| type_arguments.get(&on).map(|a| vec![*a])),
}
}
pub(crate) fn append_to_link(
from: TypeId,
parent: GenericChainParent<'a>,
value: &'a GenericArguments,
) -> GenericChainLink<'a> {
GenericChainLink::Link { parent_link: parent, value, from }
}
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn append(
from: TypeId,
parent: GenericChainParent<'a>,
value: &'a GenericArguments,
) -> GenericChain<'a> {
Some(GenericChainLink::append_to_link(from, parent, value))
}
pub(crate) fn get_single_argument(&self, on: TypeId) -> Option<TypeId> {
match self {
GenericChainLink::Link { parent_link: parent, value, from: _ } => value
.get_structure_restriction(on)
.or_else(|| parent.and_then(|parent| parent.get_single_argument(on))),
GenericChainLink::FunctionRoot {
parent_link: parent,
call_site_type_arguments,
type_arguments,
} => parent
.and_then(|parent| parent.get_structure_restriction(on))
.or_else(|| {
call_site_type_arguments.and_then(|ta1| ta1.get(&on).map(|(arg, _)| *arg))
})
.or_else(|| type_arguments.get(&on).copied()),
}
}
}
#[derive(Debug)]
pub enum NonEqualityReason {
Mismatch,
PropertiesInvalid {
errors: Vec<(PropertyKey<'static>, PropertyError)>,
},
TooStrict,
MissingParameter,
GenericParameterMismatch,
}
#[derive(Debug)]
pub enum PropertyError {
Missing,
Invalid { expected: TypeId, found: TypeId, mismatch: NonEqualityReason },
}
pub(crate) fn is_explicit_generic(on: TypeId, types: &TypeStore) -> bool {
if let Type::RootPolyType(PolyNature::FunctionGeneric { .. }) = types.get_type_by_id(on) {
true
} else if let Type::Constructor(Constructor::Property { on, under, result: _, bind_this: _ }) =
types.get_type_by_id(on)
{
is_explicit_generic(*on, types)
|| matches!(under, PropertyKey::Type(under) if is_explicit_generic(*under, types))
} else {
false
}
}
pub(crate) fn get_constraint(on: TypeId, types: &TypeStore) -> Option<TypeId> {
match types.get_type_by_id(on) {
Type::RootPolyType(nature) => Some(
*(match nature {
PolyNature::Parameter { fixed_to }
| PolyNature::MappedGeneric { name: _, eager_fixed: fixed_to }
| PolyNature::FunctionGeneric { name: _, eager_fixed: fixed_to } => fixed_to,
PolyNature::Error(ty) | PolyNature::Open(ty) => ty,
PolyNature::FreeVariable { reference: _, based_on } => based_on,
PolyNature::RecursiveFunction(_, return_ty) => return_ty,
PolyNature::StructureGeneric { constrained, .. } => {
return if *constrained {
todo!("get from TypeStore or ???")
} else {
Some(TypeId::ANY_TYPE)
}
}
PolyNature::CatchVariable(constraint) => constraint,
PolyNature::InferGeneric { .. } => return Some(TypeId::ANY_TYPE),
}),
),
Type::Constructor(constructor) => match constructor.clone() {
Constructor::BinaryOperator { lhs, operator, rhs } => {
if let MathematicalAndBitwise::Add = operator {
let lhs = get_larger_type(lhs, types);
let rhs = get_larger_type(rhs, types);
if let (TypeId::NUMBER_TYPE, TypeId::NUMBER_TYPE) = (lhs, rhs) {
Some(TypeId::NUMBER_TYPE)
} else if let (TypeId::STRING_TYPE, _) | (_, TypeId::STRING_TYPE) = (lhs, rhs) {
Some(TypeId::STRING_TYPE)
} else {
crate::utilities::notify!("lhs = {:?}", types.get_type_by_id(lhs));
crate::utilities::notify!("TODO use existing conditional");
Some(TypeId::NUMBER_TYPE)
}
} else {
Some(TypeId::NUMBER_TYPE)
}
}
Constructor::UnaryOperator { operand: _, operator: _ } => {
todo!()
}
Constructor::Awaited { on: _, result }
| Constructor::Image { on: _, with: _, result } => Some(result),
Constructor::Property { on: _, under: _, result, bind_this: _ } => {
crate::utilities::notify!("Here, result of a property get");
Some(result)
}
Constructor::ConditionalResult { result_union, .. } => {
Some(result_union)
}
Constructor::TypeOperator(_) | Constructor::CanonicalRelationOperator { .. } => {
Some(TypeId::BOOLEAN_TYPE)
}
Constructor::TypeRelationOperator(op) => match op {
crate::types::TypeRelationOperator::Extends { .. } => Some(TypeId::BOOLEAN_TYPE),
},
Constructor::KeyOf(_) => Some(TypeId::STRING_TYPE),
},
Type::Object(ObjectNature::RealDeal) => {
None
}
_ => None,
}
}
fn get_larger_type(on: TypeId, types: &TypeStore) -> TypeId {
if let Some(poly_base) = get_constraint(on, types) {
poly_base
} else if let Type::Constant(cst) = types.get_type_by_id(on) {
cst.get_backing_type_id()
} else {
on
}
}
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub enum LookUpGeneric {
NumberPropertyOfSelf, }
impl LookUpGeneric {
#[allow(unreachable_patterns)]
pub(crate) fn calculate_lookup(&self, info: &impl InformationChain, on: TypeId) -> Vec<TypeId> {
match self {
LookUpGeneric::NumberPropertyOfSelf => {
info.get_chain_of_info()
.filter_map(|info| info.current_properties.get(&on).map(|v| v.iter()))
.flatten()
.filter_map(|(_publicity, key, value)| {
if matches!(key, PropertyKey::String(s) if s == "length") {
None
} else {
Some(value.as_get_type())
}
})
.collect()
}
_ => unreachable!(), }
}
}
pub trait TypeCombinable {
fn combine(
condition: TypeId,
truthy_result: Self,
otherwise_result: Self,
types: &mut TypeStore,
) -> Self;
fn default() -> Self;
}
impl TypeCombinable for () {
fn combine(
_condition: TypeId,
_truthy_result: Self,
_otherwise_result: Self,
_types: &mut TypeStore,
) -> Self {
}
fn default() -> Self {}
}
impl TypeCombinable for TypeId {
fn combine(
condition: TypeId,
truthy_result: Self,
otherwise_result: Self,
types: &mut TypeStore,
) -> Self {
types.new_conditional_type(condition, truthy_result, otherwise_result)
}
fn default() -> Self {
TypeId::UNDEFINED_TYPE
}
}
pub enum Confirmation {
HasProperty { on: (), property: () },
IsType { on: (), ty: () },
}
pub(crate) fn get_structure_arguments_based_on_object_constraint<'a, C: InformationChain>(
object: TypeId,
info_chain: &C,
types: &'a TypeStore,
) -> Option<&'a GenericArguments> {
if let Some(object_constraint) =
info_chain.get_chain_of_info().find_map(|c| c.object_constraints.get(&object).copied())
{
let ty = types.get_type_by_id(object_constraint);
if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { arguments, .. }) = ty {
Some(arguments)
} else {
crate::utilities::notify!("Generics might be missed here {:?}", ty);
None
}
} else {
None
}
}