1use crate::{Label, ValType};
18use serde::{Deserialize, Serialize};
19use std::collections::BTreeSet;
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
43pub enum LocalTypeR {
44 End,
46 Send {
48 partner: String,
49 branches: Vec<(Label, Option<ValType>, LocalTypeR)>,
50 },
51 Recv {
53 partner: String,
54 branches: Vec<(Label, Option<ValType>, LocalTypeR)>,
55 },
56 Mu { var: String, body: Box<LocalTypeR> },
58 Var(String),
60}
61
62impl LocalTypeR {
63 fn collect_all_var_names(&self, names: &mut BTreeSet<String>) {
64 match self {
65 LocalTypeR::End => {}
66 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
67 for (_, _, cont) in branches {
68 cont.collect_all_var_names(names);
69 }
70 }
71 LocalTypeR::Mu { var, body } => {
72 names.insert(var.clone());
73 body.collect_all_var_names(names);
74 }
75 LocalTypeR::Var(t) => {
76 names.insert(t.clone());
77 }
78 }
79 }
80
81 fn all_var_names(&self) -> BTreeSet<String> {
82 let mut names = BTreeSet::new();
83 self.collect_all_var_names(&mut names);
84 names
85 }
86
87 fn fresh_var(base: &str, avoid: &BTreeSet<String>) -> String {
88 let mut idx = 0usize;
89 loop {
90 let candidate = format!("{base}_{idx}");
92 if !avoid.contains(&candidate) {
93 return candidate;
94 }
95 idx += 1;
96 }
97 }
98
99 #[must_use]
101 pub fn send(partner: impl Into<String>, label: Label, cont: LocalTypeR) -> Self {
102 LocalTypeR::Send {
103 partner: partner.into(),
104 branches: vec![(label, None, cont)],
105 }
106 }
107
108 #[must_use]
110 pub fn send_choice(
111 partner: impl Into<String>,
112 branches: Vec<(Label, Option<ValType>, LocalTypeR)>,
113 ) -> Self {
114 LocalTypeR::Send {
115 partner: partner.into(),
116 branches,
117 }
118 }
119
120 #[must_use]
122 pub fn recv(partner: impl Into<String>, label: Label, cont: LocalTypeR) -> Self {
123 LocalTypeR::Recv {
124 partner: partner.into(),
125 branches: vec![(label, None, cont)],
126 }
127 }
128
129 #[must_use]
131 pub fn recv_choice(
132 partner: impl Into<String>,
133 branches: Vec<(Label, Option<ValType>, LocalTypeR)>,
134 ) -> Self {
135 LocalTypeR::Recv {
136 partner: partner.into(),
137 branches,
138 }
139 }
140
141 #[must_use]
143 pub fn mu(var: impl Into<String>, body: LocalTypeR) -> Self {
144 LocalTypeR::Mu {
145 var: var.into(),
146 body: Box::new(body),
147 }
148 }
149
150 #[must_use]
152 pub fn var(name: impl Into<String>) -> Self {
153 LocalTypeR::Var(name.into())
154 }
155
156 #[must_use]
160 pub fn free_vars(&self) -> Vec<String> {
161 let mut result = Vec::new();
162 let mut bound = BTreeSet::new();
163 self.collect_free_vars(&mut result, &mut bound);
164 result
165 }
166
167 fn collect_free_vars(&self, free: &mut Vec<String>, bound: &mut BTreeSet<String>) {
168 match self {
169 LocalTypeR::End => {}
170 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
171 for (_, _, cont) in branches {
172 cont.collect_free_vars(free, bound);
173 }
174 }
175 LocalTypeR::Mu { var, body } => {
176 bound.insert(var.clone());
177 body.collect_free_vars(free, bound);
178 bound.remove(var);
179 }
180 LocalTypeR::Var(t) => {
181 if !bound.contains(t) && !free.contains(t) {
182 free.push(t.clone());
183 }
184 }
185 }
186 }
187
188 #[must_use]
192 pub fn substitute(&self, var_name: &str, replacement: &LocalTypeR) -> LocalTypeR {
193 match self {
194 LocalTypeR::End => LocalTypeR::End,
195 LocalTypeR::Send { partner, branches } => {
196 Self::substitute_branching(partner, branches, var_name, replacement, true)
197 }
198 LocalTypeR::Recv { partner, branches } => {
199 Self::substitute_branching(partner, branches, var_name, replacement, false)
200 }
201 LocalTypeR::Mu { var, body } => Self::substitute_mu(var, body, var_name, replacement),
202 LocalTypeR::Var(t) => {
203 if t == var_name {
204 replacement.clone()
205 } else {
206 LocalTypeR::Var(t.clone())
207 }
208 }
209 }
210 }
211
212 fn substitute_branching(
213 partner: &str,
214 branches: &[(Label, Option<ValType>, LocalTypeR)],
215 var_name: &str,
216 replacement: &LocalTypeR,
217 is_send: bool,
218 ) -> LocalTypeR {
219 let substituted = branches
220 .iter()
221 .map(|(l, vt, cont)| {
222 (
223 l.clone(),
224 vt.clone(),
225 cont.substitute(var_name, replacement),
226 )
227 })
228 .collect();
229 if is_send {
230 LocalTypeR::Send {
231 partner: partner.to_string(),
232 branches: substituted,
233 }
234 } else {
235 LocalTypeR::Recv {
236 partner: partner.to_string(),
237 branches: substituted,
238 }
239 }
240 }
241
242 fn substitute_mu(
243 var: &str,
244 body: &LocalTypeR,
245 var_name: &str,
246 replacement: &LocalTypeR,
247 ) -> LocalTypeR {
248 if var == var_name {
249 return LocalTypeR::Mu {
250 var: var.to_string(),
251 body: Box::new(body.clone()),
252 };
253 }
254 let replacement_free = replacement.free_vars();
255 if replacement_free.iter().any(|v| v == var) {
256 let mut avoid = body.all_var_names();
258 avoid.extend(replacement.all_var_names());
259 avoid.insert(var_name.to_string());
260 let fresh = Self::fresh_var(var, &avoid);
261 let renamed_body = body.substitute(var, &LocalTypeR::Var(fresh.clone()));
262 return LocalTypeR::Mu {
263 var: fresh,
264 body: Box::new(renamed_body.substitute(var_name, replacement)),
265 };
266 }
267 LocalTypeR::Mu {
268 var: var.to_string(),
269 body: Box::new(body.substitute(var_name, replacement)),
270 }
271 }
272
273 #[must_use]
277 pub fn unfold(&self) -> LocalTypeR {
278 match self {
279 LocalTypeR::Mu { var, body } => body.substitute(var, self),
280 _ => self.clone(),
281 }
282 }
283
284 #[must_use]
289 pub fn dual(&self) -> LocalTypeR {
290 match self {
291 LocalTypeR::End => LocalTypeR::End,
292 LocalTypeR::Send { partner, branches } => LocalTypeR::Recv {
293 partner: partner.clone(),
294 branches: branches
295 .iter()
296 .map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.dual()))
297 .collect(),
298 },
299 LocalTypeR::Recv { partner, branches } => LocalTypeR::Send {
300 partner: partner.clone(),
301 branches: branches
302 .iter()
303 .map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.dual()))
304 .collect(),
305 },
306 LocalTypeR::Mu { var, body } => LocalTypeR::Mu {
307 var: var.clone(),
308 body: Box::new(body.dual()),
309 },
310 LocalTypeR::Var(t) => LocalTypeR::Var(t.clone()),
311 }
312 }
313
314 #[must_use]
318 pub fn all_vars_bound(&self) -> bool {
319 self.check_vars_bound(&BTreeSet::new())
320 }
321
322 fn check_vars_bound(&self, bound: &BTreeSet<String>) -> bool {
323 match self {
324 LocalTypeR::End => true,
325 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => branches
326 .iter()
327 .all(|(_, _, cont)| cont.check_vars_bound(bound)),
328 LocalTypeR::Mu { var, body } => {
329 let mut new_bound = bound.clone();
330 new_bound.insert(var.clone());
331 body.check_vars_bound(&new_bound)
332 }
333 LocalTypeR::Var(t) => bound.contains(t),
334 }
335 }
336
337 #[must_use]
341 pub fn all_choices_non_empty(&self) -> bool {
342 match self {
343 LocalTypeR::End => true,
344 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
345 !branches.is_empty()
346 && branches
347 .iter()
348 .all(|(_, _, cont)| cont.all_choices_non_empty())
349 }
350 LocalTypeR::Mu { body, .. } => body.all_choices_non_empty(),
351 LocalTypeR::Var(_) => true,
352 }
353 }
354
355 fn branches_have_unique_names(branches: &[(Label, Option<ValType>, LocalTypeR)]) -> bool {
356 let mut seen = BTreeSet::new();
357 branches
358 .iter()
359 .all(|(label, _, _)| seen.insert(label.name.clone()))
360 }
361
362 #[must_use]
364 pub fn unique_branch_labels(&self) -> bool {
365 match self {
366 LocalTypeR::End | LocalTypeR::Var(_) => true,
367 LocalTypeR::Mu { body, .. } => body.unique_branch_labels(),
368 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
369 Self::branches_have_unique_names(branches)
370 && branches
371 .iter()
372 .all(|(_, _, cont)| cont.unique_branch_labels())
373 }
374 }
375 }
376
377 #[must_use]
385 pub fn well_formed(&self) -> bool {
386 self.all_vars_bound()
387 && self.all_choices_non_empty()
388 && self.unique_branch_labels()
389 && self.is_guarded()
390 }
391
392 #[must_use]
396 pub fn depth(&self) -> usize {
397 match self {
398 LocalTypeR::End => 0,
399 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
400 1 + branches
401 .iter()
402 .map(|(_, _, t)| t.depth())
403 .max()
404 .unwrap_or(0)
405 }
406 LocalTypeR::Mu { body, .. } => 1 + body.depth(),
407 LocalTypeR::Var(_) => 0,
408 }
409 }
410
411 #[must_use]
415 pub fn is_guarded(&self) -> bool {
416 match self {
417 LocalTypeR::End => true,
418 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
419 branches.iter().all(|(_, _, cont)| cont.is_guarded())
420 }
421 LocalTypeR::Mu { body, .. } => match body.as_ref() {
422 LocalTypeR::Var(_) | LocalTypeR::Mu { .. } => false,
423 _ => body.is_guarded(),
424 },
425 LocalTypeR::Var(_) => true,
426 }
427 }
428
429 #[must_use]
435 pub fn is_var_guarded(&self, var: &str) -> bool {
436 match self {
437 LocalTypeR::End => true,
438 LocalTypeR::Var(w) => w != var,
439 LocalTypeR::Send { .. } | LocalTypeR::Recv { .. } => true,
440 LocalTypeR::Mu { var: t, body, .. } => {
441 if var == t {
442 true
443 } else {
444 body.is_var_guarded(var)
445 }
446 }
447 }
448 }
449
450 #[must_use]
454 pub fn head_labels(&self) -> Vec<String> {
455 match self {
456 LocalTypeR::End | LocalTypeR::Var(_) => vec![],
457 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
458 branches.iter().map(|(l, _, _)| l.name.clone()).collect()
459 }
460 LocalTypeR::Mu { body, .. } => body.head_labels(),
461 }
462 }
463
464 #[must_use]
466 pub fn labels(&self) -> Vec<String> {
467 self.head_labels()
468 }
469
470 #[must_use]
472 pub fn all_labels(&self) -> Vec<String> {
473 let mut labels = BTreeSet::new();
474 self.collect_all_labels(&mut labels);
475 labels.into_iter().collect()
476 }
477
478 fn collect_all_labels(&self, labels: &mut BTreeSet<String>) {
479 match self {
480 LocalTypeR::End | LocalTypeR::Var(_) => {}
481 LocalTypeR::Mu { body, .. } => body.collect_all_labels(labels),
482 LocalTypeR::Send { branches, .. } | LocalTypeR::Recv { branches, .. } => {
483 for (label, _, cont) in branches {
484 labels.insert(label.name.clone());
485 cont.collect_all_labels(labels);
486 }
487 }
488 }
489 }
490
491 #[must_use]
495 pub fn partners(&self) -> Vec<String> {
496 let mut result = BTreeSet::new();
497 self.collect_partners(&mut result);
498 result.into_iter().collect()
499 }
500
501 fn collect_partners(&self, partners: &mut BTreeSet<String>) {
502 match self {
503 LocalTypeR::End | LocalTypeR::Var(_) => {}
504 LocalTypeR::Send { partner, branches } | LocalTypeR::Recv { partner, branches } => {
505 partners.insert(partner.clone());
506 for (_, _, cont) in branches {
507 cont.collect_partners(partners);
508 }
509 }
510 LocalTypeR::Mu { body, .. } => body.collect_partners(partners),
511 }
512 }
513
514 #[must_use]
516 pub fn mentions_partner(&self, role: &str) -> bool {
517 self.mentions_partner_inner(role)
518 }
519
520 fn mentions_partner_inner(&self, role: &str) -> bool {
521 match self {
522 LocalTypeR::End | LocalTypeR::Var(_) => false,
523 LocalTypeR::Mu { body, .. } => body.mentions_partner_inner(role),
524 LocalTypeR::Send { partner, branches } | LocalTypeR::Recv { partner, branches } => {
525 partner == role
526 || branches
527 .iter()
528 .any(|(_, _, cont)| cont.mentions_partner_inner(role))
529 }
530 }
531 }
532
533 #[must_use]
535 pub fn is_send(&self) -> bool {
536 matches!(self, LocalTypeR::Send { .. })
537 }
538
539 #[must_use]
541 pub fn is_recv(&self) -> bool {
542 matches!(self, LocalTypeR::Recv { .. })
543 }
544
545 #[must_use]
547 pub fn is_end(&self) -> bool {
548 matches!(self, LocalTypeR::End)
549 }
550}
551
552#[cfg(test)]
553mod tests {
554 use super::*;
555 use crate::PayloadSort;
556 use assert_matches::assert_matches;
557 use proptest::prelude::*;
558
559 #[test]
560 fn test_simple_local_type() {
561 let lt = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::End);
563 assert!(lt.well_formed());
564 assert_eq!(lt.partners().len(), 1);
565 assert!(lt.mentions_partner("B"));
566 }
567
568 #[test]
569 fn test_recursive_local_type() {
570 let lt = LocalTypeR::mu(
572 "t",
573 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("t")),
574 );
575 assert!(lt.well_formed());
576 assert!(lt.all_vars_bound());
577 }
578
579 #[test]
580 fn test_dual() {
581 let send = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::End);
583 let recv = send.dual();
584
585 assert_matches!(recv, LocalTypeR::Recv { partner, branches } => {
586 assert_eq!(partner, "B");
587 assert_eq!(branches.len(), 1);
588 assert_eq!((branches[0].0).name, "msg");
589 });
590 }
591
592 #[test]
593 fn test_unfold() {
594 let lt = LocalTypeR::mu(
596 "t",
597 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("t")),
598 );
599 let unfolded = lt.unfold();
600
601 assert_matches!(unfolded, LocalTypeR::Send { partner, branches } => {
602 assert_eq!(partner, "B");
603 assert_matches!(branches[0].2, LocalTypeR::Mu { .. });
604 });
605 }
606
607 #[test]
608 fn test_substitute() {
609 let lt = LocalTypeR::var("t");
610 let replacement = LocalTypeR::End;
611 assert_eq!(lt.substitute("t", &replacement), LocalTypeR::End);
612 assert_eq!(lt.substitute("s", &replacement), LocalTypeR::var("t"));
613 }
614
615 #[test]
616 fn test_substitute_avoids_capture() {
617 let lt = LocalTypeR::mu("y", LocalTypeR::var("x"));
618 let result = lt.substitute("x", &LocalTypeR::var("y"));
619
620 assert_matches!(result, LocalTypeR::Mu { var, body } => {
621 assert_ne!(var, "y");
622 assert_matches!(*body, LocalTypeR::Var(ref name) => {
623 assert_eq!(name, "y");
624 });
625 });
626 }
627
628 #[test]
629 fn test_substitute_avoids_capture_with_nested_binders() {
630 let lt = LocalTypeR::mu("y", LocalTypeR::mu("y_0", LocalTypeR::var("x")));
631 let result = lt.substitute("x", &LocalTypeR::var("y"));
632 assert!(result.free_vars().contains(&"y".to_string()));
633 }
634
635 #[test]
636 fn test_unbound_variable() {
637 let lt = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("t"));
639 assert!(!lt.all_vars_bound());
640 assert!(!lt.well_formed());
641 }
642
643 #[test]
644 fn test_guarded() {
645 let unguarded = LocalTypeR::mu("t", LocalTypeR::var("t"));
647 assert!(!unguarded.is_guarded());
648 assert!(!unguarded.well_formed()); let guarded = LocalTypeR::mu(
652 "t",
653 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("t")),
654 );
655 assert!(guarded.is_guarded());
656 assert!(guarded.well_formed()); }
658
659 #[test]
660 fn test_free_vars() {
661 let lt = LocalTypeR::mu(
663 "t",
664 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("s")),
665 );
666 let free = lt.free_vars();
667 assert_eq!(free, vec!["s"]);
668 }
669
670 #[test]
671 fn test_choice_with_payload() {
672 let branches = vec![
673 (
674 Label::with_sort("accept", PayloadSort::Bool),
675 None,
676 LocalTypeR::End,
677 ),
678 (
679 Label::with_sort("data", PayloadSort::Nat),
680 None,
681 LocalTypeR::End,
682 ),
683 ];
684 let lt = LocalTypeR::recv_choice("A", branches);
685 assert!(lt.well_formed());
686 assert_eq!(lt.head_labels(), vec!["accept", "data"]);
687 assert_eq!(lt.labels(), vec!["accept", "data"]);
688 }
689
690 #[test]
691 fn test_depth() {
692 let lt = LocalTypeR::send(
693 "B",
694 Label::new("outer"),
695 LocalTypeR::send("C", Label::new("inner"), LocalTypeR::End),
696 );
697 assert_eq!(lt.depth(), 2);
698 }
699
700 #[test]
701 fn test_is_send_recv() {
702 let send = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::End);
703 let recv = LocalTypeR::recv("B", Label::new("msg"), LocalTypeR::End);
704
705 assert!(send.is_send());
706 assert!(!send.is_recv());
707 assert!(recv.is_recv());
708 assert!(!recv.is_send());
709 }
710
711 #[test]
712 fn test_duplicate_branch_labels_not_well_formed() {
713 let lt = LocalTypeR::recv_choice(
714 "A",
715 vec![
716 (Label::new("msg"), None, LocalTypeR::End),
717 (Label::new("msg"), None, LocalTypeR::End),
718 ],
719 );
720 assert!(!lt.unique_branch_labels());
721 assert!(!lt.well_formed());
722 }
723
724 #[test]
725 fn test_partners_are_sorted() {
726 let lt = LocalTypeR::send(
727 "Zed",
728 Label::new("outer"),
729 LocalTypeR::recv(
730 "Alice",
731 Label::new("mid"),
732 LocalTypeR::send("Bob", Label::new("inner"), LocalTypeR::End),
733 ),
734 );
735
736 assert_eq!(lt.partners(), vec!["Alice", "Bob", "Zed"]);
737 }
738
739 #[test]
740 fn test_mentions_partner_direct_traversal_matches_expectation() {
741 let lt = LocalTypeR::mu(
742 "t",
743 LocalTypeR::send(
744 "A",
745 Label::new("x"),
746 LocalTypeR::recv("B", Label::new("y"), LocalTypeR::var("t")),
747 ),
748 );
749 assert!(lt.mentions_partner("A"));
750 assert!(lt.mentions_partner("B"));
751 assert!(!lt.mentions_partner("C"));
752 }
753
754 #[test]
755 fn test_all_labels_collects_nested_labels() {
756 let lt = LocalTypeR::send(
757 "B",
758 Label::new("outer"),
759 LocalTypeR::recv(
760 "A",
761 Label::new("inner"),
762 LocalTypeR::send("C", Label::new("leaf"), LocalTypeR::End),
763 ),
764 );
765 assert_eq!(lt.head_labels(), vec!["outer"]);
766 assert_eq!(lt.all_labels(), vec!["inner", "leaf", "outer"]);
767 }
768
769 fn arb_var_name() -> impl Strategy<Value = String> {
770 prop_oneof![
771 Just("x".to_string()),
772 Just("y".to_string()),
773 Just("z".to_string()),
774 Just("t".to_string()),
775 Just("u".to_string()),
776 ]
777 }
778
779 proptest! {
780 #[test]
781 fn prop_substitute_identity_when_var_absent(var in arb_var_name()) {
782 let lt = LocalTypeR::send("B", Label::new("msg"), LocalTypeR::End);
783 let replacement = LocalTypeR::var("r");
784 prop_assert_eq!(lt.substitute(&var, &replacement), lt);
785 }
786
787 #[test]
788 fn prop_substitute_avoids_capture_simple(
789 binder in arb_var_name(),
790 target in arb_var_name(),
791 ) {
792 prop_assume!(binder != target);
793 let lt = LocalTypeR::mu(&binder, LocalTypeR::var(&target));
794 let replacement = LocalTypeR::var(&binder);
795 let result = lt.substitute(&target, &replacement);
796 prop_assert!(result.free_vars().contains(&binder));
797 }
798 }
799}