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 if let Some(argument) = &ta.finalizer_argument {
347 self.walk(&argument.check)?;
348 }
349 Ok(())
350 }
351
352 fn scan_runtime_tests(
353 &mut self,
354 ta: &TypedAst,
355 ) -> Result<(), crate::compiler_error::CompilerFailure> {
356 for (key, test) in &ta.runtime_type_tests {
357 match test {
358 crate::FieldNarrowingTest::Shape(shape) => self.walk(shape)?,
359 crate::FieldNarrowingTest::Interface(interface) => {
360 if let Some(index) = &interface.index {
361 self.walk(&index.value)?;
362 }
363 for member in interface.members.values() {
364 self.walk(&member.ty)?;
365 }
366 if is_generic_interface_key(key)
367 || interface_test_is_recursive(ta, key, interface)
368 {
369 self.expanded_keys.insert(key.clone());
370 }
371 self.scan_interface_carriers(interface)?;
372 }
373 crate::FieldNarrowingTest::NonNull
374 | crate::FieldNarrowingTest::Substituted
375 | crate::FieldNarrowingTest::Representation => {}
376 }
377 }
378 Ok(())
379 }
380
381 fn scan_interface_carriers(
382 &mut self,
383 interface: &crate::InterfaceNarrowingTest,
384 ) -> Result<(), crate::compiler_error::CompilerFailure> {
385 for carrier in &interface.non_shape_carriers {
386 match carrier {
387 crate::InterfaceCarrier::Array(ty) | crate::InterfaceCarrier::Set(ty) => {
388 self.walk(ty)?;
389 }
390 crate::InterfaceCarrier::Map(key, value) => {
391 self.walk(key)?;
392 self.walk(value)?;
393 }
394 _ => {}
395 }
396 }
397 Ok(())
398 }
399
400 fn scan_local_class_fields(
401 &mut self,
402 ta: &TypedAst,
403 ) -> Result<(), crate::compiler_error::CompilerFailure> {
404 for decl in &ta.types {
405 let TypedTypeDecl::Class(class) = decl else {
406 continue;
407 };
408 for check in class
409 .fields
410 .iter()
411 .filter_map(|field| field.narrowing_check.as_deref())
412 {
413 if let crate::FieldNarrowingTest::Shape(shape) = &check.test {
414 self.walk(shape)?;
415 }
416 }
417 }
418 Ok(())
419 }
420
421 fn scan_dependency_class_fields<'b>(
422 &mut self,
423 dependency_types: impl IntoIterator<Item = &'b DependencyType<'b>>,
424 ) -> Result<(), crate::compiler_error::CompilerFailure> {
425 for dependency_type in dependency_types {
426 let TypeKind::Class {
427 narrowing_checks, ..
428 } = &dependency_type.symbol.kind
429 else {
430 continue;
431 };
432 for check in narrowing_checks.values() {
433 if let crate::FieldNarrowingTest::Shape(shape) = &check.test {
434 self.walk(shape)?;
435 }
436 }
437 }
438 Ok(())
439 }
440
441 fn finish(self) -> Result<RecursiveValidators, crate::compiler_error::CompilerFailure> {
442 let mut all_keys = self.expanded_keys.clone();
443 all_keys.extend(self.rejected_keys.iter().cloned());
444 let plans: Vec<_> = all_keys
445 .into_iter()
446 .map(|key| {
447 let rejects_polymorphic_edge =
448 self.rejected_keys.contains(&key) && !self.expanded_keys.contains(&key);
449 let body = expanded(self.bodies, &key)?;
450 Ok(ValidatorPlan {
451 key,
452 body,
453 rejects_polymorphic_edge,
454 })
455 })
456 .collect::<Result<_, crate::compiler_error::CompilerFailure>>()?;
457 let mut descriptor_types = self.descriptor_types;
458 for plan in &plans {
459 collect_descriptor_arguments(&plan.body, &mut descriptor_types);
460 }
461 for shape in &self.shapes {
462 if let Shape::Object { fields, index } = shape {
463 if let Some(index) = index {
464 collect_descriptor_arguments(&index.value, &mut descriptor_types);
465 }
466 for field in fields.values() {
467 collect_descriptor_arguments(&field.ty, &mut descriptor_types);
468 }
469 }
470 }
471 Ok(RecursiveValidators {
472 descriptor_types,
473 plans,
474 extra_shapes: self.shapes.into_iter().collect(),
475 })
476 }
477}
478
479fn interface_test_is_recursive(
480 ta: &TypedAst,
481 key: &Type,
482 interface: &crate::InterfaceNarrowingTest,
483) -> bool {
484 let mut visited = BTreeSet::from([key.clone()]);
485 interface
486 .index
487 .as_ref()
488 .is_some_and(|index| type_reaches_interface(&index.value, key.peel(), ta, &mut visited))
489 || interface
490 .members
491 .values()
492 .any(|member| type_reaches_interface(&member.ty, key.peel(), ta, &mut visited))
493}
494
495fn type_reaches_interface(
496 ty: &Type,
497 target: &Type,
498 ta: &TypedAst,
499 visited: &mut BTreeSet<Type>,
500) -> bool {
501 let ty = ty.peel();
502 if ty == target {
503 return true;
504 }
505 match ty {
506 Type::Array(element) => type_reaches_interface(element, target, ta, visited),
507 Type::Tuple(crate::types::TupleType { elements, .. }) | Type::Union(elements) => elements
508 .iter()
509 .any(|element| type_reaches_interface(element, target, ta, visited)),
510 Type::Object { fields, index } => {
511 index
512 .as_ref()
513 .is_some_and(|i| type_reaches_interface(&i.value, target, ta, visited))
514 || fields
515 .values()
516 .any(|field| type_reaches_interface(&field.ty, target, ta, visited))
517 }
518 Type::Function { params, ret, .. } => {
519 params
520 .iter()
521 .any(|param| type_reaches_interface(param, target, ta, visited))
522 || type_reaches_interface(ret, target, ta, visited)
523 }
524 Type::InterfaceRef { args, .. } => {
525 if args
526 .iter()
527 .any(|arg| type_reaches_interface(arg, target, ta, visited))
528 {
529 return true;
530 }
531 if !visited.insert(ty.clone()) {
536 return false;
537 }
538 match ta.runtime_type_tests.get(ty) {
539 Some(crate::FieldNarrowingTest::Interface(test)) => test
540 .members
541 .values()
542 .any(|member| type_reaches_interface(&member.ty, target, ta, visited)),
543 _ => false,
544 }
545 }
546 Type::ClassRef { args, .. } | Type::AliasRef { args, .. } => args
547 .iter()
548 .any(|arg| type_reaches_interface(arg, target, ta, visited)),
549 _ => false,
550 }
551}
552
553struct Traversal<'a, 'b> {
557 bodies: &'a ValidatorBodies,
558 validators: &'b mut BTreeSet<Type>,
559 rejected: &'b mut BTreeSet<Type>,
560 shapes: &'b mut BTreeSet<Shape>,
561 plain_objects: &'b mut BTreeSet<Type>,
566 active_declarations: BTreeSet<MangledName>,
567 seen_instantiations: BTreeSet<Type>,
568}
569
570impl<'a, 'b> Traversal<'a, 'b> {
571 fn new(
572 bodies: &'a ValidatorBodies,
573 validators: &'b mut BTreeSet<Type>,
574 rejected: &'b mut BTreeSet<Type>,
575 shapes: &'b mut BTreeSet<Shape>,
576 plain_objects: &'b mut BTreeSet<Type>,
577 ) -> Self {
578 Self {
579 bodies,
580 validators,
581 rejected,
582 shapes,
583 plain_objects,
584 active_declarations: BTreeSet::new(),
585 seen_instantiations: BTreeSet::new(),
586 }
587 }
588
589 fn walk(&mut self, ty: &Type) -> Result<bool, crate::compiler_error::CompilerFailure> {
592 let peeled = ty.peel();
593 Ok(match peeled {
594 Type::Object { fields, index } => {
595 if self.plain_objects.contains(peeled) {
596 return Ok(false);
597 }
598 let mut references = false;
599 if let Some(index) = index {
600 references |= self.walk(&index.value)?;
601 }
602 self.shapes.insert(Shape::Object {
603 index: index.clone(),
604 fields: fields.clone(),
605 });
606 for field in fields.values() {
607 references |= self.walk(&field.ty)?;
608 }
609 if !references {
610 self.plain_objects.insert(peeled.clone());
611 }
612 references
613 }
614 Type::Array(element) => self.walk(element)?,
615 Type::Tuple(crate::types::TupleType { elements, .. }) | Type::Union(elements) => {
616 let mut references = false;
617 for element in elements {
618 references |= self.walk(element)?;
619 }
620 references
621 }
622 Type::Function { params, ret, .. } => {
623 let mut references = false;
624 for param in params {
625 references |= self.walk(param)?;
626 }
627 references | self.walk(ret)?
628 }
629 Type::AliasRef { mangled, .. } => {
630 if expanded(self.bodies, peeled)? != *peeled {
631 self.walk_alias(peeled, mangled)?;
632 }
633 true
634 }
635 Type::ClassRef { mangled, args, .. } => {
636 if expanded(self.bodies, peeled)? != *peeled {
637 for arg in args {
638 self.walk(arg)?;
639 }
640 self.walk_interface(peeled, mangled)?;
641 }
642 true
643 }
644 Type::InterfaceRef { mangled, .. } => {
645 if expanded(self.bodies, peeled)? != *peeled {
646 self.walk_interface(peeled, mangled)?;
647 }
648 true
649 }
650 _ => false,
651 })
652 }
653
654 fn walk_alias(
655 &mut self,
656 ty: &Type,
657 identity: &MangledName,
658 ) -> Result<(), crate::compiler_error::CompilerFailure> {
659 if !self.seen_instantiations.insert(ty.clone()) {
660 return Ok(());
661 }
662 self.walk_expanded_reference(ty, identity)?;
663 Ok(())
664 }
665
666 fn walk_interface(
667 &mut self,
668 ty: &Type,
669 identity: &MangledName,
670 ) -> Result<(), crate::compiler_error::CompilerFailure> {
671 if !self.seen_instantiations.insert(ty.clone()) {
672 if self.active_declarations.contains(identity) {
673 self.validators.insert(ty.clone());
674 }
675 return Ok(());
676 }
677 self.walk_expanded_reference(ty, identity)?;
678 Ok(())
679 }
680
681 fn walk_expanded_reference(
682 &mut self,
683 ty: &Type,
684 identity: &MangledName,
685 ) -> Result<(), crate::compiler_error::CompilerFailure> {
686 if !self.active_declarations.insert(identity.clone()) {
687 if let Some(key) = self.bodies.generic_key(ty) {
688 if self.validators.insert(key.clone()) {
689 let body = expanded(self.bodies, &key)?;
690 self.walk(&body)?;
691 }
692 } else {
693 self.rejected.insert(ty.clone());
694 }
695 return Ok(());
696 }
697 self.validators.insert(ty.clone());
698 let body = expanded(self.bodies, ty)?;
699 self.walk(&body)?;
700 self.active_declarations.remove(identity);
701 Ok(())
702 }
703}
704
705fn expanded(
707 bodies: &ValidatorBodies,
708 ty: &Type,
709) -> Result<Type, crate::compiler_error::CompilerFailure> {
710 bodies.expand_one(ty).map_err(|exceeded| {
711 exceeded.into_failure(crate::compiler_error::CompilerStage::Codegen, None)
712 })
713}
714
715fn collect_descriptor_arguments(ty: &Type, out: &mut BTreeSet<Type>) {
716 match ty.peel() {
717 Type::AliasRef { args, .. }
718 | Type::InterfaceRef { args, .. }
719 | Type::ClassRef { args, .. } => {
720 for arg in args {
721 out.insert(arg.clone());
722 collect_descriptor_arguments(arg, out);
723 }
724 }
725 Type::Array(ty) => collect_descriptor_arguments(ty, out),
726 Type::Union(types)
727 | Type::Tuple(crate::types::TupleType {
728 elements: types, ..
729 }) => {
730 for ty in types {
731 collect_descriptor_arguments(ty, out);
732 }
733 }
734 Type::Object { fields, index } => {
735 if let Some(i) = index {
736 collect_descriptor_arguments(&i.value, out);
737 }
738 for field in fields.values() {
739 collect_descriptor_arguments(&field.ty, out);
740 }
741 }
742 _ => {}
743 }
744}
745
746fn is_generic_interface_key(ty: &Type) -> bool {
747 matches!(ty, Type::InterfaceRef { args, .. } if !args.is_empty() && args.iter().all(|arg| matches!(arg, Type::TypeVar(_))))
748}