Skip to main content

tsz_solver/
application.rs

1//! Application Type Evaluation
2//!
3//! This module handles evaluation of generic type applications like `Store<ExtractState<R>>`.
4//! The key operation is:
5//! 1. Resolve the base type reference (e.g., `Store`) to get its body
6//! 2. Get the type parameters from the symbol
7//! 3. Instantiate the body with the provided type arguments
8//! 4. Recursively evaluate any nested applications
9//!
10//! This module implements the solver-first architecture principle: pure type logic
11//! belongs in the solver, while the checker handles AST traversal and symbol resolution.
12
13use crate::relations::subtype::TypeResolver;
14use crate::type_queries;
15use crate::types::TypeId;
16#[cfg(test)]
17use crate::types::*;
18use crate::{TypeDatabase, TypeSubstitution, instantiate_type};
19use std::cell::RefCell;
20
21/// Result of application type evaluation.
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum ApplicationResult {
24    /// Successfully evaluated to a concrete type
25    Resolved(TypeId),
26    /// The type is not an application type (pass through)
27    NotApplication(TypeId),
28    /// Recursion limit reached
29    DepthExceeded(TypeId),
30    /// Symbol resolution failed
31    ResolutionFailed(TypeId),
32}
33
34/// Evaluator for generic type applications.
35///
36/// This evaluator takes a type application like `Box<string>` and:
37/// 1. Looks up the definition of `Box` (via the resolver)
38/// 2. Gets its type parameters
39/// 3. Substitutes the type arguments
40/// 4. Returns the resulting type
41///
42/// # Type Resolver
43///
44/// The evaluator uses a `TypeResolver` to handle symbol resolution.
45/// This abstraction allows the solver to remain independent of the binder/checker:
46/// - `resolve_ref(symbol)` - get the body type of a type alias/interface
47/// - `get_type_params(symbol)` - get the type parameters for a symbol
48pub struct ApplicationEvaluator<'a, R: TypeResolver> {
49    interner: &'a dyn TypeDatabase,
50    resolver: &'a R,
51    /// Unified recursion guard for cycle detection and depth limiting.
52    guard: RefCell<crate::recursion::RecursionGuard<TypeId>>,
53    /// Cache for evaluated applications
54    cache: RefCell<rustc_hash::FxHashMap<TypeId, TypeId>>,
55}
56
57impl<'a, R: TypeResolver> ApplicationEvaluator<'a, R> {
58    /// Create a new application evaluator.
59    pub fn new(interner: &'a dyn TypeDatabase, resolver: &'a R) -> Self {
60        Self {
61            interner,
62            resolver,
63            guard: RefCell::new(crate::recursion::RecursionGuard::with_profile(
64                crate::recursion::RecursionProfile::TypeApplication,
65            )),
66            cache: RefCell::new(rustc_hash::FxHashMap::default()),
67        }
68    }
69
70    /// Clear the evaluation cache.
71    /// Call this when contextual type changes to ensure fresh evaluation.
72    pub fn clear_cache(&self) {
73        self.cache.borrow_mut().clear();
74    }
75
76    /// Evaluate an Application type by resolving the base symbol and instantiating.
77    ///
78    /// This handles types like `Store<ExtractState<R>>` by:
79    /// 1. Resolving the base type reference to get its body
80    /// 2. Getting the type parameters
81    /// 3. Instantiating the body with the provided type arguments
82    /// 4. Recursively evaluating the result
83    ///
84    /// # Returns
85    /// - `ApplicationResult::Resolved(type_id)` - successfully evaluated
86    /// - `ApplicationResult::NotApplication(type_id)` - input was not an application type
87    /// - `ApplicationResult::DepthExceeded(type_id)` - recursion limit reached
88    /// - `ApplicationResult::ResolutionFailed(type_id)` - symbol resolution failed
89    pub fn evaluate(&self, type_id: TypeId) -> ApplicationResult {
90        // Check if it's a generic application type
91        if !type_queries::is_generic_type(self.interner, type_id) {
92            return ApplicationResult::NotApplication(type_id);
93        }
94
95        // Check cache
96        if let Some(&cached) = self.cache.borrow().get(&type_id) {
97            return ApplicationResult::Resolved(cached);
98        }
99
100        // Unified enter: checks iterations, depth, cycle detection
101        match self.guard.borrow_mut().enter(type_id) {
102            crate::recursion::RecursionResult::Entered => {}
103            crate::recursion::RecursionResult::Cycle => {
104                return ApplicationResult::Resolved(type_id);
105            }
106            crate::recursion::RecursionResult::DepthExceeded
107            | crate::recursion::RecursionResult::IterationExceeded => {
108                return ApplicationResult::DepthExceeded(type_id);
109            }
110        }
111
112        let result = self.evaluate_inner(type_id);
113
114        self.guard.borrow_mut().leave(type_id);
115
116        if let ApplicationResult::Resolved(result_type) = result {
117            self.cache.borrow_mut().insert(type_id, result_type);
118        }
119
120        result
121    }
122
123    /// Inner evaluation logic without recursion guards.
124    fn evaluate_inner(&self, type_id: TypeId) -> ApplicationResult {
125        // Get application info (base type and type arguments)
126        let Some((base, args)) = type_queries::get_application_info(self.interner, type_id) else {
127            return ApplicationResult::NotApplication(type_id);
128        };
129
130        // Get DefId from Lazy type instead of SymbolRef
131        let Some(def_id) = type_queries::get_lazy_def_id(self.interner, base) else {
132            return ApplicationResult::NotApplication(type_id);
133        };
134
135        // Resolve the DefId to get its body type
136        let Some(body_type) = self.resolver.resolve_lazy(def_id, self.interner) else {
137            return ApplicationResult::ResolutionFailed(type_id);
138        };
139
140        if body_type == TypeId::ANY || body_type == TypeId::ERROR {
141            return ApplicationResult::Resolved(type_id);
142        }
143
144        // Get type parameters for this DefId
145        let type_params = self
146            .resolver
147            .get_lazy_type_params(def_id)
148            .unwrap_or_default();
149
150        if type_params.is_empty() {
151            return ApplicationResult::Resolved(body_type);
152        }
153
154        // Evaluate type arguments recursively
155        let evaluated_args: Vec<TypeId> = args
156            .iter()
157            .map(|&arg| match self.evaluate(arg) {
158                ApplicationResult::Resolved(t) => t,
159                _ => arg,
160            })
161            .collect();
162
163        // Create substitution and instantiate
164        let substitution =
165            TypeSubstitution::from_args(self.interner, &type_params, &evaluated_args);
166        let instantiated = instantiate_type(self.interner, body_type, &substitution);
167
168        // Recursively evaluate for nested applications
169        match self.evaluate(instantiated) {
170            ApplicationResult::Resolved(result) => ApplicationResult::Resolved(result),
171            _ => ApplicationResult::Resolved(instantiated),
172        }
173    }
174
175    /// Evaluate a type and return the result, falling back to the original type.
176    ///
177    /// This is a convenience method that unwraps the `ApplicationResult`.
178    pub fn evaluate_or_original(&self, type_id: TypeId) -> TypeId {
179        match self.evaluate(type_id) {
180            ApplicationResult::Resolved(t)
181            | ApplicationResult::NotApplication(t)
182            | ApplicationResult::DepthExceeded(t)
183            | ApplicationResult::ResolutionFailed(t) => t,
184        }
185    }
186}
187
188#[cfg(test)]
189#[path = "../tests/application_tests.rs"]
190mod tests;