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;
7pub mod dafny;
8pub mod lean;
9pub mod rust;
10
11use std::collections::{HashMap, HashSet};
12
13use crate::ast::{FnDef, TopLevel, TypeDef};
14use crate::types::checker::TypeCheckResult;
15
16/// Information about a dependent module loaded for codegen.
17pub struct ModuleInfo {
18    /// Qualified module path, e.g. "Models.User".
19    pub prefix: String,
20    /// Direct `depends [...]` entries from the source module.
21    pub depends: Vec<String>,
22    /// Type definitions from the module.
23    pub type_defs: Vec<TypeDef>,
24    /// Function definitions from the module (excluding `main`).
25    pub fn_defs: Vec<FnDef>,
26}
27
28/// Collected context from the Aver program, shared across all backends.
29pub struct CodegenContext {
30    /// All top-level items (post-TCO transform, post-typecheck).
31    pub items: Vec<TopLevel>,
32    /// Function signatures: name → (param_types, return_type, effects).
33    pub fn_sigs: HashMap<String, (Vec<crate::types::Type>, crate::types::Type, Vec<String>)>,
34    /// Functions eligible for auto-memoization.
35    pub memo_fns: HashSet<String>,
36    /// Set of type names whose values are memo-safe.
37    pub memo_safe_types: HashSet<String>,
38    /// User-defined type definitions (for struct/enum generation).
39    pub type_defs: Vec<TypeDef>,
40    /// User-defined function definitions.
41    pub fn_defs: Vec<FnDef>,
42    /// Project/binary name.
43    pub project_name: String,
44    /// Dependent modules loaded for inlining.
45    pub modules: Vec<ModuleInfo>,
46    /// Set of module prefixes for qualified name resolution (e.g. "Models.User").
47    pub module_prefixes: HashSet<String>,
48    /// Embedded runtime policy from `aver.toml` for generated code.
49    pub policy: Option<crate::config::ProjectConfig>,
50    /// Emit generated scoped runtime support (replay and/or runtime-loaded policy).
51    pub emit_replay_runtime: bool,
52    /// Load runtime policy from the active module root instead of embedding it.
53    pub runtime_policy_from_env: bool,
54    /// Explicit guest entry boundary for scoped replay/policy.
55    pub guest_entry: Option<String>,
56    /// Emit extra generated helpers needed only by the cached self-host helper.
57    pub emit_self_host_support: bool,
58    /// Extra fn_defs visible during current module emission (not in `fn_defs` or `modules`).
59    /// Set temporarily by the Rust backend when emitting a dependent module so that
60    /// `find_fn_def_by_name` can resolve same-module calls.
61    pub extra_fn_defs: Vec<FnDef>,
62    /// Functions that are part of a mutual-TCO SCC group (emitted as trampoline + wrappers).
63    /// Functions NOT in this set but with TailCalls are emitted as plain self-TCO loops.
64    pub mutual_tco_members: HashSet<String>,
65}
66
67/// Output files from a codegen backend.
68pub struct ProjectOutput {
69    /// Files to write: (relative_path, content).
70    pub files: Vec<(String, String)>,
71}
72
73/// Build a CodegenContext from parsed + type-checked items.
74pub fn build_context(
75    items: Vec<TopLevel>,
76    tc_result: &TypeCheckResult,
77    memo_fns: HashSet<String>,
78    project_name: String,
79    modules: Vec<ModuleInfo>,
80) -> CodegenContext {
81    let type_defs: Vec<TypeDef> = items
82        .iter()
83        .filter_map(|item| {
84            if let TopLevel::TypeDef(td) = item {
85                Some(td.clone())
86            } else {
87                None
88            }
89        })
90        .collect();
91
92    let fn_defs: Vec<FnDef> = items
93        .iter()
94        .filter_map(|item| {
95            if let TopLevel::FnDef(fd) = item {
96                Some(fd.clone())
97            } else {
98                None
99            }
100        })
101        .collect();
102
103    let module_prefixes: HashSet<String> = modules.iter().map(|m| m.prefix.clone()).collect();
104
105    // Compute which functions are in mutual-TCO SCC groups (emitted as trampoline + wrappers).
106    let mut mutual_tco_members = HashSet::new();
107    {
108        // Entry module (non-main)
109        let entry_fns: Vec<&FnDef> = fn_defs.iter().filter(|fd| fd.name != "main").collect();
110        for group in crate::call_graph::tailcall_scc_components(&entry_fns) {
111            for fd in &group {
112                mutual_tco_members.insert(fd.name.clone());
113            }
114        }
115        // Dependent modules
116        for module in &modules {
117            let mod_fns: Vec<&FnDef> = module.fn_defs.iter().collect();
118            for group in crate::call_graph::tailcall_scc_components(&mod_fns) {
119                for fd in &group {
120                    mutual_tco_members.insert(fd.name.clone());
121                }
122            }
123        }
124    }
125
126    CodegenContext {
127        items,
128        fn_sigs: tc_result.fn_sigs.clone(),
129        memo_fns,
130        memo_safe_types: tc_result.memo_safe_types.clone(),
131        type_defs,
132        fn_defs,
133        project_name,
134        modules,
135        module_prefixes,
136        policy: None,
137        emit_replay_runtime: false,
138        runtime_policy_from_env: false,
139        guest_entry: None,
140        emit_self_host_support: false,
141        extra_fn_defs: Vec::new(),
142        mutual_tco_members,
143    }
144}