mod attributes;
mod builtin;
mod copy;
mod decls;
mod exprs;
mod game;
mod generics;
mod inherit;
mod lambdas;
mod macro_calls;
mod members;
mod model;
mod mutation;
mod names;
mod own;
mod passing;
mod shapes;
mod stmts;
pub(crate) mod surface;
mod types;
mod variants;
mod written;
use std::collections::HashMap;
pub use model::Type;
pub use types::Types;
use crate::ast::File;
use crate::index::{ClassIndex, Lookup, Side};
use own::OwnTypes;
use yog_analysis::Report;
struct Slot {
ty: Type,
fixed: bool,
}
pub(crate) struct Checker<'r, 's, 'i> {
report: &'r mut Report<'s>,
lookup: Option<Lookup<'i>>,
own: OwnTypes,
scopes: Vec<HashMap<String, Slot>>,
readonly_this: bool,
this: Option<Type>,
side: Side,
returns: Option<Type>,
directives: names::Directives,
ns: Vec<String>,
nanites: bool,
to_server: bool,
type_params: Vec<generics::Bound>,
types: Types,
}
pub(crate) struct Allowed {
pub nanites: bool,
pub to_server: bool,
}
pub(crate) fn check(
file: &File,
allowed: Allowed,
index: Option<&ClassIndex>,
report: &mut Report<'_>,
) -> Types {
let mut own = OwnTypes::collect(&[&crate::system::DECLS, &file.decls]);
own.extensions(&file.decls);
let mut c = Checker {
report,
lookup: index.map(ClassIndex::lookup),
own,
scopes: vec![HashMap::new()],
this: None,
side: Side::Shared,
returns: None,
directives: names::Directives::collect(&file.decls),
ns: Vec::new(),
nanites: allowed.nanites,
to_server: allowed.to_server,
type_params: Vec::new(),
readonly_this: false,
types: Types::default(),
};
c.decls(&file.decls);
inherit::check(&file.decls, c.report);
c.types
}
impl Checker<'_, '_, '_> {
fn scoped<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
self.scopes.push(HashMap::new());
let out = f(self);
self.scopes.pop();
out
}
fn bind(&mut self, name: &str, t: Type) {
self.bind_as(name, t, false);
}
fn bind_as(&mut self, name: &str, ty: Type, fixed: bool) {
if let Some(s) = self.scopes.last_mut() {
s.insert(name.to_string(), Slot { ty, fixed });
}
}
fn local(&self, name: &str) -> Option<&Type> {
self.slot(name).map(|s| &s.ty)
}
fn slot(&self, name: &str) -> Option<&Slot> {
self.scopes.iter().rev().find_map(|s| s.get(name))
}
fn converts(&self, got: &Type, wanted: &Type) -> bool {
if got.converts_to(wanted) {
return true;
}
match (got.non_null(), wanted.non_null()) {
(Type::Own(a, xs), Type::Own(b, ys)) => {
let args_fit = a != b
|| xs.len() != ys.len()
|| xs.iter().zip(ys).all(|(x, y)| open(x) || open(y) || x == y);
(!got.is_nullable() || wanted.is_nullable()) && self.own.is_a(a, b) && args_fit
}
_ => false,
}
}
fn base(&self) -> Type {
match &self.this {
Some(Type::Own(name, _)) => self
.own
.base_class(name)
.map_or(Type::Unknown, |b| Type::own(b.to_string())),
_ => Type::Unknown,
}
}
fn within<R>(
&mut self,
this: Option<Type>,
side: Side,
returns: Option<Type>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let saved = (
std::mem::replace(&mut self.this, this),
self.side,
std::mem::replace(&mut self.returns, returns),
);
self.side = side;
let out = self.scoped(f);
(self.this, self.side, self.returns) = saved;
out
}
}
fn open(t: &Type) -> bool {
matches!(t, Type::Param(_) | Type::Unknown)
}