logicaffeine_compile/analysis/
pipes.rs1use std::collections::{HashMap, HashSet};
10
11use logicaffeine_language::ast_depth::{for_each_node, Visited};
12
13use crate::ast_map::Arenas;
14
15use crate::ast::stmt::{Expr, SelectBranch, Stmt, TypeExpr};
16use crate::intern::{Interner, Symbol};
17
18pub fn pipe_params(stmts: &[Stmt], interner: &Interner) -> HashMap<Symbol, HashSet<usize>> {
20 let mut pipes: HashMap<Symbol, HashSet<usize>> = HashMap::new();
21 let mut handoffs: Vec<(Symbol, Symbol, usize, usize)> = Vec::new();
23 for stmt in stmts {
24 let Stmt::FunctionDef {
25 name, params, body, ..
26 } = stmt
27 else {
28 continue;
29 };
30 let position = |sym: Symbol| params.iter().position(|(p, _)| *p == sym);
31 let mut own: HashSet<usize> = params
32 .iter()
33 .enumerate()
34 .filter(|(_, (_, ty))| declares_pipe(ty, interner))
35 .map(|(i, _)| i)
36 .collect();
37 let mut pass = |callee: Symbol, args: &[&Expr]| {
38 for (j, arg) in args.iter().enumerate() {
39 if let Some(i) = identifier(arg).and_then(position) {
40 handoffs.push((*name, callee, j, i));
41 }
42 }
43 };
44 for_each_node(body, |node| match node {
45 Visited::Stmt(s) => {
46 own.extend(
47 pipe_operands(s)
48 .into_iter()
49 .filter_map(identifier)
50 .filter_map(position),
51 );
52 if let Some((callee, args)) = handoff(s) {
53 pass(callee, args);
54 }
55 }
56 Visited::Expr(Expr::Call { function, args }) => pass(*function, args),
57 Visited::Expr(_) => {}
58 });
59 pipes.insert(*name, own);
60 }
61 loop {
62 let mut grew = false;
63 for (f, callee, j, i) in &handoffs {
64 let callee_pipe = pipes.get(callee).is_some_and(|ps| ps.contains(j));
65 if callee_pipe && pipes.get_mut(f).is_some_and(|ps| ps.insert(*i)) {
66 grew = true;
67 }
68 }
69 if !grew {
70 break;
71 }
72 }
73 pipes.retain(|_, ps| !ps.is_empty());
74 pipes
75}
76
77pub fn declare_pipe_params<'a>(
80 stmts: &[Stmt<'a>],
81 arenas: Arenas<'a>,
82 interner: &mut Interner,
83) -> Vec<Stmt<'a>> {
84 let pipes = pipe_params(stmts, interner);
85 if pipes.is_empty() {
86 return stmts.to_vec();
87 }
88 let pipe = interner.intern("Pipe");
89 stmts
90 .iter()
91 .map(|stmt| match stmt {
92 Stmt::FunctionDef { name, params, .. } if pipes.contains_key(name) => {
93 let at = &pipes[name];
94 let params = params
95 .iter()
96 .enumerate()
97 .map(|(i, (p, ty))| {
98 if at.contains(&i) && !declares_pipe(ty, interner) {
99 let element = arenas.types.alloc_slice(vec![(*ty).clone()]);
100 (
101 *p,
102 &*arenas.types.alloc(TypeExpr::Generic {
103 base: pipe,
104 params: element,
105 }),
106 )
107 } else {
108 (*p, *ty)
109 }
110 })
111 .collect();
112 let mut rewritten = stmt.clone();
113 if let Stmt::FunctionDef { params: slot, .. } = &mut rewritten {
114 *slot = params;
115 }
116 rewritten
117 }
118 other => other.clone(),
119 })
120 .collect()
121}
122
123fn declares_pipe(ty: &TypeExpr, interner: &Interner) -> bool {
125 matches!(ty, TypeExpr::Generic { base, .. } if interner.resolve(*base) == "Pipe")
126}
127
128fn identifier(e: &&Expr) -> Option<Symbol> {
129 match e {
130 Expr::Identifier(sym) => Some(*sym),
131 _ => None,
132 }
133}
134
135fn pipe_operands<'s, 'a>(stmt: &'s Stmt<'a>) -> Vec<&'s &'a Expr<'a>> {
137 match stmt {
138 Stmt::SendPipe { pipe, .. }
139 | Stmt::TrySendPipe { pipe, .. }
140 | Stmt::ReceivePipe { pipe, .. }
141 | Stmt::TryReceivePipe { pipe, .. } => vec![pipe],
142 Stmt::CloseChannel { channel } => vec![channel],
143 Stmt::Select { branches } => branches
144 .iter()
145 .filter_map(|b| match b {
146 SelectBranch::Receive { pipe, .. } => Some(pipe),
147 SelectBranch::Timeout { .. } => None,
148 })
149 .collect(),
150 _ => Vec::new(),
151 }
152}
153
154fn handoff<'s, 'a>(stmt: &'s Stmt<'a>) -> Option<(Symbol, &'s [&'a Expr<'a>])> {
156 match stmt {
157 Stmt::Call { function, args }
158 | Stmt::LaunchTask { function, args }
159 | Stmt::LaunchTaskWithHandle { function, args, .. } => Some((*function, args.as_slice())),
160 _ => None,
161 }
162}