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;