1use std::collections::{BTreeMap, BTreeSet};
10
11use crate::{Shape, Span, Type, TypedAst};
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
17pub enum VirtualDispatch {
18 Mcp { server: String },
20 Tools,
22}
23
24impl VirtualDispatch {
25 pub fn package_name(&self) -> String {
27 match self {
28 VirtualDispatch::Mcp { server } => format!("@mcp/{server}"),
29 VirtualDispatch::Tools => crate::runtime::TOOLS_PACKAGE.to_string(),
30 }
31 }
32
33 pub fn host_module(&self) -> &'static str {
35 match self {
36 VirtualDispatch::Mcp { .. } => crate::runtime::MCP_MODULE_NAME,
37 VirtualDispatch::Tools => crate::runtime::TOOLS_MODULE_NAME,
38 }
39 }
40}
41
42#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
49pub struct PackageDeclaration {
50 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
53 pub runtime_functions: BTreeMap<crate::MangledName, RuntimeFunction>,
54 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
55 pub runtime_globals: BTreeMap<crate::MangledName, Type>,
56 pub package_name: String,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub virtual_dispatch: Option<VirtualDispatch>,
65 pub values: BTreeMap<String, ValueSymbol>,
66 pub types: BTreeMap<String, TypeSymbol>,
68 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
71 pub runtime_types: BTreeMap<String, TypeSymbol>,
72 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
74 pub runtime_generics: BTreeSet<crate::MangledName>,
75 pub shapes: Vec<crate::Shape>,
77 pub namespaces: BTreeMap<String, NamespaceSymbol>,
79 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
83 pub closure_caches: BTreeSet<crate::MangledName>,
84}
85
86#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
87pub struct RuntimeFunction {
88 pub params: Vec<Type>,
89 pub ret: Type,
90}
91
92impl PackageDeclaration {
93 pub fn new() -> Self {
95 Self::with_package(crate::mangle::USER_PACKAGE)
96 }
97
98 pub fn with_package(package_name: impl Into<String>) -> Self {
99 Self {
100 package_name: package_name.into(),
101 ..Self::default()
102 }
103 }
104
105 pub(crate) fn without_finalizer(mut self) -> Self {
108 self.values.remove("finalizer");
109 self
110 }
111
112 pub fn from_typed_ast(ast: &TypedAst) -> Self {
114 let mut defs = PackageDeclaration::with_package(ast.package_name.clone());
115 for f in &ast.functions {
116 if !f.generics.is_empty() {
117 defs.runtime_generics.insert(f.mangled_name.clone());
118 }
119 defs.values
120 .entry(f.name.name.clone())
121 .or_insert_with(|| ValueSymbol {
122 name: f.name.name.clone(),
123 mangled_name: f.mangled_name.clone(),
124 declaration_span: f.name.span,
125 kind: ValueKind::Function {
126 generics: f.generics.clone(),
127 params: f.params.iter().map(param_from_typed).collect(),
128 ret: f.return_type.clone(),
129 type_predicate: f.type_predicate.clone(),
130 doc: f.doc.clone(),
131 },
132 });
133 }
134 for g in &ast.globals {
135 defs.values
136 .entry(g.name.name.clone())
137 .or_insert_with(|| ValueSymbol {
138 name: g.name.name.clone(),
139 mangled_name: g.mangled_name.clone(),
140 declaration_span: g.name.span,
141 kind: match g.kind {
142 crate::GlobalKind::Let => ValueKind::Let {
143 ty: g.ty.clone(),
144 doc: g.doc.clone(),
145 },
146 crate::GlobalKind::Const => ValueKind::Const {
147 ty: g.ty.clone(),
148 doc: g.doc.clone(),
149 },
150 },
151 });
152 }
153 for ty_decl in &ast.types {
155 if let crate::TypedTypeDecl::Alias(alias) = ty_decl {
156 defs.types
157 .entry(alias.name.name.clone())
158 .or_insert_with(|| TypeSymbol {
159 name: alias.name.name.clone(),
160 mangled_name: crate::mangle::package_symbol(
161 &ast.package_name,
162 &alias.name.name,
163 ),
164 declaration_span: alias.name.span,
165 kind: TypeKind::Alias {
166 generics: alias.generics.clone(),
167 ty: alias.ty.clone(),
168 doc: alias.doc.clone(),
169 },
170 });
171 }
172 }
173 defs.refresh_shapes();
174 defs
175 }
176
177 pub fn type_symbol(&self, mangled: &crate::MangledName) -> Option<&TypeSymbol> {
182 self.runtime_types
183 .get(mangled.as_str())
184 .or_else(|| symbol_named(self.types.values(), mangled))
185 .or_else(|| {
186 self.namespaces
187 .values()
188 .find_map(|namespace| namespace_type_symbol(namespace, mangled))
189 })
190 }
191
192 pub fn refresh_shapes(&mut self) {
193 self.shapes = self.collect_shapes();
194 }
195
196 pub fn collect_shapes(&self) -> Vec<Shape> {
197 let mut collector = PackageShapeCollector::default();
198 for value in self.values.values() {
199 collector.collect_value(value);
200 }
201 for ty in self.types.values() {
202 collector.collect_type_symbol(ty);
203 }
204 for namespace in self.namespaces.values() {
205 collector.collect_namespace(namespace);
206 }
207 collector.shapes
208 }
209
210 pub fn check_type_limits(&self) -> Result<(), crate::type_size::TypeTooLarge> {
215 check_namespace_limits(&self.namespaces)?;
216 let mut result = Ok(());
217 self.for_each_type(&mut |ty| {
218 if result.is_ok() {
219 result = crate::type_size::check(ty);
220 }
221 });
222 result
223 }
224
225 fn for_each_type(&self, visit: &mut dyn FnMut(&Type)) {
229 let Self {
230 runtime_functions,
231 runtime_globals,
232 package_name: _,
233 virtual_dispatch: _,
234 values,
235 types,
236 runtime_types,
237 runtime_generics: _,
238 shapes,
239 namespaces,
240 closure_caches: _,
241 } = self;
242 for RuntimeFunction { params, ret } in runtime_functions.values() {
243 params.iter().for_each(&mut *visit);
244 visit(ret);
245 }
246 runtime_globals.values().for_each(&mut *visit);
247 values.values().for_each(|value| value_types(value, visit));
248 types
249 .values()
250 .chain(runtime_types.values())
251 .for_each(|symbol| type_symbol_types(symbol, visit));
252 for shape in shapes {
253 match shape {
254 Shape::Object { fields, index } => {
255 fields.values().for_each(|field| visit(&field.ty));
256 if let Some(index) = index {
257 visit(&index.value);
258 }
259 }
260 Shape::Array(element) => visit(element),
261 Shape::Tuple(members) => {
262 members.iter().for_each(&mut *visit);
263 }
264 Shape::Union(members) => {
265 members.iter().for_each(&mut *visit);
266 }
267 }
268 }
269 let mut pending: Vec<&NamespaceSymbol> = namespaces.values().collect();
270 while let Some(namespace) = pending.pop() {
271 let NamespaceSymbol {
272 name: _,
273 mangled_prefix: _,
274 declaration_span: _,
275 values,
276 types,
277 namespaces,
278 doc: _,
279 } = namespace;
280 values.values().for_each(|value| value_types(value, visit));
281 types
282 .values()
283 .for_each(|symbol| type_symbol_types(symbol, visit));
284 pending.extend(namespaces.values());
285 }
286 }
287}
288
289fn check_namespace_limits(
290 namespaces: &BTreeMap<String, NamespaceSymbol>,
291) -> Result<(), crate::type_size::TypeTooLarge> {
292 use crate::compiler_limits::{
293 MAX_NAMESPACE_DEPTH, MAX_NAMESPACE_NODES, MAX_NAMESPACE_PATH_BYTES,
294 };
295 use crate::type_size::TypeTooLarge;
296
297 let mut pending = vec![(namespaces.iter(), 0usize)];
299 let mut nodes = 0;
300 let mut path_bytes = 0usize;
301 while let Some((siblings, prefix_bytes)) = pending.last_mut() {
302 let Some((name, namespace)) = siblings.next() else {
303 pending.pop();
304 continue;
305 };
306 let path_len = prefix_bytes.saturating_add(name.len());
307 nodes += 1;
308 path_bytes = path_bytes.saturating_add(path_len);
309 for name in namespace.types.keys() {
310 path_bytes =
311 path_bytes.saturating_add(path_len.saturating_add(1).saturating_add(name.len()));
312 if path_bytes > MAX_NAMESPACE_PATH_BYTES {
313 return Err(TypeTooLarge::NamespaceMetadata);
314 }
315 }
316 if nodes > MAX_NAMESPACE_NODES
317 || pending.len() > MAX_NAMESPACE_DEPTH
318 || path_bytes > MAX_NAMESPACE_PATH_BYTES
319 {
320 return Err(TypeTooLarge::NamespaceMetadata);
321 }
322 if !namespace.namespaces.is_empty() {
323 pending.push((namespace.namespaces.iter(), path_len.saturating_add(1)));
324 }
325 }
326 Ok(())
327}
328
329fn namespace_type_symbol<'a>(
330 namespace: &'a NamespaceSymbol,
331 mangled: &crate::MangledName,
332) -> Option<&'a TypeSymbol> {
333 symbol_named(namespace.types.values(), mangled).or_else(|| {
334 namespace
335 .namespaces
336 .values()
337 .find_map(|child| namespace_type_symbol(child, mangled))
338 })
339}
340
341fn symbol_named<'a>(
342 mut symbols: impl Iterator<Item = &'a TypeSymbol>,
343 mangled: &crate::MangledName,
344) -> Option<&'a TypeSymbol> {
345 symbols.find(|symbol| symbol.mangled_name == *mangled)
346}
347
348fn value_types(value: &ValueSymbol, visit: &mut dyn FnMut(&Type)) {
349 match &value.kind {
350 ValueKind::Function {
351 generics,
352 params,
353 ret,
354 type_predicate,
355 doc: _,
356 } => {
357 generics.constraints.values().for_each(&mut *visit);
358 params.iter().for_each(|param| visit(¶m.ty));
359 visit(ret);
360 if let Some(predicate) = type_predicate {
361 visit(&predicate.asserted_type);
362 }
363 }
364 ValueKind::Let { ty, doc: _ } | ValueKind::Const { ty, doc: _ } => visit(ty),
365 }
366}
367
368fn type_symbol_types(symbol: &TypeSymbol, visit: &mut dyn FnMut(&Type)) {
369 match &symbol.kind {
370 TypeKind::Interface { generics, .. }
371 | TypeKind::Class { generics, .. }
372 | TypeKind::Alias { generics, .. } => generics.constraints.values().for_each(&mut *visit),
373 _ => {}
374 }
375 match &symbol.kind {
376 TypeKind::Interface {
377 generics: _,
378 methods,
379 properties,
380 index,
381 dispatch: _,
382 doc: _,
383 } => {
384 methods
385 .values()
386 .for_each(|method| method_types(method, visit));
387 properties.values().for_each(|property| visit(&property.ty));
388 if let Some(index) = index {
389 visit(&index.value);
390 }
391 }
392 TypeKind::NumberEnum { .. } | TypeKind::StringEnum { .. } => {}
393 TypeKind::Alias {
394 generics: _,
395 ty,
396 doc: _,
397 } => visit(ty),
398 TypeKind::Class {
399 generics: _,
400 fields,
401 narrowing_checks,
402 methods,
403 method_visibility: _,
404 accessors,
405 constructor,
406 constructor_visibility: _,
407 statics,
408 static_visibility: _,
409 static_fields,
410 extends,
411 implements: _,
412 doc: _,
413 } => {
414 fields
415 .values()
416 .chain(static_fields.values())
417 .for_each(|field| visit(&field.ty));
418 for check in narrowing_checks.values() {
419 narrowing_check_types(check, visit);
420 }
421 methods
422 .values()
423 .chain(statics.values())
424 .for_each(|method| method_types(method, visit));
425 for accessor in accessors {
426 match accessor {
427 AccessorSig::Getter { name: _, ret_ty } => visit(ret_ty),
428 AccessorSig::Setter { name: _, param } => visit(¶m.ty),
429 }
430 }
431 constructor.iter().for_each(|param| visit(¶m.ty));
432 if let Some(extends) = extends {
433 extends.args.iter().for_each(&mut *visit);
434 }
435 }
436 }
437}
438
439fn narrowing_check_types(check: &crate::FieldNarrowingCheck, visit: &mut dyn FnMut(&Type)) {
440 let crate::FieldNarrowingCheck {
441 declaration: _,
442 test,
443 minimal_test_target,
444 message: _,
445 } = check;
446 if let Some(target) = minimal_test_target {
447 visit(target);
448 }
449 match test {
450 crate::FieldNarrowingTest::Shape(ty) => visit(ty),
451 crate::FieldNarrowingTest::Interface(interface) => {
452 let crate::InterfaceNarrowingTest {
453 index,
454 members,
455 methods: _,
456 non_shape_carriers,
457 shape_allowed: _,
458 nullable: _,
459 undefined: _,
460 } = interface;
461 if let Some(index) = index {
462 visit(&index.value);
463 }
464 members.values().for_each(|member| visit(&member.ty));
465 for carrier in non_shape_carriers {
466 match carrier {
467 crate::InterfaceCarrier::Array(element)
468 | crate::InterfaceCarrier::Set(element) => visit(element),
469 crate::InterfaceCarrier::Map(key, value) => {
470 visit(key);
471 visit(value);
472 }
473
474 crate::InterfaceCarrier::Number
475 | crate::InterfaceCarrier::Boolean
476 | crate::InterfaceCarrier::String
477 | crate::InterfaceCarrier::BigInt
478 | crate::InterfaceCarrier::Uint8Array
479 | crate::InterfaceCarrier::ArrayAny
480 | crate::InterfaceCarrier::MapAny
481 | crate::InterfaceCarrier::SetAny
482 | crate::InterfaceCarrier::RegExp
483 | crate::InterfaceCarrier::RegExpMatch
484 | crate::InterfaceCarrier::RegExpMatchArray
485 | crate::InterfaceCarrier::TemporalInstant
486 | crate::InterfaceCarrier::TemporalDuration
487 | crate::InterfaceCarrier::TemporalZonedDateTime
488 | crate::InterfaceCarrier::TemporalPlainDate
489 | crate::InterfaceCarrier::TemporalPlainTime
490 | crate::InterfaceCarrier::TemporalPlainDateTime
491 | crate::InterfaceCarrier::TemporalPlainYearMonth
492 | crate::InterfaceCarrier::TemporalPlainMonthDay
493 | crate::InterfaceCarrier::ObjectShape
494 | crate::InterfaceCarrier::Url
495 | crate::InterfaceCarrier::FsStat
496 | crate::InterfaceCarrier::FsPeek
497 | crate::InterfaceCarrier::FsDirEntry
498 | crate::InterfaceCarrier::FsInfo
499 | crate::InterfaceCarrier::FsMountInfo
500 | crate::InterfaceCarrier::FsFileWriter
501 | crate::InterfaceCarrier::HttpResponse
502 | crate::InterfaceCarrier::HttpDownloadResult
503 | crate::InterfaceCarrier::SessionEntry
504 | crate::InterfaceCarrier::SessionPage => {}
505 }
506 }
507 }
508 crate::FieldNarrowingTest::NonNull
509 | crate::FieldNarrowingTest::Substituted
510 | crate::FieldNarrowingTest::Representation => {}
511 }
512}
513
514fn method_types(method: &MethodSig, visit: &mut dyn FnMut(&Type)) {
515 method.generics.constraints.values().for_each(&mut *visit);
516 let MethodSig {
517 optional: _,
518 generics: _,
519 params,
520 ret,
521 predicate,
522 doc: _,
523 } = method;
524 params.iter().for_each(|param| visit(¶m.ty));
525 visit(ret);
526 if let Some(predicate) = predicate {
527 visit(&predicate.asserted_type);
528 }
529}
530
531#[derive(Default)]
532struct PackageShapeCollector {
533 shapes: Vec<Shape>,
534 seen: BTreeSet<Shape>,
535}
536
537impl PackageShapeCollector {
538 fn collect_value(&mut self, value: &ValueSymbol) {
539 match &value.kind {
540 ValueKind::Function {
541 params,
542 ret,
543 type_predicate,
544 ..
545 } => {
546 for param in params {
547 self.collect_type(¶m.ty);
548 }
549 self.collect_type(ret);
550 if let Some(predicate) = type_predicate {
551 self.collect_type(&predicate.asserted_type);
552 }
553 }
554 ValueKind::Let { ty, .. } | ValueKind::Const { ty, .. } => self.collect_type(ty),
555 }
556 }
557
558 fn collect_type_symbol(&mut self, ty: &TypeSymbol) {
559 match &ty.kind {
560 TypeKind::Interface {
561 methods,
562 properties,
563 index,
564 ..
565 } => {
566 if let Some(index) = index {
567 self.collect_type(&index.value);
568 }
569 for method in methods.values() {
570 for param in &method.params {
571 self.collect_type(¶m.ty);
572 }
573 self.collect_type(&method.ret);
574 if let Some(predicate) = &method.predicate {
575 self.collect_type(&predicate.asserted_type);
576 }
577 }
578 for property in properties.values() {
579 self.collect_type(&property.ty);
580 }
581 }
582 TypeKind::Class {
583 fields,
584 methods,
585 accessors,
586 constructor,
587 ..
588 } => {
589 for field in fields.values() {
590 self.collect_type(&field.ty);
591 }
592 for method in methods.values() {
593 for param in &method.params {
594 self.collect_type(¶m.ty);
595 }
596 self.collect_type(&method.ret);
597 }
598 for acc in accessors {
599 match acc {
600 AccessorSig::Getter { ret_ty, .. } => self.collect_type(ret_ty),
601 AccessorSig::Setter { param, .. } => self.collect_type(¶m.ty),
602 }
603 }
604 for param in constructor {
605 self.collect_type(¶m.ty);
606 }
607 }
608 TypeKind::Alias { ty, .. } => self.collect_type(ty),
609 TypeKind::NumberEnum { .. } | TypeKind::StringEnum { .. } => {}
610 }
611 }
612
613 fn collect_namespace(&mut self, namespace: &NamespaceSymbol) {
614 for value in namespace.values.values() {
615 self.collect_value(value);
616 }
617 for ty in namespace.types.values() {
618 self.collect_type_symbol(ty);
619 }
620 for child in namespace.namespaces.values() {
621 self.collect_namespace(child);
622 }
623 }
624
625 fn collect_type(&mut self, ty: &Type) {
626 match ty {
627 Type::TemplateLiteral { substitutions, .. } => {
628 for inner in substitutions {
629 self.collect_type(inner);
630 }
631 }
632 Type::Object { fields, .. } => {
633 self.collect_shape(ty);
634 for field in fields.values() {
635 self.collect_type(&field.ty);
636 }
637 }
638 Type::Array(elem) => {
639 self.collect_shape(ty);
640 self.collect_type(elem);
641 }
642 Type::Tuple(elements) => {
643 self.collect_shape(ty);
644 for elem in elements {
645 self.collect_type(elem);
646 }
647 }
648 Type::Union(members) => {
649 self.collect_shape(ty);
650 for member in members {
651 self.collect_type(member);
652 }
653 }
654 Type::Function { params, ret, .. } => {
655 for param in params {
656 self.collect_type(param);
657 }
658 self.collect_type(ret);
659 }
660 Type::InterfaceRef { args, .. }
661 | Type::ClassRef { args, .. }
662 | Type::AliasRef { args, .. } => {
663 for arg in args {
664 self.collect_type(arg);
665 }
666 }
667 Type::Alias { ty, .. }
668 | Type::Refined { ty, .. }
669 | Type::Readonly(ty)
670 | Type::KeyOf(ty) => {
671 self.collect_type(ty);
672 }
673 Type::IndexedAccess { object, index } => {
674 self.collect_type(object);
675 self.collect_type(index);
676 }
677 Type::Number
678 | Type::BigInt
679 | Type::BigIntLiteral(_)
680 | Type::NumberLiteral(_)
681 | Type::String
682 | Type::StringLiteral(_)
683 | Type::Uint8Array
684 | Type::Boolean
685 | Type::BooleanLiteral(_)
686 | Type::Null
687 | Type::Undefined
688 | Type::Void
689 | Type::Never
690 | Type::Unknown
691 | Type::NonPrimitive
692 | Type::Error
693 | Type::TypeVar(_)
694 | Type::GenericParam { .. }
695 | Type::NumberEnum { .. }
696 | Type::StringEnum { .. } => {}
697 }
698 }
699
700 fn collect_shape(&mut self, ty: &Type) {
701 let Some(shape) = Shape::from_type(ty) else {
702 return;
703 };
704 if self.seen.insert(shape.clone()) {
705 self.shapes.push(shape);
706 }
707 }
708}
709
710#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
711pub struct ValueSymbol {
712 pub name: String,
714 pub mangled_name: crate::MangledName,
716 pub declaration_span: Span,
717 pub kind: ValueKind,
718}
719
720#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
721pub struct Param {
722 pub name: String,
723 pub ty: Type,
724 pub default: Option<DefaultValue>,
725 #[serde(default)]
730 pub optional: bool,
731 pub rest: bool,
733}
734
735impl Param {
736 pub fn is_omittable(&self) -> bool {
738 self.optional || self.default.is_some()
739 }
740}
741
742pub(crate) fn optional_parameter_count(params: &[Param]) -> usize {
744 params
745 .iter()
746 .rev()
747 .skip_while(|param| param.rest)
748 .take_while(|param| param.is_omittable())
749 .count()
750}
751
752impl Param {
753 pub fn new(name: impl Into<String>, ty: Type) -> Self {
754 Self {
755 name: name.into(),
756 ty,
757 default: None,
758 optional: false,
759 rest: false,
760 }
761 }
762
763 pub fn with_default(name: impl Into<String>, ty: Type, default: DefaultValue) -> Self {
764 Self {
765 name: name.into(),
766 ty,
767 default: Some(default),
768 optional: true,
769 rest: false,
770 }
771 }
772
773 pub fn optional(name: impl Into<String>, ty: Type) -> Self {
775 Self {
776 optional: true,
777 ..Self::new(name, ty)
778 }
779 }
780
781 pub fn rest(name: impl Into<String>, ty: Type) -> Self {
783 Self {
784 name: name.into(),
785 ty,
786 default: None,
787 optional: false,
788 rest: true,
789 }
790 }
791
792 pub fn anon(ty: Type) -> Self {
794 Self {
795 name: String::new(),
796 ty,
797 default: None,
798 optional: false,
799 rest: false,
800 }
801 }
802}
803
804pub(crate) fn param_from_typed(p: &crate::TypedParam) -> Param {
807 Param {
808 name: p.name.name.clone(),
809 ty: p.ty.clone(),
810 default: p.default.clone(),
811 optional: p.optional,
812 rest: p.rest,
813 }
814}
815
816#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
819pub enum DefaultValue {
820 Undefined,
821 Number(#[serde(with = "crate::artifact_f64")] f64),
822 OmittedNumber {
826 #[serde(with = "crate::artifact_f64")]
827 omitted: f64,
828 #[serde(with = "crate::artifact_f64")]
829 undefined: f64,
830 },
831 OmittedString(String),
835 String(String),
836 Boolean(bool),
837 Null,
838 EmptyArray,
841 EmptyObject,
845 GlobalConst(crate::MangledName),
847 EnumVariant {
849 enum_mangled: crate::MangledName,
850 variant: String,
851 value: EnumVariantValue,
852 },
853}
854
855impl DefaultValue {
856 pub fn replaces_undefined(&self) -> bool {
860 !matches!(self, Self::OmittedString(_))
861 }
862}
863
864#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
865pub enum EnumVariantValue {
866 Number(#[serde(with = "crate::artifact_f64")] f64),
867 String(String),
868}
869
870#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
871pub enum ValueKind {
872 Function {
873 generics: GenericParameters,
875 params: Vec<Param>,
876 ret: Type,
877 type_predicate: Option<crate::TypePredicate>,
879 doc: Option<crate::DocComment>,
880 },
881 Let {
882 ty: Type,
886 doc: Option<crate::DocComment>,
887 },
888 Const {
889 ty: Type,
890 doc: Option<crate::DocComment>,
891 },
892}
893
894#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
897pub struct GenericParameters {
898 pub names: Vec<String>,
899 pub constraints: Box<BTreeMap<String, Type>>,
900}
901
902impl std::ops::Deref for GenericParameters {
903 type Target = [String];
904
905 fn deref(&self) -> &[String] {
906 &self.names
907 }
908}
909
910impl GenericParameters {
911 pub fn unconstrained(names: Vec<String>) -> Self {
914 Self {
915 names,
916 constraints: Box::default(),
917 }
918 }
919 pub(crate) fn select_names(&self, names: &[String]) -> Self {
920 Self {
921 names: names.to_vec(),
922 constraints: Box::new(
923 names
924 .iter()
925 .filter_map(|name| {
926 self.constraints
927 .get(name)
928 .map(|bound| (name.clone(), bound.clone()))
929 })
930 .collect(),
931 ),
932 }
933 }
934}
935
936impl<'a> IntoIterator for &'a GenericParameters {
937 type Item = &'a String;
938 type IntoIter = std::slice::Iter<'a, String>;
939
940 fn into_iter(self) -> Self::IntoIter {
941 self.names.iter()
942 }
943}
944
945#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
946pub struct TypeSymbol {
947 pub name: String,
948 pub mangled_name: crate::MangledName,
950 pub declaration_span: Span,
951 pub kind: TypeKind,
952}
953
954#[allow(clippy::large_enum_variant)]
958#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
959pub enum TypeKind {
960 Interface {
963 generics: GenericParameters,
964 methods: BTreeMap<String, MethodSig>,
965 properties: BTreeMap<String, PropertySig>,
968 index: Option<crate::IndexSignature>,
969 dispatch: Dispatch,
970 doc: Option<crate::DocComment>,
971 },
972 NumberEnum {
974 #[serde(with = "crate::artifact_f64::pairs")]
975 variants: Vec<(String, f64)>,
976 doc: Option<crate::DocComment>,
977 },
978 StringEnum {
980 variants: Vec<(String, String)>,
981 doc: Option<crate::DocComment>,
982 },
983 Alias {
986 generics: GenericParameters,
987 ty: Type,
988 doc: Option<crate::DocComment>,
989 },
990 Class {
997 generics: GenericParameters,
1001 fields: BTreeMap<String, FieldSig>,
1002 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
1006 narrowing_checks: BTreeMap<String, crate::FieldNarrowingCheck>,
1007 methods: BTreeMap<String, MethodSig>,
1008 method_visibility: BTreeMap<String, crate::Visibility>,
1011 accessors: Vec<AccessorSig>,
1017 constructor: Vec<Param>,
1019 #[serde(default, skip_serializing_if = "crate::Visibility::is_public")]
1022 constructor_visibility: crate::Visibility,
1023 #[serde(default)]
1027 statics: BTreeMap<String, MethodSig>,
1028 #[serde(default)]
1030 static_visibility: BTreeMap<String, crate::Visibility>,
1031 #[serde(default)]
1034 static_fields: BTreeMap<String, FieldSig>,
1035 extends: Option<ClassExtends>,
1037 implements: Vec<crate::MangledName>,
1039 doc: Option<crate::DocComment>,
1040 },
1041 }
1043
1044#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1048pub struct ClassExtends {
1049 pub parent: crate::MangledName,
1050 pub args: Vec<Type>,
1053}
1054
1055impl ClassExtends {
1056 pub fn plain(parent: crate::MangledName) -> Self {
1058 Self {
1059 parent,
1060 args: Vec::new(),
1061 }
1062 }
1063}
1064
1065#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1066pub enum Dispatch {
1067 Direct,
1069 Static,
1071 VTable,
1073}
1074
1075#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1076pub struct MethodSig {
1077 #[serde(default)]
1079 pub optional: bool,
1080 pub generics: GenericParameters,
1081 pub params: Vec<Param>,
1082 pub ret: Type,
1083 pub predicate: Option<crate::TypePredicate>,
1085 pub doc: Option<crate::DocComment>,
1086}
1087
1088#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1092pub enum AccessorSig {
1093 Getter { name: String, ret_ty: Type },
1094 Setter { name: String, param: Param },
1095}
1096
1097impl AccessorSig {
1098 pub fn name(&self) -> &str {
1099 match self {
1100 AccessorSig::Getter { name, .. } | AccessorSig::Setter { name, .. } => name,
1101 }
1102 }
1103}
1104
1105#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1113pub struct PropertySig {
1114 pub ty: Type,
1115 pub readonly: bool,
1116 pub optional: bool,
1117 #[serde(default)]
1118 pub intrinsic: bool,
1119 pub doc: Option<crate::DocComment>,
1120}
1121
1122#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1126pub struct FieldSig {
1127 pub ty: Type,
1128 pub visibility: crate::Visibility,
1129 pub readonly: bool,
1130 pub optional: bool,
1131 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1134 pub method: bool,
1135 pub doc: Option<crate::DocComment>,
1136}
1137
1138#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1143pub struct NamespaceSymbol {
1144 pub name: String,
1145 pub mangled_prefix: crate::MangledName,
1147 pub declaration_span: Span,
1149 pub values: BTreeMap<String, ValueSymbol>,
1150 pub types: BTreeMap<String, TypeSymbol>,
1152 pub namespaces: BTreeMap<String, NamespaceSymbol>,
1153 pub doc: Option<crate::DocComment>,
1154}
1155
1156impl Drop for NamespaceSymbol {
1157 fn drop(&mut self) {
1158 drop_namespace_children(&mut self.namespaces);
1159 }
1160}
1161
1162fn drop_namespace_children(namespaces: &mut BTreeMap<String, NamespaceSymbol>) {
1163 if namespaces.is_empty() {
1164 return;
1165 }
1166 let mut pending = Vec::new();
1167 if pending.try_reserve(1).is_err() {
1168 drop_namespace_children_without_allocation(namespaces);
1169 return;
1170 }
1171 pending.push(std::mem::take(namespaces).into_values());
1172 while let Some(siblings) = pending.last_mut() {
1173 let Some(mut child) = siblings.next() else {
1174 pending.pop();
1175 continue;
1176 };
1177 if child.namespaces.is_empty() {
1178 continue;
1179 }
1180 if pending.try_reserve(1).is_err() {
1181 drop_namespace_children_without_allocation(&mut child.namespaces);
1182 } else {
1183 pending.push(std::mem::take(&mut child.namespaces).into_values());
1184 }
1185 }
1187}
1188
1189fn drop_namespace_children_without_allocation(namespaces: &mut BTreeMap<String, NamespaceSymbol>) {
1190 while !namespaces.is_empty() {
1193 let mut branch = &mut *namespaces;
1194 loop {
1195 if branch
1196 .first_key_value()
1197 .is_some_and(|(_, child)| child.namespaces.is_empty())
1198 {
1199 branch.pop_first();
1200 break;
1201 }
1202 branch = &mut branch
1205 .first_entry()
1206 .expect("nonempty namespace branch")
1207 .into_mut()
1208 .namespaces;
1209 }
1210 }
1211}
1212
1213#[cfg(test)]
1214mod tests {
1215 use super::PackageDeclaration;
1216 use crate::{Asi, Token, TokenKind, Type, capture, check, desugar, infer, parse};
1217
1218 fn typed_ast(source: &str) -> crate::TypedAst {
1219 let mut asi = Asi::new(source, crate::FileId(0));
1220 let mut tokens: Vec<Token> = Vec::new();
1221 loop {
1222 let tok = asi.next_token();
1223 let is_eof = matches!(tok.kind, TokenKind::Eof);
1224 tokens.push(tok);
1225 if is_eof {
1226 break;
1227 }
1228 }
1229 let _ = asi.into_diagnostics();
1230 let (ast, _) = parse(source, tokens, crate::FileId(0));
1231 let (prelude_defs, host_defs, _) =
1232 crate::runtime::prelude::cached_runtime_package_declarations();
1233 let mut packages = Vec::with_capacity(prelude_defs.len() + host_defs.len());
1234 packages.extend(prelude_defs.iter());
1235 packages.extend(host_defs.iter());
1236 let (mut ta, _) = infer(source, "main", &ast, &packages);
1237 let _ = check(&ta);
1238 ta = capture(ta).unwrap();
1239 ta = desugar(ta, crate::FileId(0)).unwrap();
1240 ta
1241 }
1242
1243 #[test]
1244 fn collects_object_shape_from_let_annotation() {
1245 let ta = typed_ast("let p: { x: number; y: number } = { x: 1, y: 2 };");
1246 let defs = PackageDeclaration::from_typed_ast(&ta);
1247 let object = defs
1248 .shapes
1249 .iter()
1250 .find_map(|s| match s {
1251 crate::Shape::Object { fields, .. } => Some(fields),
1252 _ => None,
1253 })
1254 .expect("object shape in defs.shapes");
1255 assert_eq!(
1256 object.get("x"),
1257 Some(&crate::ObjectField::required(Type::Number)),
1258 );
1259 assert_eq!(
1260 object.get("y"),
1261 Some(&crate::ObjectField::required(Type::Number)),
1262 );
1263 }
1264
1265 #[test]
1266 fn generic_constraints_survive_serialization_including_unused_parameters() {
1267 let (ta, diagnostics) = crate::typecheck_to_typed_ast(
1268 "type Empty<T extends number> = {};\nfunction identity<T extends number, U extends T>(value: U): U { return value; }\nfunction main(): void {}",
1269 crate::FileId(0),
1270 );
1271 assert!(
1272 diagnostics
1273 .iter()
1274 .all(|d| d.severity != crate::Severity::Error),
1275 "{diagnostics:?}"
1276 );
1277 let declaration = PackageDeclaration::from_typed_ast(&ta);
1278 let serialized = serde_json::to_string(&declaration).unwrap();
1279 let restored: PackageDeclaration = serde_json::from_str(&serialized).unwrap();
1280 let crate::ValueKind::Function { generics, .. } = &restored.values["identity"].kind else {
1281 panic!("function declaration");
1282 };
1283 assert_eq!(generics.names, ["T", "U"]);
1284 assert_eq!(generics.constraints.get("T"), Some(&Type::Number));
1285 assert_eq!(
1286 generics.constraints.get("U"),
1287 Some(&Type::TypeVar("T".into()))
1288 );
1289 let crate::TypeKind::Alias { generics, .. } = &restored.types["Empty"].kind else {
1290 panic!("alias declaration");
1291 };
1292 assert_eq!(generics.constraints.get("T"), Some(&Type::Number));
1293 restored.check_type_limits().unwrap();
1294 }
1295
1296 #[test]
1297 fn preserves_readonly_and_writable_object_shapes() {
1298 let ta =
1299 typed_ast("let a: { readonly x: number } = { x: 1 }; let b: { x: number } = { x: 2 };");
1300 let defs = PackageDeclaration::from_typed_ast(&ta);
1301 let readonly_flags: std::collections::BTreeSet<bool> = defs
1302 .shapes
1303 .iter()
1304 .filter_map(|shape| {
1305 let crate::Shape::Object { fields, .. } = shape else {
1306 return None;
1307 };
1308 fields.get("x").map(|field| field.readonly)
1309 })
1310 .collect();
1311 assert_eq!(
1312 readonly_flags,
1313 std::collections::BTreeSet::from([false, true])
1314 );
1315 }
1316
1317 #[test]
1318 fn collects_array_shape_from_let_annotation() {
1319 let ta = typed_ast("let xs: number[] = [1, 2, 3];");
1320 let defs = PackageDeclaration::from_typed_ast(&ta);
1321 assert!(
1322 defs.shapes
1323 .iter()
1324 .any(|s| matches!(s, crate::Shape::Array(_))),
1325 "expected an Array shape in defs.shapes: {:?}",
1326 defs.shapes,
1327 );
1328 }
1329
1330 #[test]
1331 fn deduplicates_identical_object_shapes() {
1332 let ta = typed_ast("let a = { x: 1 }; let b = { x: 2 };");
1333 let defs = PackageDeclaration::from_typed_ast(&ta);
1334 let object_count = defs
1335 .shapes
1336 .iter()
1337 .filter(|s| matches!(s, crate::Shape::Object { .. }))
1338 .count();
1339 assert_eq!(
1340 object_count, 1,
1341 "expected one Object entry, got {object_count}"
1342 );
1343 }
1344
1345 #[test]
1346 fn distinguishes_object_shapes_with_different_fields() {
1347 let ta = typed_ast("let a = { x: 1 }; let b = { y: 2 };");
1348 let defs = PackageDeclaration::from_typed_ast(&ta);
1349 let object_count = defs
1350 .shapes
1351 .iter()
1352 .filter(|s| matches!(s, crate::Shape::Object { .. }))
1353 .count();
1354 assert_eq!(object_count, 2);
1355 }
1356
1357 #[test]
1358 fn canonical_shape_is_stable_across_modules() {
1359 let ta_a = typed_ast("let p = { x: 1, y: 2 };");
1360 let ta_b = typed_ast("let q = { y: 2, x: 1 };");
1361 let defs_a = PackageDeclaration::from_typed_ast(&ta_a);
1362 let defs_b = PackageDeclaration::from_typed_ast(&ta_b);
1363 let key_a = defs_a
1364 .shapes
1365 .iter()
1366 .find_map(|s| match s {
1367 crate::Shape::Object { .. } => Some(s.canonical_display()),
1368 _ => None,
1369 })
1370 .expect("object shape in defs_a");
1371 let key_b = defs_b
1372 .shapes
1373 .iter()
1374 .find_map(|s| match s {
1375 crate::Shape::Object { .. } => Some(s.canonical_display()),
1376 _ => None,
1377 })
1378 .expect("object shape in defs_b");
1379 assert_eq!(key_a, key_b, "canonical key should match across modules");
1380 }
1381
1382 #[test]
1383 fn nested_array_of_object_collects_both() {
1384 let ta = typed_ast("let xs: { x: number }[] = [];");
1385 let defs = PackageDeclaration::from_typed_ast(&ta);
1386 let has_array = defs
1387 .shapes
1388 .iter()
1389 .any(|s| matches!(s, crate::Shape::Array(_)));
1390 let has_object = defs
1391 .shapes
1392 .iter()
1393 .any(|s| matches!(s, crate::Shape::Object { .. }));
1394 assert!(has_array, "expected Array shape");
1395 assert!(has_object, "expected Object shape");
1396 }
1397}
1398
1399#[cfg(test)]
1400mod type_limit_checks {
1401 use std::collections::BTreeMap;
1402
1403 use super::*;
1404 use crate::compiler_limits::MAX_TYPE_DEPTH;
1405 use crate::type_size::TypeTooLarge;
1406
1407 fn nested(depth: u32) -> Type {
1408 (1..depth).fold(Type::Number, |inner, _| Type::Array(Box::new(inner)))
1409 }
1410
1411 fn namespace(name: &str) -> NamespaceSymbol {
1412 NamespaceSymbol {
1413 name: name.into(),
1414 mangled_prefix: crate::mangle::prelude(name),
1415 declaration_span: Span::at(crate::FileId(0)),
1416 values: BTreeMap::new(),
1417 types: BTreeMap::new(),
1418 namespaces: BTreeMap::new(),
1419 doc: None,
1420 }
1421 }
1422
1423 fn alias(ty: Type) -> TypeSymbol {
1424 TypeSymbol {
1425 name: "Deep".into(),
1426 mangled_name: crate::mangle::prelude("Deep"),
1427 declaration_span: Span::at(crate::FileId(0)),
1428 kind: TypeKind::Alias {
1429 generics: Default::default(),
1430 ty,
1431 doc: None,
1432 },
1433 }
1434 }
1435
1436 #[test]
1437 fn namespace_cleanup_can_run_without_allocating_a_frontier() {
1438 let mut child = namespace("leaf");
1439 for _ in 0..1_000 {
1440 let mut parent = namespace("branch");
1441 parent.namespaces.insert("child".into(), child);
1442 parent
1443 .namespaces
1444 .insert("sibling".into(), namespace("sibling"));
1445 child = parent;
1446 }
1447 drop_namespace_children_without_allocation(&mut child.namespaces);
1448 assert!(child.namespaces.is_empty());
1449 }
1450
1451 #[test]
1452 fn a_type_in_a_nested_namespace_is_checked() {
1453 crate::type_size::tests::on_compiler_stack(|| {
1454 let mut inner = namespace("Inner");
1455 inner
1456 .types
1457 .insert("Deep".into(), alias(nested(MAX_TYPE_DEPTH + 1)));
1458 let mut outer = namespace("Outer");
1459 outer.namespaces.insert("Inner".into(), inner);
1460 let mut declaration = PackageDeclaration::with_package("dep");
1461 assert_eq!(declaration.check_type_limits(), Ok(()));
1462 declaration.namespaces.insert("Outer".into(), outer);
1463 assert_eq!(declaration.check_type_limits(), Err(TypeTooLarge::Depth));
1464 });
1465 }
1466
1467 #[test]
1468 fn runtime_declarations_are_checked() {
1469 crate::type_size::tests::on_compiler_stack(|| {
1470 let mut declaration = PackageDeclaration::with_package("dep");
1471 declaration.runtime_functions.insert(
1472 crate::mangle::prelude("f"),
1473 RuntimeFunction {
1474 params: vec![nested(MAX_TYPE_DEPTH)],
1475 ret: Type::Void,
1476 },
1477 );
1478 declaration
1479 .runtime_types
1480 .insert("Deep".into(), alias(nested(MAX_TYPE_DEPTH)));
1481 assert_eq!(declaration.check_type_limits(), Ok(()));
1482 declaration
1483 .runtime_globals
1484 .insert(crate::mangle::prelude("g"), nested(MAX_TYPE_DEPTH + 1));
1485 assert_eq!(declaration.check_type_limits(), Err(TypeTooLarge::Depth));
1486 });
1487 }
1488}
1489
1490#[cfg(test)]
1491mod non_finite_artifact_roundtrip {
1492 use super::{DefaultValue, EnumVariantValue, TypeKind};
1493 use crate::Type;
1494 use crate::types::LiteralF64;
1495
1496 #[test]
1499 fn non_finite_defaults_survive_json() {
1500 for value in [f64::INFINITY, f64::NEG_INFINITY, f64::NAN] {
1501 let json = serde_json::to_string(&DefaultValue::Number(value)).unwrap();
1502 assert!(
1503 !json.contains("null"),
1504 "non-finite default serialized to null: {json}"
1505 );
1506 let DefaultValue::Number(back) = serde_json::from_str(&json).unwrap() else {
1507 panic!("round-trip changed the variant: {json}");
1508 };
1509 assert_eq!(back.to_bits(), value.to_bits(), "round-trip lost {value}");
1510 }
1511 }
1512
1513 #[test]
1514 fn finite_defaults_still_serialize_as_numbers() {
1515 let json = serde_json::to_string(&DefaultValue::Number(1.5)).unwrap();
1516 assert_eq!(json, r#"{"Number":1.5}"#);
1517 let DefaultValue::Number(back) = serde_json::from_str(&json).unwrap() else {
1518 panic!("round-trip changed the variant");
1519 };
1520 assert_eq!(back, 1.5);
1521 }
1522
1523 #[test]
1524 fn non_finite_literal_types_survive_json() {
1525 let ty = Type::NumberLiteral(LiteralF64(f64::INFINITY));
1526 let json = serde_json::to_string(&ty).unwrap();
1527 assert_eq!(serde_json::from_str::<Type>(&json).unwrap(), ty);
1528 }
1529
1530 #[test]
1531 fn non_finite_enum_values_survive_json() {
1532 let value = EnumVariantValue::Number(f64::INFINITY);
1533 let json = serde_json::to_string(&value).unwrap();
1534 assert_eq!(
1535 serde_json::from_str::<EnumVariantValue>(&json).unwrap(),
1536 value
1537 );
1538 }
1539
1540 #[test]
1542 fn non_finite_enum_variants_survive_json() {
1543 let kind = TypeKind::NumberEnum {
1544 variants: vec![
1545 ("Huge".to_string(), f64::INFINITY),
1546 ("One".to_string(), 1.0),
1547 ],
1548 doc: None,
1549 };
1550 let json = serde_json::to_string(&kind).unwrap();
1551 assert!(
1552 json.contains(r#"["Huge","Infinity"]"#),
1553 "non-finite enum variant did not survive serialization: {json}"
1554 );
1555 let TypeKind::NumberEnum { variants, .. } = serde_json::from_str(&json).unwrap() else {
1556 panic!("round-trip changed the variant: {json}");
1557 };
1558 assert_eq!(variants[0].1.to_bits(), f64::INFINITY.to_bits());
1559 assert_eq!(variants[1].1, 1.0);
1560 }
1561}