Skip to main content

SubtypeChecker

Struct SubtypeChecker 

Source
pub struct SubtypeChecker<'a, R: TypeResolver = NoopResolver> {
Show 14 fields pub strict_function_types: bool, pub allow_void_return: bool, pub allow_bivariant_rest: bool, pub bypass_evaluation: bool, pub max_depth: u32, pub allow_bivariant_param_count: bool, pub exact_optional_property_types: bool, pub strict_null_checks: bool, pub no_unchecked_indexed_access: bool, pub disable_method_bivariance: bool, pub inheritance_graph: Option<&'a InheritanceGraph>, pub is_class_symbol: Option<&'a dyn Fn(SymbolRef) -> bool>, pub any_propagation: AnyPropagationMode, pub tracer: Option<&'a mut dyn DynSubtypeTracer>, /* private fields */
}
Expand description

Subtype checking context. Maintains the “seen” set for cycle detection.

Fields§

§strict_function_types: bool

Whether to use strict function types (contravariant parameters). Default: true (sound, correct behavior)

§allow_void_return: bool

Whether to allow any return type when the target return is void.

§allow_bivariant_rest: bool

Whether rest parameters of any/unknown should be treated as bivariant. See https://github.com/microsoft/TypeScript/issues/20007.

§bypass_evaluation: bool

When true, skip the evaluate_type() call in check_subtype. This prevents infinite recursion when TypeEvaluator calls SubtypeChecker for simplification, since TypeEvaluator has already evaluated the types.

§max_depth: u32

Maximum recursion depth for subtype checking. Used by TypeEvaluator simplification to prevent stack overflow. Default: MAX_SUBTYPE_DEPTH (100)

§allow_bivariant_param_count: bool

Whether required parameter count mismatches are allowed for bivariant methods.

§exact_optional_property_types: bool

Whether optional properties are exact (exclude implicit undefined). Default: false (legacy TS behavior).

§strict_null_checks: bool

Whether null/undefined are treated as separate types. Default: true (strict null checks).

§no_unchecked_indexed_access: bool

Whether indexed access includes undefined. Default: false (legacy TS behavior).

§disable_method_bivariance: bool§inheritance_graph: Option<&'a InheritanceGraph>

Optional inheritance graph for O(1) nominal class subtype checking. When provided, enables fast nominal checks for class inheritance.

§is_class_symbol: Option<&'a dyn Fn(SymbolRef) -> bool>

Optional callback to check if a symbol is a class (for nominal subtyping). Returns true if the symbol has the CLASS flag set.

§any_propagation: AnyPropagationMode

Controls how any is treated during subtype checks.

§tracer: Option<&'a mut dyn DynSubtypeTracer>

Optional tracer for collecting subtype failure diagnostics. When Some, enables detailed failure reason collection for error messages. When None, disables tracing for maximum performance (default).

Implementations§

Source§

impl<'a> SubtypeChecker<'a, NoopResolver>

Source

pub fn new(interner: &'a dyn TypeDatabase) -> Self

Create a new SubtypeChecker without a resolver (basic mode).

Source§

impl<'a, R: TypeResolver> SubtypeChecker<'a, R>

Source

pub fn with_resolver(interner: &'a dyn TypeDatabase, resolver: &'a R) -> Self

Create a new SubtypeChecker with a custom resolver.

Source

pub const fn with_inheritance_graph(self, graph: &'a InheritanceGraph) -> Self

Set the inheritance graph for O(1) nominal class subtype checking.

Source

pub fn with_class_check(self, check: &'a dyn Fn(SymbolRef) -> bool) -> Self

Set the callback to check if a symbol is a class.

Source

pub const fn with_any_propagation_mode(self, mode: AnyPropagationMode) -> Self

Configure how any is treated during subtype checks.

Source

pub fn with_tracer(self, tracer: &'a mut dyn DynSubtypeTracer) -> Self

Set the tracer for collecting subtype failure diagnostics. When set, enables detailed failure reason collection for error messages.

Source

pub fn with_query_db(self, db: &'a dyn QueryDatabase) -> Self

Set the query database for Salsa-backed memoization. When set, routes evaluate_type and is_subtype_of through Salsa.

Source

pub const fn with_strict_null_checks(self, strict_null_checks: bool) -> Self

Set whether strict null checks are enabled. When false, null and undefined are assignable to any type.

Source

pub fn reset(&mut self)

Reset per-check state so this checker can be reused for another subtype check.

This clears cycle detection sets and counters while preserving configuration (strict_function_types, allow_void_return, etc.) and borrowed references (interner, resolver, inheritance_graph, etc.).

Uses .clear() instead of re-allocating, so hash set memory is reused.

Source

pub const fn depth_exceeded(&self) -> bool

Whether the recursion depth was exceeded during subtype checking.

Source§

impl<'a, R: TypeResolver> SubtypeChecker<'a, R>

Source

pub fn check_subtype(&mut self, source: TypeId, target: TypeId) -> SubtypeResult

When a cycle is detected, we return CycleDetected (coinductive semantics) which implements greatest fixed point semantics - the correct behavior for recursive type checking. When depth/iteration limits are exceeded, we return DepthExceeded (conservative false) for soundness.

Source§

impl<'a, R: TypeResolver> SubtypeChecker<'a, R>

Source

pub fn explain_failure( &mut self, source: TypeId, target: TypeId, ) -> Option<SubtypeFailureReason>

Explain why source is not assignable to target.

This is the “slow path” - called only when is_assignable_to returns false and we need to generate an error message. Re-runs the subtype logic with tracing enabled to produce a structured failure reason.

Returns None if the types are actually compatible (shouldn’t happen if called correctly after a failed check).

Source

pub fn are_types_structurally_identical(&self, a: TypeId, b: TypeId) -> bool

Check if two types are structurally identical using De Bruijn indices for cycles.

This is the O(1) alternative to bidirectional subtyping for identity checks. It transforms cyclic graphs into trees to solve the Graph Isomorphism problem.

Source§

impl<'a, R: TypeResolver> SubtypeChecker<'a, R>

Source

pub fn is_subtype_of(&mut self, source: TypeId, target: TypeId) -> bool

Check if source is a subtype of target. This is the main entry point for subtype checking.

When a QueryDatabase is available (via with_query_db), fast-path checks (identity, any, unknown, never) are done locally, then the full structural check is delegated to the internal check_subtype which may use Salsa memoization for evaluate_type calls.

Source

pub fn is_assignable_to(&mut self, source: TypeId, target: TypeId) -> bool

Check if source is assignable to target. This is a strict structural check; use CompatChecker for TypeScript assignability rules.

Source§

impl<'a, R: TypeResolver> SubtypeChecker<'a, R>

Source

pub fn are_types_overlapping(&self, a: TypeId, b: TypeId) -> bool

Check if two types have any overlap (non-empty intersection).

This is used for TS2367: “This condition will always return ‘false’ since the types ‘X’ and ‘Y’ have no overlap.”

Returns true if there exists at least one type that is a subtype of both a and b. Returns false if a & b would be the never type (zero overlap).

This catches OBVIOUS non-overlaps:

  • Different primitives (string vs number, boolean vs bigint, etc.)
  • Different literals of same primitive (“a” vs “b”, 1 vs 2)
  • Object property type mismatches ({ a: string } vs { a: number })

For complex types (unions, intersections, generics), we conservatively return true to avoid false positives.

§Examples
  • are_types_overlapping(string, number) -> false (different primitives)
  • are_types_overlapping(1, 2) -> false (different number literals)
  • are_types_overlapping({ a: string }, { a: number }) -> false (property type mismatch)
  • are_types_overlapping({ a: 1 }, { b: 2 }) -> true (can have { a: 1, b: 2 })
  • are_types_overlapping(string, "hello") -> true (literal is subtype of primitive)

Trait Implementations§

Source§

impl<'a, R: TypeResolver> AssignabilityChecker for SubtypeChecker<'a, R>

Source§

fn is_assignable_to(&mut self, source: TypeId, target: TypeId) -> bool

Source§

fn is_assignable_to_bivariant_callback( &mut self, source: TypeId, target: TypeId, ) -> bool

Assignability check for bivariant callback parameters. Read more
Source§

fn evaluate_type(&mut self, type_id: TypeId) -> TypeId

Evaluate/expand a type using the checker’s resolver context. This is needed during inference constraint collection, where Application types like Func<T> must be expanded to their structural form (e.g., a Callable). The default implementation returns the type unchanged (no resolver available).
Source§

fn is_assignable_to_strict(&mut self, source: TypeId, target: TypeId) -> bool

Auto Trait Implementations§

§

impl<'a, R> Freeze for SubtypeChecker<'a, R>

§

impl<'a, R = NoopResolver> !RefUnwindSafe for SubtypeChecker<'a, R>

§

impl<'a, R = NoopResolver> !Send for SubtypeChecker<'a, R>

§

impl<'a, R = NoopResolver> !Sync for SubtypeChecker<'a, R>

§

impl<'a, R> Unpin for SubtypeChecker<'a, R>

§

impl<'a, R> UnsafeUnpin for SubtypeChecker<'a, R>

§

impl<'a, R = NoopResolver> !UnwindSafe for SubtypeChecker<'a, R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more