1use anyhow::{Result, anyhow, bail};
28use fxhash::{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
46fn 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
59fn 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 let Some(alts) = union_type_args(ir, right) {
983 let alts = alts.to_vec();
984 return alts.iter().any(|alt| {
985 expanded_variant_conforms(ir, ctx, _tag_field, _tag, variant_struct, *alt)
986 });
987 }
988 false
989}
990
991fn expanded_variant_conforms_right(
993 ir: &Ir,
994 ctx: &TypeContext,
995 left: InstId,
996 _tag_field: InstId,
997 _tag: InstId,
998 variant_struct: InstId,
999) -> bool {
1000 set_conforms(ir, ctx, left, variant_struct)
1003}
1004
1005fn const_has_base_type(ir: &Ir, const_id: InstId, type_id: InstId) -> bool {
1007 let type_name = match name_str(ir, type_id) {
1008 Some(n) => n,
1009 None => return false,
1010 };
1011 match ir.get(const_id) {
1012 Inst::Number(_) => type_name == "/number" || type_name == "/any",
1013 Inst::Float(_) => type_name == "/float64" || type_name == "/any",
1014 Inst::String(_) => type_name == "/string" || type_name == "/any",
1015 Inst::Bool(_) => type_name == "/bool" || type_name == "/any",
1016 Inst::Time(_) => type_name == "/time" || type_name == "/any",
1017 Inst::Duration(_) => type_name == "/duration" || type_name == "/any",
1018 Inst::Bytes(_) => type_name == "/bytes" || type_name == "/any",
1019 Inst::Name(n) => {
1020 if type_name == "/name" || type_name == "/any" {
1021 return true;
1022 }
1023 let name = ir.resolve_name(*n);
1025 name.starts_with(type_name)
1026 && (name.len() == type_name.len()
1027 || name.as_bytes().get(type_name.len()) == Some(&b'/'))
1028 }
1029 _ => false,
1030 }
1031}
1032
1033fn ir_eq(ir: &Ir, a: InstId, b: InstId) -> bool {
1035 if a == b {
1036 return true;
1037 }
1038 match (ir.get(a), ir.get(b)) {
1039 (Inst::Name(na), Inst::Name(nb)) => na == nb,
1040 (Inst::Number(na), Inst::Number(nb)) => na == nb,
1041 (Inst::Float(fa), Inst::Float(fb)) => fa.to_bits() == fb.to_bits(),
1042 (Inst::String(sa), Inst::String(sb)) => sa == sb,
1043 (Inst::Bool(ba), Inst::Bool(bb)) => ba == bb,
1044 _ => false,
1045 }
1046}
1047
1048pub fn upper_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1057 let mut worklist = Vec::new();
1058 for &te in type_exprs {
1059 if is_any(ir, te) {
1060 return find_or_create_name(ir, "/any");
1061 }
1062 if let Some(args) = union_type_args(ir, te) {
1063 let args = args.to_vec();
1064 worklist.extend(args);
1065 } else {
1066 worklist.push(te);
1067 }
1068 }
1069 if worklist.is_empty() {
1070 return empty_type(ir);
1071 }
1072 let mut reduced = vec![worklist[0]];
1073 'outer: for &te in &worklist[1..] {
1074 for i in 0..reduced.len() {
1075 if set_conforms(ir, ctx, te, reduced[i]) {
1076 continue 'outer;
1077 }
1078 if set_conforms(ir, ctx, reduced[i], te) {
1079 reduced[i] = te;
1080 continue 'outer;
1081 }
1082 }
1083 reduced.push(te);
1084 }
1085 if reduced.len() == 1 {
1086 return reduced[0];
1087 }
1088 new_union_or_single(ir, reduced)
1089}
1090
1091pub fn intersect_type(ir: &mut Ir, ctx: &TypeContext, a: InstId, b: InstId) -> InstId {
1093 if ir_eq(ir, a, b) {
1094 return a;
1095 }
1096 if is_any(ir, a) {
1097 return b;
1098 }
1099 if is_any(ir, b) {
1100 return a;
1101 }
1102 if let Inst::Var(v) = ir.get(a) {
1104 let v = *v;
1105 if let Some(&bound) = ctx.get(&v) {
1106 return intersect_type(ir, ctx, bound, b);
1107 }
1108 return empty_type(ir);
1109 }
1110 if let Inst::Var(v) = ir.get(b) {
1111 let v = *v;
1112 if let Some(&bound) = ctx.get(&v) {
1113 return intersect_type(ir, ctx, a, bound);
1114 }
1115 return empty_type(ir);
1116 }
1117 if set_conforms(ir, ctx, a, b) {
1118 return a;
1119 }
1120 if set_conforms(ir, ctx, b, a) {
1121 return b;
1122 }
1123 if is_union_type(ir, a) {
1125 let args = union_type_args(ir, a).unwrap().to_vec();
1126 let mut res = Vec::new();
1127 for elem in args {
1128 let u = intersect_type(ir, ctx, elem, b);
1129 if !is_empty_type(ir, u) {
1130 res.push(u);
1131 }
1132 }
1133 return upper_bound(ir, ctx, &res);
1134 }
1135 if is_union_type(ir, b) {
1137 let args = union_type_args(ir, b).unwrap().to_vec();
1138 let mut res = Vec::new();
1139 for elem in args {
1140 if set_conforms(ir, ctx, a, elem) {
1141 res.push(a);
1142 } else if set_conforms(ir, ctx, elem, a) {
1143 res.push(elem);
1144 }
1145 }
1146 return upper_bound(ir, ctx, &res);
1147 }
1148 empty_type(ir)
1149}
1150
1151pub fn lower_bound(ir: &mut Ir, ctx: &TypeContext, type_exprs: &[InstId]) -> InstId {
1155 let any = find_or_create_name(ir, "/any");
1156 let mut result = any;
1157 for &t in type_exprs {
1158 result = intersect_type(ir, ctx, result, t);
1159 if is_empty_type(ir, result) {
1160 return result;
1161 }
1162 }
1163 result
1164}
1165
1166pub fn has_type(ir: &Ir, type_expr: InstId, value: InstId) -> bool {
1176 if let Inst::Name(_) = ir.get(type_expr) {
1178 return const_has_base_type(ir, value, type_expr);
1179 }
1180
1181 if let Inst::Var(_) = ir.get(type_expr) {
1183 return true;
1184 }
1185
1186 let fname = match apply_fn_name(ir, type_expr) {
1187 Some(n) => n,
1188 None => return false,
1189 };
1190 let args = apply_fn_args(ir, type_expr).unwrap();
1191
1192 match fname {
1193 FN_SINGLETON => {
1194 args.len() == 1 && ir_eq(ir, value, args[0])
1195 }
1196
1197 FN_UNION => args.iter().any(|alt| has_type(ir, *alt, value)),
1198
1199 FN_TAGGED_UNION => {
1200 if let Some((tags, structs)) = tagged_union_variants(ir, type_expr) {
1202 let tag_field = tagged_union_tag_field(ir, type_expr).unwrap();
1203 for (tag, variant_struct) in tags.iter().zip(structs.iter()) {
1204 if has_type_tagged_variant(ir, tag_field, *tag, *variant_struct, value) {
1205 return true;
1206 }
1207 }
1208 }
1209 false
1210 }
1211
1212 FN_LIST => {
1213 if args.len() != 1 {
1214 return false;
1215 }
1216 match ir.get(value) {
1217 Inst::List(elems) => {
1218 let elems = elems.clone();
1219 elems.iter().all(|e| has_type(ir, args[0], *e))
1220 }
1221 _ => false,
1222 }
1223 }
1224
1225 FN_MAP => {
1226 if args.len() != 2 {
1227 return false;
1228 }
1229 match ir.get(value) {
1230 Inst::Map { keys, values } => {
1231 let keys = keys.clone();
1232 let values = values.clone();
1233 keys.iter().all(|k| has_type(ir, args[0], *k))
1234 && values.iter().all(|v| has_type(ir, args[1], *v))
1235 }
1236 _ => false,
1237 }
1238 }
1239
1240 FN_PAIR => {
1241 if args.len() != 2 {
1242 return false;
1243 }
1244 match ir.get(value) {
1245 Inst::List(elems) if elems.len() == 2 => {
1246 let elems = elems.clone();
1247 has_type(ir, args[0], elems[0]) && has_type(ir, args[1], elems[1])
1248 }
1249 _ => false,
1250 }
1251 }
1252
1253 FN_STRUCT => has_type_struct(ir, type_expr, value),
1254
1255 FN_OPTION => {
1256 if args.len() != 1 {
1257 return false;
1258 }
1259 if let Some(n) = name_str(ir, value) {
1261 if n == "/unit" {
1262 return true;
1263 }
1264 }
1265 has_type(ir, args[0], value)
1266 }
1267
1268 _ => false,
1269 }
1270}
1271
1272fn has_type_struct(ir: &Ir, type_id: InstId, value: InstId) -> bool {
1274 let type_fields = struct_type_fields(ir, type_id);
1275 let value_struct = match ir.get(value) {
1276 Inst::Struct { fields, values } => Some((fields.clone(), values.clone())),
1277 _ => return false,
1278 };
1279 let (vfields, vvalues) = value_struct.unwrap();
1280
1281 let value_map: FxHashMap<String, InstId> = vfields
1283 .iter()
1284 .zip(vvalues.iter())
1285 .map(|(f, v)| (ir.resolve_name(*f).to_string(), *v))
1286 .collect();
1287
1288 for (fname_id, ftype_id, is_optional) in &type_fields {
1290 if let Some(fname) = name_str(ir, *fname_id) {
1291 match value_map.get(fname) {
1292 Some(val_id) => {
1293 if !has_type(ir, *ftype_id, *val_id) {
1294 return false;
1295 }
1296 }
1297 None => {
1298 if !is_optional {
1299 return false;
1300 }
1301 }
1302 }
1303 }
1304 }
1305
1306 let type_field_names: FxHashSet<String> = type_fields
1308 .iter()
1309 .filter_map(|(f, _, _)| name_str(ir, *f).map(|s| s.to_string()))
1310 .collect();
1311 for vf in &vfields {
1312 let vf_name = ir.resolve_name(*vf);
1313 if !type_field_names.contains(vf_name) {
1314 return false;
1315 }
1316 }
1317
1318 true
1319}
1320
1321fn has_type_tagged_variant(
1323 ir: &Ir,
1324 tag_field: InstId,
1325 tag: InstId,
1326 variant_struct: InstId,
1327 value: InstId,
1328) -> bool {
1329 let (vfields, vvalues) = match ir.get(value) {
1330 Inst::Struct { fields, values } => (fields.clone(), values.clone()),
1331 _ => return false,
1332 };
1333
1334 let tag_field_name = match name_str(ir, tag_field) {
1336 Some(n) => n,
1337 None => return false,
1338 };
1339
1340 let tag_idx = vfields
1341 .iter()
1342 .position(|f| ir.resolve_name(*f) == tag_field_name);
1343 match tag_idx {
1344 Some(idx) => {
1345 if !ir_eq(ir, vvalues[idx], tag) {
1346 return false;
1347 }
1348 }
1349 None => return false,
1350 }
1351
1352 let type_fields = struct_type_fields(ir, variant_struct);
1354
1355 let value_map: FxHashMap<String, InstId> = vfields
1357 .iter()
1358 .zip(vvalues.iter())
1359 .filter(|(f, _)| ir.resolve_name(**f) != tag_field_name)
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 name = ir.resolve_name(*vf);
1388 if name != tag_field_name && !type_field_names.contains(name) {
1389 return false;
1390 }
1391 }
1392
1393 true
1394}
1395
1396pub fn new_rel_type(ir: &mut Ir, arg_types: &[InstId]) -> InstId {
1402 let rel_name = ir.intern_name(FN_REL);
1403 ir.add_inst(Inst::ApplyFn {
1404 function: rel_name,
1405 args: arg_types.to_vec(),
1406 })
1407}
1408
1409pub fn new_union_or_single(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1411 if alts.len() == 1 {
1412 return alts[0];
1413 }
1414 let union_name = ir.intern_name(FN_UNION);
1415 ir.add_inst(Inst::ApplyFn {
1416 function: union_name,
1417 args: alts,
1418 })
1419}
1420
1421pub fn rel_type_args(ir: &Ir, id: InstId) -> Option<&[InstId]> {
1423 if apply_fn_name(ir, id) != Some(FN_REL) {
1424 return None;
1425 }
1426 apply_fn_args(ir, id)
1427}
1428
1429pub fn rel_types_from_decl(ir: &mut Ir, bounds: &[InstId]) -> Vec<InstId> {
1432 bounds
1433 .iter()
1434 .filter_map(|bound_id| {
1435 if let Inst::BoundDecl { base_terms } = ir.get(*bound_id) {
1436 let terms = base_terms.clone();
1437 Some(new_rel_type(ir, &terms))
1438 } else {
1439 None
1440 }
1441 })
1442 .collect()
1443}
1444
1445pub fn get_type_context(ir: &Ir, id: InstId) -> TypeContext {
1448 let mut ctx = TypeContext::default();
1449 collect_type_vars(ir, id, &mut ctx);
1450 ctx
1451}
1452
1453fn collect_type_vars(ir: &Ir, id: InstId, ctx: &mut TypeContext) {
1454 match ir.get(id) {
1455 Inst::Var(v) => {
1456 if !ctx.contains_key(v) {
1457 ctx.insert(*v, id);
1461 }
1462 }
1463 Inst::ApplyFn { args, .. } => {
1464 for arg in args {
1465 collect_type_vars(ir, *arg, ctx);
1466 }
1467 }
1468 _ => {}
1469 }
1470}
1471
1472pub fn rel_type_alternatives(ir: &Ir, id: InstId) -> Vec<InstId> {
1477 if let Some(args) = union_type_args(ir, id) {
1478 args.to_vec()
1479 } else {
1480 vec![id]
1481 }
1482}
1483
1484pub fn rel_type_from_alternatives(ir: &mut Ir, alts: Vec<InstId>) -> InstId {
1486 if alts.is_empty() {
1487 return empty_type(ir);
1488 }
1489 new_union_or_single(ir, alts)
1490}
1491
1492pub fn remove_from_union_type(ir: &mut Ir, to_remove: InstId, union_type: InstId) -> InstId {
1495 let ctx = TypeContext::default();
1496 if let Some(args) = union_type_args(ir, union_type) {
1497 let args = args.to_vec();
1498 let remaining: Vec<InstId> = args
1499 .into_iter()
1500 .filter(|alt| !set_conforms(ir, &ctx, *alt, to_remove))
1501 .collect();
1502 if remaining.is_empty() {
1503 return empty_type(ir);
1504 }
1505 return new_union_or_single(ir, remaining);
1506 }
1507 if set_conforms(ir, &ctx, union_type, to_remove) {
1509 return empty_type(ir);
1510 }
1511 union_type
1512}
1513
1514pub fn new_list_type(ir: &mut Ir, elem: InstId) -> InstId {
1516 let name = ir.intern_name(FN_LIST);
1517 ir.add_inst(Inst::ApplyFn {
1518 function: name,
1519 args: vec![elem],
1520 })
1521}
1522
1523pub fn new_map_type(ir: &mut Ir, key: InstId, val: InstId) -> InstId {
1525 let name = ir.intern_name(FN_MAP);
1526 ir.add_inst(Inst::ApplyFn {
1527 function: name,
1528 args: vec![key, val],
1529 })
1530}
1531
1532pub fn new_struct_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1534 let name = ir.intern_name(FN_STRUCT);
1535 ir.add_inst(Inst::ApplyFn {
1536 function: name,
1537 args,
1538 })
1539}
1540
1541pub fn new_tuple_type(ir: &mut Ir, args: Vec<InstId>) -> InstId {
1543 let name = ir.intern_name(FN_TUPLE);
1544 ir.add_inst(Inst::ApplyFn {
1545 function: name,
1546 args,
1547 })
1548}
1549
1550pub fn apply_subst(ir: &mut Ir, id: InstId, subst: &FxHashMap<NameId, InstId>) -> InstId {
1553 if subst.is_empty() {
1554 return id;
1555 }
1556 match ir.get(id) {
1557 Inst::Var(v) => {
1558 let v = *v;
1559 if let Some(&replacement) = subst.get(&v) {
1560 replacement
1561 } else {
1562 id
1563 }
1564 }
1565 Inst::ApplyFn { function, args } => {
1566 let function = *function;
1567 let args = args.clone();
1568 let new_args: Vec<InstId> = args
1569 .iter()
1570 .map(|a| apply_subst(ir, *a, subst))
1571 .collect();
1572 if new_args == args {
1573 return id;
1574 }
1575 ir.add_inst(Inst::ApplyFn {
1576 function,
1577 args: new_args,
1578 })
1579 }
1580 _ => id,
1581 }
1582}
1583
1584pub fn collect_vars(ir: &Ir, id: InstId, vars: &mut FxHashSet<NameId>) {
1586 match ir.get(id) {
1587 Inst::Var(v) => {
1588 vars.insert(*v);
1589 }
1590 Inst::ApplyFn { args, .. } => {
1591 for arg in args {
1592 collect_vars(ir, *arg, vars);
1593 }
1594 }
1595 _ => {}
1596 }
1597}
1598
1599#[cfg(test)]
1604mod tests {
1605 use super::*;
1606
1607 fn make_name(ir: &mut Ir, s: &str) -> InstId {
1608 let n = ir.intern_name(s);
1609 ir.add_inst(Inst::Name(n))
1610 }
1611
1612 fn make_apply(ir: &mut Ir, fn_name: &str, args: Vec<InstId>) -> InstId {
1613 let n = ir.intern_name(fn_name);
1614 ir.add_inst(Inst::ApplyFn {
1615 function: n,
1616 args,
1617 })
1618 }
1619
1620 #[test]
1623 fn wellformed_base_types() {
1624 let mut ir = Ir::new();
1625 let ctx = TypeContext::default();
1626 let num = make_name(&mut ir, "/number");
1627 let str_ = make_name(&mut ir, "/string");
1628 let any = make_name(&mut ir, "/any");
1629 assert!(wellformed_type(&ir, &ctx, num).is_ok());
1630 assert!(wellformed_type(&ir, &ctx, str_).is_ok());
1631 assert!(wellformed_type(&ir, &ctx, any).is_ok());
1632 }
1633
1634 #[test]
1635 fn wellformed_struct() {
1636 let mut ir = Ir::new();
1637 let ctx = TypeContext::default();
1638 let x = make_name(&mut ir, "/x");
1639 let num = make_name(&mut ir, "/number");
1640 let y = make_name(&mut ir, "/y");
1641 let str_ = make_name(&mut ir, "/string");
1642 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1643 assert!(wellformed_type(&ir, &ctx, s).is_ok());
1644 }
1645
1646 #[test]
1647 fn wellformed_struct_odd_args() {
1648 let mut ir = Ir::new();
1649 let ctx = TypeContext::default();
1650 let x = make_name(&mut ir, "/x");
1651 let num = make_name(&mut ir, "/number");
1652 let y = make_name(&mut ir, "/y");
1653 let s = make_apply(&mut ir, FN_STRUCT, vec![x, num, y]);
1655 assert!(wellformed_type(&ir, &ctx, s).is_err());
1656 }
1657
1658 #[test]
1659 fn wellformed_tagged_union() {
1660 let mut ir = Ir::new();
1661 let ctx = TypeContext::default();
1662 let kind = make_name(&mut ir, "/kind");
1663 let move_ = make_name(&mut ir, "/move");
1664 let x = make_name(&mut ir, "/x");
1665 let num = make_name(&mut ir, "/number");
1666 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
1667 let quit = make_name(&mut ir, "/quit");
1668 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1669 let tu = make_apply(
1670 &mut ir,
1671 FN_TAGGED_UNION,
1672 vec![kind, move_, move_struct, quit, quit_struct],
1673 );
1674 assert!(wellformed_type(&ir, &ctx, tu).is_ok());
1675 }
1676
1677 #[test]
1678 fn wellformed_tagged_union_negative_too_few_args() {
1679 let mut ir = Ir::new();
1680 let ctx = TypeContext::default();
1681 let kind = make_name(&mut ir, "/kind");
1682 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind]);
1683 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1684 }
1685
1686 #[test]
1687 fn wellformed_tagged_union_negative_even_args() {
1688 let mut ir = Ir::new();
1689 let ctx = TypeContext::default();
1690 let kind = make_name(&mut ir, "/kind");
1691 let move_ = make_name(&mut ir, "/move");
1692 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1693 let quit = make_name(&mut ir, "/quit");
1694 let tu = make_apply(
1696 &mut ir,
1697 FN_TAGGED_UNION,
1698 vec![kind, move_, move_struct, quit],
1699 );
1700 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1701 }
1702
1703 #[test]
1704 fn wellformed_tagged_union_negative_dup_tags() {
1705 let mut ir = Ir::new();
1706 let ctx = TypeContext::default();
1707 let kind = make_name(&mut ir, "/kind");
1708 let move_ = make_name(&mut ir, "/move");
1709 let s1 = make_apply(&mut ir, FN_STRUCT, vec![]);
1710 let move2 = make_name(&mut ir, "/move");
1711 let s2 = make_apply(&mut ir, FN_STRUCT, vec![]);
1712 let tu = make_apply(
1713 &mut ir,
1714 FN_TAGGED_UNION,
1715 vec![kind, move_, s1, move2, s2],
1716 );
1717 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1718 }
1719
1720 #[test]
1721 fn wellformed_tagged_union_negative_tag_field_in_variant() {
1722 let mut ir = Ir::new();
1723 let ctx = TypeContext::default();
1724 let kind = make_name(&mut ir, "/kind");
1725 let move_ = make_name(&mut ir, "/move");
1726 let kind2 = make_name(&mut ir, "/kind");
1728 let num = make_name(&mut ir, "/number");
1729 let s = make_apply(&mut ir, FN_STRUCT, vec![kind2, num]);
1730 let tu = make_apply(&mut ir, FN_TAGGED_UNION, vec![kind, move_, s]);
1731 assert!(wellformed_type(&ir, &ctx, tu).is_err());
1732 }
1733
1734 #[test]
1737 fn conforms_base_types() {
1738 let mut ir = Ir::new();
1739 let ctx = TypeContext::default();
1740 let num = make_name(&mut ir, "/number");
1741 let any = make_name(&mut ir, "/any");
1742 let str_ = make_name(&mut ir, "/string");
1743
1744 assert!(set_conforms(&ir, &ctx, num, any));
1745 assert!(set_conforms(&ir, &ctx, num, num));
1746 assert!(!set_conforms(&ir, &ctx, num, str_));
1747 }
1748
1749 #[test]
1750 fn conforms_name_hierarchy() {
1751 let mut ir = Ir::new();
1752 let ctx = TypeContext::default();
1753 let foo = make_name(&mut ir, "/foo");
1754 let foo_bar = make_name(&mut ir, "/foo/bar");
1755 let name = make_name(&mut ir, "/name");
1756
1757 assert!(set_conforms(&ir, &ctx, foo_bar, foo));
1758 assert!(set_conforms(&ir, &ctx, foo, name));
1759 assert!(set_conforms(&ir, &ctx, foo_bar, name));
1760 assert!(!set_conforms(&ir, &ctx, foo, foo_bar));
1761 }
1762
1763 #[test]
1764 fn conforms_singleton() {
1765 let mut ir = Ir::new();
1766 let ctx = TypeContext::default();
1767 let move_ = make_name(&mut ir, "/move");
1768 let singleton = make_apply(&mut ir, FN_SINGLETON, vec![move_]);
1769 let name = make_name(&mut ir, "/name");
1770 let num = make_name(&mut ir, "/number");
1771
1772 assert!(set_conforms(&ir, &ctx, singleton, name));
1773 assert!(!set_conforms(&ir, &ctx, singleton, num));
1774 }
1775
1776 #[test]
1777 fn conforms_union() {
1778 let mut ir = Ir::new();
1779 let ctx = TypeContext::default();
1780 let num = make_name(&mut ir, "/number");
1781 let str_ = make_name(&mut ir, "/string");
1782 let union = make_apply(&mut ir, FN_UNION, vec![num, str_]);
1783
1784 let any = make_name(&mut ir, "/any");
1786 assert!(set_conforms(&ir, &ctx, union, any));
1787 assert!(set_conforms(&ir, &ctx, num, union));
1789 assert!(!set_conforms(&ir, &ctx, union, num));
1791 }
1792
1793 #[test]
1794 fn conforms_struct_subtyping() {
1795 let mut ir = Ir::new();
1796 let ctx = TypeContext::default();
1797 let x = make_name(&mut ir, "/x");
1799 let num = make_name(&mut ir, "/number");
1800 let y = make_name(&mut ir, "/y");
1801 let str_ = make_name(&mut ir, "/string");
1802 let wider = make_apply(&mut ir, FN_STRUCT, vec![x, num, y, str_]);
1803 let x2 = make_name(&mut ir, "/x");
1805 let num2 = make_name(&mut ir, "/number");
1806 let narrower = make_apply(&mut ir, FN_STRUCT, vec![x2, num2]);
1807
1808 assert!(set_conforms(&ir, &ctx, wider, narrower));
1810 assert!(!set_conforms(&ir, &ctx, narrower, wider));
1812 }
1813
1814 #[test]
1815 fn conforms_list() {
1816 let mut ir = Ir::new();
1817 let ctx = TypeContext::default();
1818 let num = make_name(&mut ir, "/number");
1819 let any = make_name(&mut ir, "/any");
1820 let list_num = make_apply(&mut ir, FN_LIST, vec![num]);
1821 let list_any = make_apply(&mut ir, FN_LIST, vec![any]);
1822
1823 assert!(set_conforms(&ir, &ctx, list_num, list_any));
1824 assert!(!set_conforms(&ir, &ctx, list_any, list_num));
1825 }
1826
1827 #[test]
1830 fn has_type_base() {
1831 let mut ir = Ir::new();
1832 let num_type = make_name(&mut ir, "/number");
1833 let str_type = make_name(&mut ir, "/string");
1834 let val_42 = ir.add_inst(Inst::Number(42));
1835 let val_hello = {
1836 let s = ir.intern_string("hello");
1837 ir.add_inst(Inst::String(s))
1838 };
1839
1840 assert!(has_type(&ir, num_type, val_42));
1841 assert!(!has_type(&ir, str_type, val_42));
1842 assert!(has_type(&ir, str_type, val_hello));
1843 }
1844
1845 #[test]
1846 fn has_type_struct() {
1847 let mut ir = Ir::new();
1848 let x_name = make_name(&mut ir, "/x");
1850 let num_type = make_name(&mut ir, "/number");
1851 let struct_type = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1852
1853 let x_field = ir.intern_name("/x");
1855 let val_42 = ir.add_inst(Inst::Number(42));
1856 let val_struct = ir.add_inst(Inst::Struct {
1857 fields: vec![x_field],
1858 values: vec![val_42],
1859 });
1860
1861 assert!(has_type(&ir, struct_type, val_struct));
1862 }
1863
1864 #[test]
1865 fn has_type_tagged_union() {
1866 let mut ir = Ir::new();
1867
1868 let kind = make_name(&mut ir, "/kind");
1870 let move_tag = make_name(&mut ir, "/move");
1871 let x_name = make_name(&mut ir, "/x");
1872 let num_type = make_name(&mut ir, "/number");
1873 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x_name, num_type]);
1874 let quit_tag = make_name(&mut ir, "/quit");
1875 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1876 let tu = make_apply(
1877 &mut ir,
1878 FN_TAGGED_UNION,
1879 vec![kind, move_tag, move_struct, quit_tag, quit_struct],
1880 );
1881
1882 let kind_f = ir.intern_name("/kind");
1884 let move_v = ir.intern_name("/move");
1885 let move_val = ir.add_inst(Inst::Name(move_v));
1886 let x_f = ir.intern_name("/x");
1887 let val_10 = ir.add_inst(Inst::Number(10));
1888 let val_move = ir.add_inst(Inst::Struct {
1889 fields: vec![kind_f, x_f],
1890 values: vec![move_val, val_10],
1891 });
1892 assert!(has_type(&ir, tu, val_move));
1893
1894 let kind_f2 = ir.intern_name("/kind");
1896 let quit_v = ir.intern_name("/quit");
1897 let quit_val = ir.add_inst(Inst::Name(quit_v));
1898 let val_quit = ir.add_inst(Inst::Struct {
1899 fields: vec![kind_f2],
1900 values: vec![quit_val],
1901 });
1902 assert!(has_type(&ir, tu, val_quit));
1903
1904 let kind_f3 = ir.intern_name("/kind");
1906 let bad_v = ir.intern_name("/bad");
1907 let bad_val = ir.add_inst(Inst::Name(bad_v));
1908 let val_bad = ir.add_inst(Inst::Struct {
1909 fields: vec![kind_f3],
1910 values: vec![bad_val],
1911 });
1912 assert!(!has_type(&ir, tu, val_bad));
1913 }
1914
1915 #[test]
1918 fn expand_tagged_union() {
1919 let mut ir = Ir::new();
1920 let kind = make_name(&mut ir, "/kind");
1921 let move_ = make_name(&mut ir, "/move");
1922 let x = make_name(&mut ir, "/x");
1923 let num = make_name(&mut ir, "/number");
1924 let move_struct = make_apply(&mut ir, FN_STRUCT, vec![x, num]);
1925 let quit = make_name(&mut ir, "/quit");
1926 let quit_struct = make_apply(&mut ir, FN_STRUCT, vec![]);
1927 let tu = make_apply(
1928 &mut ir,
1929 FN_TAGGED_UNION,
1930 vec![kind, move_, move_struct, quit, quit_struct],
1931 );
1932
1933 let expanded = expand_tagged_union_type(&mut ir, tu).unwrap();
1934 assert!(is_union_type(&ir, expanded));
1935 let alts = union_type_args(&ir, expanded).unwrap();
1936 assert_eq!(alts.len(), 2);
1937 assert!(is_struct_type(&ir, alts[0]));
1939 assert!(is_struct_type(&ir, alts[1]));
1941 }
1942
1943 #[test]
1946 fn upper_bound_basic() {
1947 let mut ir = Ir::new();
1948 let ctx = TypeContext::default();
1949 let num = make_name(&mut ir, "/number");
1950 let str_ = make_name(&mut ir, "/string");
1951
1952 assert_eq!(upper_bound(&mut ir, &ctx, &[num]), num);
1954 let ub = upper_bound(&mut ir, &ctx, &[num, str_]);
1956 assert!(is_union_type(&ir, ub));
1957 let ub_empty = upper_bound(&mut ir, &ctx, &[]);
1959 assert!(is_empty_type(&ir, ub_empty));
1960 }
1961
1962 #[test]
1963 fn upper_bound_eliminates_redundant() {
1964 let mut ir = Ir::new();
1965 let ctx = TypeContext::default();
1966 let num = make_name(&mut ir, "/number");
1967 let any = make_name(&mut ir, "/any");
1968
1969 let ub = upper_bound(&mut ir, &ctx, &[num, any]);
1971 assert!(is_any(&ir, ub));
1972 }
1973
1974 #[test]
1975 fn upper_bound_name_hierarchy() {
1976 let mut ir = Ir::new();
1977 let ctx = TypeContext::default();
1978 let animal = make_name(&mut ir, "/animal");
1979 let animal_dog = make_name(&mut ir, "/animal/dog");
1980
1981 let ub = upper_bound(&mut ir, &ctx, &[animal, animal_dog]);
1983 assert_eq!(ub, animal);
1984 }
1985
1986 #[test]
1987 fn lower_bound_basic() {
1988 let mut ir = Ir::new();
1989 let ctx = TypeContext::default();
1990 let num = make_name(&mut ir, "/number");
1991 let any = make_name(&mut ir, "/any");
1992
1993 let lb = lower_bound(&mut ir, &ctx, &[num, any]);
1995 assert_eq!(lb, num);
1996 }
1997
1998 #[test]
1999 fn lower_bound_empty() {
2000 let mut ir = Ir::new();
2001 let ctx = TypeContext::default();
2002 let num = make_name(&mut ir, "/number");
2003 let str_ = make_name(&mut ir, "/string");
2004
2005 let lb = lower_bound(&mut ir, &ctx, &[num, str_]);
2007 assert!(is_empty_type(&ir, lb));
2008 }
2009
2010 #[test]
2011 fn intersect_type_with_union() {
2012 let mut ir = Ir::new();
2013 let ctx = TypeContext::default();
2014 let num = make_name(&mut ir, "/number");
2015 let str_ = make_name(&mut ir, "/string");
2016 let union_ns = make_apply(&mut ir, FN_UNION, vec![num, str_]);
2017
2018 let result = intersect_type(&mut ir, &ctx, union_ns, num);
2020 assert_eq!(result, num);
2021 }
2022}