use super::super::model::Type;
use super::super::own::TypeKey;
use super::super::{Checker, Types};
use crate::ast::Expr;
use crate::source::token::Span;
#[derive(Debug, Clone, PartialEq)]
pub struct Capture {
pub name: String,
pub ty: Type,
pub at: Span,
pub kind: Captured,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Captured {
Local,
This,
Static,
}
impl Checker<'_, '_, '_> {
pub(in super::super) fn in_lambda<R>(&mut self, e: &Expr, f: impl FnOnce(&mut Self) -> R) -> R {
self.lambdas.push((Types::key_of(e), self.scopes.len()));
let out = f(self);
self.lambdas.pop();
out
}
pub(in super::super) fn local_read(&mut self, name: &str, ty: &Type, depth: usize, at: Span) {
self.captured(depth, || Capture {
name: name.to_string(),
ty: ty.clone(),
at,
kind: Captured::Local,
});
}
pub(in super::super) fn this_read(&mut self, at: Span) {
let Some(this) = self.this.clone() else {
return;
};
self.captured(0, || Capture {
name: crate::words::this::WORD.text.to_string(),
ty: this.clone(),
at,
kind: Captured::This,
});
}
fn static_read(&mut self, owner: &str, name: &str, ty: &Type, at: Span) {
self.captured(0, || Capture {
name: format!("{owner}.{name}"),
ty: ty.clone(),
at,
kind: Captured::Static,
});
}
pub(in super::super) fn member_read(&mut self, name: &str, ty: &Type, at: Span) {
if self.lambdas.is_empty() || !ty.known() {
return;
}
let Some(Type::Own(owner, args)) = self.this.clone() else {
return;
};
let Some(own) = self.own.of_key(&TypeKey::new(&owner, args.len())) else {
return;
};
if own.statics.iter().any(|s| s == name) {
self.static_field_read(&owner, name, ty, at);
} else if own.values.contains_key(name) {
self.this_read(at);
}
}
pub(in super::super) fn method_read(&mut self, owner: &str, name: &str, at: Span) {
let instance = self
.own
.named(owner)
.is_some_and(|(_, o)| !o.static_methods.iter().any(|s| s == name));
if instance {
self.this_read(at);
}
}
pub(in super::super) fn static_field_read(
&mut self,
owner: &str,
name: &str,
ty: &Type,
at: Span,
) {
let own = self.own.named(owner).map(|(_, o)| o);
let field = own.filter(|o| o.statics.iter().any(|s| s == name));
if self.lambdas.is_empty()
|| field.is_none_or(|o| o.thread_statics.iter().any(|s| s == name))
{
return;
}
self.static_read(owner, name, ty, at);
}
fn captured(&mut self, depth: usize, made: impl Fn() -> Capture) {
for &(lambda, floor) in &self.lambdas {
if depth < floor {
self.types.capture(lambda, made());
}
}
}
}