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