1use std::collections::{HashMap, HashSet};
23
24use crate::analysis::unify::InferType;
25use crate::analysis::{FieldDef, FieldType, TypeDef, TypeRegistry};
26use crate::ast::stmt::{
27 ClosureBody, DomQuery, Expr, MatchArm, Pattern, ReadSource, SelectBranch, Stmt, StringPart,
28 TypeExpr,
29};
30use crate::ast_map::{self, Arenas, AstMap};
31use crate::intern::{Interner, Symbol};
32
33pub const PROGRAM_RECORD: &str = "__Program";
35
36pub const PROGRAM_BINDING: &str = "__program";
38
39pub struct Lifted<'a> {
41 pub stmts: Vec<Stmt<'a>>,
42 pub registry: TypeRegistry,
43}
44
45pub fn lift_program_bindings<'a>(
49 stmts: &[Stmt<'a>],
50 registry: &TypeRegistry,
51 interner: &mut Interner,
52 arenas: Arenas<'a>,
53) -> Result<Option<Lifted<'a>>, String> {
54 let functions: HashSet<Symbol> =
58 crate::analysis::callgraph::aot_compiled_functions(stmts, interner);
59
60 let mut resolver = Resolver::new(&functions);
61 for (at, s) in stmts.iter().enumerate() {
62 if !matches!(s, Stmt::FunctionDef { .. }) {
63 resolver.root_statement(at, s);
64 }
65 }
66 for s in stmts {
67 if let Stmt::FunctionDef {
68 name, params, body, ..
69 } = s
70 {
71 if functions.contains(name) {
72 resolver.function(*name, params.iter().map(|(p, _)| *p), body);
73 }
74 }
75 }
76
77 let lifted: HashSet<Symbol> = resolver.reached.values().flatten().copied().collect();
78 if lifted.is_empty() {
79 return Ok(None);
80 }
81 let needs = resolver.needs();
82 let reach = resolver.reach_through_calls();
83 resolver.refusals(&lifted, &needs, &reach, interner)?;
84
85 let root_types = match crate::analysis::root_binding_types(stmts, interner, registry) {
86 Ok(types) => types,
87 Err(_) => return Ok(None),
89 };
90 let mut order: Vec<Symbol> = lifted.iter().copied().collect();
91 order.sort_by(|a, b| interner.resolve(*a).cmp(interner.resolve(*b)));
92 let mut fields = Vec::with_capacity(order.len());
93 for name in &order {
94 let written = root_types.get(name).and_then(|t| field_type(t, interner));
95 let Some(ty) = written else {
96 return Err(format!(
97 "`{}` is one of the program's bindings, and a function reads it, so this tier keeps it \
98 where every function can reach it — which needs its type. Its `Let` does not decide \
99 one; write it: `Let {}: <Type> be …`.",
100 interner.resolve(*name),
101 interner.resolve(*name)
102 ));
103 };
104 fields.push(FieldDef {
105 name: *name,
106 ty,
107 is_public: true,
108 });
109 }
110 let record = interner.intern(PROGRAM_RECORD);
111 let program = interner.intern(PROGRAM_BINDING);
112 let mut out_registry = registry.clone();
113 out_registry.register(
114 record,
115 TypeDef::Struct {
116 fields,
117 generics: Vec::new(),
118 is_portable: false,
119 is_shared: false,
120 },
121 );
122
123 let named: &'a TypeExpr<'a> = arenas.types.alloc(TypeExpr::Named(record));
124 let lent: &'a TypeExpr<'a> = arenas.types.alloc(TypeExpr::Mutable { inner: named });
125 let mut lift = Lift {
126 arenas,
127 reads: &resolver.reads,
128 sets: &resolver.sets,
129 lifted: &lifted,
130 needs: &needs,
131 program,
132 lent,
133 };
134 let fresh = arenas.exprs.alloc(Expr::New {
135 type_name: record,
136 type_args: Vec::new(),
137 init_fields: Vec::new(),
138 });
139 let mut out = Vec::with_capacity(stmts.len() + 1);
140 out.push(Stmt::Let {
141 var: program,
142 ty: Some(named),
143 value: fresh,
144 mutable: true,
145 });
146 for s in stmts {
147 match s {
148 Stmt::Let { var, value, .. } if lifted.contains(var) => {
149 let value = lift.map_expr(value);
150 out.push(Stmt::SetField {
151 object: lift.program_ref(),
152 field: *var,
153 value,
154 });
155 }
156 _ => out.push(lift.map_stmt(s)),
157 }
158 }
159 Ok(Some(Lifted {
160 stmts: out,
161 registry: out_registry,
162 }))
163}
164
165fn field_type(t: &InferType, interner: &mut Interner) -> Option<FieldType> {
167 let generic =
168 |base: &str, params: Vec<FieldType>, interner: &mut Interner| FieldType::Generic {
169 base: interner.intern(base),
170 params,
171 };
172 Some(match t {
173 InferType::Seq(item) => {
174 let item = field_type(item, interner)?;
175 generic("Seq", vec![item], interner)
176 }
177 InferType::Set(item) => {
178 let item = field_type(item, interner)?;
179 generic("Set", vec![item], interner)
180 }
181 InferType::Option(item) => {
182 let item = field_type(item, interner)?;
183 generic("Option", vec![item], interner)
184 }
185 InferType::Map(k, v) => {
186 let (k, v) = (field_type(k, interner)?, field_type(v, interner)?);
187 generic("Map", vec![k, v], interner)
188 }
189 InferType::UserDefined(name, args) if args.is_empty() => FieldType::Named(*name),
190 InferType::UserDefined(name, args) => {
191 let params = args
192 .iter()
193 .map(|a| field_type(a, interner))
194 .collect::<Option<Vec<_>>>()?;
195 FieldType::Generic {
196 base: *name,
197 params,
198 }
199 }
200 other => FieldType::Primitive(interner.intern(other.primitive_name()?)),
201 })
202}
203
204struct Resolver<'a, 'f> {
206 functions: &'f HashSet<Symbol>,
207 roots: HashSet<Symbol>,
209 defined_at: HashMap<Symbol, usize>,
211 scopes: Vec<HashSet<Symbol>>,
213 current: Option<Symbol>,
215 root_at: usize,
217 reads: HashSet<*const Expr<'a>>,
219 sets: HashSet<*const Stmt<'a>>,
221 reached: HashMap<Symbol, HashSet<Symbol>>,
223 calls: HashMap<Symbol, HashSet<Symbol>>,
225 root_calls: HashMap<usize, HashSet<Symbol>>,
227 as_values: Vec<(Symbol, &'static str, Option<Symbol>)>,
229 named_slots: Vec<(Symbol, &'static str, Option<Symbol>)>,
232}
233
234impl<'a, 'f> Resolver<'a, 'f> {
235 fn new(functions: &'f HashSet<Symbol>) -> Self {
236 Self {
237 functions,
238 roots: HashSet::new(),
239 defined_at: HashMap::new(),
240 scopes: Vec::new(),
241 current: None,
242 root_at: 0,
243 reads: HashSet::new(),
244 sets: HashSet::new(),
245 reached: HashMap::new(),
246 calls: HashMap::new(),
247 root_calls: HashMap::new(),
248 as_values: Vec::new(),
249 named_slots: Vec::new(),
250 }
251 }
252
253 fn root_statement(&mut self, at: usize, s: &Stmt<'a>) {
254 self.current = None;
255 self.root_at = at;
256 self.stmt(s);
257 }
258
259 fn function(
260 &mut self,
261 name: Symbol,
262 params: impl Iterator<Item = Symbol>,
263 body: &'a [Stmt<'a>],
264 ) {
265 self.current = Some(name);
266 self.scopes = vec![params.collect()];
267 self.stmts(body);
268 self.scopes.clear();
269 self.current = None;
270 }
271
272 fn is_local(&self, name: Symbol) -> bool {
273 self.scopes.iter().any(|scope| scope.contains(&name))
274 }
275
276 fn define(&mut self, name: Symbol) {
279 match self.scopes.last_mut() {
280 Some(scope) => {
281 scope.insert(name);
282 }
283 None => {
284 self.roots.insert(name);
285 self.defined_at.entry(name).or_insert(self.root_at);
286 }
287 }
288 }
289
290 fn reach(&mut self, name: Symbol) {
292 if let Some(f) = self.current {
293 self.reached.entry(f).or_default().insert(name);
294 }
295 }
296
297 fn names_program_binding(&self, name: Symbol) -> bool {
298 !self.is_local(name) && self.roots.contains(&name)
299 }
300
301 fn read(&mut self, e: &'a Expr<'a>, name: Symbol) {
302 if self.names_program_binding(name) {
303 self.reads.insert(e as *const _);
304 self.reach(name);
305 } else if !self.is_local(name) && self.functions.contains(&name) {
306 self.as_values
307 .push((name, "passed as a value", self.current));
308 }
309 }
310
311 fn call(&mut self, function: Symbol) {
312 if self.is_local(function) || !self.functions.contains(&function) {
313 return;
314 }
315 match self.current {
316 Some(f) => {
317 self.calls.entry(f).or_default().insert(function);
318 }
319 None => {
320 self.root_calls
321 .entry(self.root_at)
322 .or_default()
323 .insert(function);
324 }
325 }
326 }
327
328 fn named_write(&mut self, name: Symbol, form: &'static str) {
330 if self.names_program_binding(name) {
331 self.named_slots.push((name, form, self.current));
332 self.reach(name);
333 }
334 }
335
336 fn named_define(&mut self, name: Symbol, form: &'static str) {
339 if self.scopes.is_empty() {
340 self.named_slots.push((name, form, None));
341 }
342 self.define(name);
343 }
344
345 fn stmts(&mut self, block: &'a [Stmt<'a>]) {
346 for s in block {
347 self.stmt(s);
348 }
349 }
350
351 fn scoped(&mut self, binds: impl IntoIterator<Item = Symbol>, block: &'a [Stmt<'a>]) {
352 self.scopes.push(binds.into_iter().collect());
353 self.stmts(block);
354 self.scopes.pop();
355 }
356
357 fn arm(&mut self, arm: &MatchArm<'a>) {
358 self.scoped(arm.bindings.iter().map(|(_, b)| *b), arm.body);
359 }
360
361 fn exprs(&mut self, exprs: &[&'a Expr<'a>]) {
362 for e in exprs {
363 self.expr(e);
364 }
365 }
366
367 fn expr(&mut self, e: &'a Expr<'a>) {
368 match e {
369 Expr::Identifier(name) => self.read(e, *name),
370 Expr::Literal(_) | Expr::Escape { .. } | Expr::OptionNone | Expr::View(_) => {}
371 Expr::BinaryOp { left, right, .. } => {
372 self.expr(left);
373 self.expr(right);
374 }
375 Expr::Not { operand } => self.expr(operand),
376 Expr::Call { function, args } => {
377 self.call(*function);
378 self.exprs(args);
379 }
380 Expr::Index { collection, index } => {
381 self.expr(collection);
382 self.expr(index);
383 }
384 Expr::Slice {
385 collection,
386 start,
387 end,
388 } => {
389 self.expr(collection);
390 self.expr(start);
391 self.expr(end);
392 }
393 Expr::Copy { expr } => self.expr(expr),
394 Expr::Give { value } => self.expr(value),
395 Expr::Length { collection } => self.expr(collection),
396 Expr::Contains { collection, value } => {
397 self.expr(collection);
398 self.expr(value);
399 }
400 Expr::Union { left, right } | Expr::Intersection { left, right } => {
401 self.expr(left);
402 self.expr(right);
403 }
404 Expr::ManifestOf { zone } => self.expr(zone),
405 Expr::ChunkAt { index, zone } => {
406 self.expr(index);
407 self.expr(zone);
408 }
409 Expr::List(items) | Expr::Tuple(items) => self.exprs(items),
410 Expr::Range { start, end } => {
411 self.expr(start);
412 self.expr(end);
413 }
414 Expr::FieldAccess { object, .. } => self.expr(object),
415 Expr::New { init_fields, .. } => {
416 for (_, value) in init_fields {
417 self.expr(value);
418 }
419 }
420 Expr::NewVariant { fields, .. } => {
421 for (_, value) in fields {
422 self.expr(value);
423 }
424 }
425 Expr::OptionSome { value } => self.expr(value),
426 Expr::WithCapacity { value, capacity } => {
427 self.expr(value);
428 self.expr(capacity);
429 }
430 Expr::Closure { params, body, .. } => {
431 self.scopes.push(params.iter().map(|(p, _)| *p).collect());
432 match body {
433 ClosureBody::Expression(x) => self.expr(x),
434 ClosureBody::Block(b) => self.stmts(b),
435 }
436 self.scopes.pop();
437 }
438 Expr::CallExpr { callee, args } => {
439 match callee {
440 Expr::Identifier(f) if !self.is_local(*f) && self.functions.contains(f) => {
441 self.call(*f)
442 }
443 _ => self.expr(callee),
444 }
445 self.exprs(args);
446 }
447 Expr::InterpolatedString(parts) => {
448 for part in parts {
449 if let StringPart::Expr { value, .. } = part {
450 self.expr(value);
451 }
452 }
453 }
454 Expr::Dom(q) => match q {
455 DomQuery::Text { selector }
456 | DomQuery::Count { selector }
457 | DomQuery::Value { selector }
458 | DomQuery::Visible { selector }
459 | DomQuery::Hidden { selector }
460 | DomQuery::Disabled { selector }
461 | DomQuery::Checked { selector }
462 | DomQuery::Focused { selector } => self.expr(selector),
463 DomQuery::Journal
464 | DomQuery::JournalShows { .. }
465 | DomQuery::Title
466 | DomQuery::Url => {}
467 },
468 }
469 }
470
471 fn stmt(&mut self, s: &Stmt<'a>) {
472 match s {
473 Stmt::Let { var, value, .. } => {
474 self.expr(value);
475 self.define(*var);
476 }
477 Stmt::Set { target, value } => {
478 self.expr(value);
479 if self.names_program_binding(*target) {
480 self.sets.insert(s as *const _);
481 self.reach(*target);
482 }
483 }
484 Stmt::Call { function, args } => {
485 self.call(*function);
486 self.exprs(args);
487 }
488 Stmt::If {
489 cond,
490 then_block,
491 else_block,
492 } => {
493 self.expr(cond);
494 self.scoped([], then_block);
495 if let Some(b) = else_block {
496 self.scoped([], b);
497 }
498 }
499 Stmt::While {
500 cond,
501 body,
502 decreasing,
503 } => {
504 self.expr(cond);
505 if let Some(d) = decreasing {
506 self.expr(d);
507 }
508 self.scoped([], body);
509 }
510 Stmt::Repeat {
511 pattern,
512 iterable,
513 body,
514 } => {
515 self.expr(iterable);
516 let binds = match pattern {
517 Pattern::Identifier(x) => vec![*x],
518 Pattern::Tuple(xs) => xs.clone(),
519 };
520 self.scoped(binds, body);
521 }
522 Stmt::Return { value } => {
523 if let Some(v) = value {
524 self.expr(v);
525 }
526 }
527 Stmt::RuntimeAssert {
528 condition, reason, ..
529 } => {
530 self.expr(condition);
531 if let Some(r) = reason {
532 self.expr(r);
533 }
534 }
535 Stmt::Fail { reason } => self.expr(reason),
536 Stmt::Give { object, recipient } => {
537 self.expr(object);
538 self.expr(recipient);
539 }
540 Stmt::Show { object, recipient } => {
541 self.expr(object);
542 match recipient {
543 Expr::Identifier(f) if !self.is_local(*f) && self.functions.contains(f) => {
544 self.call(*f)
545 }
546 _ => self.expr(recipient),
547 }
548 }
549 Stmt::SetField { object, value, .. } => {
550 self.expr(object);
551 self.expr(value);
552 }
553 Stmt::FunctionDef {
554 name, params, body, ..
555 } => {
556 let (outer_scopes, outer) = (std::mem::take(&mut self.scopes), self.current);
557 self.function(*name, params.iter().map(|(p, _)| *p), body);
558 self.scopes = outer_scopes;
559 self.current = outer;
560 }
561 Stmt::TestDef { .. } => {}
563 Stmt::Expect { condition } => self.expr(condition),
564 Stmt::ExpectFail { body, .. } => self.scoped([], body),
565 Stmt::AdvanceTime { millis } => self.expr(millis),
566 Stmt::Observe { payloads, .. } => {
567 for (_, value) in payloads {
568 self.expr(value);
569 }
570 }
571 Stmt::During { body, .. } => self.scoped([], body),
572 Stmt::ForAll {
573 vars,
574 constraint,
575 body,
576 ..
577 } => {
578 self.scopes.push(vars.iter().copied().collect());
579 if let Some(c) = constraint {
580 self.expr(c);
581 }
582 self.stmts(body);
583 self.scopes.pop();
584 }
585 Stmt::Inspect { target, arms, .. } => {
586 self.expr(target);
587 for arm in arms {
588 self.arm(arm);
589 }
590 }
591 Stmt::Push { value, collection }
592 | Stmt::Add { value, collection }
593 | Stmt::Remove { value, collection } => {
594 self.expr(value);
595 self.expr(collection);
596 }
597 Stmt::Pop { collection, into } => {
598 self.expr(collection);
599 if let Some(x) = into {
600 self.named_define(*x, "`Pop … into`");
601 }
602 }
603 Stmt::SetIndex {
604 collection,
605 index,
606 value,
607 } => {
608 self.expr(collection);
609 self.expr(index);
610 self.expr(value);
611 }
612 Stmt::Splice { body } => self.stmts(body),
614 Stmt::Reversibly { body } => self.scoped([], body),
615 Stmt::Zone { body, .. } => self.scoped([], body),
616 Stmt::Concurrent { tasks } | Stmt::Parallel { tasks } => self.scoped([], tasks),
617 Stmt::ReadFrom { var, source } => {
618 if let ReadSource::File(path) = source {
619 self.expr(path);
620 }
621 self.named_define(*var, "`Read … into`");
622 }
623 Stmt::WriteFile { content, path } => {
624 self.expr(content);
625 self.expr(path);
626 }
627 Stmt::SendMessage {
628 message,
629 destination,
630 ..
631 } => {
632 self.expr(message);
633 self.expr(destination);
634 }
635 Stmt::AwaitMessage { source, into, .. } => {
636 self.expr(source);
637 self.named_define(*into, "`Await … into`");
638 }
639 Stmt::StreamMessage {
640 values,
641 destination,
642 } => {
643 self.expr(values);
644 self.expr(destination);
645 }
646 Stmt::MergeCrdt { source, target } | Stmt::JoinCrdt { source, target } => {
647 self.expr(source);
648 self.expr(target);
649 }
650 Stmt::IncreaseCrdt { object, amount, .. }
651 | Stmt::DecreaseCrdt { object, amount, .. } => {
652 self.expr(object);
653 self.expr(amount);
654 }
655 Stmt::AppendToSequence { sequence, value } => {
656 self.expr(sequence);
657 self.expr(value);
658 }
659 Stmt::ResolveConflict { object, value, .. } => {
660 self.expr(object);
661 self.expr(value);
662 }
663 Stmt::Listen { address, secure } | Stmt::ConnectTo { address, secure } => {
664 self.expr(address);
665 if let Some(pad) = secure {
666 self.expr(pad.pad);
667 }
668 }
669 Stmt::Serve { port, handler } => {
670 self.expr(port);
671 match handler {
672 Expr::Identifier(h) if !self.is_local(*h) && self.functions.contains(h) => {
673 self.call(*h)
674 }
675 _ => self.expr(handler),
676 }
677 }
678 Stmt::LetPeerAgent { var, address } => {
679 self.expr(address);
680 self.named_define(*var, "`Let … be a peer agent`");
681 }
682 Stmt::Sleep { milliseconds } => self.expr(milliseconds),
683 Stmt::Sync { var, topic } => {
684 self.expr(topic);
685 self.named_write(*var, "`Sync`");
686 }
687 Stmt::Fetch { url, body, .. } => {
689 self.expr(url);
690 if let Some(b) = body {
691 self.expr(b);
692 }
693 }
694 Stmt::GivenFetch {
695 url, status, body, ..
696 } => {
697 self.expr(url);
698 self.expr(status);
699 self.expr(body);
700 }
701 Stmt::DirectiveDef { .. } | Stmt::MiddlewareDef { .. } => {}
703 Stmt::Mount { var, path } => {
704 self.expr(path);
705 self.named_define(*var, "`Mount … at`");
706 }
707 Stmt::LaunchTask { function, args } => {
708 self.call(*function);
709 if self.functions.contains(function) && !self.is_local(*function) {
710 self.as_values
711 .push((*function, "launched as a task", self.current));
712 }
713 self.exprs(args);
714 }
715 Stmt::LaunchTaskWithHandle {
716 handle,
717 function,
718 args,
719 } => {
720 self.call(*function);
721 if self.functions.contains(function) && !self.is_local(*function) {
722 self.as_values
723 .push((*function, "launched as a task", self.current));
724 }
725 self.exprs(args);
726 self.named_define(*handle, "`Launch a task … with handle`");
727 }
728 Stmt::SendPipe { value, pipe } => {
729 self.expr(value);
730 self.expr(pipe);
731 }
732 Stmt::ReceivePipe { var, pipe } | Stmt::TryReceivePipe { var, pipe } => {
733 self.expr(pipe);
734 self.named_define(*var, "`Receive … from`");
735 }
736 Stmt::TrySendPipe {
737 value,
738 pipe,
739 result,
740 } => {
741 self.expr(value);
742 self.expr(pipe);
743 if let Some(r) = result {
744 self.named_define(*r, "`Try to send … into`");
745 }
746 }
747 Stmt::StopTask { handle } => self.expr(handle),
748 Stmt::CloseChannel { channel } => self.expr(channel),
749 Stmt::AwaitTask { handle, result } => {
750 self.expr(handle);
751 if let Some(r) = result {
752 self.named_define(*r, "`Await … into`");
753 }
754 }
755 Stmt::Select { branches } => {
756 for branch in branches {
757 match branch {
758 SelectBranch::Receive { var, pipe, body } => {
759 self.expr(pipe);
760 self.scoped([*var], body);
761 }
762 SelectBranch::Timeout { milliseconds, body } => {
763 self.expr(milliseconds);
764 self.scoped([], body);
765 }
766 }
767 }
768 }
769 Stmt::Swap { a, b } => {
770 self.named_write(*a, "`Swap`");
771 self.named_write(*b, "`Swap`");
772 }
773 Stmt::CreatePipe { var, .. } => self.named_define(*var, "`Let … be a new Pipe`"),
774 Stmt::Spawn { name, .. } => self.named_define(*name, "`Spawn`"),
775 Stmt::Check { subject, .. } => self.named_write(*subject, "`Check`"),
776 Stmt::Break
777 | Stmt::StructDef { .. }
778 | Stmt::ExpectOutput { .. }
779 | Stmt::GivenFile { .. }
780 | Stmt::Replay { .. }
781 | Stmt::ServeFetches
782 | Stmt::RedirectDef { .. }
783 | Stmt::Escape { .. }
784 | Stmt::Require { .. }
785 | Stmt::Site(_) => {}
786 Stmt::Assert { .. }
787 | Stmt::Trust { .. }
788 | Stmt::Theorem(_)
789 | Stmt::Definition(_)
790 | Stmt::Axiom(_)
791 | Stmt::Theory(_) => {}
792 Stmt::Ui(_) | Stmt::ComponentDef(_) | Stmt::RouteDef { .. } => {}
793 }
794 }
795
796 fn needs(&self) -> HashSet<Symbol> {
798 let mut needs: HashSet<Symbol> = self
799 .reached
800 .iter()
801 .filter(|(_, names)| !names.is_empty())
802 .map(|(f, _)| *f)
803 .collect();
804 loop {
805 let more: Vec<Symbol> = self
806 .calls
807 .iter()
808 .filter(|(f, callees)| {
809 !needs.contains(*f) && callees.iter().any(|c| needs.contains(c))
810 })
811 .map(|(f, _)| *f)
812 .collect();
813 if more.is_empty() {
814 return needs;
815 }
816 needs.extend(more);
817 }
818 }
819
820 fn reach_through_calls(&self) -> HashMap<Symbol, HashSet<Symbol>> {
822 let mut reach: HashMap<Symbol, HashSet<Symbol>> = self.reached.clone();
823 loop {
824 let mut grew = false;
825 for (f, callees) in &self.calls {
826 let inherited: HashSet<Symbol> = callees
827 .iter()
828 .filter_map(|c| reach.get(c))
829 .flatten()
830 .copied()
831 .collect();
832 let own = reach.entry(*f).or_default();
833 let before = own.len();
834 own.extend(inherited);
835 grew |= own.len() != before;
836 }
837 if !grew {
838 return reach;
839 }
840 }
841 }
842
843 fn refusals(
845 &self,
846 lifted: &HashSet<Symbol>,
847 needs: &HashSet<Symbol>,
848 reach: &HashMap<Symbol, HashSet<Symbol>>,
849 interner: &Interner,
850 ) -> Result<(), String> {
851 let name = |s: Symbol| interner.resolve(s).to_string();
852 let within = |w: Option<Symbol>| match w {
853 Some(f) => format!("in `{}`", interner.resolve(f)),
854 None => "in `## Main`".to_string(),
855 };
856 let reached_of = |f: Symbol| -> String {
857 let mut names: Vec<String> = reach
858 .get(&f)
859 .into_iter()
860 .flatten()
861 .filter(|x| lifted.contains(x))
862 .map(|x| name(*x))
863 .collect();
864 names.sort();
865 names.join("`, `")
866 };
867 for (binding, form, at) in &self.named_slots {
868 if lifted.contains(binding) {
869 return Err(format!(
870 "{form} {} writes `{}`, one of the program's bindings that a function reads. On this \
871 tier such a binding is made with `Let {} be …` and changed with `Set {} to …`.",
872 within(*at),
873 name(*binding),
874 name(*binding),
875 name(*binding)
876 ));
877 }
878 }
879 for (function, how, at) in &self.as_values {
880 if needs.contains(function) {
881 return Err(format!(
882 "`{}` is {how} {}, and it reaches the program's `{}`. On this tier a function that \
883 reaches the program's bindings runs only where it is called.",
884 name(*function),
885 within(*at),
886 reached_of(*function)
887 ));
888 }
889 }
890 let mut calls: Vec<(usize, Symbol)> = self
891 .root_calls
892 .iter()
893 .flat_map(|(at, fs)| fs.iter().map(move |f| (*at, *f)))
894 .collect();
895 calls.sort_by_key(|(at, f)| (*at, name(*f)));
896 for (at, function) in calls {
897 let mut early: Vec<Symbol> = reach
898 .get(&function)
899 .into_iter()
900 .flatten()
901 .filter(|x| lifted.contains(x) && self.defined_at.get(x).is_none_or(|d| *d >= at))
902 .copied()
903 .collect();
904 early.sort_by_key(|x| name(*x));
905 if let Some(binding) = early.first() {
906 return Err(format!(
907 "`{}` reads the program's `{}`, and `## Main` calls it before `Let {} be …` has run. \
908 Declare `{}` before the first call.",
909 name(function),
910 name(*binding),
911 name(*binding),
912 name(*binding)
913 ));
914 }
915 }
916 Ok(())
917 }
918}
919
920struct Lift<'a, 's> {
923 arenas: Arenas<'a>,
924 reads: &'s HashSet<*const Expr<'a>>,
925 sets: &'s HashSet<*const Stmt<'a>>,
926 lifted: &'s HashSet<Symbol>,
927 needs: &'s HashSet<Symbol>,
928 program: Symbol,
929 lent: &'a TypeExpr<'a>,
930}
931
932impl<'a, 's> Lift<'a, 's> {
933 fn program_ref(&self) -> &'a Expr<'a> {
934 self.arenas.exprs.alloc(Expr::Identifier(self.program))
935 }
936
937 fn lent_args(&mut self, args: &[&'a Expr<'a>]) -> Vec<&'a Expr<'a>> {
938 let mut out = Vec::with_capacity(args.len() + 1);
939 out.push(self.program_ref());
940 out.extend(args.iter().map(|a| self.map_expr(a)));
941 out
942 }
943}
944
945impl<'a, 's> AstMap<'a> for Lift<'a, 's> {
946 fn arenas(&self) -> Arenas<'a> {
947 self.arenas
948 }
949
950 fn map_expr(&mut self, e: &'a Expr<'a>) -> &'a Expr<'a> {
951 match e {
952 Expr::Identifier(name)
953 if self.lifted.contains(name) && self.reads.contains(&(e as *const _)) =>
954 {
955 let object = self.program_ref();
956 self.arenas.exprs.alloc(Expr::FieldAccess {
957 object,
958 field: *name,
959 })
960 }
961 Expr::Call { function, args } if self.needs.contains(function) => {
962 let args = self.lent_args(args);
963 self.arenas.exprs.alloc(Expr::Call {
964 function: *function,
965 args,
966 })
967 }
968 Expr::CallExpr {
969 callee: Expr::Identifier(f),
970 args,
971 } if self.needs.contains(f) => {
972 let args = self.lent_args(args);
973 self.arenas.exprs.alloc(Expr::Call { function: *f, args })
974 }
975 _ => ast_map::walk_expr(self, e),
976 }
977 }
978
979 fn map_stmt(&mut self, s: &Stmt<'a>) -> Stmt<'a> {
980 match s {
981 Stmt::Set { target, value }
982 if self.lifted.contains(target) && self.sets.contains(&(s as *const _)) =>
983 {
984 let value = self.map_expr(value);
985 Stmt::SetField {
986 object: self.program_ref(),
987 field: *target,
988 value,
989 }
990 }
991 Stmt::Call { function, args } if self.needs.contains(function) => Stmt::Call {
992 function: *function,
993 args: self.lent_args(args),
994 },
995 Stmt::Show {
996 object,
997 recipient: Expr::Identifier(f),
998 } if self.needs.contains(f) => Stmt::Call {
999 function: *f,
1000 args: self.lent_args(&[*object]),
1001 },
1002 Stmt::FunctionDef {
1003 name,
1004 generics,
1005 params,
1006 body,
1007 return_type,
1008 is_native,
1009 native_path,
1010 is_exported,
1011 export_target,
1012 opt_flags,
1013 } if self.needs.contains(name) => {
1014 let mut lent_params = Vec::with_capacity(params.len() + 1);
1015 lent_params.push((self.program, self.lent));
1016 lent_params.extend(params.iter().map(|(p, t)| (*p, self.map_type(t))));
1017 Stmt::FunctionDef {
1018 name: *name,
1019 generics: generics.clone(),
1020 params: lent_params,
1021 body: self.map_block(body),
1022 return_type: return_type.map(|t| self.map_type(t)),
1023 is_native: *is_native,
1024 native_path: *native_path,
1025 is_exported: *is_exported,
1026 export_target: *export_target,
1027 opt_flags: *opt_flags,
1028 }
1029 }
1030 _ => ast_map::walk_stmt(self, s),
1031 }
1032 }
1033}
1034
1035#[cfg(test)]
1036#[path = "../../logicaffeine_tests/unit/logicaffeine_compile/program_bindings/tests.rs"]
1037mod tests;