1use anyhow::{Result, anyhow, bail};
28use mangle_ir::{Inst, InstId, Ir, NameId};
29use rustc_hash::{FxHashMap, FxHashSet};
30
31pub const FN_STRUCT: &str = "fn:Struct";
33pub const FN_UNION: &str = "fn:Union";
34pub const FN_TAGGED_UNION: &str = "fn:TaggedUnion";
35pub const FN_SINGLETON: &str = "fn:Singleton";
36pub const FN_LIST: &str = "fn:List";
37pub const FN_MAP: &str = "fn:Map";
38pub const FN_PAIR: &str = "fn:Pair";
39pub const FN_TUPLE: &str = "fn:Tuple";
40pub const FN_FUN: &str = "fn:Fun";
41pub const FN_REL: &str = "fn:Rel";
42pub const FN_OPTION: &str = "fn:Option";
43pub const FN_OPT: &str = "fn:opt";
44pub const FN_EMPTY_TYPE: &str = "fn:EmptyType";
45
46pub fn apply_fn_name(ir: &Ir, id: InstId) -> Option<&str> {
52 if let Inst::ApplyFn { function, .. } = ir.get(id) {
53 Some(ir.resolve_name(*function))
54 } else {
55 None
56 }
57}
58
59pub fn apply_fn_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
61 if let Inst::ApplyFn { args, .. } = ir.get(id) {
62 Some(args.as_slice())
63 } else {
64 None
65 }
66}
67
68fn name_str(ir: &Ir, id: InstId) -> Option<&str> {
70 if let Inst::Name(n) = ir.get(id) {
71 Some(ir.resolve_name(*n))
72 } else {
73 None
74 }
75}
76
77fn name_id(ir: &Ir, id: InstId) -> Option<NameId> {
79 if let Inst::Name(n) = ir.get(id) {
80 Some(*n)
81 } else {
82 None
83 }
84}
85
86pub fn is_empty_type(ir: &Ir, id: InstId) -> bool {
88 apply_fn_name(ir, id) == Some(FN_EMPTY_TYPE)
89}
90
91pub fn is_any(ir: &Ir, id: InstId) -> bool {
93 name_str(ir, id) == Some("/any")
94}
95
96pub fn find_or_create_name(ir: &mut Ir, name: &str) -> InstId {
98 if let Some(name_id) = ir.name_store.lookup(name) {
99 for (idx, inst) in ir.insts.iter().enumerate() {
100 if let Inst::Name(n) = inst
101 && *n == name_id
102 {
103 return InstId::new(idx);
104 }
105 }
106 }
107 let n = ir.intern_name(name);
108 ir.add_inst(Inst::Name(n))
109}
110
111pub fn empty_type(ir: &mut Ir) -> InstId {
113 for (idx, inst) in ir.insts.iter().enumerate() {
114 if let Inst::ApplyFn { function, args } = inst
115 && ir.resolve_name(*function) == FN_EMPTY_TYPE
116 && args.is_empty()
117 {
118 return InstId::new(idx);
119 }
120 }
121 let fn_name = ir.intern_name(FN_EMPTY_TYPE);
122 ir.add_inst(Inst::ApplyFn {
123 function: fn_name,
124 args: vec![],
125 })
126}
127
128pub fn is_struct_type(ir: &Ir, id: InstId) -> bool {
133 apply_fn_name(ir, id) == Some(FN_STRUCT)
134}
135
136pub fn is_union_type(ir: &Ir, id: InstId) -> bool {
137 apply_fn_name(ir, id) == Some(FN_UNION)
138}
139
140pub fn is_singleton_type(ir: &Ir, id: InstId) -> bool {
141 apply_fn_name(ir, id) == Some(FN_SINGLETON)
142}
143
144pub fn is_tagged_union_type(ir: &Ir, id: InstId) -> bool {
145 apply_fn_name(ir, id) == Some(FN_TAGGED_UNION)
146}
147
148pub fn is_list_type(ir: &Ir, id: InstId) -> bool {
149 apply_fn_name(ir, id) == Some(FN_LIST)
150}
151
152pub fn is_map_type(ir: &Ir, id: InstId) -> bool {
153 apply_fn_name(ir, id) == Some(FN_MAP)
154}
155
156pub fn is_pair_type(ir: &Ir, id: InstId) -> bool {
157 apply_fn_name(ir, id) == Some(FN_PAIR)
158}
159
160pub fn is_tuple_type(ir: &Ir, id: InstId) -> bool {
161 apply_fn_name(ir, id) == Some(FN_TUPLE)
162}
163
164pub fn is_fun_type(ir: &Ir, id: InstId) -> bool {
165 apply_fn_name(ir, id) == Some(FN_FUN)
166}
167
168pub fn is_rel_type(ir: &Ir, id: InstId) -> bool {
169 apply_fn_name(ir, id) == Some(FN_REL)
170}
171
172pub fn is_option_type(ir: &Ir, id: InstId) -> bool {
173 apply_fn_name(ir, id) == Some(FN_OPTION)
174}
175
176pub fn is_opt_field(ir: &Ir, id: InstId) -> bool {
177 apply_fn_name(ir, id) == Some(FN_OPT)
178}
179
180pub fn is_base_type(ir: &Ir, id: InstId) -> bool {
182 if let Some(name) = name_str(ir, id) {
183 matches!(
184 name,
185 "/any"
186 | "/bot"
187 | "/number"
188 | "/float64"
189 | "/string"
190 | "/bytes"
191 | "/name"
192 | "/bool"
193 | "/time"
194 | "/duration"
195 | "/unit"
196 )
197 } else {
198 false
199 }
200}
201
202pub fn is_name_const(ir: &Ir, id: InstId) -> bool {
205 matches!(ir.get(id), Inst::Name(_))
206}
207
208pub fn is_type_var(ir: &Ir, id: InstId) -> bool {
210 matches!(ir.get(id), Inst::Var(_))
211}
212
213pub fn list_type_arg(ir: &Ir, id: InstId) -> Option<InstId> {
219 let args = apply_fn_args(ir, id)?;
220 if apply_fn_name(ir, id) != Some(FN_LIST) {
221 return None;
222 }
223 args.first().copied()
224}
225
226pub fn map_type_args(ir: &Ir, id: InstId) -> Option<(InstId, InstId)> {
228 let args = apply_fn_args(ir, id)?;
229 if apply_fn_name(ir, id) != Some(FN_MAP) || args.len() != 2 {
230 return None;
231 }
232 Some((args[0], args[1]))
233}
234
235pub fn union_type_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
237 if apply_fn_name(ir, id) != Some(FN_UNION) {
238 return None;
239 }
240 apply_fn_args(ir, id)
241}
242
243pub fn tagged_union_tag_field(ir: &Ir, id: InstId) -> Option<InstId> {
245 if apply_fn_name(ir, id) != Some(FN_TAGGED_UNION) {
246 return None;
247 }
248 apply_fn_args(ir, id).and_then(|args| args.first().copied())
249}
250
251pub fn tagged_union_variants(ir: &Ir, id: InstId) -> Option<(Vec<InstId>, Vec<InstId>)> {
254 if apply_fn_name(ir, id) != Some(FN_TAGGED_UNION) {
255 return None;
256 }
257 let args = apply_fn_args(ir, id)?;
258 if args.len() < 3 || args.len() % 2 == 0 {
259 return None;
260 }
261 let mut tags = Vec::new();
262 let mut structs = Vec::new();
263 for i in (1..args.len()).step_by(2) {
264 tags.push(args[i]);
265 structs.push(args[i + 1]);
266 }
267 Some((tags, structs))
268}
269
270pub fn struct_type_fields(ir: &Ir, id: InstId) -> Vec<(InstId, InstId, bool)> {
276 let args = match apply_fn_args(ir, id) {
277 Some(a) if is_struct_type(ir, id) => a,
278 _ => return Vec::new(),
279 };
280 let mut result = Vec::new();
281 let mut i = 0;
282 while i < args.len() {
283 if is_opt_field(ir, args[i]) {
284 if let Some(opt_args) = apply_fn_args(ir, args[i])
286 && opt_args.len() == 2
287 {
288 result.push((opt_args[0], opt_args[1], true));
289 }
290 i += 1;
291 } else {
292 if i + 1 < args.len() {
294 result.push((args[i], args[i + 1], false));
295 }
296 i += 2;
297 }
298 }
299 result
300}
301
302pub fn struct_type_field(ir: &Ir, type_id: InstId, field: NameId) -> Option<InstId> {
304 if !is_struct_type(ir, type_id) {
305 return None;
306 }
307 let field_name = ir.resolve_name(field);
308 for (fname_id, ftype_id, _optional) in struct_type_fields(ir, type_id) {
309 if let Some(n) = name_str(ir, fname_id)
310 && n == field_name
311 {
312 return Some(ftype_id);
313 }
314 }
315 None
316}
317
318pub fn struct_type_field_deep(ir: &mut Ir, type_id: InstId, field: NameId) -> Option<InstId> {
321 if is_struct_type(ir, type_id) {
322 return struct_type_field(ir, type_id, field);
323 }
324 if is_tagged_union_type(ir, type_id) {
325 let tag_field = tagged_union_tag_field(ir, type_id)?;
326 let (tags, structs) = tagged_union_variants(ir, type_id)?;
327 let field_name = ir.resolve_name(field).to_string();
328 let tag_field_name = name_str(ir, tag_field)?.to_string();
329
330 if field_name == tag_field_name {
331 let singleton_fn = ir.intern_name(FN_SINGLETON);
333 let mut tag_types = Vec::new();
334 for tag in &tags {
335 let s = ir.add_inst(Inst::ApplyFn {
336 function: singleton_fn,
337 args: vec![*tag],
338 });
339 tag_types.push(s);
340 }
341 return Some(new_union_or_single(ir, tag_types));
342 }
343 let mut field_types = Vec::new();
344 for variant_struct in &structs {
345 let vfield_name = ir.intern_name(&field_name);
346 if let Some(ft) = struct_type_field(ir, *variant_struct, vfield_name) {
347 field_types.push(ft);
348 }
349 }
350 if field_types.is_empty() {
351 return None;
352 }
353 let ctx = TypeContext::default();
354 return Some(upper_bound(ir, &ctx, &field_types));
355 }
356 if is_union_type(ir, type_id) {
357 let args = union_type_args(ir, type_id)?.to_vec();
358 let mut field_types = Vec::new();
359 for alt in &args {
360 {
361 let ft = struct_type_field_deep(ir, *alt, field)?;
362 field_types.push(ft);
363 }
364 }
365 if field_types.is_empty() {
366 return None;
367 }
368 let ctx = TypeContext::default();
369 return Some(upper_bound(ir, &ctx, &field_types));
370 }
371 None
372}
373
374pub fn expand_tagged_union_type(ir: &mut Ir, tu_id: InstId) -> Result<InstId> {
383 let (tag_field_id, tags, structs) = {
384 let args = apply_fn_args(ir, tu_id)
385 .ok_or_else(|| anyhow!("not a TaggedUnion"))?
386 .to_vec();
387 if args.len() < 3 || args.len() % 2 == 0 {
388 bail!("TaggedUnion: bad arity");
389 }
390 let tag_field = args[0];
391 let mut tags = Vec::new();
392 let mut structs = Vec::new();
393 for i in (1..args.len()).step_by(2) {
394 tags.push(args[i]);
395 structs.push(args[i + 1]);
396 }
397 (tag_field, tags, structs)
398 };
399
400 let singleton_name = ir.intern_name(FN_SINGLETON);
401 let struct_name = ir.intern_name(FN_STRUCT);
402 let union_name = ir.intern_name(FN_UNION);
403
404 let mut variant_structs = Vec::new();
405 for (tag_id, struct_id) in tags.iter().zip(structs.iter()) {
406 let singleton = ir.add_inst(Inst::ApplyFn {
408 function: singleton_name,
409 args: vec![*tag_id],
410 });
411
412 let variant_fields: Vec<InstId> = apply_fn_args(ir, *struct_id).unwrap_or(&[]).to_vec();
414
415 let mut new_args = vec![tag_field_id, singleton];
417 new_args.extend_from_slice(&variant_fields);
418
419 let new_struct = ir.add_inst(Inst::ApplyFn {
420 function: struct_name,
421 args: new_args,
422 });
423 variant_structs.push(new_struct);
424 }
425
426 let union_id = ir.add_inst(Inst::ApplyFn {
427 function: union_name,
428 args: variant_structs,
429 });
430 Ok(union_id)
431}
432
433pub fn expand_tagged_union_for_bounds(ir: &mut Ir, tu_id: InstId) -> Result<InstId> {
437 let (tag_field_id, tags, structs) = {
438 let args = apply_fn_args(ir, tu_id)
439 .ok_or_else(|| anyhow!("not a TaggedUnion"))?
440 .to_vec();
441 if args.len() < 3 || args.len() % 2 == 0 {
442 bail!("TaggedUnion: bad arity");
443 }
444 let tag_field = args[0];
445 let mut tags = Vec::new();
446 let mut structs = Vec::new();
447 for i in (1..args.len()).step_by(2) {
448 tags.push(args[i]);
449 structs.push(args[i + 1]);
450 }
451 (tag_field, tags, structs)
452 };
453
454 let name_type = ir.intern_name("/name");
455 let name_type_id = ir.add_inst(Inst::Name(name_type));
456 let struct_name = ir.intern_name(FN_STRUCT);
457 let union_name = ir.intern_name(FN_UNION);
458
459 let mut variant_structs = Vec::new();
460 for (_tag_id, struct_id) in tags.iter().zip(structs.iter()) {
461 let variant_fields: Vec<InstId> = apply_fn_args(ir, *struct_id).unwrap_or(&[]).to_vec();
462
463 let mut new_args = vec![tag_field_id, name_type_id];
464 new_args.extend_from_slice(&variant_fields);
465
466 let new_struct = ir.add_inst(Inst::ApplyFn {
467 function: struct_name,
468 args: new_args,
469 });
470 variant_structs.push(new_struct);
471 }
472
473 let union_id = ir.add_inst(Inst::ApplyFn {
474 function: union_name,
475 args: variant_structs,
476 });
477 Ok(union_id)
478}
479
480pub type TypeContext = FxHashMap<NameId, InstId>;
486
487pub fn wellformed_type(ir: &Ir, ctx: &TypeContext, id: InstId) -> Result<()> {
489 match ir.get(id) {
490 Inst::Name(n) => {
491 let name = ir.resolve_name(*n);
492 if is_base_type(ir, id) || name.starts_with('/') {
494 return Ok(());
495 }
496 bail!("unknown type name: {}", name)
497 }
498 Inst::Var(v) => {
499 if ctx.contains_key(v) {
500 Ok(())
501 } else {
502 bail!("type variable {} not in context", ir.resolve_name(*v))
503 }
504 }
505 Inst::ApplyFn { function, args } => {
506 let fname = ir.resolve_name(*function);
507 match fname {
508 FN_STRUCT => check_struct_type_expr(ir, ctx, args),
509 FN_UNION => {
510 if args.len() < 2 {
511 bail!("fn:Union requires at least 2 alternatives");
512 }
513 for arg in args {
514 wellformed_type(ir, ctx, *arg)?;
515 }
516 Ok(())
517 }
518 FN_SINGLETON => {
519 if args.len() != 1 {
520 bail!("fn:Singleton requires exactly 1 argument");
521 }
522 match ir.get(args[0]) {
524 Inst::Name(_)
525 | Inst::Number(_)
526 | Inst::String(_)
527 | Inst::Float(_)
528 | Inst::Bool(_)
529 | Inst::Time(_)
530 | Inst::Duration(_) => Ok(()),
531 _ => bail!("fn:Singleton argument must be a constant"),
532 }
533 }
534 FN_LIST => {
535 if args.len() != 1 {
536 bail!("fn:List requires exactly 1 argument");
537 }
538 wellformed_type(ir, ctx, args[0])
539 }
540 FN_MAP => {
541 if args.len() != 2 {
542 bail!("fn:Map requires exactly 2 arguments");
543 }
544 wellformed_type(ir, ctx, args[0])?;
545 wellformed_type(ir, ctx, args[1])
546 }
547 FN_PAIR => {
548 if args.len() != 2 {
549 bail!("fn:Pair requires exactly 2 arguments");
550 }
551 wellformed_type(ir, ctx, args[0])?;
552 wellformed_type(ir, ctx, args[1])
553 }
554 FN_TUPLE => {
555 if args.len() < 2 {
556 bail!("fn:Tuple requires at least 2 arguments");
557 }
558 for arg in args {
559 wellformed_type(ir, ctx, *arg)?;
560 }
561 Ok(())
562 }
563 FN_OPTION => {
564 if args.len() != 1 {
565 bail!("fn:Option requires exactly 1 argument");
566 }
567 wellformed_type(ir, ctx, args[0])
568 }
569 FN_FUN => check_fun_type_expr(ir, ctx, args),
570 FN_REL => {
571 if args.is_empty() {
572 bail!("fn:Rel requires at least 1 argument");
573 }
574 for arg in args {
575 wellformed_type(ir, ctx, *arg)?;
576 }
577 Ok(())
578 }
579 FN_TAGGED_UNION => check_tagged_union_type_expr(ir, ctx, args),
580 FN_OPT => {
581 if args.len() != 2 {
583 bail!("fn:opt requires exactly 2 arguments");
584 }
585 if !is_name_const(ir, args[0]) {
586 bail!("fn:opt first argument must be a name constant");
587 }
588 wellformed_type(ir, ctx, args[1])
589 }
590 other => bail!("unknown type constructor: {}", other),
591 }
592 }
593 _ => bail!("not a valid type expression"),
594 }
595}
596
597fn check_struct_type_expr(ir: &Ir, ctx: &TypeContext, args: &[InstId]) -> Result<()> {
598 let mut i = 0;
599 let mut seen_fields = FxHashSet::default();
600 while i < args.len() {
601 if is_opt_field(ir, args[i]) {
602 let opt_args = apply_fn_args(ir, args[i]).ok_or_else(|| anyhow!("malformed fn:opt"))?;
604 if opt_args.len() != 2 {
605 bail!("fn:opt requires 2 arguments");
606 }
607 let field_name = name_str(ir, opt_args[0])
608 .ok_or_else(|| anyhow!("fn:opt field name must be a name constant"))?;
609 if !seen_fields.insert(field_name.to_string()) {
610 bail!("duplicate struct field: {}", field_name);
611 }
612 wellformed_type(ir, ctx, opt_args[1])?;
613 i += 1;
614 } else {
615 if i + 1 >= args.len() {
617 bail!("fn:Struct: odd number of args (missing type for last field)");
618 }
619 let field_name = name_str(ir, args[i])
620 .ok_or_else(|| anyhow!("fn:Struct field name must be a name constant"))?;
621 if !seen_fields.insert(field_name.to_string()) {
622 bail!("duplicate struct field: {}", field_name);
623 }
624 wellformed_type(ir, ctx, args[i + 1])?;
625 i += 2;
626 }
627 }
628 Ok(())
629}
630
631fn check_tagged_union_type_expr(ir: &Ir, ctx: &TypeContext, args: &[InstId]) -> Result<()> {
632 if args.len() < 3 {
633 bail!("fn:TaggedUnion requires at least 3 arguments (tag_field, tag, struct)");
634 }
635 if args.len().is_multiple_of(2) {
636 bail!("fn:TaggedUnion requires odd number of arguments");
637 }
638
639 let tag_field_name = name_str(ir, args[0])
641 .ok_or_else(|| anyhow!("fn:TaggedUnion tag field must be a name constant"))?;
642
643 let mut seen_tags = FxHashSet::default();
644 for i in (1..args.len()).step_by(2) {
645 let tag_name = name_str(ir, args[i])
647 .ok_or_else(|| anyhow!("fn:TaggedUnion variant tag must be a name constant"))?;
648 if !seen_tags.insert(tag_name.to_string()) {
649 bail!("fn:TaggedUnion duplicate variant tag: {}", tag_name);
650 }
651
652 let variant = args[i + 1];
654 if !is_struct_type(ir, variant) {
655 bail!(
656 "fn:TaggedUnion variant type for {} must be fn:Struct",
657 tag_name
658 );
659 }
660 wellformed_type(ir, ctx, variant)?;
661
662 for (fname_id, _ftype_id, _optional) in struct_type_fields(ir, variant) {
664 if let Some(n) = name_str(ir, fname_id)
665 && n == tag_field_name
666 {
667 bail!(
668 "fn:TaggedUnion tag field {} must not appear in variant struct for {}",
669 tag_field_name,
670 tag_name
671 );
672 }
673 }
674 }
675
676 Ok(())
677}
678
679fn check_fun_type_expr(ir: &Ir, ctx: &TypeContext, args: &[InstId]) -> Result<()> {
680 if args.is_empty() {
681 bail!("fn:Fun requires at least 1 argument (the result type)");
682 }
683 for arg in args {
684 wellformed_type(ir, ctx, *arg)?;
685 }
686 Ok(())
687}
688
689pub fn set_conforms(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
699 if left == right {
701 return true;
702 }
703
704 if name_str(ir, right) == Some("/any") {
706 return true;
707 }
708 if name_str(ir, left) == Some("/bot") {
710 return true;
711 }
712
713 if is_tagged_union_type(ir, left) {
717 return tagged_union_set_conforms_left(ir, ctx, left, right);
718 }
719 if is_tagged_union_type(ir, right) {
721 return tagged_union_set_conforms_right(ir, ctx, left, right);
722 }
723
724 if let Some(left_args) = union_type_args(ir, left) {
726 let left_args = left_args.to_vec();
727 return left_args
728 .iter()
729 .all(|alt| set_conforms(ir, ctx, *alt, right));
730 }
731
732 if let Some(right_args) = union_type_args(ir, right) {
734 let right_args = right_args.to_vec();
735 return right_args
736 .iter()
737 .any(|alt| set_conforms(ir, ctx, left, *alt));
738 }
739
740 type_conforms(ir, ctx, left, right)
741}
742
743pub fn type_conforms(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
745 if left == right {
746 return true;
747 }
748 if name_str(ir, right) == Some("/any") {
749 return true;
750 }
751 if name_str(ir, left) == Some("/bot") {
752 return true;
753 }
754
755 if let (Some(left_name), Some(right_name)) = (name_str(ir, left), name_str(ir, right)) {
757 if right_name == "/name" && left_name.starts_with('/') {
758 return true;
759 }
760 return left_name.starts_with(right_name)
761 && (left_name.len() == right_name.len()
762 || left_name.as_bytes().get(right_name.len()) == Some(&b'/'));
763 }
764
765 if is_singleton_type(ir, left)
767 && let Some(args) = apply_fn_args(ir, left)
768 && args.len() == 1
769 {
770 return const_has_base_type(ir, args[0], right);
771 }
772
773 if let Inst::Var(v) = ir.get(left)
775 && let Some(&bound) = ctx.get(v)
776 {
777 return set_conforms(ir, ctx, bound, right);
778 }
779 if let Inst::Var(v) = ir.get(right)
780 && let Some(&bound) = ctx.get(v)
781 {
782 return set_conforms(ir, ctx, left, bound);
783 }
784
785 let left_fn = apply_fn_name(ir, left);
786 let right_fn = apply_fn_name(ir, right);
787 if left_fn != right_fn {
788 return false;
789 }
790
791 match left_fn {
792 Some(FN_LIST) => {
793 let la = apply_fn_args(ir, left).unwrap();
795 let ra = apply_fn_args(ir, right).unwrap();
796 la.len() == 1 && ra.len() == 1 && set_conforms(ir, ctx, la[0], ra[0])
797 }
798 Some(FN_MAP) => {
799 let la = apply_fn_args(ir, left).unwrap();
801 let ra = apply_fn_args(ir, right).unwrap();
802 la.len() == 2
803 && ra.len() == 2
804 && set_conforms(ir, ctx, la[0], ra[0])
805 && set_conforms(ir, ctx, la[1], ra[1])
806 }
807 Some(FN_PAIR) => {
808 let la = apply_fn_args(ir, left).unwrap();
809 let ra = apply_fn_args(ir, right).unwrap();
810 la.len() == 2
811 && ra.len() == 2
812 && set_conforms(ir, ctx, la[0], ra[0])
813 && set_conforms(ir, ctx, la[1], ra[1])
814 }
815 Some(FN_TUPLE) => {
816 let la = apply_fn_args(ir, left).unwrap();
817 let ra = apply_fn_args(ir, right).unwrap();
818 la.len() == ra.len()
819 && la
820 .iter()
821 .zip(ra.iter())
822 .all(|(l, r)| set_conforms(ir, ctx, *l, *r))
823 }
824 Some(FN_STRUCT) => struct_type_conforms(ir, ctx, left, right),
825 Some(FN_SINGLETON) => {
826 let la = apply_fn_args(ir, left).unwrap();
827 let ra = apply_fn_args(ir, right).unwrap();
828 la.len() == 1 && ra.len() == 1 && ir_eq(ir, la[0], ra[0])
829 }
830 Some(FN_FUN) => {
831 let la = apply_fn_args(ir, left).unwrap();
833 let ra = apply_fn_args(ir, right).unwrap();
834 if la.len() != ra.len() || la.is_empty() {
835 return false;
836 }
837 if !set_conforms(ir, ctx, la[0], ra[0]) {
839 return false;
840 }
841 la[1..]
843 .iter()
844 .zip(ra[1..].iter())
845 .all(|(l, r)| set_conforms(ir, ctx, *r, *l))
846 }
847 Some(FN_REL) => {
848 let la = apply_fn_args(ir, left).unwrap();
849 let ra = apply_fn_args(ir, right).unwrap();
850 la.len() == ra.len()
851 && la
852 .iter()
853 .zip(ra.iter())
854 .all(|(l, r)| set_conforms(ir, ctx, *l, *r))
855 }
856 Some(FN_OPTION) => {
857 let la = apply_fn_args(ir, left).unwrap();
858 let ra = apply_fn_args(ir, right).unwrap();
859 la.len() == 1 && ra.len() == 1 && set_conforms(ir, ctx, la[0], ra[0])
860 }
861 _ => false,
862 }
863}
864
865fn struct_type_conforms(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
870 let left_fields = struct_type_fields(ir, left);
871 let right_fields = struct_type_fields(ir, right);
872
873 let left_map: FxHashMap<String, (InstId, bool)> = left_fields
875 .iter()
876 .filter_map(|(fname, ftype, opt)| {
877 name_str(ir, *fname).map(|n| (n.to_string(), (*ftype, *opt)))
878 })
879 .collect();
880
881 for (fname, ftype, is_optional) in &right_fields {
882 if let Some(fname_str) = name_str(ir, *fname) {
883 match left_map.get(fname_str) {
884 Some((left_type, _left_opt)) => {
885 if !set_conforms(ir, ctx, *left_type, *ftype) {
886 return false;
887 }
888 }
889 None => {
890 if !is_optional {
892 return false;
893 }
894 }
895 }
896 }
897 }
898 true
899}
900
901fn tagged_union_set_conforms_left(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
903 if let Some((tags, structs)) = tagged_union_variants(ir, left) {
905 let tag_field = tagged_union_tag_field(ir, left).unwrap();
906 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
907 if !expanded_variant_conforms(ir, ctx, tag_field, *tag, *variant_struct, right) {
910 return false;
911 }
912 }
913 true
914 } else {
915 false
916 }
917}
918
919fn tagged_union_set_conforms_right(
921 ir: &Ir,
922 ctx: &TypeContext,
923 left: InstId,
924 right: InstId,
925) -> bool {
926 if let Some((tags, structs)) = tagged_union_variants(ir, right) {
928 let tag_field = tagged_union_tag_field(ir, right).unwrap();
929 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
930 if expanded_variant_conforms_right(ir, ctx, left, tag_field, *tag, *variant_struct) {
931 return true;
932 }
933 }
934 false
935 } else {
936 false
937 }
938}
939
940fn expanded_variant_conforms(
943 ir: &Ir,
944 ctx: &TypeContext,
945 tag_field: InstId,
946 tag: InstId,
947 variant_struct: InstId,
948 right: InstId,
949) -> bool {
950 if name_str(ir, right) == Some("/any") {
957 return true;
958 }
959 if is_struct_type(ir, right) {
961 return set_conforms(ir, ctx, variant_struct, right);
963 }
964 if is_tagged_union_type(ir, right) {
968 if let Some((r_tags, r_structs)) = tagged_union_variants(ir, right) {
969 let r_tag_field = tagged_union_tag_field(ir, right).unwrap();
970 if !ir_eq(ir, tag_field, r_tag_field) {
971 return false;
972 }
973 return r_tags
974 .iter()
975 .zip(r_structs.iter())
976 .any(|(rt, rs)| ir_eq(ir, tag, *rt) && set_conforms(ir, ctx, variant_struct, *rs));
977 }
978 return false;
979 }
980 if let Some(alts) = union_type_args(ir, right) {
982 let alts = alts.to_vec();
983 return alts
984 .iter()
985 .any(|alt| expanded_variant_conforms(ir, ctx, tag_field, tag, variant_struct, *alt));
986 }
987 false
988}
989
990fn expanded_variant_conforms_right(
992 ir: &Ir,
993 ctx: &TypeContext,
994 left: InstId,
995 _tag_field: InstId,
996 _tag: InstId,
997 variant_struct: InstId,
998) -> bool {
999 set_conforms(ir, ctx, left, variant_struct)
1002}
1003
1004fn const_has_base_type(ir: &Ir, const_id: InstId, type_id: InstId) -> bool {
1006 let type_name = match name_str(ir, type_id) {
1007 Some(n) => n,
1008 None => return false,
1009 };
1010 match ir.get(const_id) {
1011 Inst::Number(_) => type_name == "/number" || type_name == "/any",
1012 Inst::Float(_) => type_name == "/float64" || type_name == "/any",
1013 Inst::String(_) => type_name == "/string" || type_name == "/any",
1014 Inst::Bool(_) => type_name == "/bool" || type_name == "/any",
1015 Inst::Time(_) => type_name == "/time" || type_name == "/any",
1016 Inst::Duration(_) => type_name == "/duration" || type_name == "/any",
1017 Inst::Bytes(_) => type_name == "/bytes" || type_name == "/any",
1018 Inst::Name(n) => {
1019 if type_name == "/name" || type_name == "/any" {
1020 return true;
1021 }
1022 let name = ir.resolve_name(*n);
1024 name.starts_with(type_name)
1025 && (name.len() == type_name.len()
1026 || name.as_bytes().get(type_name.len()) == Some(&b'/'))
1027 }
1028 _ => false,
1029 }
1030}
1031
1032fn ir_eq(ir: &Ir, a: InstId, b: InstId) -> bool {
1034 if a == b {
1035 return true;
1036 }
1037 match (ir.get(a), ir.get(b)) {
1038 (Inst::Name(na), Inst::Name(nb)) => na == nb,
1039 (Inst::Number(na), Inst::Number(nb)) => na == nb,
1040 (Inst::Float(fa), Inst::Float(fb)) => fa.to_bits() == fb.to_bits(),
1041 (Inst::String(sa), Inst::String(sb)) => sa == sb,
1042 (Inst::Bool(ba), Inst::Bool(bb)) => ba == bb,
1043 _ => false,
1044 }
1045}
1046
1047pub fn upper_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1056 let mut worklist = Vec::new();
1057 for &te in type_exprs {
1058 if is_any(ir, te) {
1059 return find_or_create_name(ir, "/any");
1060 }
1061 if let Some(args) = union_type_args(ir, te) {
1062 let args = args.to_vec();
1063 worklist.extend(args);
1064 } else {
1065 worklist.push(te);
1066 }
1067 }
1068 if worklist.is_empty() {
1069 return empty_type(ir);
1070 }
1071 let mut reduced = vec![worklist[0]];
1072 'outer: for &te in &worklist[1..] {
1073 for i in 0..reduced.len() {
1074 if set_conforms(ir, ctx, te, reduced[i]) {
1075 continue 'outer;
1076 }
1077 if set_conforms(ir, ctx, reduced[i], te) {
1078 reduced[i] = te;
1079 continue 'outer;
1080 }
1081 }
1082 reduced.push(te);
1083 }
1084 if reduced.len() == 1 {
1085 return reduced[0];
1086 }
1087 new_union_or_single(ir, reduced)
1088}
1089
1090pub fn intersect_type(ir: &mut Ir, ctx: &TypeContext, a: InstId, b: InstId) -> InstId {
1092 if ir_eq(ir, a, b) {
1093 return a;
1094 }
1095 if is_any(ir, a) {
1096 return b;
1097 }
1098 if is_any(ir, b) {
1099 return a;
1100 }
1101 if let Inst::Var(v) = ir.get(a) {
1103 let v = *v;
1104 if let Some(&bound) = ctx.get(&v) {
1105 return intersect_type(ir, ctx, bound, b);
1106 }
1107 return empty_type(ir);
1108 }
1109 if let Inst::Var(v) = ir.get(b) {
1110 let v = *v;
1111 if let Some(&bound) = ctx.get(&v) {
1112 return intersect_type(ir, ctx, a, bound);
1113 }
1114 return empty_type(ir);
1115 }
1116 if set_conforms(ir, ctx, a, b) {
1117 return a;
1118 }
1119 if set_conforms(ir, ctx, b, a) {
1120 return b;
1121 }
1122 if is_union_type(ir, a) {
1124 let args = union_type_args(ir, a).unwrap().to_vec();
1125 let mut res = Vec::new();
1126 for elem in args {
1127 let u = intersect_type(ir, ctx, elem, b);
1128 if !is_empty_type(ir, u) {
1129 res.push(u);
1130 }
1131 }
1132 return upper_bound(ir, ctx, &res);
1133 }
1134 if is_union_type(ir, b) {
1136 let args = union_type_args(ir, b).unwrap().to_vec();
1137 let mut res = Vec::new();
1138 for elem in args {
1139 if set_conforms(ir, ctx, a, elem) {
1140 res.push(a);
1141 } else if set_conforms(ir, ctx, elem, a) {
1142 res.push(elem);
1143 }
1144 }
1145 return upper_bound(ir, ctx, &res);
1146 }
1147 empty_type(ir)
1148}
1149
1150pub fn lower_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1154 let any = find_or_create_name(ir, "/any");
1155 let mut result = any;
1156 for &t in type_exprs {
1157 result = intersect_type(ir, ctx, result, t);
1158 if is_empty_type(ir, result) {
1159 return result;
1160 }
1161 }
1162 result
1163}
1164
1165pub fn has_type(ir: &Ir, type_expr: InstId, value: InstId) -> bool {
1175 if let Inst::Name(_) = ir.get(type_expr) {
1177 return const_has_base_type(ir, value, type_expr);
1178 }
1179
1180 if let Inst::Var(_) = ir.get(type_expr) {
1182 return true;
1183 }
1184
1185 let fname = match apply_fn_name(ir, type_expr) {
1186 Some(n) => n,
1187 None => return false,
1188 };
1189 let args = apply_fn_args(ir, type_expr).unwrap();
1190
1191 match fname {
1192 FN_SINGLETON => args.len() == 1 && ir_eq(ir, value, args[0]),
1193
1194 FN_UNION => args.iter().any(|alt| has_type(ir, *alt, value)),
1195
1196 FN_TAGGED_UNION => {
1197 if let Some((tags, structs)) = tagged_union_variants(ir, type_expr) {
1199 let tag_field = tagged_union_tag_field(ir, type_expr).unwrap();
1200 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
1201 if has_type_tagged_variant(ir, tag_field, *tag, *variant_struct, value) {
1202 return true;
1203 }
1204 }
1205 }
1206 false
1207 }
1208
1209 FN_LIST => {
1210 if args.len() != 1 {
1211 return false;
1212 }
1213 match list_value_elems(ir, value) {
1214 Some(elems) => elems.iter().all(|e| has_type(ir, args[0], *e)),
1215 None => false,
1216 }
1217 }
1218
1219 FN_MAP => {
1220 if args.len() != 2 {
1221 return false;
1222 }
1223 match map_value_entries(ir, value) {
1224 Some((keys, values)) => {
1225 keys.iter().all(|k| has_type(ir, args[0], *k))
1226 && values.iter().all(|v| has_type(ir, args[1], *v))
1227 }
1228 None => false,
1229 }
1230 }
1231
1232 FN_PAIR => {
1233 if args.len() != 2 {
1234 return false;
1235 }
1236 match list_value_elems(ir, value) {
1237 Some(elems) if elems.len() == 2 => {
1238 has_type(ir, args[0], elems[0]) && has_type(ir, args[1], elems[1])
1239 }
1240 _ => false,
1241 }
1242 }
1243
1244 FN_STRUCT => has_type_struct(ir, type_expr, value),
1245
1246 FN_OPTION => {
1247 if args.len() != 1 {
1248 return false;
1249 }
1250 if let Some(n) = name_str(ir, value)
1252 && n == "/unit"
1253 {
1254 return true;
1255 }
1256 has_type(ir, args[0], value)
1257 }
1258
1259 _ => false,
1260 }
1261}
1262
1263fn list_value_elems(ir: &Ir, value: InstId) -> Option<Vec<InstId>> {
1275 match ir.get(value) {
1276 Inst::List(elems) => Some(elems.clone()),
1277 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:list" => {
1278 Some(args.clone())
1279 }
1280 _ => None,
1281 }
1282}
1283
1284fn map_value_entries(ir: &Ir, value: InstId) -> Option<(Vec<InstId>, Vec<InstId>)> {
1287 match ir.get(value) {
1288 Inst::Map { keys, values } => Some((keys.clone(), values.clone())),
1289 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:map" => {
1290 let args = args.clone();
1291 if args.len() % 2 != 0 {
1292 return None;
1293 }
1294 let mut keys = Vec::with_capacity(args.len() / 2);
1295 let mut values = Vec::with_capacity(args.len() / 2);
1296 for pair in args.chunks_exact(2) {
1297 keys.push(pair[0]);
1298 values.push(pair[1]);
1299 }
1300 Some((keys, values))
1301 }
1302 _ => None,
1303 }
1304}
1305
1306fn struct_value_fields(ir: &Ir, value: InstId) -> Option<(Vec<NameId>, Vec<InstId>)> {
1308 match ir.get(value) {
1309 Inst::Struct { fields, values } => Some((fields.clone(), values.clone())),
1310 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:struct" => {
1311 let args = args.clone();
1312 if args.len() % 2 != 0 {
1313 return None;
1314 }
1315 let mut fields = Vec::with_capacity(args.len() / 2);
1316 let mut values = Vec::with_capacity(args.len() / 2);
1317 for pair in args.chunks_exact(2) {
1318 match ir.get(pair[0]) {
1319 Inst::Name(n) => fields.push(*n),
1320 _ => return None,
1321 }
1322 values.push(pair[1]);
1323 }
1324 Some((fields, values))
1325 }
1326 _ => None,
1327 }
1328}
1329
1330fn has_type_struct(ir: &Ir, type_id: InstId, value: InstId) -> bool {
1332 let type_fields = struct_type_fields(ir, type_id);
1333 let (vfields, vvalues) = match struct_value_fields(ir, value) {
1334 Some(pair) => pair,
1335 None => return false,
1336 };
1337
1338 let value_map: FxHashMap<String, InstId> = vfields
1340 .iter()
1341 .zip(vvalues.iter())
1342 .map(|(f, v)| (ir.resolve_name(*f).to_string(), *v))
1343 .collect();
1344
1345 for (fname_id, ftype_id, is_optional) in &type_fields {
1347 if let Some(fname) = name_str(ir, *fname_id) {
1348 match value_map.get(fname) {
1349 Some(val_id) => {
1350 if !has_type(ir, *ftype_id, *val_id) {
1351 return false;
1352 }
1353 }
1354 None => {
1355 if !is_optional {
1356 return false;
1357 }
1358 }
1359 }
1360 }
1361 }
1362
1363 let type_field_names: FxHashSet<String> = type_fields
1365 .iter()
1366 .filter_map(|(f, _, _)| name_str(ir, *f).map(|s| s.to_string()))
1367 .collect();
1368 for vf in &vfields {
1369 let vf_name = ir.resolve_name(*vf);
1370 if !type_field_names.contains(vf_name) {
1371 return false;
1372 }
1373 }
1374
1375 true
1376}
1377
1378fn has_type_tagged_variant(
1380 ir: &Ir,
1381 tag_field: InstId,
1382 tag: InstId,
1383 variant_struct: InstId,
1384 value: InstId,
1385) -> bool {
1386 let (vfields, vvalues) = match struct_value_fields(ir, value) {
1387 Some(pair) => pair,
1388 None => return false,
1389 };
1390
1391 let tag_field_name = match name_str(ir, tag_field) {
1393 Some(n) => n,
1394 None => return false,
1395 };
1396
1397 let tag_idx = vfields
1398 .iter()
1399 .position(|f| ir.resolve_name(*f) == tag_field_name);
1400 match tag_idx {
1401 Some(idx) => {
1402 if !ir_eq(ir, vvalues[idx], tag) {
1403 return false;
1404 }
1405 }
1406 None => return false,
1407 }
1408
1409 let type_fields = struct_type_fields(ir, variant_struct);
1411
1412 let value_map: FxHashMap<String, InstId> = vfields
1414 .iter()
1415 .zip(vvalues.iter())
1416 .filter(|(f, _)| ir.resolve_name(**f) != tag_field_name)
1417 .map(|(f, v)| (ir.resolve_name(*f).to_string(), *v))
1418 .collect();
1419
1420 for (fname_id, ftype_id, is_optional) in &type_fields {
1422 if let Some(fname) = name_str(ir, *fname_id) {
1423 match value_map.get(fname) {
1424 Some(val_id) => {
1425 if !has_type(ir, *ftype_id, *val_id) {
1426 return false;
1427 }
1428 }
1429 None => {
1430 if !is_optional {
1431 return false;
1432 }
1433 }
1434 }
1435 }
1436 }
1437
1438 let type_field_names: FxHashSet<String> = type_fields
1440 .iter()
1441 .filter_map(|(f, _, _)| name_str(ir, *f).map(|s| s.to_string()))
1442 .collect();
1443 for vf in &vfields {
1444 let name = ir.resolve_name(*vf);
1445 if name != tag_field_name && !type_field_names.contains(name) {
1446 return false;
1447 }
1448 }
1449
1450 true
1451}
1452
1453pub fn new_rel_type(ir: &mut Ir, arg_types: &[InstId]) -> InstId {
1459 let rel_name = ir.intern_name(FN_REL);
1460 ir.add_inst(Inst::ApplyFn {
1461 function: rel_name,
1462 args: arg_types.to_vec(),
1463 })
1464}
1465
1466pub fn new_union_or_single(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1468 if alts.len() == 1 {
1469 return alts[0];
1470 }
1471 let union_name = ir.intern_name(FN_UNION);
1472 ir.add_inst(Inst::ApplyFn {
1473 function: union_name,
1474 args: alts,
1475 })
1476}
1477
1478pub fn rel_type_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
1480 if apply_fn_name(ir, id) != Some(FN_REL) {
1481 return None;
1482 }
1483 apply_fn_args(ir, id)
1484}
1485
1486pub fn rel_types_from_decl(ir: &mut Ir, bounds: &[InstId]) -> Vec<InstId> {
1489 bounds
1490 .iter()
1491 .filter_map(|bound_id| {
1492 if let Inst::BoundDecl { base_terms } = ir.get(*bound_id) {
1493 let terms = base_terms.clone();
1494 Some(new_rel_type(ir, &terms))
1495 } else {
1496 None
1497 }
1498 })
1499 .collect()
1500}
1501
1502pub fn get_type_context(ir: &Ir, id: InstId) -> TypeContext {
1505 let mut ctx = TypeContext::default();
1506 collect_type_vars(ir, id, &mut ctx);
1507 ctx
1508}
1509
1510fn collect_type_vars(ir: &Ir, id: InstId, ctx: &mut TypeContext) {
1511 match ir.get(id) {
1512 Inst::Var(v) => {
1513 if !ctx.contains_key(v) {
1514 ctx.insert(*v, id);
1518 }
1519 }
1520 Inst::ApplyFn { args, .. } => {
1521 for arg in args {
1522 collect_type_vars(ir, *arg, ctx);
1523 }
1524 }
1525 _ => {}
1526 }
1527}
1528
1529pub fn rel_type_alternatives(ir: &Ir, id: InstId) -> Vec<InstId> {
1534 if let Some(args) = union_type_args(ir, id) {
1535 args.to_vec()
1536 } else {
1537 vec![id]
1538 }
1539}
1540
1541pub fn rel_type_from_alternatives(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1543 if alts.is_empty() {
1544 return empty_type(ir);
1545 }
1546 new_union_or_single(ir, alts)
1547}
1548
1549pub fn remove_from_union_type(ir: &mut Ir, to_remove: InstId, union_type: InstId) -> InstId {
1552 let ctx = TypeContext::default();
1553 if let Some(args) = union_type_args(ir, union_type) {
1554 let args = args.to_vec();
1555 let remaining: Vec<InstId> = args
1556 .into_iter()
1557 .filter(|alt| !set_conforms(ir, &ctx, *alt, to_remove))
1558 .collect();
1559 if remaining.is_empty() {
1560 return empty_type(ir);
1561 }
1562 return new_union_or_single(ir, remaining);
1563 }
1564 if set_conforms(ir, &ctx, union_type, to_remove) {
1566 return empty_type(ir);
1567 }
1568 union_type
1569}
1570
1571pub fn new_list_type(ir: &mut Ir, elem: InstId) -> InstId {
1573 let name = ir.intern_name(FN_LIST);
1574 ir.add_inst(Inst::ApplyFn {
1575 function: name,
1576 args: vec![elem],
1577 })
1578}
1579
1580pub fn new_map_type(ir: &mut Ir, key: InstId, val: InstId) -> InstId {
1582 let name = ir.intern_name(FN_MAP);
1583 ir.add_inst(Inst::ApplyFn {
1584 function: name,
1585 args: vec![key, val],
1586 })
1587}
1588
1589pub fn new_struct_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1591 let name = ir.intern_name(FN_STRUCT);
1592 ir.add_inst(Inst::ApplyFn {
1593 function: name,
1594 args,
1595 })
1596}
1597
1598pub fn new_tuple_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1600 let name = ir.intern_name(FN_TUPLE);
1601 ir.add_inst(Inst::ApplyFn {
1602 function: name,
1603 args,
1604 })
1605}
1606
1607pub fn apply_subst(ir: &mut Ir, id: InstId, subst: &FxHashMap<NameId, InstId>) -> InstId {
1610 if subst.is_empty() {
1611 return id;
1612 }
1613 match ir.get(id) {
1614 Inst::Var(v) => {
1615 let v = *v;
1616 if let Some(&replacement) = subst.get(&v) {
1617 replacement
1618 } else {
1619 id
1620 }
1621 }
1622 Inst::ApplyFn { function, args } => {
1623 let function = *function;
1624 let args = args.clone();
1625 let new_args: Vec<InstId> = args.iter().map(|a| apply_subst(ir, *a, subst)).collect();
1626 if new_args == args {
1627 return id;
1628 }
1629 ir.add_inst(Inst::ApplyFn {
1630 function,
1631 args: new_args,
1632 })
1633 }
1634 _ => id,
1635 }
1636}
1637
1638pub fn collect_vars(ir: &Ir, id: InstId, vars: &mut FxHashSet<NameId>) {
1640 match ir.get(id) {
1641 Inst::Var(v) => {
1642 vars.insert(*v);
1643 }
1644 Inst::ApplyFn { args, .. } => {
1645 for arg in args {
1646 collect_vars(ir, *arg, vars);
1647 }
1648 }
1649 _ => {}
1650 }
1651}
1652
1653#[cfg(test)]
1658mod tests {
1659 use super::*;
1660
1661 fn make_name(ir: &mut Ir, s: &str) -> InstId {
1662 let n = ir.intern_name(s);
1663 ir.add_inst(Inst::Name(n))
1664 }
1665
1666 fn make_apply(ir: &mut Ir, fn_name: &str, args: Vec<InstId>) -> InstId {
1667 let n = ir.intern_name(fn_name);
1668 ir.add_inst(Inst::ApplyFn { function: n, args })
1669 }
1670
1671 #[test]
1674 fn wellformed_base_types() {
1675 let mut ir = Ir::new();
1676 let ctx = TypeContext::default();
1677 let num = make_name(&mut ir, "/number");
1678 let str_ = make_name(&mut ir, "/string");
1679 let any = make_name(&mut ir, "/any");
1680 assert!(wellformed_type(&ir, &ctx, num).is_ok());
1681 assert!(wellformed_type(&ir, &ctx, str_).is_ok());
1682 assert!(wellformed_type(&ir, &ctx, any).is_ok());
1683 }
1684
1685 #[test]
1686 fn wellformed_struct() {
1687 let mut ir = Ir::new();
1688 let ctx = TypeContext::default();
1689 let x = make_name(&mut ir, "/x");
1690 let num = make_name(&mut ir, "/number");
1691 let y = make_name(&mut ir, "/y");
1692 let str_ = make_name(&mut ir, "/string");
1693 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1694 assert!(wellformed_type(&ir, &ctx, s).is_ok());
1695 }
1696
1697 #[test]
1698 fn wellformed_struct_odd_args() {
1699 let mut ir = Ir::new();
1700 let ctx = TypeContext::default();
1701 let x = make_name(&mut ir, "/x");
1702 let num = make_name(&mut ir, "/number");
1703 let y = make_name(&mut ir, "/y");
1704 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y]);
1706 assert!(wellformed_type(&ir, &ctx, s).is_err());
1707 }
1708
1709 #[test]
1710 fn wellformed_tagged_union() {
1711 let mut ir = Ir::new();
1712 let ctx = TypeContext::default();
1713 let kind = make_name(&mut ir, "/kind");
1714 let move_ = make_name(&mut ir, "/move");
1715 let x = make_name(&mut ir, "/x");
1716 let num = make_name(&mut ir, "/number");
1717 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
1718 let quit = make_name(&mut ir, "/quit");
1719 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1720 let tu = make_apply(
1721 &mut ir,
1722 FN_TAGGED_UNION,
1723 vec![kind, move_, move_struct, quit, quit_struct],
1724 );
1725 assert!(wellformed_type(&ir, &ctx, tu).is_ok());
1726 }
1727
1728 #[test]
1729 fn wellformed_tagged_union_negative_too_few_args() {
1730 let mut ir = Ir::new();
1731 let ctx = TypeContext::default();
1732 let kind = make_name(&mut ir, "/kind");
1733 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind]);
1734 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1735 }
1736
1737 #[test]
1738 fn wellformed_tagged_union_negative_even_args() {
1739 let mut ir = Ir::new();
1740 let ctx = TypeContext::default();
1741 let kind = make_name(&mut ir, "/kind");
1742 let move_ = make_name(&mut ir, "/move");
1743 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1744 let quit = make_name(&mut ir, "/quit");
1745 let tu = make_apply(
1747 &mut ir,
1748 FN_TAGGED_UNION,
1749 vec![kind, move_, move_struct, quit],
1750 );
1751 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1752 }
1753
1754 #[test]
1755 fn wellformed_tagged_union_negative_dup_tags() {
1756 let mut ir = Ir::new();
1757 let ctx = TypeContext::default();
1758 let kind = make_name(&mut ir, "/kind");
1759 let move_ = make_name(&mut ir, "/move");
1760 let s1 = make_apply(&mut ir, FN_STRUCT, vec![]);
1761 let move2 = make_name(&mut ir, "/move");
1762 let s2 = make_apply(&mut ir, FN_STRUCT, vec![]);
1763 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind, move_, s1, move2, s2]);
1764 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1765 }
1766
1767 #[test]
1768 fn wellformed_tagged_union_negative_tag_field_in_variant() {
1769 let mut ir = Ir::new();
1770 let ctx = TypeContext::default();
1771 let kind = make_name(&mut ir, "/kind");
1772 let move_ = make_name(&mut ir, "/move");
1773 let kind2 = make_name(&mut ir, "/kind");
1775 let num = make_name(&mut ir, "/number");
1776 let s = make_apply(&mut ir, FN_STRUCT, vec![kind2, num]);
1777 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind, move_, s]);
1778 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1779 }
1780
1781 #[test]
1784 fn conforms_base_types() {
1785 let mut ir = Ir::new();
1786 let ctx = TypeContext::default();
1787 let num = make_name(&mut ir, "/number");
1788 let any = make_name(&mut ir, "/any");
1789 let str_ = make_name(&mut ir, "/string");
1790
1791 assert!(set_conforms(&ir, &ctx, num, any));
1792 assert!(set_conforms(&ir, &ctx, num, num));
1793 assert!(!set_conforms(&ir, &ctx, num, str_));
1794 }
1795
1796 #[test]
1797 fn conforms_name_hierarchy() {
1798 let mut ir = Ir::new();
1799 let ctx = TypeContext::default();
1800 let foo = make_name(&mut ir, "/foo");
1801 let foo_bar = make_name(&mut ir, "/foo/bar");
1802 let name = make_name(&mut ir, "/name");
1803
1804 assert!(set_conforms(&ir, &ctx, foo_bar, foo));
1805 assert!(set_conforms(&ir, &ctx, foo, name));
1806 assert!(set_conforms(&ir, &ctx, foo_bar, name));
1807 assert!(!set_conforms(&ir, &ctx, foo, foo_bar));
1808 }
1809
1810 #[test]
1811 fn conforms_singleton() {
1812 let mut ir = Ir::new();
1813 let ctx = TypeContext::default();
1814 let move_ = make_name(&mut ir, "/move");
1815 let singleton = make_apply(&mut ir, FN_SINGLETON, vec![move_]);
1816 let name = make_name(&mut ir, "/name");
1817 let num = make_name(&mut ir, "/number");
1818
1819 assert!(set_conforms(&ir, &ctx, singleton, name));
1820 assert!(!set_conforms(&ir, &ctx, singleton, num));
1821 }
1822
1823 #[test]
1824 fn conforms_union() {
1825 let mut ir = Ir::new();
1826 let ctx = TypeContext::default();
1827 let num = make_name(&mut ir, "/number");
1828 let str_ = make_name(&mut ir, "/string");
1829 let union = make_apply(&mut ir, FN_UNION, vec![num, str_]);
1830
1831 let any = make_name(&mut ir, "/any");
1833 assert!(set_conforms(&ir, &ctx, union, any));
1834 assert!(set_conforms(&ir, &ctx, num, union));
1836 assert!(!set_conforms(&ir, &ctx, union, num));
1838 }
1839
1840 #[test]
1841 fn conforms_struct_subtyping() {
1842 let mut ir = Ir::new();
1843 let ctx = TypeContext::default();
1844 let x = make_name(&mut ir, "/x");
1846 let num = make_name(&mut ir, "/number");
1847 let y = make_name(&mut ir, "/y");
1848 let str_ = make_name(&mut ir, "/string");
1849 let wider = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1850 let x2 = make_name(&mut ir, "/x");
1852 let num2 = make_name(&mut ir, "/number");
1853 let narrower = make_apply(&mut ir, FN_STRUCT, vec![x2, num2]);
1854
1855 assert!(set_conforms(&ir, &ctx, wider, narrower));
1857 assert!(!set_conforms(&ir, &ctx, narrower, wider));
1859 }
1860
1861 #[test]
1862 fn conforms_list() {
1863 let mut ir = Ir::new();
1864 let ctx = TypeContext::default();
1865 let num = make_name(&mut ir, "/number");
1866 let any = make_name(&mut ir, "/any");
1867 let list_num = make_apply(&mut ir, FN_LIST, vec![num]);
1868 let list_any = make_apply(&mut ir, FN_LIST, vec![any]);
1869
1870 assert!(set_conforms(&ir, &ctx, list_num, list_any));
1871 assert!(!set_conforms(&ir, &ctx, list_any, list_num));
1872 }
1873
1874 #[test]
1877 fn has_type_base() {
1878 let mut ir = Ir::new();
1879 let num_type = make_name(&mut ir, "/number");
1880 let str_type = make_name(&mut ir, "/string");
1881 let val_42 = ir.add_inst(Inst::Number(42));
1882 let val_hello = {
1883 let s = ir.intern_string("hello");
1884 ir.add_inst(Inst::String(s))
1885 };
1886
1887 assert!(has_type(&ir, num_type, val_42));
1888 assert!(!has_type(&ir, str_type, val_42));
1889 assert!(has_type(&ir, str_type, val_hello));
1890 }
1891
1892 #[test]
1893 fn has_type_struct() {
1894 let mut ir = Ir::new();
1895 let x_name = make_name(&mut ir, "/x");
1897 let num_type = make_name(&mut ir, "/number");
1898 let struct_type = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1899
1900 let x_field = ir.intern_name("/x");
1902 let val_42 = ir.add_inst(Inst::Number(42));
1903 let val_struct = ir.add_inst(Inst::Struct {
1904 fields: vec![x_field],
1905 values: vec![val_42],
1906 });
1907
1908 assert!(has_type(&ir, struct_type, val_struct));
1909 }
1910
1911 #[test]
1912 fn has_type_tagged_union() {
1913 let mut ir = Ir::new();
1914
1915 let kind = make_name(&mut ir, "/kind");
1917 let move_tag = make_name(&mut ir, "/move");
1918 let x_name = make_name(&mut ir, "/x");
1919 let num_type = make_name(&mut ir, "/number");
1920 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1921 let quit_tag = make_name(&mut ir, "/quit");
1922 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1923 let tu = make_apply(
1924 &mut ir,
1925 FN_TAGGED_UNION,
1926 vec![kind, move_tag, move_struct, quit_tag, quit_struct],
1927 );
1928
1929 let kind_f = ir.intern_name("/kind");
1931 let move_v = ir.intern_name("/move");
1932 let move_val = ir.add_inst(Inst::Name(move_v));
1933 let x_f = ir.intern_name("/x");
1934 let val_10 = ir.add_inst(Inst::Number(10));
1935 let val_move = ir.add_inst(Inst::Struct {
1936 fields: vec![kind_f, x_f],
1937 values: vec![move_val, val_10],
1938 });
1939 assert!(has_type(&ir, tu, val_move));
1940
1941 let kind_f2 = ir.intern_name("/kind");
1943 let quit_v = ir.intern_name("/quit");
1944 let quit_val = ir.add_inst(Inst::Name(quit_v));
1945 let val_quit = ir.add_inst(Inst::Struct {
1946 fields: vec![kind_f2],
1947 values: vec![quit_val],
1948 });
1949 assert!(has_type(&ir, tu, val_quit));
1950
1951 let kind_f3 = ir.intern_name("/kind");
1953 let bad_v = ir.intern_name("/bad");
1954 let bad_val = ir.add_inst(Inst::Name(bad_v));
1955 let val_bad = ir.add_inst(Inst::Struct {
1956 fields: vec![kind_f3],
1957 values: vec![bad_val],
1958 });
1959 assert!(!has_type(&ir, tu, val_bad));
1960 }
1961
1962 #[test]
1963 fn has_type_struct_accepts_applyfn_shape() {
1964 let mut ir = Ir::new();
1967 let x = make_name(&mut ir, "/x");
1968 let y = make_name(&mut ir, "/y");
1969 let num = make_name(&mut ir, "/number");
1970 let str_ = make_name(&mut ir, "/string");
1971 let stype = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1972
1973 let x_v = make_name(&mut ir, "/x");
1974 let y_v = make_name(&mut ir, "/y");
1975 let n = ir.add_inst(Inst::Number(42));
1976 let sid = ir.intern_string("hi");
1977 let s = ir.add_inst(Inst::String(sid));
1978 let value = make_apply(&mut ir, "fn:struct", vec![x_v, n, y_v, s]);
1979 assert!(has_type(&ir, stype, value));
1980 }
1981
1982 #[test]
1983 fn has_type_list_accepts_applyfn_shape() {
1984 let mut ir = Ir::new();
1986 let num = make_name(&mut ir, "/number");
1987 let list_num = make_apply(&mut ir, FN_LIST, vec![num]);
1988 let n1 = ir.add_inst(Inst::Number(1));
1989 let n2 = ir.add_inst(Inst::Number(2));
1990 let n3 = ir.add_inst(Inst::Number(3));
1991 let value = make_apply(&mut ir, "fn:list", vec![n1, n2, n3]);
1992 assert!(has_type(&ir, list_num, value));
1993 }
1994
1995 #[test]
1996 fn conforms_tagged_union_identity() {
1997 let mut ir = Ir::new();
2000 let ctx = TypeContext::default();
2001 let build = |ir: &mut Ir| {
2002 let t = make_name(ir, "/type");
2003 let c = make_name(ir, "/create");
2004 let name_f = make_name(ir, "/name");
2005 let str_ = make_name(ir, "/string");
2006 let create_s = make_apply(ir, FN_STRUCT, vec![name_f, str_]);
2007 let p = make_name(ir, "/ping");
2008 let ping_s = make_apply(ir, FN_STRUCT, vec![]);
2009 make_apply(ir, FN_TAGGED_UNION, vec![t, c, create_s, p, ping_s])
2010 };
2011 let tu1 = build(&mut ir);
2012 let tu2 = build(&mut ir);
2013 assert!(set_conforms(&ir, &ctx, tu1, tu2));
2014 }
2015
2016 #[test]
2019 fn expand_tagged_union() {
2020 let mut ir = Ir::new();
2021 let kind = make_name(&mut ir, "/kind");
2022 let move_ = make_name(&mut ir, "/move");
2023 let x = make_name(&mut ir, "/x");
2024 let num = make_name(&mut ir, "/number");
2025 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
2026 let quit = make_name(&mut ir, "/quit");
2027 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
2028 let tu = make_apply(
2029 &mut ir,
2030 FN_TAGGED_UNION,
2031 vec![kind, move_, move_struct, quit, quit_struct],
2032 );
2033
2034 let expanded = expand_tagged_union_type(&mut ir, tu).unwrap();
2035 assert!(is_union_type(&ir, expanded));
2036 let alts = union_type_args(&ir, expanded).unwrap();
2037 assert_eq!(alts.len(), 2);
2038 assert!(is_struct_type(&ir, alts[0]));
2040 assert!(is_struct_type(&ir, alts[1]));
2042 }
2043
2044 #[test]
2047 fn upper_bound_basic() {
2048 let mut ir = Ir::new();
2049 let ctx = TypeContext::default();
2050 let num = make_name(&mut ir, "/number");
2051 let str_ = make_name(&mut ir, "/string");
2052
2053 assert_eq!(upper_bound(&mut ir, &ctx, &[num]), num);
2055 let ub = upper_bound(&mut ir, &ctx, &[num, str_]);
2057 assert!(is_union_type(&ir, ub));
2058 let ub_empty = upper_bound(&mut ir, &ctx, &[]);
2060 assert!(is_empty_type(&ir, ub_empty));
2061 }
2062
2063 #[test]
2064 fn upper_bound_eliminates_redundant() {
2065 let mut ir = Ir::new();
2066 let ctx = TypeContext::default();
2067 let num = make_name(&mut ir, "/number");
2068 let any = make_name(&mut ir, "/any");
2069
2070 let ub = upper_bound(&mut ir, &ctx, &[num, any]);
2072 assert!(is_any(&ir, ub));
2073 }
2074
2075 #[test]
2076 fn upper_bound_name_hierarchy() {
2077 let mut ir = Ir::new();
2078 let ctx = TypeContext::default();
2079 let animal = make_name(&mut ir, "/animal");
2080 let animal_dog = make_name(&mut ir, "/animal/dog");
2081
2082 let ub = upper_bound(&mut ir, &ctx, &[animal, animal_dog]);
2084 assert_eq!(ub, animal);
2085 }
2086
2087 #[test]
2088 fn lower_bound_basic() {
2089 let mut ir = Ir::new();
2090 let ctx = TypeContext::default();
2091 let num = make_name(&mut ir, "/number");
2092 let any = make_name(&mut ir, "/any");
2093
2094 let lb = lower_bound(&mut ir, &ctx, &[num, any]);
2096 assert_eq!(lb, num);
2097 }
2098
2099 #[test]
2100 fn lower_bound_empty() {
2101 let mut ir = Ir::new();
2102 let ctx = TypeContext::default();
2103 let num = make_name(&mut ir, "/number");
2104 let str_ = make_name(&mut ir, "/string");
2105
2106 let lb = lower_bound(&mut ir, &ctx, &[num, str_]);
2108 assert!(is_empty_type(&ir, lb));
2109 }
2110
2111 #[test]
2112 fn intersect_type_with_union() {
2113 let mut ir = Ir::new();
2114 let ctx = TypeContext::default();
2115 let num = make_name(&mut ir, "/number");
2116 let str_ = make_name(&mut ir, "/string");
2117 let union_ns = make_apply(&mut ir, FN_UNION, vec![num, str_]);
2118
2119 let result = intersect_type(&mut ir, &ctx, union_ns, num);
2121 assert_eq!(result, num);
2122 }
2123}