Skip to main content

aver/codegen/
mod.rs

1/// Aver → target language transpilation.
2///
3/// The codegen module transforms a type-checked Aver AST into source code
4/// for a target language. Current backends: Rust deployment and Lean proof export.
5pub(crate) mod builtin_helpers;
6pub(crate) mod builtin_records;
7pub(crate) mod builtins;
8pub(crate) mod common;
9#[cfg(feature = "runtime")]
10pub mod dafny;
11#[cfg(feature = "runtime")]
12pub mod lean;
13#[cfg(feature = "runtime")]
14pub mod recursion;
15#[cfg(feature = "runtime")]
16pub mod rust;
17#[cfg(feature = "wasm-compile")]
18pub mod wasm;
19
20use std::collections::{HashMap, HashSet};
21
22use crate::ast::{FnDef, TopLevel, TypeDef};
23use crate::types::checker::TypeCheckResult;
24
25/// Information about a dependent module loaded for codegen.
26pub struct ModuleInfo {
27    /// Qualified module path, e.g. "Models.User".
28    pub prefix: String,
29    /// Direct `depends [...]` entries from the source module.
30    pub depends: Vec<String>,
31    /// Type definitions from the module.
32    pub type_defs: Vec<TypeDef>,
33    /// Function definitions from the module (excluding `main`).
34    pub fn_defs: Vec<FnDef>,
35}
36
37/// Collected context from the Aver program, shared across all backends.
38pub struct CodegenContext {
39    /// All top-level items (post-TCO transform, post-typecheck).
40    pub items: Vec<TopLevel>,
41    /// Function signatures: name → (param_types, return_type, effects).
42    pub fn_sigs: HashMap<String, (Vec<crate::types::Type>, crate::types::Type, Vec<String>)>,
43    /// Functions eligible for auto-memoization.
44    pub memo_fns: HashSet<String>,
45    /// Set of type names whose values are memo-safe.
46    pub memo_safe_types: HashSet<String>,
47    /// User-defined type definitions (for struct/enum generation).
48    pub type_defs: Vec<TypeDef>,
49    /// User-defined function definitions.
50    pub fn_defs: Vec<FnDef>,
51    /// Project/binary name.
52    pub project_name: String,
53    /// Dependent modules loaded for inlining.
54    pub modules: Vec<ModuleInfo>,
55    /// Set of module prefixes for qualified name resolution (e.g. "Models.User").
56    pub module_prefixes: HashSet<String>,
57    /// Embedded runtime policy from `aver.toml` for generated code.
58    #[cfg(feature = "runtime")]
59    pub policy: Option<crate::config::ProjectConfig>,
60    /// Emit generated scoped runtime support (replay and/or runtime-loaded policy).
61    pub emit_replay_runtime: bool,
62    /// Load runtime policy from the active module root instead of embedding it.
63    pub runtime_policy_from_env: bool,
64    /// Explicit guest entry boundary for scoped replay/policy.
65    pub guest_entry: Option<String>,
66    /// Emit extra generated helpers needed only by the cached self-host helper.
67    pub emit_self_host_support: bool,
68    /// Extra fn_defs visible during current module emission (not in `fn_defs` or `modules`).
69    /// Set temporarily by the Rust backend when emitting a dependent module so that
70    /// `find_fn_def_by_name` can resolve same-module calls.
71    pub extra_fn_defs: Vec<FnDef>,
72    /// Functions that are part of a mutual-TCO SCC group (emitted as trampoline + wrappers).
73    /// Functions NOT in this set but with TailCalls are emitted as plain self-TCO loops.
74    pub mutual_tco_members: HashSet<String>,
75}
76
77/// Output files from a codegen backend.
78pub struct ProjectOutput {
79    /// Files to write: (relative_path, content).
80    pub files: Vec<(String, String)>,
81}
82
83/// Build a CodegenContext from parsed + type-checked items.
84pub fn build_context(
85    items: Vec<TopLevel>,
86    tc_result: &TypeCheckResult,
87    memo_fns: HashSet<String>,
88    project_name: String,
89    modules: Vec<ModuleInfo>,
90) -> CodegenContext {
91    let type_defs: Vec<TypeDef> = items
92        .iter()
93        .filter_map(|item| {
94            if let TopLevel::TypeDef(td) = item {
95                Some(td.clone())
96            } else {
97                None
98            }
99        })
100        .collect();
101
102    let fn_defs: Vec<FnDef> = items
103        .iter()
104        .filter_map(|item| {
105            if let TopLevel::FnDef(fd) = item {
106                Some(fd.clone())
107            } else {
108                None
109            }
110        })
111        .collect();
112
113    let module_prefixes: HashSet<String> = modules.iter().map(|m| m.prefix.clone()).collect();
114
115    // Compute which functions are in mutual-TCO SCC groups (emitted as trampoline + wrappers).
116    let mut mutual_tco_members = HashSet::new();
117    {
118        // Entry module (non-main)
119        let entry_fns: Vec<&FnDef> = fn_defs.iter().filter(|fd| fd.name != "main").collect();
120        for group in crate::call_graph::tailcall_scc_components(&entry_fns) {
121            for fd in &group {
122                mutual_tco_members.insert(fd.name.clone());
123            }
124        }
125        // Dependent modules
126        for module in &modules {
127            let mod_fns: Vec<&FnDef> = module.fn_defs.iter().collect();
128            for group in crate::call_graph::tailcall_scc_components(&mod_fns) {
129                for fd in &group {
130                    mutual_tco_members.insert(fd.name.clone());
131                }
132            }
133        }
134    }
135
136    // Start with checker's fn_sigs (exposed API), then add signatures for
137    // ALL module functions (including private helpers) via SymbolRegistry.
138    // Codegen emits full module implementations, so it needs signatures for
139    // intra-module calls that the checker intentionally omits.
140    let mut fn_sigs = tc_result.fn_sigs.clone();
141    {
142        let pairs: Vec<(String, Vec<TopLevel>)> = modules
143            .iter()
144            .map(|m| {
145                let items: Vec<TopLevel> = m
146                    .fn_defs
147                    .iter()
148                    .map(|fd| TopLevel::FnDef(fd.clone()))
149                    .chain(m.type_defs.iter().map(|td| TopLevel::TypeDef(td.clone())))
150                    .collect();
151                (m.prefix.clone(), items)
152            })
153            .collect();
154        let registry = crate::visibility::SymbolRegistry::from_modules_all(&pairs);
155        for entry in &registry.entries {
156            if fn_sigs.contains_key(&entry.canonical_name) {
157                continue;
158            }
159            if let crate::visibility::SymbolKind::Function {
160                params,
161                return_type,
162                effects,
163                ..
164            } = &entry.kind
165            {
166                let parsed_params: Vec<crate::types::Type> = params
167                    .iter()
168                    .map(|(_, ty_str)| crate::types::parse_type_str(ty_str))
169                    .collect();
170                let ret = crate::types::parse_type_str(return_type);
171                fn_sigs.insert(
172                    entry.canonical_name.clone(),
173                    (parsed_params, ret, effects.clone()),
174                );
175            }
176        }
177    }
178
179    CodegenContext {
180        items,
181        fn_sigs,
182        memo_fns,
183        memo_safe_types: tc_result.memo_safe_types.clone(),
184        type_defs,
185        fn_defs,
186        project_name,
187        modules,
188        module_prefixes,
189        #[cfg(feature = "runtime")]
190        policy: None,
191        emit_replay_runtime: false,
192        runtime_policy_from_env: false,
193        guest_entry: None,
194        emit_self_host_support: false,
195        extra_fn_defs: Vec::new(),
196        mutual_tco_members,
197    }
198}