1use anyhow::{Result, anyhow, bail};
28use rustc_hash::{FxHashMap, FxHashSet};
29use mangle_ir::{Inst, InstId, Ir, NameId};
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<'a>(ir: &'a Ir, id: InstId) -> Option<&'a 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<'a>(ir: &'a Ir, id: InstId) -> Option<&'a 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 if *n == name_id {
102 return InstId::new(idx);
103 }
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 if ir.resolve_name(*function) == FN_EMPTY_TYPE && args.is_empty() {
116 return InstId::new(idx);
117 }
118 }
119 }
120 let fn_name = ir.intern_name(FN_EMPTY_TYPE);
121 ir.add_inst(Inst::ApplyFn {
122 function: fn_name,
123 args: vec![],
124 })
125}
126
127pub fn is_struct_type(ir: &Ir, id: InstId) -> bool {
132 apply_fn_name(ir, id) == Some(FN_STRUCT)
133}
134
135pub fn is_union_type(ir: &Ir, id: InstId) -> bool {
136 apply_fn_name(ir, id) == Some(FN_UNION)
137}
138
139pub fn is_singleton_type(ir: &Ir, id: InstId) -> bool {
140 apply_fn_name(ir, id) == Some(FN_SINGLETON)
141}
142
143pub fn is_tagged_union_type(ir: &Ir, id: InstId) -> bool {
144 apply_fn_name(ir, id) == Some(FN_TAGGED_UNION)
145}
146
147pub fn is_list_type(ir: &Ir, id: InstId) -> bool {
148 apply_fn_name(ir, id) == Some(FN_LIST)
149}
150
151pub fn is_map_type(ir: &Ir, id: InstId) -> bool {
152 apply_fn_name(ir, id) == Some(FN_MAP)
153}
154
155pub fn is_pair_type(ir: &Ir, id: InstId) -> bool {
156 apply_fn_name(ir, id) == Some(FN_PAIR)
157}
158
159pub fn is_tuple_type(ir: &Ir, id: InstId) -> bool {
160 apply_fn_name(ir, id) == Some(FN_TUPLE)
161}
162
163pub fn is_fun_type(ir: &Ir, id: InstId) -> bool {
164 apply_fn_name(ir, id) == Some(FN_FUN)
165}
166
167pub fn is_rel_type(ir: &Ir, id: InstId) -> bool {
168 apply_fn_name(ir, id) == Some(FN_REL)
169}
170
171pub fn is_option_type(ir: &Ir, id: InstId) -> bool {
172 apply_fn_name(ir, id) == Some(FN_OPTION)
173}
174
175pub fn is_opt_field(ir: &Ir, id: InstId) -> bool {
176 apply_fn_name(ir, id) == Some(FN_OPT)
177}
178
179pub fn is_base_type(ir: &Ir, id: InstId) -> bool {
181 if let Some(name) = name_str(ir, id) {
182 matches!(
183 name,
184 "/any"
185 | "/bot"
186 | "/number"
187 | "/float64"
188 | "/string"
189 | "/bytes"
190 | "/name"
191 | "/bool"
192 | "/time"
193 | "/duration"
194 | "/unit"
195 )
196 } else {
197 false
198 }
199}
200
201pub fn is_name_const(ir: &Ir, id: InstId) -> bool {
204 matches!(ir.get(id), Inst::Name(_))
205}
206
207pub fn is_type_var(ir: &Ir, id: InstId) -> bool {
209 matches!(ir.get(id), Inst::Var(_))
210}
211
212pub fn list_type_arg(ir: &Ir, id: InstId) -> Option<InstId> {
218 let args = apply_fn_args(ir, id)?;
219 if apply_fn_name(ir, id) != Some(FN_LIST) {
220 return None;
221 }
222 args.first().copied()
223}
224
225pub fn map_type_args(ir: &Ir, id: InstId) -> Option<(InstId, InstId)> {
227 let args = apply_fn_args(ir, id)?;
228 if apply_fn_name(ir, id) != Some(FN_MAP) || args.len() != 2 {
229 return None;
230 }
231 Some((args[0], args[1]))
232}
233
234pub fn union_type_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
236 if apply_fn_name(ir, id) != Some(FN_UNION) {
237 return None;
238 }
239 apply_fn_args(ir, id)
240}
241
242pub fn tagged_union_tag_field(ir: &Ir, id: InstId) -> Option<InstId> {
244 if apply_fn_name(ir, id) != Some(FN_TAGGED_UNION) {
245 return None;
246 }
247 apply_fn_args(ir, id).and_then(|args| args.first().copied())
248}
249
250pub fn tagged_union_variants(ir: &Ir, id: InstId) -> Option<(Vec<InstId>, Vec<InstId>)> {
253 if apply_fn_name(ir, id) != Some(FN_TAGGED_UNION) {
254 return None;
255 }
256 let args = apply_fn_args(ir, id)?;
257 if args.len() < 3 || args.len() % 2 == 0 {
258 return None;
259 }
260 let mut tags = Vec::new();
261 let mut structs = Vec::new();
262 for i in (1..args.len()).step_by(2) {
263 tags.push(args[i]);
264 structs.push(args[i + 1]);
265 }
266 Some((tags, structs))
267}
268
269pub fn struct_type_fields(ir: &Ir, id: InstId) -> Vec<(InstId, InstId, bool)> {
275 let args = match apply_fn_args(ir, id) {
276 Some(a) if is_struct_type(ir, id) => a,
277 _ => return Vec::new(),
278 };
279 let mut result = Vec::new();
280 let mut i = 0;
281 while i < args.len() {
282 if is_opt_field(ir, args[i]) {
283 if let Some(opt_args) = apply_fn_args(ir, args[i]) {
285 if opt_args.len() == 2 {
286 result.push((opt_args[0], opt_args[1], true));
287 }
288 }
289 i += 1;
290 } else {
291 if i + 1 < args.len() {
293 result.push((args[i], args[i + 1], false));
294 }
295 i += 2;
296 }
297 }
298 result
299}
300
301pub fn struct_type_field(ir: &Ir, type_id: InstId, field: NameId) -> Option<InstId> {
303 if !is_struct_type(ir, type_id) {
304 return None;
305 }
306 let field_name = ir.resolve_name(field);
307 for (fname_id, ftype_id, _optional) in struct_type_fields(ir, type_id) {
308 if let Some(n) = name_str(ir, fname_id) {
309 if n == field_name {
310 return Some(ftype_id);
311 }
312 }
313 }
314 None
315}
316
317pub fn struct_type_field_deep(ir: &mut Ir, type_id: InstId, field: NameId) -> Option<InstId> {
320 if is_struct_type(ir, type_id) {
321 return struct_type_field(ir, type_id, field);
322 }
323 if is_tagged_union_type(ir, type_id) {
324 let tag_field = tagged_union_tag_field(ir, type_id)?;
325 let (tags, structs) = tagged_union_variants(ir, type_id)?;
326 let field_name = ir.resolve_name(field).to_string();
327 let tag_field_name = name_str(ir, tag_field)?.to_string();
328
329 if field_name == tag_field_name {
330 let singleton_fn = ir.intern_name(FN_SINGLETON);
332 let mut tag_types = Vec::new();
333 for tag in &tags {
334 let s = ir.add_inst(Inst::ApplyFn {
335 function: singleton_fn,
336 args: vec![*tag],
337 });
338 tag_types.push(s);
339 }
340 return Some(new_union_or_single(ir, tag_types));
341 }
342 let mut field_types = Vec::new();
343 for variant_struct in &structs {
344 let vfield_name = ir.intern_name(&field_name);
345 if let Some(ft) = struct_type_field(ir, *variant_struct, vfield_name) {
346 field_types.push(ft);
347 }
348 }
349 if field_types.is_empty() {
350 return None;
351 }
352 let ctx = TypeContext::default();
353 return Some(upper_bound(ir, &ctx, &field_types));
354 }
355 if is_union_type(ir, type_id) {
356 let args = union_type_args(ir, type_id)?.to_vec();
357 let mut field_types = Vec::new();
358 for alt in &args {
359 if let Some(ft) = struct_type_field_deep(ir, *alt, field) {
360 field_types.push(ft);
361 } else {
362 return None; }
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> =
414 apply_fn_args(ir, *struct_id).unwrap_or(&[]).to_vec();
415
416 let mut new_args = vec![tag_field_id, singleton];
418 new_args.extend_from_slice(&variant_fields);
419
420 let new_struct = ir.add_inst(Inst::ApplyFn {
421 function: struct_name,
422 args: new_args,
423 });
424 variant_structs.push(new_struct);
425 }
426
427 let union_id = ir.add_inst(Inst::ApplyFn {
428 function: union_name,
429 args: variant_structs,
430 });
431 Ok(union_id)
432}
433
434pub fn expand_tagged_union_for_bounds(ir: &mut Ir, tu_id: InstId) -> Result<InstId> {
438 let (tag_field_id, tags, structs) = {
439 let args = apply_fn_args(ir, tu_id)
440 .ok_or_else(|| anyhow!("not a TaggedUnion"))?
441 .to_vec();
442 if args.len() < 3 || args.len() % 2 == 0 {
443 bail!("TaggedUnion: bad arity");
444 }
445 let tag_field = args[0];
446 let mut tags = Vec::new();
447 let mut structs = Vec::new();
448 for i in (1..args.len()).step_by(2) {
449 tags.push(args[i]);
450 structs.push(args[i + 1]);
451 }
452 (tag_field, tags, structs)
453 };
454
455 let name_type = ir.intern_name("/name");
456 let name_type_id = ir.add_inst(Inst::Name(name_type));
457 let struct_name = ir.intern_name(FN_STRUCT);
458 let union_name = ir.intern_name(FN_UNION);
459
460 let mut variant_structs = Vec::new();
461 for (_tag_id, struct_id) in tags.iter().zip(structs.iter()) {
462 let variant_fields: Vec<InstId> =
463 apply_fn_args(ir, *struct_id).unwrap_or(&[]).to_vec();
464
465 let mut new_args = vec![tag_field_id, name_type_id];
466 new_args.extend_from_slice(&variant_fields);
467
468 let new_struct = ir.add_inst(Inst::ApplyFn {
469 function: struct_name,
470 args: new_args,
471 });
472 variant_structs.push(new_struct);
473 }
474
475 let union_id = ir.add_inst(Inst::ApplyFn {
476 function: union_name,
477 args: variant_structs,
478 });
479 Ok(union_id)
480}
481
482pub type TypeContext = FxHashMap<NameId, InstId>;
488
489pub fn wellformed_type(ir: &Ir, ctx: &TypeContext, id: InstId) -> Result<()> {
491 match ir.get(id) {
492 Inst::Name(n) => {
493 let name = ir.resolve_name(*n);
494 if is_base_type(ir, id) || name.starts_with('/') {
496 return Ok(());
497 }
498 bail!("unknown type name: {}", name)
499 }
500 Inst::Var(v) => {
501 if ctx.contains_key(v) {
502 Ok(())
503 } else {
504 bail!(
505 "type variable {} not in context",
506 ir.resolve_name(*v)
507 )
508 }
509 }
510 Inst::ApplyFn { function, args } => {
511 let fname = ir.resolve_name(*function);
512 match fname {
513 FN_STRUCT => check_struct_type_expr(ir, ctx, args),
514 FN_UNION => {
515 if args.len() < 2 {
516 bail!("fn:Union requires at least 2 alternatives");
517 }
518 for arg in args {
519 wellformed_type(ir, ctx, *arg)?;
520 }
521 Ok(())
522 }
523 FN_SINGLETON => {
524 if args.len() != 1 {
525 bail!("fn:Singleton requires exactly 1 argument");
526 }
527 match ir.get(args[0]) {
529 Inst::Name(_) | Inst::Number(_) | Inst::String(_)
530 | Inst::Float(_) | Inst::Bool(_) | Inst::Time(_)
531 | Inst::Duration(_) => Ok(()),
532 _ => bail!("fn:Singleton argument must be a constant"),
533 }
534 }
535 FN_LIST => {
536 if args.len() != 1 {
537 bail!("fn:List requires exactly 1 argument");
538 }
539 wellformed_type(ir, ctx, args[0])
540 }
541 FN_MAP => {
542 if args.len() != 2 {
543 bail!("fn:Map requires exactly 2 arguments");
544 }
545 wellformed_type(ir, ctx, args[0])?;
546 wellformed_type(ir, ctx, args[1])
547 }
548 FN_PAIR => {
549 if args.len() != 2 {
550 bail!("fn:Pair requires exactly 2 arguments");
551 }
552 wellformed_type(ir, ctx, args[0])?;
553 wellformed_type(ir, ctx, args[1])
554 }
555 FN_TUPLE => {
556 if args.len() < 2 {
557 bail!("fn:Tuple requires at least 2 arguments");
558 }
559 for arg in args {
560 wellformed_type(ir, ctx, *arg)?;
561 }
562 Ok(())
563 }
564 FN_OPTION => {
565 if args.len() != 1 {
566 bail!("fn:Option requires exactly 1 argument");
567 }
568 wellformed_type(ir, ctx, args[0])
569 }
570 FN_FUN => check_fun_type_expr(ir, ctx, args),
571 FN_REL => {
572 if args.is_empty() {
573 bail!("fn:Rel requires at least 1 argument");
574 }
575 for arg in args {
576 wellformed_type(ir, ctx, *arg)?;
577 }
578 Ok(())
579 }
580 FN_TAGGED_UNION => check_tagged_union_type_expr(ir, ctx, args),
581 FN_OPT => {
582 if args.len() != 2 {
584 bail!("fn:opt requires exactly 2 arguments");
585 }
586 if !is_name_const(ir, args[0]) {
587 bail!("fn:opt first argument must be a name constant");
588 }
589 wellformed_type(ir, ctx, args[1])
590 }
591 other => bail!("unknown type constructor: {}", other),
592 }
593 }
594 _ => bail!("not a valid type expression"),
595 }
596}
597
598fn check_struct_type_expr(ir: &Ir, ctx: &TypeContext, args: &[InstId]) -> Result<()> {
599 let mut i = 0;
600 let mut seen_fields = FxHashSet::default();
601 while i < args.len() {
602 if is_opt_field(ir, args[i]) {
603 let opt_args = apply_fn_args(ir, args[i])
605 .ok_or_else(|| anyhow!("malformed fn:opt"))?;
606 if opt_args.len() != 2 {
607 bail!("fn:opt requires 2 arguments");
608 }
609 let field_name = name_str(ir, opt_args[0])
610 .ok_or_else(|| anyhow!("fn:opt field name must be a name constant"))?;
611 if !seen_fields.insert(field_name.to_string()) {
612 bail!("duplicate struct field: {}", field_name);
613 }
614 wellformed_type(ir, ctx, opt_args[1])?;
615 i += 1;
616 } else {
617 if i + 1 >= args.len() {
619 bail!("fn:Struct: odd number of args (missing type for last field)");
620 }
621 let field_name = name_str(ir, args[i])
622 .ok_or_else(|| anyhow!("fn:Struct field name must be a name constant"))?;
623 if !seen_fields.insert(field_name.to_string()) {
624 bail!("duplicate struct field: {}", field_name);
625 }
626 wellformed_type(ir, ctx, args[i + 1])?;
627 i += 2;
628 }
629 }
630 Ok(())
631}
632
633fn check_tagged_union_type_expr(
634 ir: &Ir,
635 ctx: &TypeContext,
636 args: &[InstId],
637) -> Result<()> {
638 if args.len() < 3 {
639 bail!("fn:TaggedUnion requires at least 3 arguments (tag_field, tag, struct)");
640 }
641 if args.len() % 2 == 0 {
642 bail!("fn:TaggedUnion requires odd number of arguments");
643 }
644
645 let tag_field_name = name_str(ir, args[0])
647 .ok_or_else(|| anyhow!("fn:TaggedUnion tag field must be a name constant"))?;
648
649 let mut seen_tags = FxHashSet::default();
650 for i in (1..args.len()).step_by(2) {
651 let tag_name = name_str(ir, args[i])
653 .ok_or_else(|| anyhow!("fn:TaggedUnion variant tag must be a name constant"))?;
654 if !seen_tags.insert(tag_name.to_string()) {
655 bail!("fn:TaggedUnion duplicate variant tag: {}", tag_name);
656 }
657
658 let variant = args[i + 1];
660 if !is_struct_type(ir, variant) {
661 bail!(
662 "fn:TaggedUnion variant type for {} must be fn:Struct",
663 tag_name
664 );
665 }
666 wellformed_type(ir, ctx, variant)?;
667
668 for (fname_id, _ftype_id, _optional) in struct_type_fields(ir, variant) {
670 if let Some(n) = name_str(ir, fname_id) {
671 if n == tag_field_name {
672 bail!(
673 "fn:TaggedUnion tag field {} must not appear in variant struct for {}",
674 tag_field_name,
675 tag_name
676 );
677 }
678 }
679 }
680 }
681
682 Ok(())
683}
684
685fn check_fun_type_expr(ir: &Ir, ctx: &TypeContext, args: &[InstId]) -> Result<()> {
686 if args.is_empty() {
687 bail!("fn:Fun requires at least 1 argument (the result type)");
688 }
689 for arg in args {
690 wellformed_type(ir, ctx, *arg)?;
691 }
692 Ok(())
693}
694
695pub fn set_conforms(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
705 if left == right {
707 return true;
708 }
709
710 if name_str(ir, right) == Some("/any") {
712 return true;
713 }
714 if name_str(ir, left) == Some("/bot") {
716 return true;
717 }
718
719 if is_tagged_union_type(ir, left) {
723 return tagged_union_set_conforms_left(ir, ctx, left, right);
724 }
725 if is_tagged_union_type(ir, right) {
727 return tagged_union_set_conforms_right(ir, ctx, left, right);
728 }
729
730 if let Some(left_args) = union_type_args(ir, left) {
732 let left_args = left_args.to_vec();
733 return left_args
734 .iter()
735 .all(|alt| set_conforms(ir, ctx, *alt, right));
736 }
737
738 if let Some(right_args) = union_type_args(ir, right) {
740 let right_args = right_args.to_vec();
741 return right_args
742 .iter()
743 .any(|alt| set_conforms(ir, ctx, left, *alt));
744 }
745
746 type_conforms(ir, ctx, left, right)
747}
748
749pub fn type_conforms(ir: &Ir, ctx: &TypeContext, left: InstId, right: InstId) -> bool {
751 if left == right {
752 return true;
753 }
754 if name_str(ir, right) == Some("/any") {
755 return true;
756 }
757 if name_str(ir, left) == Some("/bot") {
758 return true;
759 }
760
761 if let (Some(left_name), Some(right_name)) = (name_str(ir, left), name_str(ir, right)) {
763 if right_name == "/name" && left_name.starts_with('/') {
764 return true;
765 }
766 return left_name.starts_with(right_name)
767 && (left_name.len() == right_name.len()
768 || left_name.as_bytes().get(right_name.len()) == Some(&b'/'));
769 }
770
771 if is_singleton_type(ir, left) {
773 if let Some(args) = apply_fn_args(ir, left) {
774 if args.len() == 1 {
775 return const_has_base_type(ir, args[0], right);
776 }
777 }
778 }
779
780 if let Inst::Var(v) = ir.get(left) {
782 if let Some(&bound) = ctx.get(v) {
783 return set_conforms(ir, ctx, bound, right);
784 }
785 }
786 if let Inst::Var(v) = ir.get(right) {
787 if let Some(&bound) = ctx.get(v) {
788 return set_conforms(ir, ctx, left, bound);
789 }
790 }
791
792 let left_fn = apply_fn_name(ir, left);
793 let right_fn = apply_fn_name(ir, right);
794 if left_fn != right_fn {
795 return false;
796 }
797
798 match left_fn {
799 Some(FN_LIST) => {
800 let la = apply_fn_args(ir, left).unwrap();
802 let ra = apply_fn_args(ir, right).unwrap();
803 la.len() == 1 && ra.len() == 1 && set_conforms(ir, ctx, la[0], ra[0])
804 }
805 Some(FN_MAP) => {
806 let la = apply_fn_args(ir, left).unwrap();
808 let ra = apply_fn_args(ir, right).unwrap();
809 la.len() == 2
810 && ra.len() == 2
811 && set_conforms(ir, ctx, la[0], ra[0])
812 && set_conforms(ir, ctx, la[1], ra[1])
813 }
814 Some(FN_PAIR) => {
815 let la = apply_fn_args(ir, left).unwrap();
816 let ra = apply_fn_args(ir, right).unwrap();
817 la.len() == 2
818 && ra.len() == 2
819 && set_conforms(ir, ctx, la[0], ra[0])
820 && set_conforms(ir, ctx, la[1], ra[1])
821 }
822 Some(FN_TUPLE) => {
823 let la = apply_fn_args(ir, left).unwrap();
824 let ra = apply_fn_args(ir, right).unwrap();
825 la.len() == ra.len()
826 && la
827 .iter()
828 .zip(ra.iter())
829 .all(|(l, r)| set_conforms(ir, ctx, *l, *r))
830 }
831 Some(FN_STRUCT) => struct_type_conforms(ir, ctx, left, right),
832 Some(FN_SINGLETON) => {
833 let la = apply_fn_args(ir, left).unwrap();
834 let ra = apply_fn_args(ir, right).unwrap();
835 la.len() == 1 && ra.len() == 1 && ir_eq(ir, la[0], ra[0])
836 }
837 Some(FN_FUN) => {
838 let la = apply_fn_args(ir, left).unwrap();
840 let ra = apply_fn_args(ir, right).unwrap();
841 if la.len() != ra.len() || la.is_empty() {
842 return false;
843 }
844 if !set_conforms(ir, ctx, la[0], ra[0]) {
846 return false;
847 }
848 la[1..]
850 .iter()
851 .zip(ra[1..].iter())
852 .all(|(l, r)| set_conforms(ir, ctx, *r, *l))
853 }
854 Some(FN_REL) => {
855 let la = apply_fn_args(ir, left).unwrap();
856 let ra = apply_fn_args(ir, right).unwrap();
857 la.len() == ra.len()
858 && la
859 .iter()
860 .zip(ra.iter())
861 .all(|(l, r)| set_conforms(ir, ctx, *l, *r))
862 }
863 Some(FN_OPTION) => {
864 let la = apply_fn_args(ir, left).unwrap();
865 let ra = apply_fn_args(ir, right).unwrap();
866 la.len() == 1 && ra.len() == 1 && set_conforms(ir, ctx, la[0], ra[0])
867 }
868 _ => false,
869 }
870}
871
872fn struct_type_conforms(
877 ir: &Ir,
878 ctx: &TypeContext,
879 left: InstId,
880 right: InstId,
881) -> bool {
882 let left_fields = struct_type_fields(ir, left);
883 let right_fields = struct_type_fields(ir, right);
884
885 let left_map: FxHashMap<String, (InstId, bool)> = left_fields
887 .iter()
888 .filter_map(|(fname, ftype, opt)| {
889 name_str(ir, *fname).map(|n| (n.to_string(), (*ftype, *opt)))
890 })
891 .collect();
892
893 for (fname, ftype, is_optional) in &right_fields {
894 if let Some(fname_str) = name_str(ir, *fname) {
895 match left_map.get(fname_str) {
896 Some((left_type, _left_opt)) => {
897 if !set_conforms(ir, ctx, *left_type, *ftype) {
898 return false;
899 }
900 }
901 None => {
902 if !is_optional {
904 return false;
905 }
906 }
907 }
908 }
909 }
910 true
911}
912
913fn tagged_union_set_conforms_left(
915 ir: &Ir,
916 ctx: &TypeContext,
917 left: InstId,
918 right: InstId,
919) -> bool {
920 if let Some((tags, structs)) = tagged_union_variants(ir, left) {
922 let tag_field = tagged_union_tag_field(ir, left).unwrap();
923 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
924 if !expanded_variant_conforms(ir, ctx, tag_field, *tag, *variant_struct, right) {
927 return false;
928 }
929 }
930 true
931 } else {
932 false
933 }
934}
935
936fn tagged_union_set_conforms_right(
938 ir: &Ir,
939 ctx: &TypeContext,
940 left: InstId,
941 right: InstId,
942) -> bool {
943 if let Some((tags, structs)) = tagged_union_variants(ir, right) {
945 let tag_field = tagged_union_tag_field(ir, right).unwrap();
946 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
947 if expanded_variant_conforms_right(ir, ctx, left, tag_field, *tag, *variant_struct) {
948 return true;
949 }
950 }
951 false
952 } else {
953 false
954 }
955}
956
957fn expanded_variant_conforms(
960 ir: &Ir,
961 ctx: &TypeContext,
962 tag_field: InstId,
963 tag: InstId,
964 variant_struct: InstId,
965 right: InstId,
966) -> bool {
967 if name_str(ir, right) == Some("/any") {
974 return true;
975 }
976 if is_struct_type(ir, right) {
978 return set_conforms(ir, ctx, variant_struct, right);
980 }
981 if is_tagged_union_type(ir, right) {
985 if let Some((r_tags, r_structs)) = tagged_union_variants(ir, right) {
986 let r_tag_field = tagged_union_tag_field(ir, right).unwrap();
987 if !ir_eq(ir, tag_field, r_tag_field) {
988 return false;
989 }
990 return r_tags.iter().zip(r_structs.iter()).any(|(rt, rs)| {
991 ir_eq(ir, tag, *rt) && set_conforms(ir, ctx, variant_struct, *rs)
992 });
993 }
994 return false;
995 }
996 if let Some(alts) = union_type_args(ir, right) {
998 let alts = alts.to_vec();
999 return alts.iter().any(|alt| {
1000 expanded_variant_conforms(ir, ctx, tag_field, tag, variant_struct, *alt)
1001 });
1002 }
1003 false
1004}
1005
1006fn expanded_variant_conforms_right(
1008 ir: &Ir,
1009 ctx: &TypeContext,
1010 left: InstId,
1011 _tag_field: InstId,
1012 _tag: InstId,
1013 variant_struct: InstId,
1014) -> bool {
1015 set_conforms(ir, ctx, left, variant_struct)
1018}
1019
1020fn const_has_base_type(ir: &Ir, const_id: InstId, type_id: InstId) -> bool {
1022 let type_name = match name_str(ir, type_id) {
1023 Some(n) => n,
1024 None => return false,
1025 };
1026 match ir.get(const_id) {
1027 Inst::Number(_) => type_name == "/number" || type_name == "/any",
1028 Inst::Float(_) => type_name == "/float64" || type_name == "/any",
1029 Inst::String(_) => type_name == "/string" || type_name == "/any",
1030 Inst::Bool(_) => type_name == "/bool" || type_name == "/any",
1031 Inst::Time(_) => type_name == "/time" || type_name == "/any",
1032 Inst::Duration(_) => type_name == "/duration" || type_name == "/any",
1033 Inst::Bytes(_) => type_name == "/bytes" || type_name == "/any",
1034 Inst::Name(n) => {
1035 if type_name == "/name" || type_name == "/any" {
1036 return true;
1037 }
1038 let name = ir.resolve_name(*n);
1040 name.starts_with(type_name)
1041 && (name.len() == type_name.len()
1042 || name.as_bytes().get(type_name.len()) == Some(&b'/'))
1043 }
1044 _ => false,
1045 }
1046}
1047
1048fn ir_eq(ir: &Ir, a: InstId, b: InstId) -> bool {
1050 if a == b {
1051 return true;
1052 }
1053 match (ir.get(a), ir.get(b)) {
1054 (Inst::Name(na), Inst::Name(nb)) => na == nb,
1055 (Inst::Number(na), Inst::Number(nb)) => na == nb,
1056 (Inst::Float(fa), Inst::Float(fb)) => fa.to_bits() == fb.to_bits(),
1057 (Inst::String(sa), Inst::String(sb)) => sa == sb,
1058 (Inst::Bool(ba), Inst::Bool(bb)) => ba == bb,
1059 _ => false,
1060 }
1061}
1062
1063pub fn upper_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1072 let mut worklist = Vec::new();
1073 for &te in type_exprs {
1074 if is_any(ir, te) {
1075 return find_or_create_name(ir, "/any");
1076 }
1077 if let Some(args) = union_type_args(ir, te) {
1078 let args = args.to_vec();
1079 worklist.extend(args);
1080 } else {
1081 worklist.push(te);
1082 }
1083 }
1084 if worklist.is_empty() {
1085 return empty_type(ir);
1086 }
1087 let mut reduced = vec![worklist[0]];
1088 'outer: for &te in &worklist[1..] {
1089 for i in 0..reduced.len() {
1090 if set_conforms(ir, ctx, te, reduced[i]) {
1091 continue 'outer;
1092 }
1093 if set_conforms(ir, ctx, reduced[i], te) {
1094 reduced[i] = te;
1095 continue 'outer;
1096 }
1097 }
1098 reduced.push(te);
1099 }
1100 if reduced.len() == 1 {
1101 return reduced[0];
1102 }
1103 new_union_or_single(ir, reduced)
1104}
1105
1106pub fn intersect_type(ir: &mut Ir, ctx: &TypeContext, a: InstId, b: InstId) -> InstId {
1108 if ir_eq(ir, a, b) {
1109 return a;
1110 }
1111 if is_any(ir, a) {
1112 return b;
1113 }
1114 if is_any(ir, b) {
1115 return a;
1116 }
1117 if let Inst::Var(v) = ir.get(a) {
1119 let v = *v;
1120 if let Some(&bound) = ctx.get(&v) {
1121 return intersect_type(ir, ctx, bound, b);
1122 }
1123 return empty_type(ir);
1124 }
1125 if let Inst::Var(v) = ir.get(b) {
1126 let v = *v;
1127 if let Some(&bound) = ctx.get(&v) {
1128 return intersect_type(ir, ctx, a, bound);
1129 }
1130 return empty_type(ir);
1131 }
1132 if set_conforms(ir, ctx, a, b) {
1133 return a;
1134 }
1135 if set_conforms(ir, ctx, b, a) {
1136 return b;
1137 }
1138 if is_union_type(ir, a) {
1140 let args = union_type_args(ir, a).unwrap().to_vec();
1141 let mut res = Vec::new();
1142 for elem in args {
1143 let u = intersect_type(ir, ctx, elem, b);
1144 if !is_empty_type(ir, u) {
1145 res.push(u);
1146 }
1147 }
1148 return upper_bound(ir, ctx, &res);
1149 }
1150 if is_union_type(ir, b) {
1152 let args = union_type_args(ir, b).unwrap().to_vec();
1153 let mut res = Vec::new();
1154 for elem in args {
1155 if set_conforms(ir, ctx, a, elem) {
1156 res.push(a);
1157 } else if set_conforms(ir, ctx, elem, a) {
1158 res.push(elem);
1159 }
1160 }
1161 return upper_bound(ir, ctx, &res);
1162 }
1163 empty_type(ir)
1164}
1165
1166pub fn lower_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1170 let any = find_or_create_name(ir, "/any");
1171 let mut result = any;
1172 for &t in type_exprs {
1173 result = intersect_type(ir, ctx, result, t);
1174 if is_empty_type(ir, result) {
1175 return result;
1176 }
1177 }
1178 result
1179}
1180
1181pub fn has_type(ir: &Ir, type_expr: InstId, value: InstId) -> bool {
1191 if let Inst::Name(_) = ir.get(type_expr) {
1193 return const_has_base_type(ir, value, type_expr);
1194 }
1195
1196 if let Inst::Var(_) = ir.get(type_expr) {
1198 return true;
1199 }
1200
1201 let fname = match apply_fn_name(ir, type_expr) {
1202 Some(n) => n,
1203 None => return false,
1204 };
1205 let args = apply_fn_args(ir, type_expr).unwrap();
1206
1207 match fname {
1208 FN_SINGLETON => {
1209 args.len() == 1 && ir_eq(ir, value, args[0])
1210 }
1211
1212 FN_UNION => args.iter().any(|alt| has_type(ir, *alt, value)),
1213
1214 FN_TAGGED_UNION => {
1215 if let Some((tags, structs)) = tagged_union_variants(ir, type_expr) {
1217 let tag_field = tagged_union_tag_field(ir, type_expr).unwrap();
1218 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
1219 if has_type_tagged_variant(ir, tag_field, *tag, *variant_struct, value) {
1220 return true;
1221 }
1222 }
1223 }
1224 false
1225 }
1226
1227 FN_LIST => {
1228 if args.len() != 1 {
1229 return false;
1230 }
1231 match list_value_elems(ir, value) {
1232 Some(elems) => elems.iter().all(|e| has_type(ir, args[0], *e)),
1233 None => false,
1234 }
1235 }
1236
1237 FN_MAP => {
1238 if args.len() != 2 {
1239 return false;
1240 }
1241 match map_value_entries(ir, value) {
1242 Some((keys, values)) => {
1243 keys.iter().all(|k| has_type(ir, args[0], *k))
1244 && values.iter().all(|v| has_type(ir, args[1], *v))
1245 }
1246 None => false,
1247 }
1248 }
1249
1250 FN_PAIR => {
1251 if args.len() != 2 {
1252 return false;
1253 }
1254 match list_value_elems(ir, value) {
1255 Some(elems) if elems.len() == 2 => {
1256 has_type(ir, args[0], elems[0]) && has_type(ir, args[1], elems[1])
1257 }
1258 _ => false,
1259 }
1260 }
1261
1262 FN_STRUCT => has_type_struct(ir, type_expr, value),
1263
1264 FN_OPTION => {
1265 if args.len() != 1 {
1266 return false;
1267 }
1268 if let Some(n) = name_str(ir, value) {
1270 if n == "/unit" {
1271 return true;
1272 }
1273 }
1274 has_type(ir, args[0], value)
1275 }
1276
1277 _ => false,
1278 }
1279}
1280
1281fn list_value_elems(ir: &Ir, value: InstId) -> Option<Vec<InstId>> {
1293 match ir.get(value) {
1294 Inst::List(elems) => Some(elems.clone()),
1295 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:list" => {
1296 Some(args.clone())
1297 }
1298 _ => None,
1299 }
1300}
1301
1302fn map_value_entries(ir: &Ir, value: InstId) -> Option<(Vec<InstId>, Vec<InstId>)> {
1305 match ir.get(value) {
1306 Inst::Map { keys, values } => Some((keys.clone(), values.clone())),
1307 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:map" => {
1308 let args = args.clone();
1309 if args.len() % 2 != 0 {
1310 return None;
1311 }
1312 let mut keys = Vec::with_capacity(args.len() / 2);
1313 let mut values = Vec::with_capacity(args.len() / 2);
1314 for pair in args.chunks_exact(2) {
1315 keys.push(pair[0]);
1316 values.push(pair[1]);
1317 }
1318 Some((keys, values))
1319 }
1320 _ => None,
1321 }
1322}
1323
1324fn struct_value_fields(ir: &Ir, value: InstId) -> Option<(Vec<NameId>, Vec<InstId>)> {
1326 match ir.get(value) {
1327 Inst::Struct { fields, values } => Some((fields.clone(), values.clone())),
1328 Inst::ApplyFn { function, args } if ir.resolve_name(*function) == "fn:struct" => {
1329 let args = args.clone();
1330 if args.len() % 2 != 0 {
1331 return None;
1332 }
1333 let mut fields = Vec::with_capacity(args.len() / 2);
1334 let mut values = Vec::with_capacity(args.len() / 2);
1335 for pair in args.chunks_exact(2) {
1336 match ir.get(pair[0]) {
1337 Inst::Name(n) => fields.push(*n),
1338 _ => return None,
1339 }
1340 values.push(pair[1]);
1341 }
1342 Some((fields, values))
1343 }
1344 _ => None,
1345 }
1346}
1347
1348fn has_type_struct(ir: &Ir, type_id: InstId, value: InstId) -> bool {
1350 let type_fields = struct_type_fields(ir, type_id);
1351 let (vfields, vvalues) = match struct_value_fields(ir, value) {
1352 Some(pair) => pair,
1353 None => return false,
1354 };
1355
1356 let value_map: FxHashMap<String, InstId> = vfields
1358 .iter()
1359 .zip(vvalues.iter())
1360 .map(|(f, v)| (ir.resolve_name(*f).to_string(), *v))
1361 .collect();
1362
1363 for (fname_id, ftype_id, is_optional) in &type_fields {
1365 if let Some(fname) = name_str(ir, *fname_id) {
1366 match value_map.get(fname) {
1367 Some(val_id) => {
1368 if !has_type(ir, *ftype_id, *val_id) {
1369 return false;
1370 }
1371 }
1372 None => {
1373 if !is_optional {
1374 return false;
1375 }
1376 }
1377 }
1378 }
1379 }
1380
1381 let type_field_names: FxHashSet<String> = type_fields
1383 .iter()
1384 .filter_map(|(f, _, _)| name_str(ir, *f).map(|s| s.to_string()))
1385 .collect();
1386 for vf in &vfields {
1387 let vf_name = ir.resolve_name(*vf);
1388 if !type_field_names.contains(vf_name) {
1389 return false;
1390 }
1391 }
1392
1393 true
1394}
1395
1396fn has_type_tagged_variant(
1398 ir: &Ir,
1399 tag_field: InstId,
1400 tag: InstId,
1401 variant_struct: InstId,
1402 value: InstId,
1403) -> bool {
1404 let (vfields, vvalues) = match struct_value_fields(ir, value) {
1405 Some(pair) => pair,
1406 None => return false,
1407 };
1408
1409 let tag_field_name = match name_str(ir, tag_field) {
1411 Some(n) => n,
1412 None => return false,
1413 };
1414
1415 let tag_idx = vfields
1416 .iter()
1417 .position(|f| ir.resolve_name(*f) == tag_field_name);
1418 match tag_idx {
1419 Some(idx) => {
1420 if !ir_eq(ir, vvalues[idx], tag) {
1421 return false;
1422 }
1423 }
1424 None => return false,
1425 }
1426
1427 let type_fields = struct_type_fields(ir, variant_struct);
1429
1430 let value_map: FxHashMap<String, InstId> = vfields
1432 .iter()
1433 .zip(vvalues.iter())
1434 .filter(|(f, _)| ir.resolve_name(**f) != tag_field_name)
1435 .map(|(f, v)| (ir.resolve_name(*f).to_string(), *v))
1436 .collect();
1437
1438 for (fname_id, ftype_id, is_optional) in &type_fields {
1440 if let Some(fname) = name_str(ir, *fname_id) {
1441 match value_map.get(fname) {
1442 Some(val_id) => {
1443 if !has_type(ir, *ftype_id, *val_id) {
1444 return false;
1445 }
1446 }
1447 None => {
1448 if !is_optional {
1449 return false;
1450 }
1451 }
1452 }
1453 }
1454 }
1455
1456 let type_field_names: FxHashSet<String> = type_fields
1458 .iter()
1459 .filter_map(|(f, _, _)| name_str(ir, *f).map(|s| s.to_string()))
1460 .collect();
1461 for vf in &vfields {
1462 let name = ir.resolve_name(*vf);
1463 if name != tag_field_name && !type_field_names.contains(name) {
1464 return false;
1465 }
1466 }
1467
1468 true
1469}
1470
1471pub fn new_rel_type(ir: &mut Ir, arg_types: &[InstId]) -> InstId {
1477 let rel_name = ir.intern_name(FN_REL);
1478 ir.add_inst(Inst::ApplyFn {
1479 function: rel_name,
1480 args: arg_types.to_vec(),
1481 })
1482}
1483
1484pub fn new_union_or_single(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1486 if alts.len() == 1 {
1487 return alts[0];
1488 }
1489 let union_name = ir.intern_name(FN_UNION);
1490 ir.add_inst(Inst::ApplyFn {
1491 function: union_name,
1492 args: alts,
1493 })
1494}
1495
1496pub fn rel_type_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
1498 if apply_fn_name(ir, id) != Some(FN_REL) {
1499 return None;
1500 }
1501 apply_fn_args(ir, id)
1502}
1503
1504pub fn rel_types_from_decl(ir: &mut Ir, bounds: &[InstId]) -> Vec<InstId> {
1507 bounds
1508 .iter()
1509 .filter_map(|bound_id| {
1510 if let Inst::BoundDecl { base_terms } = ir.get(*bound_id) {
1511 let terms = base_terms.clone();
1512 Some(new_rel_type(ir, &terms))
1513 } else {
1514 None
1515 }
1516 })
1517 .collect()
1518}
1519
1520pub fn get_type_context(ir: &Ir, id: InstId) -> TypeContext {
1523 let mut ctx = TypeContext::default();
1524 collect_type_vars(ir, id, &mut ctx);
1525 ctx
1526}
1527
1528fn collect_type_vars(ir: &Ir, id: InstId, ctx: &mut TypeContext) {
1529 match ir.get(id) {
1530 Inst::Var(v) => {
1531 if !ctx.contains_key(v) {
1532 ctx.insert(*v, id);
1536 }
1537 }
1538 Inst::ApplyFn { args, .. } => {
1539 for arg in args {
1540 collect_type_vars(ir, *arg, ctx);
1541 }
1542 }
1543 _ => {}
1544 }
1545}
1546
1547pub fn rel_type_alternatives(ir: &Ir, id: InstId) -> Vec<InstId> {
1552 if let Some(args) = union_type_args(ir, id) {
1553 args.to_vec()
1554 } else {
1555 vec![id]
1556 }
1557}
1558
1559pub fn rel_type_from_alternatives(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1561 if alts.is_empty() {
1562 return empty_type(ir);
1563 }
1564 new_union_or_single(ir, alts)
1565}
1566
1567pub fn remove_from_union_type(ir: &mut Ir, to_remove: InstId, union_type: InstId) -> InstId {
1570 let ctx = TypeContext::default();
1571 if let Some(args) = union_type_args(ir, union_type) {
1572 let args = args.to_vec();
1573 let remaining: Vec<InstId> = args
1574 .into_iter()
1575 .filter(|alt| !set_conforms(ir, &ctx, *alt, to_remove))
1576 .collect();
1577 if remaining.is_empty() {
1578 return empty_type(ir);
1579 }
1580 return new_union_or_single(ir, remaining);
1581 }
1582 if set_conforms(ir, &ctx, union_type, to_remove) {
1584 return empty_type(ir);
1585 }
1586 union_type
1587}
1588
1589pub fn new_list_type(ir: &mut Ir, elem: InstId) -> InstId {
1591 let name = ir.intern_name(FN_LIST);
1592 ir.add_inst(Inst::ApplyFn {
1593 function: name,
1594 args: vec![elem],
1595 })
1596}
1597
1598pub fn new_map_type(ir: &mut Ir, key: InstId, val: InstId) -> InstId {
1600 let name = ir.intern_name(FN_MAP);
1601 ir.add_inst(Inst::ApplyFn {
1602 function: name,
1603 args: vec![key, val],
1604 })
1605}
1606
1607pub fn new_struct_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1609 let name = ir.intern_name(FN_STRUCT);
1610 ir.add_inst(Inst::ApplyFn {
1611 function: name,
1612 args,
1613 })
1614}
1615
1616pub fn new_tuple_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1618 let name = ir.intern_name(FN_TUPLE);
1619 ir.add_inst(Inst::ApplyFn {
1620 function: name,
1621 args,
1622 })
1623}
1624
1625pub fn apply_subst(ir: &mut Ir, id: InstId, subst: &FxHashMap<NameId, InstId>) -> InstId {
1628 if subst.is_empty() {
1629 return id;
1630 }
1631 match ir.get(id) {
1632 Inst::Var(v) => {
1633 let v = *v;
1634 if let Some(&replacement) = subst.get(&v) {
1635 replacement
1636 } else {
1637 id
1638 }
1639 }
1640 Inst::ApplyFn { function, args } => {
1641 let function = *function;
1642 let args = args.clone();
1643 let new_args: Vec<InstId> = args
1644 .iter()
1645 .map(|a| apply_subst(ir, *a, subst))
1646 .collect();
1647 if new_args == args {
1648 return id;
1649 }
1650 ir.add_inst(Inst::ApplyFn {
1651 function,
1652 args: new_args,
1653 })
1654 }
1655 _ => id,
1656 }
1657}
1658
1659pub fn collect_vars(ir: &Ir, id: InstId, vars: &mut FxHashSet<NameId>) {
1661 match ir.get(id) {
1662 Inst::Var(v) => {
1663 vars.insert(*v);
1664 }
1665 Inst::ApplyFn { args, .. } => {
1666 for arg in args {
1667 collect_vars(ir, *arg, vars);
1668 }
1669 }
1670 _ => {}
1671 }
1672}
1673
1674#[cfg(test)]
1679mod tests {
1680 use super::*;
1681
1682 fn make_name(ir: &mut Ir, s: &str) -> InstId {
1683 let n = ir.intern_name(s);
1684 ir.add_inst(Inst::Name(n))
1685 }
1686
1687 fn make_apply(ir: &mut Ir, fn_name: &str, args: Vec<InstId>) -> InstId {
1688 let n = ir.intern_name(fn_name);
1689 ir.add_inst(Inst::ApplyFn {
1690 function: n,
1691 args,
1692 })
1693 }
1694
1695 #[test]
1698 fn wellformed_base_types() {
1699 let mut ir = Ir::new();
1700 let ctx = TypeContext::default();
1701 let num = make_name(&mut ir, "/number");
1702 let str_ = make_name(&mut ir, "/string");
1703 let any = make_name(&mut ir, "/any");
1704 assert!(wellformed_type(&ir, &ctx, num).is_ok());
1705 assert!(wellformed_type(&ir, &ctx, str_).is_ok());
1706 assert!(wellformed_type(&ir, &ctx, any).is_ok());
1707 }
1708
1709 #[test]
1710 fn wellformed_struct() {
1711 let mut ir = Ir::new();
1712 let ctx = TypeContext::default();
1713 let x = make_name(&mut ir, "/x");
1714 let num = make_name(&mut ir, "/number");
1715 let y = make_name(&mut ir, "/y");
1716 let str_ = make_name(&mut ir, "/string");
1717 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1718 assert!(wellformed_type(&ir, &ctx, s).is_ok());
1719 }
1720
1721 #[test]
1722 fn wellformed_struct_odd_args() {
1723 let mut ir = Ir::new();
1724 let ctx = TypeContext::default();
1725 let x = make_name(&mut ir, "/x");
1726 let num = make_name(&mut ir, "/number");
1727 let y = make_name(&mut ir, "/y");
1728 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y]);
1730 assert!(wellformed_type(&ir, &ctx, s).is_err());
1731 }
1732
1733 #[test]
1734 fn wellformed_tagged_union() {
1735 let mut ir = Ir::new();
1736 let ctx = TypeContext::default();
1737 let kind = make_name(&mut ir, "/kind");
1738 let move_ = make_name(&mut ir, "/move");
1739 let x = make_name(&mut ir, "/x");
1740 let num = make_name(&mut ir, "/number");
1741 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
1742 let quit = make_name(&mut ir, "/quit");
1743 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1744 let tu = make_apply(
1745 &mut ir,
1746 FN_TAGGED_UNION,
1747 vec![kind, move_, move_struct, quit, quit_struct],
1748 );
1749 assert!(wellformed_type(&ir, &ctx, tu).is_ok());
1750 }
1751
1752 #[test]
1753 fn wellformed_tagged_union_negative_too_few_args() {
1754 let mut ir = Ir::new();
1755 let ctx = TypeContext::default();
1756 let kind = make_name(&mut ir, "/kind");
1757 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind]);
1758 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1759 }
1760
1761 #[test]
1762 fn wellformed_tagged_union_negative_even_args() {
1763 let mut ir = Ir::new();
1764 let ctx = TypeContext::default();
1765 let kind = make_name(&mut ir, "/kind");
1766 let move_ = make_name(&mut ir, "/move");
1767 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1768 let quit = make_name(&mut ir, "/quit");
1769 let tu = make_apply(
1771 &mut ir,
1772 FN_TAGGED_UNION,
1773 vec![kind, move_, move_struct, quit],
1774 );
1775 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1776 }
1777
1778 #[test]
1779 fn wellformed_tagged_union_negative_dup_tags() {
1780 let mut ir = Ir::new();
1781 let ctx = TypeContext::default();
1782 let kind = make_name(&mut ir, "/kind");
1783 let move_ = make_name(&mut ir, "/move");
1784 let s1 = make_apply(&mut ir, FN_STRUCT, vec![]);
1785 let move2 = make_name(&mut ir, "/move");
1786 let s2 = make_apply(&mut ir, FN_STRUCT, vec![]);
1787 let tu = make_apply(
1788 &mut ir,
1789 FN_TAGGED_UNION,
1790 vec![kind, move_, s1, move2, s2],
1791 );
1792 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1793 }
1794
1795 #[test]
1796 fn wellformed_tagged_union_negative_tag_field_in_variant() {
1797 let mut ir = Ir::new();
1798 let ctx = TypeContext::default();
1799 let kind = make_name(&mut ir, "/kind");
1800 let move_ = make_name(&mut ir, "/move");
1801 let kind2 = make_name(&mut ir, "/kind");
1803 let num = make_name(&mut ir, "/number");
1804 let s = make_apply(&mut ir, FN_STRUCT, vec![kind2, num]);
1805 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind, move_, s]);
1806 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1807 }
1808
1809 #[test]
1812 fn conforms_base_types() {
1813 let mut ir = Ir::new();
1814 let ctx = TypeContext::default();
1815 let num = make_name(&mut ir, "/number");
1816 let any = make_name(&mut ir, "/any");
1817 let str_ = make_name(&mut ir, "/string");
1818
1819 assert!(set_conforms(&ir, &ctx, num, any));
1820 assert!(set_conforms(&ir, &ctx, num, num));
1821 assert!(!set_conforms(&ir, &ctx, num, str_));
1822 }
1823
1824 #[test]
1825 fn conforms_name_hierarchy() {
1826 let mut ir = Ir::new();
1827 let ctx = TypeContext::default();
1828 let foo = make_name(&mut ir, "/foo");
1829 let foo_bar = make_name(&mut ir, "/foo/bar");
1830 let name = make_name(&mut ir, "/name");
1831
1832 assert!(set_conforms(&ir, &ctx, foo_bar, foo));
1833 assert!(set_conforms(&ir, &ctx, foo, name));
1834 assert!(set_conforms(&ir, &ctx, foo_bar, name));
1835 assert!(!set_conforms(&ir, &ctx, foo, foo_bar));
1836 }
1837
1838 #[test]
1839 fn conforms_singleton() {
1840 let mut ir = Ir::new();
1841 let ctx = TypeContext::default();
1842 let move_ = make_name(&mut ir, "/move");
1843 let singleton = make_apply(&mut ir, FN_SINGLETON, vec![move_]);
1844 let name = make_name(&mut ir, "/name");
1845 let num = make_name(&mut ir, "/number");
1846
1847 assert!(set_conforms(&ir, &ctx, singleton, name));
1848 assert!(!set_conforms(&ir, &ctx, singleton, num));
1849 }
1850
1851 #[test]
1852 fn conforms_union() {
1853 let mut ir = Ir::new();
1854 let ctx = TypeContext::default();
1855 let num = make_name(&mut ir, "/number");
1856 let str_ = make_name(&mut ir, "/string");
1857 let union = make_apply(&mut ir, FN_UNION, vec![num, str_]);
1858
1859 let any = make_name(&mut ir, "/any");
1861 assert!(set_conforms(&ir, &ctx, union, any));
1862 assert!(set_conforms(&ir, &ctx, num, union));
1864 assert!(!set_conforms(&ir, &ctx, union, num));
1866 }
1867
1868 #[test]
1869 fn conforms_struct_subtyping() {
1870 let mut ir = Ir::new();
1871 let ctx = TypeContext::default();
1872 let x = make_name(&mut ir, "/x");
1874 let num = make_name(&mut ir, "/number");
1875 let y = make_name(&mut ir, "/y");
1876 let str_ = make_name(&mut ir, "/string");
1877 let wider = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1878 let x2 = make_name(&mut ir, "/x");
1880 let num2 = make_name(&mut ir, "/number");
1881 let narrower = make_apply(&mut ir, FN_STRUCT, vec![x2, num2]);
1882
1883 assert!(set_conforms(&ir, &ctx, wider, narrower));
1885 assert!(!set_conforms(&ir, &ctx, narrower, wider));
1887 }
1888
1889 #[test]
1890 fn conforms_list() {
1891 let mut ir = Ir::new();
1892 let ctx = TypeContext::default();
1893 let num = make_name(&mut ir, "/number");
1894 let any = make_name(&mut ir, "/any");
1895 let list_num = make_apply(&mut ir, FN_LIST, vec![num]);
1896 let list_any = make_apply(&mut ir, FN_LIST, vec![any]);
1897
1898 assert!(set_conforms(&ir, &ctx, list_num, list_any));
1899 assert!(!set_conforms(&ir, &ctx, list_any, list_num));
1900 }
1901
1902 #[test]
1905 fn has_type_base() {
1906 let mut ir = Ir::new();
1907 let num_type = make_name(&mut ir, "/number");
1908 let str_type = make_name(&mut ir, "/string");
1909 let val_42 = ir.add_inst(Inst::Number(42));
1910 let val_hello = {
1911 let s = ir.intern_string("hello");
1912 ir.add_inst(Inst::String(s))
1913 };
1914
1915 assert!(has_type(&ir, num_type, val_42));
1916 assert!(!has_type(&ir, str_type, val_42));
1917 assert!(has_type(&ir, str_type, val_hello));
1918 }
1919
1920 #[test]
1921 fn has_type_struct() {
1922 let mut ir = Ir::new();
1923 let x_name = make_name(&mut ir, "/x");
1925 let num_type = make_name(&mut ir, "/number");
1926 let struct_type = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1927
1928 let x_field = ir.intern_name("/x");
1930 let val_42 = ir.add_inst(Inst::Number(42));
1931 let val_struct = ir.add_inst(Inst::Struct {
1932 fields: vec![x_field],
1933 values: vec![val_42],
1934 });
1935
1936 assert!(has_type(&ir, struct_type, val_struct));
1937 }
1938
1939 #[test]
1940 fn has_type_tagged_union() {
1941 let mut ir = Ir::new();
1942
1943 let kind = make_name(&mut ir, "/kind");
1945 let move_tag = make_name(&mut ir, "/move");
1946 let x_name = make_name(&mut ir, "/x");
1947 let num_type = make_name(&mut ir, "/number");
1948 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1949 let quit_tag = make_name(&mut ir, "/quit");
1950 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1951 let tu = make_apply(
1952 &mut ir,
1953 FN_TAGGED_UNION,
1954 vec![kind, move_tag, move_struct, quit_tag, quit_struct],
1955 );
1956
1957 let kind_f = ir.intern_name("/kind");
1959 let move_v = ir.intern_name("/move");
1960 let move_val = ir.add_inst(Inst::Name(move_v));
1961 let x_f = ir.intern_name("/x");
1962 let val_10 = ir.add_inst(Inst::Number(10));
1963 let val_move = ir.add_inst(Inst::Struct {
1964 fields: vec![kind_f, x_f],
1965 values: vec![move_val, val_10],
1966 });
1967 assert!(has_type(&ir, tu, val_move));
1968
1969 let kind_f2 = ir.intern_name("/kind");
1971 let quit_v = ir.intern_name("/quit");
1972 let quit_val = ir.add_inst(Inst::Name(quit_v));
1973 let val_quit = ir.add_inst(Inst::Struct {
1974 fields: vec![kind_f2],
1975 values: vec![quit_val],
1976 });
1977 assert!(has_type(&ir, tu, val_quit));
1978
1979 let kind_f3 = ir.intern_name("/kind");
1981 let bad_v = ir.intern_name("/bad");
1982 let bad_val = ir.add_inst(Inst::Name(bad_v));
1983 let val_bad = ir.add_inst(Inst::Struct {
1984 fields: vec![kind_f3],
1985 values: vec![bad_val],
1986 });
1987 assert!(!has_type(&ir, tu, val_bad));
1988 }
1989
1990 #[test]
1991 fn has_type_struct_accepts_applyfn_shape() {
1992 let mut ir = Ir::new();
1995 let x = make_name(&mut ir, "/x");
1996 let y = make_name(&mut ir, "/y");
1997 let num = make_name(&mut ir, "/number");
1998 let str_ = make_name(&mut ir, "/string");
1999 let stype = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
2000
2001 let x_v = make_name(&mut ir, "/x");
2002 let y_v = make_name(&mut ir, "/y");
2003 let n = ir.add_inst(Inst::Number(42));
2004 let sid = ir.intern_string("hi");
2005 let s = ir.add_inst(Inst::String(sid));
2006 let value = make_apply(&mut ir, "fn:struct", vec![x_v, n, y_v, s]);
2007 assert!(has_type(&ir, stype, value));
2008 }
2009
2010 #[test]
2011 fn has_type_list_accepts_applyfn_shape() {
2012 let mut ir = Ir::new();
2014 let num = make_name(&mut ir, "/number");
2015 let list_num = make_apply(&mut ir, FN_LIST, vec![num]);
2016 let n1 = ir.add_inst(Inst::Number(1));
2017 let n2 = ir.add_inst(Inst::Number(2));
2018 let n3 = ir.add_inst(Inst::Number(3));
2019 let value = make_apply(&mut ir, "fn:list", vec![n1, n2, n3]);
2020 assert!(has_type(&ir, list_num, value));
2021 }
2022
2023 #[test]
2024 fn conforms_tagged_union_identity() {
2025 let mut ir = Ir::new();
2028 let ctx = TypeContext::default();
2029 let build = |ir: &mut Ir| {
2030 let t = make_name(ir, "/type");
2031 let c = make_name(ir, "/create");
2032 let name_f = make_name(ir, "/name");
2033 let str_ = make_name(ir, "/string");
2034 let create_s = make_apply(ir, FN_STRUCT, vec![name_f, str_]);
2035 let p = make_name(ir, "/ping");
2036 let ping_s = make_apply(ir, FN_STRUCT, vec![]);
2037 make_apply(ir, FN_TAGGED_UNION, vec![t, c, create_s, p, ping_s])
2038 };
2039 let tu1 = build(&mut ir);
2040 let tu2 = build(&mut ir);
2041 assert!(set_conforms(&ir, &ctx, tu1, tu2));
2042 }
2043
2044 #[test]
2047 fn expand_tagged_union() {
2048 let mut ir = Ir::new();
2049 let kind = make_name(&mut ir, "/kind");
2050 let move_ = make_name(&mut ir, "/move");
2051 let x = make_name(&mut ir, "/x");
2052 let num = make_name(&mut ir, "/number");
2053 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
2054 let quit = make_name(&mut ir, "/quit");
2055 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
2056 let tu = make_apply(
2057 &mut ir,
2058 FN_TAGGED_UNION,
2059 vec![kind, move_, move_struct, quit, quit_struct],
2060 );
2061
2062 let expanded = expand_tagged_union_type(&mut ir, tu).unwrap();
2063 assert!(is_union_type(&ir, expanded));
2064 let alts = union_type_args(&ir, expanded).unwrap();
2065 assert_eq!(alts.len(), 2);
2066 assert!(is_struct_type(&ir, alts[0]));
2068 assert!(is_struct_type(&ir, alts[1]));
2070 }
2071
2072 #[test]
2075 fn upper_bound_basic() {
2076 let mut ir = Ir::new();
2077 let ctx = TypeContext::default();
2078 let num = make_name(&mut ir, "/number");
2079 let str_ = make_name(&mut ir, "/string");
2080
2081 assert_eq!(upper_bound(&mut ir, &ctx, &[num]), num);
2083 let ub = upper_bound(&mut ir, &ctx, &[num, str_]);
2085 assert!(is_union_type(&ir, ub));
2086 let ub_empty = upper_bound(&mut ir, &ctx, &[]);
2088 assert!(is_empty_type(&ir, ub_empty));
2089 }
2090
2091 #[test]
2092 fn upper_bound_eliminates_redundant() {
2093 let mut ir = Ir::new();
2094 let ctx = TypeContext::default();
2095 let num = make_name(&mut ir, "/number");
2096 let any = make_name(&mut ir, "/any");
2097
2098 let ub = upper_bound(&mut ir, &ctx, &[num, any]);
2100 assert!(is_any(&ir, ub));
2101 }
2102
2103 #[test]
2104 fn upper_bound_name_hierarchy() {
2105 let mut ir = Ir::new();
2106 let ctx = TypeContext::default();
2107 let animal = make_name(&mut ir, "/animal");
2108 let animal_dog = make_name(&mut ir, "/animal/dog");
2109
2110 let ub = upper_bound(&mut ir, &ctx, &[animal, animal_dog]);
2112 assert_eq!(ub, animal);
2113 }
2114
2115 #[test]
2116 fn lower_bound_basic() {
2117 let mut ir = Ir::new();
2118 let ctx = TypeContext::default();
2119 let num = make_name(&mut ir, "/number");
2120 let any = make_name(&mut ir, "/any");
2121
2122 let lb = lower_bound(&mut ir, &ctx, &[num, any]);
2124 assert_eq!(lb, num);
2125 }
2126
2127 #[test]
2128 fn lower_bound_empty() {
2129 let mut ir = Ir::new();
2130 let ctx = TypeContext::default();
2131 let num = make_name(&mut ir, "/number");
2132 let str_ = make_name(&mut ir, "/string");
2133
2134 let lb = lower_bound(&mut ir, &ctx, &[num, str_]);
2136 assert!(is_empty_type(&ir, lb));
2137 }
2138
2139 #[test]
2140 fn intersect_type_with_union() {
2141 let mut ir = Ir::new();
2142 let ctx = TypeContext::default();
2143 let num = make_name(&mut ir, "/number");
2144 let str_ = make_name(&mut ir, "/string");
2145 let union_ns = make_apply(&mut ir, FN_UNION, vec![num, str_]);
2146
2147 let result = intersect_type(&mut ir, &ctx, union_ns, num);
2149 assert_eq!(result, num);
2150 }
2151}