beck_core/
lib.rs

1//! Types, `Core`, placement and splitting — stages 4 through 8 of §4.1's pipeline.
2//!
3//! ```text
4//!  4 Resolve    modules, imports, name binding, hygiene scopes
5//!  5 Typecheck  HM → typed AST
6//!  6 Lower      desugar to CORE
7//!  7 PLACE      ◀── the product.  every Core node carries a tier
8//!  8 Split      partition Core per tier; SYNTHESISE boundaries
9//! ```
10//!
11//! Stages 4–6 are one pass ([`check`]) because §4.2 permits three IRs and no more: a separate
12//! resolved-but-untyped tree would be a fourth. Stages 7 and 8 are [`place`] and [`split`], which
13//! §4.1 calls out as "novel … where the engineering budget goes".
14
15pub mod backend;
16pub mod bundle;
17pub mod check;
18pub mod clock;
19pub mod command;
20pub mod compat;
21pub mod core;
22pub mod cost;
23pub mod delta;
24pub mod diff;
25pub mod digest;
26pub mod docgen;
27pub mod edge;
28pub mod engine;
29pub mod fields;
30pub mod frames;
31pub mod fuse;
32pub mod gen;
33pub mod graph;
34pub mod html;
35pub mod iface;
36pub mod incremental;
37pub mod liveness;
38pub mod net;
39pub mod place;
40pub mod plan;
41pub mod pmap;
42pub mod prelude;
43pub mod project;
44pub mod read;
45pub mod render;
46pub mod repr;
47pub mod row;
48pub mod secure;
49pub mod signal;
50pub mod split;
51pub mod stdlib;
52pub mod testing;
53pub mod ty;
54
55pub use backend::{Backend, Callable, ExecError};
56pub use bundle::Bundle;
57pub use check::{check_module, Def, Program, SignalDecl};
58pub use compat::{compare, is_breaking, Change};
59pub use core::{digest, Const, Core, CoreKind, Env, Prim, Value, VarId};
60pub use diff::{diff, Op as DiffOp, Path as DiffPath};
61pub use graph::{DepGraph, EdgeKind, GraphBuilder, GraphNode, NodeId, NodeKind};
62pub use html::Html;
63pub use iface::Interface;
64pub use place::{Key, Lock, Method, Solution};
65pub use pmap::PMap;
66pub use project::{compile_project, Sources};
67pub use row::{Ambient, Effect, Row};
68pub use secure::{sendable, storable, NotSendable};
69pub use signal::{Cut, Graph as SignalGraph, Op as SignalOp, SigId};
70pub use split::{Placed, Roles, StateRole};
71pub use testing::{Clause, Expectation, TestDef};
72pub use ty::{Tier, Ty, TyDecl};
73
74use beck_diag::{Diagnostics, FileId, SourceMap};
75
76/// The whole front end: parse, expand, check, place, split.
77///
78/// One function, so `beck check`, `beck run`, `beck build` and the test harnesses cannot drift
79/// apart — §4.6's "one binary serves `beck build`, `beck check`, `beck lsp` and `beck explain`;
80/// there is no separate language server implementation to drift."
81pub fn compile(file: FileId, name: &str, src: &str, diags: &mut Diagnostics) -> Option<Placed> {
82    compile_with(file, name, src, None, diags)
83}
84
85/// The same, against a previously solved placement — §3.4's stability guardrail.
86pub fn compile_with(
87    file: FileId,
88    name: &str,
89    src: &str,
90    lock: Option<&Lock>,
91    diags: &mut Diagnostics,
92) -> Option<Placed> {
93    let parsed = beck_syntax::parse_file(file, name, src, diags);
94    let expanded = beck_macro::expand_module(&parsed, diags);
95    let mut program = check_module(&expanded, diags);
96    // Stage 7: solve first, then verify. Verification runs over the *solved* tiers as well as the
97    // written ones, so an annotation and an inference are held to one standard.
98    let solution = place::solve(&program, lock);
99    place::apply(&mut program, &solution);
100    place::check_placement(&program, diags);
101    secure::check_security(&program, diags);
102    if diags.has_errors() {
103        return None;
104    }
105    // Three facts about the finished program, computed once for every backend rather than by one
106    // of them: which read of a local is its last (`liveness`), how many bindings each body makes,
107    // so a call can reserve them in one frame (`frames`), and where a record literal's fields go,
108    // so building one places them rather than sorting them (`fields`).
109    liveness::mark_program(&mut program);
110    frames::reserve_program(&mut program);
111    fields::order_program(&mut program);
112    let mut placed = split::split(program, diags)?;
113    placed.placement = solution;
114    Some(placed)
115}
116
117/// Parse, expand and check one source string, stopping before placement.
118///
119/// The shape a test that is interested in *inference* wants: a program whose rows are known even
120/// when its placement is the thing under test.
121pub fn check_str(name: &str, src: &str) -> (check::Program, Diagnostics, SourceMap) {
122    let mut map = SourceMap::new();
123    let file = map.add(name, src);
124    let mut diags = Diagnostics::new();
125    let parsed = beck_syntax::parse_file(file, name, src, &mut diags);
126    let expanded = beck_macro::expand_module(&parsed, &mut diags);
127    let program = check_module(&expanded, &mut diags);
128    (program, diags, map)
129}
130
131/// The same, admitting a **library**: a module with no merge point comes back as a
132/// [`split::Placed`] whose roles are placeholders rather than as `None`.
133///
134/// Separate from [`compile_str`] rather than replacing it, because "this compiled" and "this is an
135/// application" are different questions and every existing caller is asking the second. `beck test`
136/// asks the first (docs/27 §27.4); so does anything that only wants the module's definitions.
137pub fn compile_or_library_str(name: &str, src: &str) -> (Option<Placed>, Diagnostics, SourceMap) {
138    let mut map = SourceMap::new();
139    let file = map.add(name, src);
140    let mut diags = Diagnostics::new();
141    let parsed = beck_syntax::parse_file(file, name, src, &mut diags);
142    let expanded = beck_macro::expand_module(&parsed, &mut diags);
143    let mut program = check_module(&expanded, &mut diags);
144    let solution = place::solve(&program, None);
145    place::apply(&mut program, &solution);
146    place::check_placement(&program, &mut diags);
147    secure::check_security(&program, &mut diags);
148    if diags.has_errors() {
149        return (None, diags, map);
150    }
151    liveness::mark_program(&mut program);
152    frames::reserve_program(&mut program);
153    fields::order_program(&mut program);
154    let interface = iface::Interface::of(&program);
155    let placed = project::slice_or_library(
156        project::Project {
157            program,
158            solution,
159            interface,
160        },
161        &mut diags,
162    );
163    (placed, diags, map)
164}
165
166/// Compile one source string against a fresh source map. The shape every test uses.
167pub fn compile_str(name: &str, src: &str) -> (Option<Placed>, Diagnostics, SourceMap) {
168    let mut map = SourceMap::new();
169    let file = map.add(name, src);
170    let mut diags = Diagnostics::new();
171    let placed = compile(file, name, src, &mut diags);
172    (placed, diags, map)
173}