1use std::collections::{BTreeMap, BTreeSet};
16
17use crate::codegen::dependency_usage::DependencyType;
18use crate::type_size::{TypeLimits, TypeTooLarge};
19use crate::typechecker::type_param_substitution::TypeParamSubstitution;
20use crate::{
21 MangledName, ObjectField, Shape, Type, TypeKind, TypedAst, TypedExprKind, TypedInterfaceMember,
22 TypedTypeDecl,
23};
24
25pub struct ValidatorBodies {
31 class_fields: BTreeMap<Type, Type>,
32 imported_aliases: BTreeMap<MangledName, (Vec<String>, Type)>,
33 local_aliases: BTreeMap<String, (Vec<String>, Type)>,
34 imported_interfaces: BTreeMap<MangledName, (Vec<String>, BTreeMap<String, ObjectField>)>,
38 local_interfaces: BTreeMap<String, (Vec<String>, BTreeMap<String, ObjectField>)>,
39 limits: TypeLimits,
41}
42
43impl ValidatorBodies {
44 pub fn collect<'a>(
45 ta: &TypedAst,
46 dependency_types: impl IntoIterator<Item = &'a DependencyType<'a>>,
47 ) -> Self {
48 let mut imported_aliases = BTreeMap::new();
49 let mut imported_interfaces = BTreeMap::new();
50 for dependency_type in dependency_types {
51 let sym = dependency_type.symbol;
52 match &sym.kind {
53 TypeKind::Alias { generics, ty, .. } => {
54 imported_aliases
55 .entry(sym.mangled_name.clone())
56 .or_insert_with(|| (generics.clone(), ty.clone()));
57 }
58 TypeKind::Interface {
59 generics,
60 methods,
61 properties,
62 ..
63 } if methods.is_empty() => {
64 imported_interfaces
65 .entry(sym.mangled_name.clone())
66 .or_insert_with(|| {
67 let fields = properties
68 .iter()
69 .map(|(field, sig)| {
70 (
71 field.clone(),
72 ObjectField {
73 ty: sig.ty.clone(),
74 optional: sig.optional,
75 readonly: sig.readonly,
76 method: false,
77 },
78 )
79 })
80 .collect();
81 (generics.clone(), fields)
82 });
83 }
84 _ => {}
85 }
86 }
87 let mut local_aliases = BTreeMap::new();
88 let mut local_interfaces = BTreeMap::new();
89 for decl in &ta.types {
90 match decl {
91 TypedTypeDecl::Alias(alias) => {
92 local_aliases.insert(
93 alias.name.name.clone(),
94 (alias.generics.clone(), alias.ty.clone()),
95 );
96 }
97 TypedTypeDecl::Interface(iface) => {
98 if let Some(fields) = data_only_fields(&iface.members) {
99 local_interfaces
100 .insert(iface.name.name.clone(), (iface.generics.clone(), fields));
101 }
102 }
103 _ => {}
104 }
105 }
106 Self {
107 class_fields: ta.runtime_class_fields.clone(),
108 imported_aliases,
109 local_aliases,
110 imported_interfaces,
111 local_interfaces,
112 limits: TypeLimits::default(),
113 }
114 }
115
116 pub fn generic_key(&self, ty: &Type) -> Option<Type> {
118 let generics = match ty.peel() {
119 Type::AliasRef { mangled, name, .. } => {
120 &self
121 .imported_aliases
122 .get(mangled)
123 .or_else(|| self.local_aliases.get(name))?
124 .0
125 }
126 Type::InterfaceRef { mangled, name, .. } => {
127 &self
128 .imported_interfaces
129 .get(mangled)
130 .or_else(|| self.local_interfaces.get(name))?
131 .0
132 }
133 _ => return None,
134 };
135 if generics.is_empty() {
136 return None;
137 }
138 let arguments = generics.iter().cloned().map(Type::TypeVar).collect();
139 let key = match ty.peel() {
140 Type::AliasRef {
141 package,
142 mangled,
143 name,
144 ..
145 } => Type::AliasRef {
146 package: package.clone(),
147 mangled: mangled.clone(),
148 name: name.clone(),
149 args: arguments,
150 },
151 Type::InterfaceRef {
152 package,
153 mangled,
154 name,
155 ..
156 } => Type::InterfaceRef {
157 package: package.clone(),
158 mangled: mangled.clone(),
159 name: name.clone(),
160 args: arguments,
161 },
162 _ => return None,
163 };
164 Some(key)
165 }
166
167 pub fn expand_one(&self, ty: &Type) -> Result<Type, TypeTooLarge> {
172 Ok(match ty.peel() {
173 Type::ClassRef { .. } => self
174 .class_fields
175 .get(ty.peel())
176 .cloned()
177 .unwrap_or_else(|| ty.peel().clone()),
178 Type::AliasRef {
179 mangled,
180 name,
181 args,
182 ..
183 } => {
184 let Some((generics, body)) = self
185 .imported_aliases
186 .get(mangled)
187 .or_else(|| self.local_aliases.get(name))
188 else {
189 return Ok(ty.peel().clone());
190 };
191 let expanded = if generics.is_empty() {
192 body.clone()
193 } else {
194 TypeParamSubstitution::from_pairs(generics, args).apply(body, &self.limits)?
195 };
196 expanded.peel().clone()
197 }
198 Type::InterfaceRef {
199 mangled,
200 name,
201 args,
202 ..
203 } => {
204 let Some((generics, fields)) = self
205 .imported_interfaces
206 .get(mangled)
207 .or_else(|| self.local_interfaces.get(name))
208 else {
209 return Ok(ty.peel().clone());
210 };
211 let sub = (!generics.is_empty())
212 .then(|| TypeParamSubstitution::from_pairs(generics, args));
213 let fields = fields
214 .iter()
215 .map(|(field, f)| {
216 let ty = match &sub {
217 Some(sub) => sub.apply(&f.ty, &self.limits)?,
218 None => f.ty.clone(),
219 };
220 Ok((
221 field.clone(),
222 ObjectField {
223 ty,
224 optional: f.optional,
225 readonly: f.readonly,
226 method: false,
227 },
228 ))
229 })
230 .collect::<Result<_, TypeTooLarge>>()?;
231 Type::Object {
232 index: None,
233 fields,
234 }
235 }
236 other => other.clone(),
237 })
238 }
239}
240
241fn data_only_fields(members: &[TypedInterfaceMember]) -> Option<BTreeMap<String, ObjectField>> {
244 let mut fields = BTreeMap::new();
245 for member in members {
246 match member {
247 TypedInterfaceMember::Method { .. } => return None,
248 TypedInterfaceMember::Property {
249 name,
250 ty,
251 optional,
252 readonly,
253 ..
254 } => {
255 fields.insert(
256 name.name.clone(),
257 ObjectField {
258 ty: ty.clone(),
259 optional: *optional,
260 readonly: *readonly,
261 method: false,
262 },
263 );
264 }
265 }
266 }
267 Some(fields)
268}
269
270#[derive(Clone, Debug)]
273pub struct ValidatorPlan {
274 pub key: Type,
275 pub body: Type,
276 pub rejects_polymorphic_edge: bool,
277}
278
279pub struct RecursiveValidators {
281 pub plans: Vec<ValidatorPlan>,
282 pub extra_shapes: Vec<Shape>,
283 pub descriptor_types: BTreeSet<Type>,
284}
285
286pub fn discover<'a>(
287 ta: &TypedAst,
288 dependency_types: impl IntoIterator<Item = &'a DependencyType<'a>>,
289 bodies: &ValidatorBodies,
290) -> Result<RecursiveValidators, crate::compiler_error::CompilerFailure> {
291 let mut discovery = Discovery::new(bodies);
292 discovery.scan_expressions(ta)?;
293 discovery.scan_runtime_tests(ta)?;
294 discovery.scan_local_class_fields(ta)?;
295 discovery.scan_dependency_class_fields(dependency_types)?;
296 discovery.finish()
297}
298
299struct Discovery<'a> {
300 bodies: &'a ValidatorBodies,
301 shapes: BTreeSet<Shape>,
302 plain_objects: BTreeSet<Type>,
303 expanded_keys: BTreeSet<Type>,
304 rejected_keys: BTreeSet<Type>,
305 descriptor_types: BTreeSet<Type>,
306}
307
308impl<'a> Discovery<'a> {
309 fn new(bodies: &'a ValidatorBodies) -> Self {
310 Self {
311 bodies,
312 shapes: BTreeSet::new(),
313 plain_objects: BTreeSet::new(),
314 expanded_keys: BTreeSet::new(),
315 rejected_keys: BTreeSet::new(),
316 descriptor_types: BTreeSet::new(),
317 }
318 }
319
320 fn walk(&mut self, ty: &Type) -> Result<(), crate::compiler_error::CompilerFailure> {
321 collect_descriptor_arguments(ty, &mut self.descriptor_types);
322 Traversal::new(
323 self.bodies,
324 &mut self.expanded_keys,
325 &mut self.rejected_keys,
326 &mut self.shapes,
327 &mut self.plain_objects,
328 )
329 .walk(ty)?;
330 Ok(())
331 }
332
333 fn scan_expressions(
334 &mut self,
335 ta: &TypedAst,
336 ) -> Result<(), crate::compiler_error::CompilerFailure> {
337 for i in ta.expr_ids().map_err(crate::codegen::arena_failure)? {
338 let expr = ta.try_expr(i).map_err(crate::codegen::arena_failure)?;
339 if let TypedExprKind::Cast {
340 check: Some(shape), ..
341 } = &expr.kind
342 {
343 self.walk(shape)?;
344 }
345 }
346 Ok(())
347 }
348
349 fn scan_runtime_tests(
350 &mut self,
351 ta: &TypedAst,
352 ) -> Result<(), crate::compiler_error::CompilerFailure> {
353 for (key, test) in &ta.runtime_type_tests {
354 match test {
355 crate::FieldNarrowingTest::Shape(shape) => self.walk(shape)?,
356 crate::FieldNarrowingTest::Interface(interface) => {
357 if let Some(index) = &interface.index {
358 self.walk(&index.value)?;
359 }
360 for member in interface.members.values() {
361 self.walk(&member.ty)?;
362 }
363 if is_generic_interface_key(key)
364 || interface_test_is_recursive(ta, key, interface)
365 {
366 self.expanded_keys.insert(key.clone());
367 }
368 self.scan_interface_carriers(interface)?;
369 }
370 crate::FieldNarrowingTest::NonNull
371 | crate::FieldNarrowingTest::Substituted
372 | crate::FieldNarrowingTest::Representation => {}
373 }
374 }
375 Ok(())
376 }
377
378 fn scan_interface_carriers(
379 &mut self,
380 interface: &crate::InterfaceNarrowingTest,
381 ) -> Result<(), crate::compiler_error::CompilerFailure> {
382 for carrier in &interface.non_shape_carriers {
383 match carrier {
384 crate::InterfaceCarrier::Array(ty) | crate::InterfaceCarrier::Set(ty) => {
385 self.walk(ty)?;
386 }
387 crate::InterfaceCarrier::Map(key, value) => {
388 self.walk(key)?;
389 self.walk(value)?;
390 }
391 _ => {}
392 }
393 }
394 Ok(())
395 }
396
397 fn scan_local_class_fields(
398 &mut self,
399 ta: &TypedAst,
400 ) -> Result<(), crate::compiler_error::CompilerFailure> {
401 for decl in &ta.types {
402 let TypedTypeDecl::Class(class) = decl else {
403 continue;
404 };
405 for check in class
406 .fields
407 .iter()
408 .filter_map(|field| field.narrowing_check.as_deref())
409 {
410 if let crate::FieldNarrowingTest::Shape(shape) = &check.test {
411 self.walk(shape)?;
412 }
413 }
414 }
415 Ok(())
416 }
417
418 fn scan_dependency_class_fields<'b>(
419 &mut self,
420 dependency_types: impl IntoIterator<Item = &'b DependencyType<'b>>,
421 ) -> Result<(), crate::compiler_error::CompilerFailure> {
422 for dependency_type in dependency_types {
423 let TypeKind::Class {
424 narrowing_checks, ..
425 } = &dependency_type.symbol.kind
426 else {
427 continue;
428 };
429 for check in narrowing_checks.values() {
430 if let crate::FieldNarrowingTest::Shape(shape) = &check.test {
431 self.walk(shape)?;
432 }
433 }
434 }
435 Ok(())
436 }
437
438 fn finish(self) -> Result<RecursiveValidators, crate::compiler_error::CompilerFailure> {
439 let mut all_keys = self.expanded_keys.clone();
440 all_keys.extend(self.rejected_keys.iter().cloned());
441 let plans: Vec<_> = all_keys
442 .into_iter()
443 .map(|key| {
444 let rejects_polymorphic_edge =
445 self.rejected_keys.contains(&key) && !self.expanded_keys.contains(&key);
446 let body = expanded(self.bodies, &key)?;
447 Ok(ValidatorPlan {
448 key,
449 body,
450 rejects_polymorphic_edge,
451 })
452 })
453 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()?;
454 let mut descriptor_types = self.descriptor_types;
455 for plan in &plans {
456 collect_descriptor_arguments(&plan.body, &mut descriptor_types);
457 }
458 for shape in &self.shapes {
459 if let Shape::Object { fields, index } = shape {
460 if let Some(index) = index {
461 collect_descriptor_arguments(&index.value, &mut descriptor_types);
462 }
463 for field in fields.values() {
464 collect_descriptor_arguments(&field.ty, &mut descriptor_types);
465 }
466 }
467 }
468 Ok(RecursiveValidators {
469 descriptor_types,
470 plans,
471 extra_shapes: self.shapes.into_iter().collect(),
472 })
473 }
474}
475
476fn interface_test_is_recursive(
477 ta: &TypedAst,
478 key: &Type,
479 interface: &crate::InterfaceNarrowingTest,
480) -> bool {
481 let mut visited = BTreeSet::from([key.clone()]);
482 interface
483 .index
484 .as_ref()
485 .is_some_and(|index| type_reaches_interface(&index.value, key.peel(), ta, &mut visited))
486 || interface
487 .members
488 .values()
489 .any(|member| type_reaches_interface(&member.ty, key.peel(), ta, &mut visited))
490}
491
492fn type_reaches_interface(
493 ty: &Type,
494 target: &Type,
495 ta: &TypedAst,
496 visited: &mut BTreeSet<Type>,
497) -> bool {
498 let ty = ty.peel();
499 if ty == target {
500 return true;
501 }
502 match ty {
503 Type::Array(element) => type_reaches_interface(element, target, ta, visited),
504 Type::Tuple(crate::types::TupleType { elements, .. }) | Type::Union(elements) => elements
505 .iter()
506 .any(|element| type_reaches_interface(element, target, ta, visited)),
507 Type::Object { fields, index } => {
508 index
509 .as_ref()
510 .is_some_and(|i| type_reaches_interface(&i.value, target, ta, visited))
511 || fields
512 .values()
513 .any(|field| type_reaches_interface(&field.ty, target, ta, visited))
514 }
515 Type::Function { params, ret, .. } => {
516 params
517 .iter()
518 .any(|param| type_reaches_interface(param, target, ta, visited))
519 || type_reaches_interface(ret, target, ta, visited)
520 }
521 Type::InterfaceRef { args, .. } => {
522 if args
523 .iter()
524 .any(|arg| type_reaches_interface(arg, target, ta, visited))
525 {
526 return true;
527 }
528 if !visited.insert(ty.clone()) {
533 return false;
534 }
535 match ta.runtime_type_tests.get(ty) {
536 Some(crate::FieldNarrowingTest::Interface(test)) => test
537 .members
538 .values()
539 .any(|member| type_reaches_interface(&member.ty, target, ta, visited)),
540 _ => false,
541 }
542 }
543 Type::ClassRef { args, .. } | Type::AliasRef { args, .. } => args
544 .iter()
545 .any(|arg| type_reaches_interface(arg, target, ta, visited)),
546 _ => false,
547 }
548}
549
550struct Traversal<'a, 'b> {
554 bodies: &'a ValidatorBodies,
555 validators: &'b mut BTreeSet<Type>,
556 rejected: &'b mut BTreeSet<Type>,
557 shapes: &'b mut BTreeSet<Shape>,
558 plain_objects: &'b mut BTreeSet<Type>,
563 active_declarations: BTreeSet<MangledName>,
564 seen_instantiations: BTreeSet<Type>,
565}
566
567impl<'a, 'b> Traversal<'a, 'b> {
568 fn new(
569 bodies: &'a ValidatorBodies,
570 validators: &'b mut BTreeSet<Type>,
571 rejected: &'b mut BTreeSet<Type>,
572 shapes: &'b mut BTreeSet<Shape>,
573 plain_objects: &'b mut BTreeSet<Type>,
574 ) -> Self {
575 Self {
576 bodies,
577 validators,
578 rejected,
579 shapes,
580 plain_objects,
581 active_declarations: BTreeSet::new(),
582 seen_instantiations: BTreeSet::new(),
583 }
584 }
585
586 fn walk(&mut self, ty: &Type) -> Result<bool, crate::compiler_error::CompilerFailure> {
589 let peeled = ty.peel();
590 Ok(match peeled {
591 Type::Object { fields, index } => {
592 if self.plain_objects.contains(peeled) {
593 return Ok(false);
594 }
595 let mut references = false;
596 if let Some(index) = index {
597 references |= self.walk(&index.value)?;
598 }
599 self.shapes.insert(Shape::Object {
600 index: index.clone(),
601 fields: fields.clone(),
602 });
603 for field in fields.values() {
604 references |= self.walk(&field.ty)?;
605 }
606 if !references {
607 self.plain_objects.insert(peeled.clone());
608 }
609 references
610 }
611 Type::Array(element) => self.walk(element)?,
612 Type::Tuple(crate::types::TupleType { elements, .. }) | Type::Union(elements) => {
613 let mut references = false;
614 for element in elements {
615 references |= self.walk(element)?;
616 }
617 references
618 }
619 Type::Function { params, ret, .. } => {
620 let mut references = false;
621 for param in params {
622 references |= self.walk(param)?;
623 }
624 references | self.walk(ret)?
625 }
626 Type::AliasRef { mangled, .. } => {
627 if expanded(self.bodies, peeled)? != *peeled {
628 self.walk_alias(peeled, mangled)?;
629 }
630 true
631 }
632 Type::ClassRef { mangled, args, .. } => {
633 if expanded(self.bodies, peeled)? != *peeled {
634 for arg in args {
635 self.walk(arg)?;
636 }
637 self.walk_interface(peeled, mangled)?;
638 }
639 true
640 }
641 Type::InterfaceRef { mangled, .. } => {
642 if expanded(self.bodies, peeled)? != *peeled {
643 self.walk_interface(peeled, mangled)?;
644 }
645 true
646 }
647 _ => false,
648 })
649 }
650
651 fn walk_alias(
652 &mut self,
653 ty: &Type,
654 identity: &MangledName,
655 ) -> Result<(), crate::compiler_error::CompilerFailure> {
656 if !self.seen_instantiations.insert(ty.clone()) {
657 return Ok(());
658 }
659 self.walk_expanded_reference(ty, identity)?;
660 Ok(())
661 }
662
663 fn walk_interface(
664 &mut self,
665 ty: &Type,
666 identity: &MangledName,
667 ) -> Result<(), crate::compiler_error::CompilerFailure> {
668 if !self.seen_instantiations.insert(ty.clone()) {
669 if self.active_declarations.contains(identity) {
670 self.validators.insert(ty.clone());
671 }
672 return Ok(());
673 }
674 self.walk_expanded_reference(ty, identity)?;
675 Ok(())
676 }
677
678 fn walk_expanded_reference(
679 &mut self,
680 ty: &Type,
681 identity: &MangledName,
682 ) -> Result<(), crate::compiler_error::CompilerFailure> {
683 if !self.active_declarations.insert(identity.clone()) {
684 if let Some(key) = self.bodies.generic_key(ty) {
685 if self.validators.insert(key.clone()) {
686 let body = expanded(self.bodies, &key)?;
687 self.walk(&body)?;
688 }
689 } else {
690 self.rejected.insert(ty.clone());
691 }
692 return Ok(());
693 }
694 self.validators.insert(ty.clone());
695 let body = expanded(self.bodies, ty)?;
696 self.walk(&body)?;
697 self.active_declarations.remove(identity);
698 Ok(())
699 }
700}
701
702fn expanded(
704 bodies: &ValidatorBodies,
705 ty: &Type,
706) -> Result<Type, crate::compiler_error::CompilerFailure> {
707 bodies.expand_one(ty).map_err(|exceeded| {
708 exceeded.into_failure(crate::compiler_error::CompilerStage::Codegen, None)
709 })
710}
711
712fn collect_descriptor_arguments(ty: &Type, out: &mut BTreeSet<Type>) {
713 match ty.peel() {
714 Type::AliasRef { args, .. }
715 | Type::InterfaceRef { args, .. }
716 | Type::ClassRef { args, .. } => {
717 for arg in args {
718 out.insert(arg.clone());
719 collect_descriptor_arguments(arg, out);
720 }
721 }
722 Type::Array(ty) => collect_descriptor_arguments(ty, out),
723 Type::Union(types)
724 | Type::Tuple(crate::types::TupleType {
725 elements: types, ..
726 }) => {
727 for ty in types {
728 collect_descriptor_arguments(ty, out);
729 }
730 }
731 Type::Object { fields, index } => {
732 if let Some(i) = index {
733 collect_descriptor_arguments(&i.value, out);
734 }
735 for field in fields.values() {
736 collect_descriptor_arguments(&field.ty, out);
737 }
738 }
739 _ => {}
740 }
741}
742
743fn is_generic_interface_key(ty: &Type) -> bool {
744 matches!(ty, Type::InterfaceRef { args, .. } if !args.is_empty() && args.iter().all(|arg| matches!(arg, Type::TypeVar(_))))
745}