yog_script_analyze/typeck/
mod.rs1mod arrays;
9mod attributes;
10mod awaits;
11mod betterness;
12mod bits;
13mod builtin;
14mod callees;
15mod calls;
16mod collections;
17pub mod commands;
18mod constraints;
19mod content;
20mod content_ids;
21mod context;
22mod copy;
23mod ctors;
24mod decls;
25mod delegates;
26mod exprs;
27mod game;
28mod generics;
29mod groups;
30mod indexers;
31mod inherit;
32mod iterators;
33mod list_patterns;
34mod lambdas;
35mod lineage;
36mod macro_calls;
37mod members;
38mod messages;
39mod model;
40mod mutation;
41mod names;
42mod nulls;
43mod operators;
44mod overloads;
45mod own;
46mod passing;
47mod pick;
48mod shapes;
49mod required;
50mod saved;
51mod stmts;
52mod templates;
53pub(crate) mod surface;
54mod threads;
55mod tuples;
56mod types;
57mod variance;
58mod variants;
59mod written;
60
61use std::collections::HashMap;
62
63pub use content::content_path;
64pub use iterators::{body_yields, item_of};
65pub use model::Type;
66pub use nulls::leaves;
67pub use list_patterns::list_item;
68pub use templates::{MessageTemplate, holes};
69pub use types::{Message, MethodUse, OperatorUse, Quoted, Types};
70
71use crate::ast::File;
72use crate::index::{ClassIndex, Lookup, Side};
73use own::OwnTypes;
74use yog_analysis::Report;
75
76struct Slot {
78 ty: Type,
79 fixed: bool,
80}
81
82pub(crate) struct Checker<'r, 's, 'i> {
83 report: &'r mut Report<'s>,
84 lookup: Option<Lookup<'i>>,
85 own: OwnTypes,
86 scopes: Vec<HashMap<String, Slot>>,
88 in_content: bool,
92 readonly_this: bool,
93 this: Option<Type>,
95 side: Side,
97 returns: Option<Type>,
99 yields: Option<Type>,
101 switched: Vec<Type>,
103 state_owner: bool,
105 inferred: Option<Vec<Type>>,
107 directives: names::Directives,
109 ns: Vec<String>,
111 nanites: bool,
113 to_server: bool,
115 content: Option<Vec<(String, String)>>,
117 type_params: Vec<generics::Bound>,
119 types: Types,
121 bases: lineage::Bases,
123 delegates: delegates::Delegates,
125 operators: operators::Operators,
127 lambdas: Vec<(usize, usize)>,
130 unsafe_code: usize,
132 constraints: Vec<constraints::Constraints>,
134 meeting: bool,
136 non_null: Vec<(usize, String)>,
139 local_fns: Vec<(usize, crate::ast::Method)>,
142 indexing: bool,
144}
145
146pub(crate) struct Allowed {
149 pub nanites: bool,
150 pub to_server: bool,
151 pub content: Option<Vec<(String, String)>>,
154}
155
156pub fn builtin_type(word: &str) -> Option<Type> {
159 builtin::by_word(word)
160}
161
162pub fn system_type(name: &str) -> Option<Type> {
165 builtin::by_system_name(name)
166 .or_else(|| content_ids::content_id_kind(name).map(|k| Type::Content(k.to_string())))
167}
168
169pub fn system_name(t: &Type) -> Option<&'static str> {
172 builtin::system_name(t)
173}
174
175pub(crate) fn check<'d>(
178 file: &'d File,
179 allowed: Allowed,
180 (index, others): (Option<&ClassIndex>, &[&'d [crate::ast::Decl]]),
181 report: &mut Report<'_>,
182) -> Types {
183 let mut own = OwnTypes::collect_project(&crate::system::DECLS, others, &file.decls);
184 for decls in others {
185 own.extensions(decls);
186 }
187 own.extensions(&file.decls);
188 let mut c = Checker {
189 report,
190 lookup: index.map(ClassIndex::lookup),
191 own,
192 scopes: vec![HashMap::new()],
193 this: None,
194 side: Side::Shared,
195 returns: None,
196 yields: None,
197 switched: Vec::new(),
198 state_owner: false,
199 inferred: None,
200 directives: names::Directives::collect(&file.decls),
201 ns: Vec::new(),
202 nanites: allowed.nanites,
203 to_server: allowed.to_server,
204 content: allowed.content,
205 type_params: Vec::new(),
206 readonly_this: false,
207 in_content: false,
208 types: Types::default(),
209 bases: lineage::Bases::new(),
210 delegates: delegates::Delegates::new(),
211 operators: operators::Operators::new(),
212 lambdas: Vec::new(),
213 unsafe_code: 0,
214 constraints: Vec::new(),
215 meeting: false,
216 non_null: Vec::new(),
217 local_fns: Vec::new(),
218 indexing: false,
219 };
220 c.type_bases();
221 c.type_delegates();
222 c.type_operators();
223 c.decls(&file.decls);
224 inherit::check((&crate::system::DECLS, others), &file.decls, c.report);
225 c.types
226}
227
228impl Checker<'_, '_, '_> {
229 fn scoped<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
231 self.scopes.push(HashMap::new());
232 let out = f(self);
233 self.scopes.pop();
234 self.scopes_left(self.scopes.len());
235 let depth = self.scopes.len();
236 self.local_fns.retain(|(d, _)| *d <= depth);
237 out
238 }
239
240 fn bind(&mut self, name: &str, t: Type) {
241 self.bind_as(name, t, false);
242 }
243
244 fn bind_as(&mut self, name: &str, ty: Type, fixed: bool) {
247 self.non_null.retain(|(_, n)| n != name);
249 if let Some(s) = self.scopes.last_mut() {
250 s.insert(name.to_string(), Slot { ty, fixed });
251 }
252 }
253
254 fn local(&self, name: &str) -> Option<&Type> {
255 self.slot(name).map(|s| &s.ty)
256 }
257
258 fn slot(&self, name: &str) -> Option<&Slot> {
259 self.scopes.iter().rev().find_map(|s| s.get(name))
260 }
261
262 fn converts(&self, got: &Type, wanted: &Type) -> bool {
265 self.converts_plainly(got, wanted) || self.implicit(got, wanted).is_some()
266 }
267
268 fn converts_plainly(&self, got: &Type, wanted: &Type) -> bool {
272 if got.converts_to(wanted) {
273 return true;
274 }
275 if got.is_nullable() && !wanted.is_nullable() {
276 return false;
277 }
278 match (got.non_null(), wanted.non_null()) {
279 (Type::Own(a, xs), Type::Own(b, ys)) => self.own_converts(a, xs, (b, ys), true),
281 (Type::Array(..), wanted) => self.array_converts(got.non_null(), wanted),
282 (got @ (Type::Content(_) | Type::Tag(_)), wanted) => content_ids::id_converts(got, wanted),
284 (Type::Tuple(got, _), Type::Tuple(wanted, _)) => self.tuple_converts(got, wanted),
285 (Type::Fn(..), Type::Own(..)) => {
287 match (self.delegate_sig(got), self.delegate_sig(wanted)) {
288 (Some((gp, gr)), Some((wp, wr))) => {
289 gp.len() == wp.len()
290 && wp.iter().zip(&gp).all(|(w, g)| self.converts(w, g))
291 && self.converts(&gr, &wr)
292 }
293 _ => false,
294 }
295 }
296 _ => false,
297 }
298 }
299
300 fn base(&self) -> Type {
302 match &self.this {
303 Some(Type::Own(name, _)) => self
304 .own
305 .base_class(name)
306 .map_or(Type::Unknown, |b| Type::own(b.to_string())),
307 _ => Type::Unknown,
308 }
309 }
310
311 fn within<R>(
313 &mut self,
314 this: Option<Type>,
315 side: Side,
316 returns: Option<Type>,
317 f: impl FnOnce(&mut Self) -> R,
318 ) -> R {
319 let saved = (
320 std::mem::replace(&mut self.this, this),
321 self.side,
322 std::mem::replace(&mut self.returns, returns),
323 );
324 self.side = side;
325 let out = self.scoped(f);
326 (self.this, self.side, self.returns) = saved;
327 out
328 }
329}