lisette_semantics/checker/
scopes.rs1use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
2use std::cell::Cell;
3use syntax::ast::BindingId;
4use syntax::ast::Span;
5use syntax::types::{Symbol, Type};
6
7#[derive(Debug, Clone, Default)]
8pub struct DepthCounter(Cell<usize>);
9
10impl DepthCounter {
11 pub fn new() -> Self {
12 Self(Cell::new(0))
13 }
14 pub fn with_value(n: usize) -> Self {
15 Self(Cell::new(n))
16 }
17 pub fn get(&self) -> usize {
18 self.0.get()
19 }
20 pub fn increment(&self) {
21 self.0.set(self.0.get() + 1);
22 }
23 pub fn decrement(&self) {
24 self.0.set(self.0.get().saturating_sub(1));
25 }
26 pub fn is_active(&self) -> bool {
27 self.0.get() > 0
28 }
29 pub fn reset(&self) -> usize {
30 let prev = self.0.get();
31 self.0.set(0);
32 prev
33 }
34 pub fn restore(&self, depth: usize) {
35 self.0.set(depth);
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40pub enum UseContext {
41 #[default]
42 Statement,
43 Value,
44 Callee,
45 AssignmentTarget,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum CarrierKind {
50 Result,
51 Option,
52}
53
54#[derive(Debug, Clone)]
55pub struct TryBlockContext {
56 pub ok_ty: Type,
57 pub err_ty: Type,
58 pub carrier: Cell<Option<CarrierKind>>,
59 pub has_question_mark: Cell<bool>,
60 pub try_span: Span,
61 pub loop_depth: DepthCounter,
62}
63
64#[derive(Debug, Clone)]
65pub struct RecoverBlockContext {
66 pub inner_ty: Type,
67 pub recover_span: Span,
68 pub loop_depth: DepthCounter,
69}
70
71#[derive(Debug, Clone)]
72pub struct Scope {
73 pub values: HashMap<String, Type>,
75 pub mutables: Option<HashSet<String>>,
76 pub consts: Option<HashSet<String>>,
77 pub type_params: Option<HashMap<String, usize>>,
78 pub trait_bounds: Option<HashMap<Symbol, Vec<Type>>>,
79 pub fn_return_type: Option<Type>,
80 pub try_block_context: Option<TryBlockContext>,
81 pub recover_block_context: Option<RecoverBlockContext>,
82 pub loop_break_type: Option<Type>,
83 pub loop_depth: DepthCounter,
84 pub defer_block_depth: DepthCounter,
85 pub negation_depth: DepthCounter,
86 pub type_param_depth: DepthCounter,
87 pub use_context: Cell<UseContext>,
88 pub name_to_binding: HashMap<String, BindingId>,
90}
91
92impl Default for Scope {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98impl Scope {
99 pub fn new() -> Self {
100 Scope {
101 values: HashMap::default(),
102 mutables: None,
103 consts: None,
104 type_params: None,
105 trait_bounds: None,
106 fn_return_type: None,
107 try_block_context: None,
108 recover_block_context: None,
109 loop_break_type: None,
110 loop_depth: DepthCounter::new(),
111 defer_block_depth: DepthCounter::new(),
112 negation_depth: DepthCounter::new(),
113 type_param_depth: DepthCounter::new(),
114 use_context: Cell::new(UseContext::Statement),
115 name_to_binding: HashMap::default(),
116 }
117 }
118}
119
120pub struct Scopes {
121 stack: Vec<Scope>,
122 in_match_arm: Cell<bool>,
126 loop_needs_label_stack: std::cell::RefCell<Vec<bool>>,
130 in_subexpression: Cell<bool>,
134 dot_access_base: Cell<bool>,
138 let_binding_rhs: Cell<bool>,
142 impl_receiver_type: Option<Type>,
146}
147
148impl Default for Scopes {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154impl Scopes {
155 pub fn new() -> Self {
156 Scopes {
157 stack: vec![Scope::new()],
158 in_match_arm: Cell::new(false),
159 loop_needs_label_stack: std::cell::RefCell::new(Vec::new()),
160 in_subexpression: Cell::new(false),
161 dot_access_base: Cell::new(false),
162 let_binding_rhs: Cell::new(false),
163 impl_receiver_type: None,
164 }
165 }
166
167 pub fn current(&self) -> &Scope {
168 self.stack.last().expect("scope stack must not be empty")
169 }
170
171 pub fn current_mut(&mut self) -> &mut Scope {
172 self.stack
173 .last_mut()
174 .expect("scope stack must not be empty")
175 }
176
177 pub fn push(&mut self) {
178 let current = self.current();
179 let mut scope = Scope::new();
180 scope.loop_break_type = current.loop_break_type.clone();
181 scope.loop_depth = DepthCounter::with_value(current.loop_depth.get());
182 scope.defer_block_depth = DepthCounter::with_value(current.defer_block_depth.get());
183 scope.negation_depth = DepthCounter::with_value(current.negation_depth.get());
184 scope.type_param_depth = DepthCounter::with_value(current.type_param_depth.get());
185 scope.use_context = Cell::new(current.use_context.get());
186 self.stack.push(scope);
187 }
188
189 pub fn pop(&mut self) {
190 if self.stack.len() > 1 {
191 self.stack.pop();
192 }
193 }
194
195 pub fn reset(&mut self) {
196 self.stack.clear();
197 self.stack.push(Scope::new());
198 self.in_match_arm.set(false);
199 self.loop_needs_label_stack.borrow_mut().clear();
200 self.in_subexpression.set(false);
201 self.dot_access_base.set(false);
202 self.impl_receiver_type = None;
203 }
204
205 pub fn lookup_value(&self, name: &str) -> Option<&Type> {
207 for scope in self.stack.iter().rev() {
208 if let Some(ty) = scope.values.get(name) {
209 return Some(ty);
210 }
211 }
212 None
213 }
214
215 pub fn lookup_mutable(&self, name: &str) -> bool {
217 self.stack
218 .iter()
219 .rev()
220 .any(|s| s.mutables.as_ref().is_some_and(|m| m.contains(name)))
221 }
222
223 pub fn lookup_const(&self, name: &str) -> bool {
225 self.stack
226 .iter()
227 .rev()
228 .any(|s| s.consts.as_ref().is_some_and(|c| c.contains(name)))
229 }
230
231 pub fn lookup_binding_id(&self, name: &str) -> Option<BindingId> {
233 for scope in self.stack.iter().rev() {
234 if let Some(id) = scope.name_to_binding.get(name) {
235 return Some(*id);
236 }
237 }
238 None
239 }
240
241 pub fn lookup_type_param(&self, name: &str) -> Option<usize> {
243 for scope in self.stack.iter().rev() {
244 if let Some(idx) = scope.type_params.as_ref().and_then(|tp| tp.get(name)) {
245 return Some(*idx);
246 }
247 }
248 None
249 }
250
251 pub fn lookup_fn_return_type(&self) -> Option<&Type> {
253 for scope in self.stack.iter().rev() {
254 if let Some(ref ty) = scope.fn_return_type {
255 return Some(ty);
256 }
257 }
258 None
259 }
260
261 pub fn lookup_try_block_context(&self) -> Option<&TryBlockContext> {
263 for scope in self.stack.iter().rev() {
264 if scope.try_block_context.is_some() {
265 return scope.try_block_context.as_ref();
266 }
267 if scope.fn_return_type.is_some() {
268 return None;
269 }
270 }
271 None
272 }
273
274 pub fn lookup_recover_block_context(&self) -> Option<&RecoverBlockContext> {
276 for scope in self.stack.iter().rev() {
277 if scope.recover_block_context.is_some() {
278 return scope.recover_block_context.as_ref();
279 }
280 if scope.fn_return_type.is_some() {
281 return None;
282 }
283 }
284 None
285 }
286
287 pub fn collect_all_value_names(&self) -> Vec<String> {
288 let mut names = Vec::new();
289 for scope in &self.stack {
290 names.extend(scope.values.keys().cloned());
291 }
292 names
293 }
294
295 pub fn collect_all_trait_bounds(&self) -> HashMap<Symbol, Vec<Type>> {
296 let mut all_bounds = HashMap::default();
297 for scope in &self.stack {
299 if let Some(ref bounds) = scope.trait_bounds {
300 for (key, value) in bounds {
301 all_bounds.insert(key.clone(), value.clone());
302 }
303 }
304 }
305 all_bounds
306 }
307
308 pub fn for_each_bound_on_param<F: FnMut(&Type)>(&self, param_name: &str, mut visit: F) {
309 for scope in self.stack.iter().rev() {
310 let introduces = scope
311 .type_params
312 .as_ref()
313 .is_some_and(|tp| tp.contains_key(param_name));
314 if !introduces {
315 continue;
316 }
317 if let Some(ref bounds) = scope.trait_bounds {
318 for (key, types) in bounds {
319 if key.last_segment() == param_name {
320 for ty in types {
321 visit(ty);
322 }
323 }
324 }
325 }
326 return;
327 }
328 }
329
330 pub fn increment_loop_depth(&self) {
331 self.current().loop_depth.increment();
332 }
333
334 pub fn decrement_loop_depth(&self) {
335 self.current().loop_depth.decrement();
336 }
337
338 pub fn is_inside_loop(&self) -> bool {
339 self.current().loop_depth.is_active()
340 }
341
342 pub fn set_loop_break_type(&mut self, ty: Type) {
343 self.current_mut().loop_break_type = Some(ty);
344 }
345
346 pub fn clear_loop_break_type(&mut self) {
347 self.current_mut().loop_break_type = None;
348 }
349
350 pub fn loop_break_type(&self) -> Option<&Type> {
351 self.current().loop_break_type.as_ref()
352 }
353
354 pub fn increment_defer_block_depth(&self) {
355 self.current().defer_block_depth.increment();
356 }
357
358 pub fn decrement_defer_block_depth(&self) {
359 self.current().defer_block_depth.decrement();
360 }
361
362 pub fn is_inside_defer_block(&self) -> bool {
363 self.current().defer_block_depth.is_active()
364 }
365
366 pub fn defer_block_loop_depth(&self) -> usize {
367 self.current().loop_depth.get()
368 }
369
370 pub fn increment_negation_depth(&self) {
371 self.current().negation_depth.increment();
372 }
373
374 pub fn decrement_negation_depth(&self) {
375 self.current().negation_depth.decrement();
376 }
377
378 pub fn is_inside_negation(&self) -> bool {
379 self.current().negation_depth.is_active()
380 }
381
382 pub fn reset_loop_depth(&self) -> usize {
383 self.current().loop_depth.reset()
384 }
385
386 pub fn restore_loop_depth(&self, depth: usize) {
387 self.current().loop_depth.restore(depth);
388 }
389
390 pub fn set_value_context(&self) -> UseContext {
391 let prev = self.current().use_context.get();
392 self.current().use_context.set(UseContext::Value);
393 prev
394 }
395
396 pub fn set_statement_context(&self) -> UseContext {
397 let prev = self.current().use_context.get();
398 self.current().use_context.set(UseContext::Statement);
399 prev
400 }
401
402 pub fn restore_use_context(&self, ctx: UseContext) {
403 self.current().use_context.set(ctx);
404 }
405
406 pub fn is_value_context(&self) -> bool {
407 self.current().use_context.get() == UseContext::Value
408 }
409
410 pub fn set_callee_context(&self) -> UseContext {
411 let prev = self.current().use_context.get();
412 self.current().use_context.set(UseContext::Callee);
413 prev
414 }
415
416 pub fn is_callee_context(&self) -> bool {
417 self.current().use_context.get() == UseContext::Callee
418 }
419
420 pub fn set_assignment_target_context(&self) -> UseContext {
421 let prev = self.current().use_context.get();
422 self.current().use_context.set(UseContext::AssignmentTarget);
423 prev
424 }
425
426 pub fn is_assignment_target_context(&self) -> bool {
427 self.current().use_context.get() == UseContext::AssignmentTarget
428 }
429
430 pub fn is_in_match_arm(&self) -> bool {
431 self.in_match_arm.get()
432 }
433
434 pub fn set_in_match_arm(&self, value: bool) -> bool {
435 self.in_match_arm.replace(value)
436 }
437
438 pub fn push_loop_needs_label(&self) {
439 self.loop_needs_label_stack.borrow_mut().push(false);
440 }
441
442 pub fn pop_loop_needs_label(&self) -> bool {
443 self.loop_needs_label_stack
444 .borrow_mut()
445 .pop()
446 .expect("loop_needs_label_stack must not be empty when popping")
447 }
448
449 pub fn mark_current_loop_needs_label(&self) {
450 if let Some(flag) = self.loop_needs_label_stack.borrow_mut().last_mut() {
451 *flag = true;
452 }
453 }
454
455 pub fn is_in_subexpression(&self) -> bool {
456 self.in_subexpression.get()
457 }
458
459 pub fn set_in_subexpression(&self, value: bool) -> bool {
460 self.in_subexpression.replace(value)
461 }
462
463 pub fn is_dot_access_base(&self) -> bool {
464 self.dot_access_base.get()
465 }
466
467 pub fn set_dot_access_base(&self, value: bool) -> bool {
468 self.dot_access_base.replace(value)
469 }
470
471 pub fn is_let_binding_rhs(&self) -> bool {
472 self.let_binding_rhs.get()
473 }
474
475 pub fn set_let_binding_rhs(&self, value: bool) -> bool {
476 self.let_binding_rhs.replace(value)
477 }
478
479 pub fn increment_type_param_depth(&self) {
480 self.current().type_param_depth.increment();
481 }
482
483 pub fn decrement_type_param_depth(&self) {
484 self.current().type_param_depth.decrement();
485 }
486
487 pub fn is_inside_type_param(&self) -> bool {
488 self.current().type_param_depth.is_active()
489 }
490
491 pub fn set_impl_receiver_type(&mut self, ty: Option<Type>) {
492 self.impl_receiver_type = ty;
493 }
494
495 pub fn impl_receiver_type(&self) -> Option<&Type> {
496 self.impl_receiver_type.as_ref()
497 }
498}