//! Lowering from Core IR to LLVM IR.
//!
//! This module implements the translation from BHC's Core IR to LLVM IR.
//! The lowering handles:
//!
//! - Literals: Int, Double, Char, String
//! - Variables: Mapped to LLVM SSA values
//! - Function applications: Compiled to calls
//! - Lambdas: Compiled to closures or direct functions
//! - Let bindings: Compiled to LLVM allocas/phis
//! - Case expressions: Compiled to switch/branch
use crate::{CodegenError, CodegenResult};
use bhc_core::{Alt, AltCon, Bind, CoreModule, DataCon, Expr, ForeignImport, Literal, Var, VarId};
use bhc_intern::Symbol;
use rustc_hash::FxHashSet;
/// Primitive operations that compile directly to LLVM instructions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PrimOp {
// Arithmetic
Add,
Sub,
Mul,
Div,
Mod,
Rem,
Quot,
// Comparison
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
// Boolean
And,
Or,
Not,
// Unary numeric
Negate,
Abs,
Signum,
// Bitwise
BitAnd,
BitOr,
BitXor,
ShiftL,
ShiftR,
Complement,
}
use bhc_index::Idx;
use bhc_types::Ty;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::types::{BasicType, BasicTypeEnum};
use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, IntValue, PointerValue};
use rustc_hash::FxHashMap;
use super::context::LlvmContext;
use super::module::LlvmModule;
use super::types::TypeMapper;
/// FFI type representation for foreign import marshalling.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FfiType {
/// 8-bit integer (CChar, Int8, Word8).
Int8,
/// 32-bit integer (CInt on some platforms, Int32).
Int32,
/// 64-bit integer (Int, CSize, Word64, etc.).
Int64,
/// 32-bit float (CFloat, Float).
Float,
/// 64-bit float (CDouble, Double).
Double,
/// Opaque pointer (Ptr a, FunPtr a, CString).
Ptr,
/// Void (unit return).
Void,
}
/// Metadata about a data constructor, used for code generation.
#[derive(Clone, Debug)]
pub struct ConstructorMeta {
/// The constructor's tag (0-based index within the data type).
pub tag: u32,
/// The number of fields this constructor has.
pub arity: u32,
/// The data type this constructor belongs to (if known).
pub type_name: Option<String>,
/// Whether this constructor is a newtype constructor (identity at runtime).
pub is_newtype: bool,
}
/// A symbol exported by an already-compiled module, used for cross-module linking.
#[derive(Clone, Debug)]
pub struct CompiledSymbol {
/// The source-level name (interned).
pub name: Symbol,
/// The LLVM symbol name (module-qualified).
pub llvm_name: String,
/// Number of parameters the function takes.
pub param_count: usize,
}
/// A single layer in a transformer stack.
///
/// Used to track which transformer's bind/return semantics to use during lowering,
/// and to enable automatic lift insertion for cross-transformer operations.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum TransformerLayer {
IO,
ReaderT,
StateT,
ExceptT,
WriterT,
}
/// Coercion mode for type-specialized show functions.
#[derive(Clone, Copy, Debug)]
enum ShowCoerce {
/// Coerce to i64 for show_int
Int,
/// Coerce to f64 (bitcast from i64) for show_double
Double,
/// Coerce to f32 for show_float
Float,
/// Coerce to i32 (truncate from i64) for show_char
Char,
/// Extract ADT tag (i64) for show_bool
Bool,
/// Pass pointer directly for show_string ([Char])
StringList,
/// Pass pointer + elem type tag for show_list
List,
/// Pass pointer + elem type tag for show_maybe
MaybeOf,
/// Pass pointer + left/right type tags for show_either
EitherOf,
/// Pass pointer + fst/snd type tags for show_tuple2
Tuple2Of,
/// Pass pointer directly for show_unit
Unit,
/// Extract ADT tag (i64) for show_ordering
Ordering,
/// Pass pointer directly for show_integer (arbitrary precision)
Integer,
}
/// A stack of transformer layers, tracking the full transformer stack.
///
/// The stack is ordered [outermost, ..., IO], so the current (outermost) layer
/// is at index 0. This enables automatic lift insertion when an operation's
/// native layer doesn't match the stack top.
#[derive(Clone, Debug)]
struct TransformerStack {
layers: Vec<TransformerLayer>,
}
impl TransformerStack {
/// Create a new stack with just IO at the base.
fn new() -> Self {
Self {
layers: vec![TransformerLayer::IO],
}
}
/// Push a new transformer layer onto the stack (becomes the new outermost).
fn push(&mut self, layer: TransformerLayer) {
self.layers.insert(0, layer);
}
/// Pop the outermost transformer layer. Returns None if only IO remains.
fn pop(&mut self) -> Option<TransformerLayer> {
if self.layers.len() > 1 {
Some(self.layers.remove(0))
} else {
None
}
}
/// Get the current (outermost) transformer layer.
fn current(&self) -> TransformerLayer {
self.layers.first().copied().unwrap_or(TransformerLayer::IO)
}
/// Calculate how many lifts are needed to reach the target layer.
/// Returns None if the target layer is not in the stack.
fn lifts_to_reach(&self, target: TransformerLayer) -> Option<usize> {
self.layers.iter().position(|&l| l == target)
}
/// Check if the stack contains the given layer.
fn contains(&self, layer: TransformerLayer) -> bool {
self.layers.contains(&layer)
}
/// Check if this is a nested StateT over ReaderT context.
///
/// Returns true if the current layer is StateT and ReaderT is in the stack below it.
fn is_state_t_over_reader_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::StateT
&& self
.layers
.iter()
.skip(1)
.any(|&l| l == TransformerLayer::ReaderT)
}
/// Check if this is a genuine nested ReaderT over StateT context.
///
/// Returns true if the top two layers are [ReaderT, StateT] with no additional
/// transformer layers below (only IO or empty). This distinguishes genuine RtOvSt
/// from auto-lift artifacts in StOvRt, where the stack looks like
/// [ReaderT, StateT, StateT, ReaderT, IO].
fn is_reader_t_over_state_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::ReaderT
&& self.layers[1] == TransformerLayer::StateT
&& !self
.layers
.iter()
.skip(2)
.any(|&l| l == TransformerLayer::StateT || l == TransformerLayer::ReaderT)
}
/// Check if this is a nested ExceptT over StateT context.
///
/// Returns true if the top two layers are [ExceptT, StateT] with no additional
/// ExceptT/StateT/ReaderT below. Excludes auto-lift transient stacks.
fn is_except_t_over_state_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::ExceptT
&& self.layers[1] == TransformerLayer::StateT
&& !self.layers.iter().skip(2).any(|&l| {
l == TransformerLayer::ExceptT
|| l == TransformerLayer::StateT
|| l == TransformerLayer::ReaderT
})
}
/// Check if this is a nested ExceptT over ReaderT context.
///
/// Returns true if the top two layers are [ExceptT, ReaderT] with no additional
/// ExceptT/StateT/ReaderT below. Excludes auto-lift transient stacks.
fn is_except_t_over_reader_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::ExceptT
&& self.layers[1] == TransformerLayer::ReaderT
&& !self.layers.iter().skip(2).any(|&l| {
l == TransformerLayer::ExceptT
|| l == TransformerLayer::StateT
|| l == TransformerLayer::ReaderT
})
}
/// Check if this is a nested WriterT over StateT context.
///
/// Returns true if the top two layers are [WriterT, StateT] with no additional
/// WriterT/StateT/ReaderT below. Excludes auto-lift transient stacks.
fn is_writer_t_over_state_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::WriterT
&& self.layers[1] == TransformerLayer::StateT
&& !self.layers.iter().skip(2).any(|&l| {
l == TransformerLayer::WriterT
|| l == TransformerLayer::StateT
|| l == TransformerLayer::ReaderT
})
}
/// Check if this is a nested WriterT over ReaderT context.
///
/// Returns true if the top two layers are [WriterT, ReaderT] with no additional
/// WriterT/StateT/ReaderT below. Excludes auto-lift transient stacks.
fn is_writer_t_over_reader_t(&self) -> bool {
self.layers.len() >= 2
&& self.layers[0] == TransformerLayer::WriterT
&& self.layers[1] == TransformerLayer::ReaderT
&& !self.layers.iter().skip(2).any(|&l| {
l == TransformerLayer::WriterT
|| l == TransformerLayer::StateT
|| l == TransformerLayer::ReaderT
})
}
}
/// State for lowering Core IR to LLVM IR.
///
/// The struct has two lifetimes:
/// - `'ctx`: The LLVM context lifetime (for types and values)
/// - `'m`: The module reference lifetime (can be shorter than 'ctx)
pub struct Lowering<'ctx, 'm> {
/// The underlying LLVM context (borrowed from LlvmContext).
llvm_ctx: &'ctx Context,
/// The LLVM module being generated.
module: &'m LlvmModule<'ctx>,
/// Mapping from Core variables to LLVM values.
env: FxHashMap<VarId, BasicValueEnum<'ctx>>,
/// Mapping from Core variables to LLVM functions (for top-level bindings).
functions: FxHashMap<VarId, FunctionValue<'ctx>>,
/// Counter for generating unique closure names.
closure_counter: u32,
/// Mapping from constructor names to metadata (tag, arity).
/// This is populated from DataCon entries in case alternatives.
constructor_metadata: FxHashMap<String, ConstructorMeta>,
/// Whether we're currently lowering an expression in tail position.
/// Used for tail call optimization.
in_tail_position: bool,
/// Optional module name for symbol mangling in multi-module compilation.
module_name: Option<String>,
/// External functions imported from other compiled modules.
external_functions: FxHashMap<Symbol, FunctionValue<'ctx>>,
/// Stack tracking the full transformer stack for automatic lift insertion.
/// Pushed when entering transformer runners (runReaderT, evalStateT, etc.).
transformer_stack: TransformerStack,
/// Set of function names whose bodies use StateT operations.
/// Used to detect monad context when these functions appear in bind chains.
state_t_functions: FxHashSet<String>,
/// Set of function names whose bodies use ReaderT operations.
reader_t_functions: FxHashSet<String>,
/// Set of function names whose bodies use ExceptT operations.
except_t_functions: FxHashSet<String>,
/// Set of function names whose bodies use WriterT operations.
writer_t_functions: FxHashSet<String>,
/// Map from type name → VarId of the $derived_show_TypeName function.
derived_show_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_read_TypeName function.
derived_read_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_eq_TypeName function.
derived_eq_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_compare_TypeName function.
derived_compare_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_fmap_TypeName function.
derived_functor_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_foldr_TypeName function.
derived_foldable_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_traverse_TypeName function.
derived_traversable_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_fromEnum_TypeName function.
derived_from_enum_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_toEnum_TypeName function.
derived_to_enum_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_minBound_TypeName function.
derived_min_bound_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_maxBound_TypeName function.
derived_max_bound_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_from_TypeName function (GHC.Generics).
derived_from_fns: FxHashMap<String, VarId>,
/// Map from type name → VarId of the $derived_to_TypeName function (GHC.Generics).
derived_to_fns: FxHashMap<String, VarId>,
/// Counter for generating unique show descriptor global names.
show_desc_counter: usize,
/// Whether {-# LANGUAGE OverloadedStrings #-} is enabled.
overloaded_strings: bool,
/// E.44: Set of variable IDs that were thunked (lazy let-bindings).
/// When these variables are looked up, they must be forced via bhc_force.
thunked_vars: FxHashSet<VarId>,
/// E.45: Set of variable IDs that hold Integer (arbitrary precision) values.
/// Used by show dispatch to route to bhc_integer_show instead of show_int.
integer_vars: FxHashSet<VarId>,
/// Cache of generated stub functions for unimplemented external package functions.
/// When a stub variable is referenced, we lazily generate an LLVM function that
/// calls bhc_error with the stub name and cache it here.
stub_functions: FxHashMap<String, FunctionValue<'ctx>>,
}
impl<'ctx, 'm> Lowering<'ctx, 'm> {
/// Create a new lowering context.
pub fn new(ctx: &'ctx LlvmContext, module: &'m LlvmModule<'ctx>) -> Self {
let mut lowering = Self {
llvm_ctx: ctx.llvm_context(),
module,
env: FxHashMap::default(),
functions: FxHashMap::default(),
closure_counter: 0,
constructor_metadata: FxHashMap::default(),
in_tail_position: false,
module_name: None,
external_functions: FxHashMap::default(),
transformer_stack: TransformerStack::new(),
state_t_functions: FxHashSet::default(),
reader_t_functions: FxHashSet::default(),
except_t_functions: FxHashSet::default(),
writer_t_functions: FxHashSet::default(),
derived_show_fns: FxHashMap::default(),
derived_read_fns: FxHashMap::default(),
derived_eq_fns: FxHashMap::default(),
derived_compare_fns: FxHashMap::default(),
derived_functor_fns: FxHashMap::default(),
derived_foldable_fns: FxHashMap::default(),
derived_traversable_fns: FxHashMap::default(),
derived_from_enum_fns: FxHashMap::default(),
derived_to_enum_fns: FxHashMap::default(),
derived_min_bound_fns: FxHashMap::default(),
derived_max_bound_fns: FxHashMap::default(),
derived_from_fns: FxHashMap::default(),
derived_to_fns: FxHashMap::default(),
show_desc_counter: 0,
overloaded_strings: false,
thunked_vars: FxHashSet::default(),
integer_vars: FxHashSet::default(),
stub_functions: FxHashMap::default(),
};
lowering.declare_rts_functions();
lowering
}
/// Create a new lowering context for multi-module compilation.
///
/// Accepts a module name for symbol mangling and a list of imported symbols
/// from already-compiled modules to declare as external.
pub fn new_multimodule(
ctx: &'ctx LlvmContext,
module: &'m LlvmModule<'ctx>,
module_name: &str,
imported_symbols: &[CompiledSymbol],
) -> CodegenResult<Self> {
let mut lowering = Self {
llvm_ctx: ctx.llvm_context(),
module,
env: FxHashMap::default(),
functions: FxHashMap::default(),
closure_counter: 0,
constructor_metadata: FxHashMap::default(),
in_tail_position: false,
module_name: Some(module_name.to_string()),
external_functions: FxHashMap::default(),
transformer_stack: TransformerStack::new(),
state_t_functions: FxHashSet::default(),
reader_t_functions: FxHashSet::default(),
except_t_functions: FxHashSet::default(),
writer_t_functions: FxHashSet::default(),
derived_show_fns: FxHashMap::default(),
derived_read_fns: FxHashMap::default(),
derived_eq_fns: FxHashMap::default(),
derived_compare_fns: FxHashMap::default(),
derived_functor_fns: FxHashMap::default(),
derived_foldable_fns: FxHashMap::default(),
derived_traversable_fns: FxHashMap::default(),
derived_from_enum_fns: FxHashMap::default(),
derived_to_enum_fns: FxHashMap::default(),
derived_min_bound_fns: FxHashMap::default(),
derived_max_bound_fns: FxHashMap::default(),
derived_from_fns: FxHashMap::default(),
derived_to_fns: FxHashMap::default(),
show_desc_counter: 0,
overloaded_strings: false,
thunked_vars: FxHashSet::default(),
integer_vars: FxHashSet::default(),
stub_functions: FxHashMap::default(),
};
lowering.declare_rts_functions();
lowering.declare_external_symbols(imported_symbols)?;
Ok(lowering)
}
/// Push a transformer layer onto the stack (e.g. when entering a transformer runner).
fn push_transformer_layer(&mut self, layer: TransformerLayer) {
self.transformer_stack.push(layer);
}
/// Pop the transformer stack (e.g. when leaving a transformer runner).
fn pop_transformer_layer(&mut self) {
self.transformer_stack.pop();
}
/// Return the current transformer layer (outermost, defaults to IO).
fn current_transformer_layer(&self) -> TransformerLayer {
self.transformer_stack.current()
}
/// Detect the native transformer layer of an operation by inspecting the head symbol.
/// This determines which transformer the operation belongs to natively
/// (e.g. `ask` belongs to ReaderT, `get` belongs to StateT).
fn detect_operation_layer(&self, expr: &Expr) -> TransformerLayer {
match expr {
Expr::Var(v, _) => {
let name = v.name.as_str();
match name {
"ask" | "asks" | "local" | "ReaderT.>>=" | "ReaderT.>>" | "ReaderT.pure"
| "ReaderT.fmap" | "ReaderT.<*>" | "ReaderT.lift" | "ReaderT.liftIO" => {
TransformerLayer::ReaderT
}
"get" | "put" | "modify" | "gets" | "StateT.>>=" | "StateT.>>"
| "StateT.pure" | "StateT.fmap" | "StateT.<*>" | "StateT.lift"
| "StateT.liftIO" => TransformerLayer::StateT,
"throwE" | "catchE" | "throwError" | "catchError" | "ExceptT.>>="
| "ExceptT.>>" | "ExceptT.pure" | "ExceptT.fmap" | "ExceptT.<*>"
| "ExceptT.lift" | "ExceptT.liftIO" => TransformerLayer::ExceptT,
"tell" | "WriterT.>>=" | "WriterT.>>" | "WriterT.pure" | "WriterT.fmap"
| "WriterT.<*>" | "WriterT.lift" | "WriterT.liftIO" => {
TransformerLayer::WriterT
}
_ => {
// Check if this is a user-defined function that was detected
// as using StateT/ReaderT/ExceptT/WriterT operations in its body.
if self.state_t_functions.contains(name) {
TransformerLayer::StateT
} else if self.reader_t_functions.contains(name) {
TransformerLayer::ReaderT
} else if self.except_t_functions.contains(name) {
TransformerLayer::ExceptT
} else if self.writer_t_functions.contains(name) {
TransformerLayer::WriterT
} else {
self.current_transformer_layer()
}
}
}
}
Expr::App(func, _, _) => self.detect_operation_layer(func),
_ => self.current_transformer_layer(),
}
}
/// Extract the transformer stack from a type signature.
///
/// For a type like `StateT Int (ReaderT String IO) Int`, this returns
/// `[StateT, ReaderT, IO]` from outermost to innermost.
///
/// For nested transformers, we examine the monad parameter (the second-to-last
/// type argument) recursively.
///
/// Used for nested transformer support to set up the full transformer stack.
fn extract_transformer_stack_from_type(&self, ty: &Ty) -> Vec<TransformerLayer> {
let mut stack = Vec::new();
self.extract_transformer_stack_recursive(ty, &mut stack);
stack
}
/// Helper for recursively building the transformer stack from a type.
fn extract_transformer_stack_recursive(&self, ty: &Ty, stack: &mut Vec<TransformerLayer>) {
match ty {
// Check for IO at the base
Ty::Con(tycon) if tycon.name.as_str() == "IO" => {
stack.push(TransformerLayer::IO);
}
// Check for transformer application. Can be either:
// - T x m a (fully applied): App(App(App(Con(T), x), m), a)
// - T x m (partial application): App(App(Con(T), x), m)
Ty::App(inner, arg) => {
// Case 1: Full application T x m a - go inside to get App(App(Con(T), x), m)
if let Ty::App(inner2, monad_arg) = inner.as_ref() {
// inner2 might be App(Con(T), x) or Con(T) or App(App(Con(T), x), m)
if let Ty::App(con_or_app, _param) = inner2.as_ref() {
// con_or_app should be Con(T)
if let Ty::Con(tycon) = con_or_app.as_ref() {
let layer = match tycon.name.as_str() {
"StateT" => Some(TransformerLayer::StateT),
"ReaderT" => Some(TransformerLayer::ReaderT),
"ExceptT" => Some(TransformerLayer::ExceptT),
"WriterT" => Some(TransformerLayer::WriterT),
_ => None,
};
if let Some(l) = layer {
stack.push(l);
// Recursively extract from the monad argument
self.extract_transformer_stack_recursive(monad_arg, stack);
return;
}
}
}
// Case 2: Partial application T x m - inner2 is Con(T)
// Structure is App(App(Con(T), x), m) where we're at the outer App
// Here, inner2 = Con(T), monad_arg = m (but we need to check inner/arg differently)
if let Ty::Con(tycon) = inner2.as_ref() {
let layer = match tycon.name.as_str() {
"StateT" => Some(TransformerLayer::StateT),
"ReaderT" => Some(TransformerLayer::ReaderT),
"ExceptT" => Some(TransformerLayer::ExceptT),
"WriterT" => Some(TransformerLayer::WriterT),
_ => None,
};
if let Some(l) = layer {
stack.push(l);
// The arg is the monad 'm' for partial applications
self.extract_transformer_stack_recursive(arg, stack);
return;
}
}
}
// Case 3: Partial application - inner is Con(T)
// Structure is App(Con(T), x) - not enough args, recurse on arg
if let Ty::Con(tycon) = inner.as_ref() {
// Check if it's IO
if tycon.name.as_str() == "IO" {
stack.push(TransformerLayer::IO);
return;
}
// Otherwise, single-param application, continue
}
// Fallback: recurse on arg to find inner monads
self.extract_transformer_stack_recursive(arg, stack);
}
_ => {}
}
}
/// Check whether an expression tree contains StateT/ReaderT/ExceptT/WriterT operations.
/// Used to determine the transformer layer for top-level function definitions
/// that are used as transformer computations.
fn detect_transformer_for_body(&self, expr: &Expr) -> Option<TransformerLayer> {
match expr {
Expr::Var(v, _) => {
let name = v.name.as_str();
match name {
"get" | "put" | "modify" | "gets" | "StateT.>>=" | "StateT.>>"
| "StateT.pure" => Some(TransformerLayer::StateT),
"ask" | "asks" | "local" | "ReaderT.>>=" | "ReaderT.>>" | "ReaderT.pure" => {
Some(TransformerLayer::ReaderT)
}
"throwE" | "catchE" | "throwError" | "catchError" | "ExceptT.>>="
| "ExceptT.>>" | "ExceptT.pure" => Some(TransformerLayer::ExceptT),
"tell" | "WriterT.>>=" | "WriterT.>>" | "WriterT.pure" => {
Some(TransformerLayer::WriterT)
}
_ => None,
}
}
Expr::App(func, arg, _) => self
.detect_transformer_for_body(func)
.or_else(|| self.detect_transformer_for_body(arg)),
Expr::Lam(_, body, _) => self.detect_transformer_for_body(body),
Expr::Let(bind, body, _) => {
let in_bind = match bind.as_ref() {
Bind::NonRec(_, e) => self.detect_transformer_for_body(e),
Bind::Rec(bindings) => bindings
.iter()
.find_map(|(_, e)| self.detect_transformer_for_body(e)),
};
in_bind.or_else(|| self.detect_transformer_for_body(body))
}
Expr::Case(scrut, alts, _, _) => {
self.detect_transformer_for_body(scrut).or_else(|| {
alts.iter()
.find_map(|alt| self.detect_transformer_for_body(&alt.rhs))
})
}
Expr::TyApp(e, _, _) => self.detect_transformer_for_body(e),
_ => None,
}
}
// ========================================================================
// Automatic Lift Insertion for Cross-Transformer Operations
// ========================================================================
/// Lower a bind operation with automatic lifting for cross-transformer use.
///
/// When the LHS operation's native layer differs from the current stack top,
/// we wrap the operation result with appropriate lifts before binding.
fn lower_bind_with_auto_lift(
&mut self,
lhs_expr: &Expr,
rhs_expr: &Expr,
native_layer: TransformerLayer,
current_layer: TransformerLayer,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the LHS with its native context to get the inner action
self.push_transformer_layer(native_layer);
let inner_val = self
.lower_expr(lhs_expr)?
.ok_or_else(|| CodegenError::Internal("auto-lift: LHS has no value".to_string()))?;
self.pop_transformer_layer();
// Apply the lift to wrap from native layer to current layer
let lifted_val = self.apply_lift_for_layer(inner_val, native_layer, current_layer)?;
// Now dispatch bind to the current layer with the lifted value
match current_layer {
TransformerLayer::StateT => self.lower_state_t_bind_with_value(lifted_val, rhs_expr),
TransformerLayer::ReaderT => self.lower_reader_t_bind_with_value(lifted_val, rhs_expr),
TransformerLayer::ExceptT => self.lower_except_t_bind_with_value(lifted_val, rhs_expr),
TransformerLayer::WriterT => self.lower_writer_t_bind_with_value(lifted_val, rhs_expr),
TransformerLayer::IO => self.lower_io_bind_with_value(lifted_val, rhs_expr),
}
}
/// Lower a then operation with automatic lifting for cross-transformer use.
fn lower_then_with_auto_lift(
&mut self,
lhs_expr: &Expr,
rhs_expr: &Expr,
native_layer: TransformerLayer,
current_layer: TransformerLayer,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the LHS with its native context to get the inner action
self.push_transformer_layer(native_layer);
let inner_val = self.lower_expr(lhs_expr)?.ok_or_else(|| {
CodegenError::Internal("auto-lift then: LHS has no value".to_string())
})?;
self.pop_transformer_layer();
// Apply the lift to wrap from native layer to current layer
let lifted_val = self.apply_lift_for_layer(inner_val, native_layer, current_layer)?;
// Now dispatch then to the current layer with the lifted value
match current_layer {
TransformerLayer::StateT => self.lower_state_t_then_with_value(lifted_val, rhs_expr),
TransformerLayer::ReaderT => self.lower_reader_t_then_with_value(lifted_val, rhs_expr),
TransformerLayer::ExceptT => self.lower_except_t_then_with_value(lifted_val, rhs_expr),
TransformerLayer::WriterT => self.lower_writer_t_then_with_value(lifted_val, rhs_expr),
TransformerLayer::IO => self.lower_io_then_with_value(lifted_val, rhs_expr),
}
}
/// Apply a lift to wrap an inner transformer's action in the outer transformer.
///
/// For example, lifting a ReaderT action into StateT produces:
/// `StateT.lift (runReaderT action env)` which becomes `\s -> (runReaderT action env, s)`
fn apply_lift_for_layer(
&mut self,
inner_val: BasicValueEnum<'ctx>,
_native_layer: TransformerLayer,
outer_layer: TransformerLayer,
) -> CodegenResult<BasicValueEnum<'ctx>> {
// The inner_val is already a closure representing the inner transformer's action.
// We need to wrap it with the outer transformer's lift operation.
match outer_layer {
TransformerLayer::StateT => {
// StateT.lift action = \s -> (action, s)
// The action from native_layer returns a value when run,
// and we pair it with the unchanged state.
self.apply_state_t_lift_to_value(inner_val)
}
TransformerLayer::ReaderT => {
// ReaderT.lift action = \r -> action
// The action from native_layer is returned ignoring the environment.
self.apply_reader_t_lift_to_value(inner_val)
}
TransformerLayer::ExceptT => {
// ExceptT.lift action = wrap action in Right
self.apply_except_t_lift_to_value(inner_val)
}
TransformerLayer::WriterT => {
// WriterT.lift action = (action, mempty)
self.apply_writer_t_lift_to_value(inner_val)
}
TransformerLayer::IO => {
// IO is the base, no lifting needed
Ok(inner_val)
}
}
}
/// Apply StateT.lift to a value (creates closure \s -> (action, s))
///
/// For nested transformers (StateT over ReaderT), creates a 3-arg closure:
/// \(env, s, reader_env) -> (run_reader(action, reader_env), s)
fn apply_state_t_lift_to_value(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let ptr_type = self.type_mapper().ptr_type();
// Check if we're lifting a ReaderT action in a nested StateT-over-ReaderT context
if self.transformer_stack.is_state_t_over_reader_t() {
// Nested case: create a 3-arg closure that runs the inner ReaderT action
let fn_name = "bhc_state_t_lift_reader_t_nested";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s, reader_env) -> (call(inner_closure, inner_closure, reader_env), s)
// where inner_closure = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let reader_env = func.get_nth_param(2).unwrap();
// Extract the inner ReaderT closure from our closure's environment
let inner_closure = self.extract_closure_env_elem(env, 1, 0)?;
// Call the inner ReaderT closure with (inner_closure_as_env, reader_env)
// ReaderT closures take (closure_env, reader_env) and return the result
let inner_fn = self.extract_closure_fn_ptr(inner_closure)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
inner_fn,
&[inner_closure.into(), reader_env.into()],
"lift_reader_result",
)
.map_err(|e| {
CodegenError::Internal(format!("state_t_lift_nested call inner: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"state_t_lift_nested: inner returned void".to_string(),
)
})?;
// Return (result, s)
let pair = self.alloc_pair(result, s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("state_t_lift_nested return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900100), action_ptr.into())])?;
return Ok(closure_ptr.into());
}
// Non-nested case: simple 2-arg closure
let fn_name = "bhc_state_t_lift_auto";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> (action, s) where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(action.into(), s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("state_t_lift_auto return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900100), action_ptr.into())])?;
Ok(closure_ptr.into())
}
/// Apply ReaderT.lift to a value (creates closure \r -> action)
fn apply_reader_t_lift_to_value(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let fn_name = "bhc_reader_t_lift_auto";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _r) -> action where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
self.builder().build_return(Some(&action)).map_err(|e| {
CodegenError::Internal(format!("reader_t_lift_auto return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900101), action_ptr.into())])?;
Ok(closure_ptr.into())
}
/// Apply ExceptT.lift to a value (wraps in Right)
fn apply_except_t_lift_to_value(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
// ExceptT over StateT: \(env, s) -> (Right(inner(s).fst), inner(s).snd)
if self.transformer_stack.is_except_t_over_state_t() {
return self.apply_except_t_lift_to_value_over_st(action_val);
}
// ExceptT over ReaderT: \(env, r) -> Right(inner(r))
if self.transformer_stack.is_except_t_over_reader_t() {
return self.apply_except_t_lift_to_value_over_rt(action_val);
}
let fn_name = "bhc_except_t_lift_auto";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> Right action where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
// Allocate Right ADT: tag=1, arity=1
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, action.into())?;
self.builder().build_return(Some(&right_adt)).map_err(|e| {
CodegenError::Internal(format!("except_t_lift_auto return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900102), action_ptr.into())])?;
Ok(closure_ptr.into())
}
/// Apply WriterT.lift to a value (pairs with mempty)
fn apply_writer_t_lift_to_value(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
// WriterT over StateT: \(env, s) -> ((action(action, s).fst, []), action(action, s).snd)
if self.transformer_stack.is_writer_t_over_state_t() {
return self.apply_writer_t_lift_to_value_over_st(action_val);
}
// WriterT over ReaderT: \(env, r) -> (action(action, r), [])
if self.transformer_stack.is_writer_t_over_reader_t() {
return self.apply_writer_t_lift_to_value_over_rt(action_val);
}
let fn_name = "bhc_writer_t_lift_auto";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> (action, []) where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
// Build empty list as mempty for the writer log
let empty_list = self.build_nil()?;
let pair = self.alloc_pair(action.into(), empty_list.into())?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("writer_t_lift_auto return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900103), action_ptr.into())])?;
Ok(closure_ptr.into())
}
// ========================================================================
// Bind/Then with Pre-Evaluated LHS Value
// ========================================================================
/// StateT bind with a pre-evaluated LHS value
fn lower_state_t_bind_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// The LHS is already a StateT closure (function s -> (a, s'))
// We need to create a new closure that runs LHS, then runs RHS with the result
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("state_t_bind_val: RHS has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_bind_val";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> let (a, s') = m(s) in k(a)(s')
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// Run m(s) to get (a, s')
let m_fn = self.extract_closure_fn_ptr(m)?;
let s_ptr = self.value_to_ptr(s)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), s_ptr.into()], "state_bind_m")
.map_err(|e| CodegenError::Internal(format!("state_t_bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state_t_bind m: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
// Run k(a) to get a new StateT closure
let k_fn = self.extract_closure_fn_ptr(k)?;
let a_ptr = self.value_to_ptr(a.into())?;
let k_result = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a_ptr.into()], "state_bind_k")
.map_err(|e| CodegenError::Internal(format!("state_t_bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state_t_bind k: void".to_string()))?
.into_pointer_value();
// Run the resulting closure with s'
let k_result_fn = self.extract_closure_fn_ptr(k_result)?;
let s_prime_ptr = self.value_to_ptr(s_prime.into())?;
let final_result = self
.builder()
.build_indirect_call(
fn_type,
k_result_fn,
&[k_result.into(), s_prime_ptr.into()],
"state_bind_final",
)
.map_err(|e| CodegenError::Internal(format!("state_t_bind final: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state_t_bind final: void".to_string()))?;
self.builder()
.build_return(Some(&final_result))
.map_err(|e| CodegenError::Internal(format!("state_t_bind return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(lhs_val)?;
let k_ptr = self.value_to_ptr(rhs_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900200), m_ptr.into()),
(VarId::new(900201), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT bind with a pre-evaluated LHS value
fn lower_reader_t_bind_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("reader_t_bind_val: RHS has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_bind_val";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> let a = m(r) in k(a)(r)
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// Run m(r) to get a
let m_fn = self.extract_closure_fn_ptr(m)?;
let r_ptr = self.value_to_ptr(r)?;
let a = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r_ptr.into()], "reader_bind_m")
.map_err(|e| CodegenError::Internal(format!("reader_t_bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader_t_bind m: void".to_string()))?;
// Run k(a) to get new ReaderT closure
let k_fn = self.extract_closure_fn_ptr(k)?;
let a_ptr = self.value_to_ptr(a)?;
let k_result = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a_ptr.into()], "reader_bind_k")
.map_err(|e| CodegenError::Internal(format!("reader_t_bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader_t_bind k: void".to_string()))?
.into_pointer_value();
// Run k_result(r)
let k_result_fn = self.extract_closure_fn_ptr(k_result)?;
let final_result = self
.builder()
.build_indirect_call(
fn_type,
k_result_fn,
&[k_result.into(), r_ptr.into()],
"reader_bind_final",
)
.map_err(|e| CodegenError::Internal(format!("reader_t_bind final: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader_t_bind final: void".to_string()))?;
self.builder()
.build_return(Some(&final_result))
.map_err(|e| CodegenError::Internal(format!("reader_t_bind return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(lhs_val)?;
let k_ptr = self.value_to_ptr(rhs_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900202), m_ptr.into()),
(VarId::new(900203), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT bind with pre-evaluated LHS - uses standard implementation
fn lower_except_t_bind_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For ExceptT, we can use the standard bind since the lifted value is already wrapped
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("except_t_bind_val: RHS has no value".to_string())
})?;
self.lower_except_t_bind_impl(lhs_val, rhs_val)
}
/// WriterT bind with pre-evaluated LHS - uses standard implementation
fn lower_writer_t_bind_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("writer_t_bind_val: RHS has no value".to_string())
})?;
self.lower_writer_t_bind_impl(lhs_val, rhs_val)
}
/// IO bind with pre-evaluated LHS
fn lower_io_bind_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For IO, we just run the LHS and pass its result to RHS
let rhs_val = self
.lower_expr(rhs_expr)?
.ok_or_else(|| CodegenError::Internal("io_bind_val: RHS has no value".to_string()))?;
// Call RHS with LHS result
let rhs_ptr = rhs_val.into_pointer_value();
let fn_ptr = self.extract_closure_fn_ptr(rhs_ptr)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let lhs_ptr = self.value_to_ptr(lhs_val)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[rhs_ptr.into(), lhs_ptr.into()],
"io_bind_val",
)
.map_err(|e| CodegenError::Internal(format!("io_bind_val: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("io_bind_val: void".to_string()))?;
Ok(Some(result))
}
/// StateT then with pre-evaluated LHS
fn lower_state_t_then_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For then, we ignore the result of LHS and just run RHS
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("state_t_then_val: RHS has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_then_val";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> let (_, s') = m(s) in n(s')
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let n = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// Run m(s) to get (_, s')
let m_fn = self.extract_closure_fn_ptr(m)?;
let s_ptr = self.value_to_ptr(s)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), s_ptr.into()], "state_then_m")
.map_err(|e| CodegenError::Internal(format!("state_t_then m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state_t_then m: void".to_string()))?
.into_pointer_value();
let s_prime = self.extract_pair_snd(pair)?;
// Run n(s')
let n_fn = self.extract_closure_fn_ptr(n)?;
let s_prime_ptr = self.value_to_ptr(s_prime.into())?;
let result = self
.builder()
.build_indirect_call(
fn_type,
n_fn,
&[n.into(), s_prime_ptr.into()],
"state_then_n",
)
.map_err(|e| CodegenError::Internal(format!("state_t_then n: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state_t_then n: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("state_t_then return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(lhs_val)?;
let n_ptr = self.value_to_ptr(rhs_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900210), m_ptr.into()),
(VarId::new(900211), n_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT then with pre-evaluated LHS
fn lower_reader_t_then_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("reader_t_then_val: RHS has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_then_val";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> let _ = m(r) in n(r)
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let n = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// Run m(r) and ignore result
let m_fn = self.extract_closure_fn_ptr(m)?;
let r_ptr = self.value_to_ptr(r)?;
let _m_result = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r_ptr.into()], "reader_then_m")
.map_err(|e| CodegenError::Internal(format!("reader_t_then m: {:?}", e)))?;
// Run n(r)
let n_fn = self.extract_closure_fn_ptr(n)?;
let result = self
.builder()
.build_indirect_call(fn_type, n_fn, &[n.into(), r_ptr.into()], "reader_then_n")
.map_err(|e| CodegenError::Internal(format!("reader_t_then n: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader_t_then n: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("reader_t_then return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(lhs_val)?;
let n_ptr = self.value_to_ptr(rhs_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900212), m_ptr.into()),
(VarId::new(900213), n_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT then with pre-evaluated LHS - uses standard implementation
fn lower_except_t_then_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("except_t_then_val: RHS has no value".to_string())
})?;
self.lower_except_t_then_impl(lhs_val, rhs_val)
}
/// WriterT then with pre-evaluated LHS - uses standard implementation
fn lower_writer_t_then_with_value(
&mut self,
lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rhs_val = self.lower_expr(rhs_expr)?.ok_or_else(|| {
CodegenError::Internal("writer_t_then_val: RHS has no value".to_string())
})?;
self.lower_writer_t_then_impl(lhs_val, rhs_val)
}
/// IO then with pre-evaluated LHS
fn lower_io_then_with_value(
&mut self,
_lhs_val: BasicValueEnum<'ctx>,
rhs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For IO then, LHS is already evaluated (side effect happened)
// Just evaluate and return RHS
self.lower_expr(rhs_expr)
}
/// Mangle a function name with the module name for multi-module compilation.
///
/// In multi-module mode, function names are qualified as `Module.name` to avoid
/// collisions. The entry point wrapper (C `main`) is created separately by the
/// driver, so the Haskell `main` gets mangled to `Main.main` like any other function.
fn mangle_name(&self, name: &str) -> String {
if let Some(ref mod_name) = self.module_name {
format!("{}.{}", mod_name, name)
} else {
name.to_string()
}
}
/// Declare external symbols from already-compiled modules.
///
/// For each imported symbol, we add a function declaration (extern) to the
/// current LLVM module so that references to cross-module functions can be
/// resolved at link time.
fn declare_external_symbols(&mut self, symbols: &[CompiledSymbol]) -> CodegenResult<()> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
for sym in symbols {
// All BHC functions use uniform calling convention with an env/closure
// pointer as the first parameter. param_count from CompiledSymbol counts
// only the Haskell-level params (lambdas), so we add 1 for the env pointer.
let total_params = sym.param_count + 1;
let param_types: Vec<inkwell::types::BasicMetadataTypeEnum> =
(0..total_params).map(|_| ptr_type.into()).collect();
let fn_type = ptr_type.fn_type(¶m_types, false);
let fn_val = self.module.add_function(&sym.llvm_name, fn_type);
self.external_functions.insert(sym.name, fn_val);
}
Ok(())
}
/// Register a constructor's metadata for later use.
/// Preserves existing type_name if already set (e.g., by detect_derived_instance_methods).
pub fn register_constructor(&mut self, name: &str, tag: u32, arity: u32) {
let key = name.to_string();
if let Some(existing) = self.constructor_metadata.get_mut(&key) {
existing.tag = tag;
existing.arity = arity;
// Preserve type_name and is_newtype if already set
} else {
self.constructor_metadata.insert(
key,
ConstructorMeta {
tag,
arity,
type_name: None,
is_newtype: false,
},
);
}
}
/// Check if a constructor is a newtype constructor (identity at runtime).
pub fn is_newtype_constructor(&self, name: &str) -> bool {
self.constructor_metadata
.get(name)
.is_some_and(|meta| meta.is_newtype)
}
/// Declare external RTS functions.
fn declare_rts_functions(&mut self) {
// Get all types upfront to avoid borrow conflicts
let void_type = self.llvm_ctx.void_type();
let i64_type = self.type_mapper().i64_type();
let f64_type = self.type_mapper().f64_type();
let i32_type = self.type_mapper().i32_type();
let i8_ptr_type = self.llvm_ctx.ptr_type(inkwell::AddressSpace::default());
// bhc_print_int_ln(i64) -> void
let print_int_ln_type = void_type.fn_type(&[i64_type.into()], false);
let print_int_ln =
self.module
.llvm_module()
.add_function("bhc_print_int_ln", print_int_ln_type, None);
self.functions.insert(VarId::new(1000000), print_int_ln); // Base 1_000_000 to avoid collisions with user DefIds
// bhc_print_double_ln(f64) -> void
let print_double_ln_type = void_type.fn_type(&[f64_type.into()], false);
let print_double_ln = self.module.llvm_module().add_function(
"bhc_print_double_ln",
print_double_ln_type,
None,
);
self.functions.insert(VarId::new(1000001), print_double_ln);
// bhc_print_string_ln(*i8) -> void
let print_string_ln_type = void_type.fn_type(&[i8_ptr_type.into()], false);
let print_string_ln = self.module.llvm_module().add_function(
"bhc_print_string_ln",
print_string_ln_type,
None,
);
self.functions.insert(VarId::new(1000002), print_string_ln);
// bhc_print_int(i64) -> void
let print_int_type = void_type.fn_type(&[i64_type.into()], false);
let print_int =
self.module
.llvm_module()
.add_function("bhc_print_int", print_int_type, None);
self.functions.insert(VarId::new(1000003), print_int);
// bhc_print_string(*i8) -> void
let print_string_type = void_type.fn_type(&[i8_ptr_type.into()], false);
let print_string =
self.module
.llvm_module()
.add_function("bhc_print_string", print_string_type, None);
self.functions.insert(VarId::new(1000004), print_string);
// bhc_alloc(size: i64) -> ptr - allocate heap memory
let alloc_type = i8_ptr_type.fn_type(&[i64_type.into()], false);
let alloc_fn = self
.module
.llvm_module()
.add_function("bhc_alloc", alloc_type, None);
self.functions.insert(VarId::new(1000005), alloc_fn);
// bhc_error(*i8) -> void - runtime error (does not return)
let error_type = void_type.fn_type(&[i8_ptr_type.into()], false);
let error_fn = self
.module
.llvm_module()
.add_function("bhc_error", error_type, None);
self.functions.insert(VarId::new(1000006), error_fn);
// bhc_print_bool(i64) -> void - print True/False
let print_bool_type = void_type.fn_type(&[i64_type.into()], false);
let print_bool =
self.module
.llvm_module()
.add_function("bhc_print_bool", print_bool_type, None);
self.functions.insert(VarId::new(1000007), print_bool);
// bhc_print_bool_ln(i64) -> void - print True/False with newline
let print_bool_ln_type = void_type.fn_type(&[i64_type.into()], false);
let print_bool_ln =
self.module
.llvm_module()
.add_function("bhc_print_bool_ln", print_bool_ln_type, None);
self.functions.insert(VarId::new(1000008), print_bool_ln);
// bhc_print_char(i32) -> void - print a character
let print_char_type = void_type.fn_type(&[i32_type.into()], false);
let print_char =
self.module
.llvm_module()
.add_function("bhc_print_char", print_char_type, None);
self.functions.insert(VarId::new(1000009), print_char);
// bhc_print_newline() -> void
let print_newline_type = void_type.fn_type(&[], false);
let print_newline =
self.module
.llvm_module()
.add_function("bhc_print_newline", print_newline_type, None);
self.functions.insert(VarId::new(1000010), print_newline);
// bhc_force(ptr) -> ptr - Force thunk evaluation to WHNF
let force_type = i8_ptr_type.fn_type(&[i8_ptr_type.into()], false);
let force_fn = self
.module
.llvm_module()
.add_function("bhc_force", force_type, None);
self.functions.insert(VarId::new(1000011), force_fn);
// bhc_is_thunk(ptr) -> i32 - Check if object is an unevaluated thunk
let is_thunk_type = i32_type.fn_type(&[i8_ptr_type.into()], false);
let is_thunk_fn =
self.module
.llvm_module()
.add_function("bhc_is_thunk", is_thunk_type, None);
self.functions.insert(VarId::new(1000012), is_thunk_fn);
// === Math RTS functions ===
let f64_to_f64 = f64_type.fn_type(&[f64_type.into()], false);
let f32_type = self.llvm_ctx.f32_type();
let f64_to_i64 = i64_type.fn_type(&[f64_type.into()], false);
let f64_f64_to_f64 = f64_type.fn_type(&[f64_type.into(), f64_type.into()], false);
let i64_to_i64 = i64_type.fn_type(&[i64_type.into()], false);
// bhc_sqrt_double(f64) -> f64
let sqrt_double =
self.module
.llvm_module()
.add_function("bhc_sqrt_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000013), sqrt_double);
// bhc_exp_double(f64) -> f64
let exp_double = self
.module
.llvm_module()
.add_function("bhc_exp_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000014), exp_double);
// bhc_log_double(f64) -> f64
let log_double = self
.module
.llvm_module()
.add_function("bhc_log_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000015), log_double);
// bhc_sin_double(f64) -> f64
let sin_double = self
.module
.llvm_module()
.add_function("bhc_sin_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000016), sin_double);
// bhc_cos_double(f64) -> f64
let cos_double = self
.module
.llvm_module()
.add_function("bhc_cos_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000017), cos_double);
// bhc_tan_double(f64) -> f64
let tan_double = self
.module
.llvm_module()
.add_function("bhc_tan_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000018), tan_double);
// bhc_asin_double(f64) -> f64
let asin_double =
self.module
.llvm_module()
.add_function("bhc_asin_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000019), asin_double);
// bhc_acos_double(f64) -> f64
let acos_double =
self.module
.llvm_module()
.add_function("bhc_acos_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000020), acos_double);
// bhc_atan_double(f64) -> f64
let atan_double =
self.module
.llvm_module()
.add_function("bhc_atan_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000021), atan_double);
// bhc_atan2_double(f64, f64) -> f64
let atan2_double =
self.module
.llvm_module()
.add_function("bhc_atan2_double", f64_f64_to_f64, None);
self.functions.insert(VarId::new(1000022), atan2_double);
// bhc_ceiling_double(f64) -> i64
let ceiling_double =
self.module
.llvm_module()
.add_function("bhc_ceiling_double", f64_to_i64, None);
self.functions.insert(VarId::new(1000023), ceiling_double);
// bhc_floor_double(f64) -> i64
let floor_double =
self.module
.llvm_module()
.add_function("bhc_floor_double", f64_to_i64, None);
self.functions.insert(VarId::new(1000024), floor_double);
// bhc_round_double(f64) -> i64
let round_double =
self.module
.llvm_module()
.add_function("bhc_round_double", f64_to_i64, None);
self.functions.insert(VarId::new(1000025), round_double);
// bhc_truncate_double(f64) -> i64
let truncate_double =
self.module
.llvm_module()
.add_function("bhc_truncate_double", f64_to_i64, None);
self.functions.insert(VarId::new(1000026), truncate_double);
// bhc_negate_int(i64) -> i64
let negate_int = self
.module
.llvm_module()
.add_function("bhc_negate_int", i64_to_i64, None);
self.functions.insert(VarId::new(1000027), negate_int);
// bhc_abs_int(i64) -> i64
let abs_int = self
.module
.llvm_module()
.add_function("bhc_abs_int", i64_to_i64, None);
self.functions.insert(VarId::new(1000028), abs_int);
// bhc_signum_int(i64) -> i64
let signum_int = self
.module
.llvm_module()
.add_function("bhc_signum_int", i64_to_i64, None);
self.functions.insert(VarId::new(1000029), signum_int);
// === Character RTS functions ===
let u32_type = self.llvm_ctx.i32_type();
let u32_to_bool = self.llvm_ctx.bool_type().fn_type(&[u32_type.into()], false);
let u32_to_u32 = u32_type.fn_type(&[u32_type.into()], false);
let u32_to_i32 = i32_type.fn_type(&[u32_type.into()], false);
let i32_to_u32 = u32_type.fn_type(&[i32_type.into()], false);
// bhc_char_is_alpha(u32) -> bool
let char_is_alpha =
self.module
.llvm_module()
.add_function("bhc_char_is_alpha", u32_to_bool, None);
self.functions.insert(VarId::new(1000030), char_is_alpha);
// bhc_char_is_digit(u32) -> bool
let char_is_digit =
self.module
.llvm_module()
.add_function("bhc_char_is_digit", u32_to_bool, None);
self.functions.insert(VarId::new(1000031), char_is_digit);
// bhc_char_is_alphanumeric(u32) -> bool
let char_is_alnum =
self.module
.llvm_module()
.add_function("bhc_char_is_alphanumeric", u32_to_bool, None);
self.functions.insert(VarId::new(1000032), char_is_alnum);
// bhc_char_is_space(u32) -> bool
let char_is_space =
self.module
.llvm_module()
.add_function("bhc_char_is_space", u32_to_bool, None);
self.functions.insert(VarId::new(1000033), char_is_space);
// bhc_char_is_upper(u32) -> bool
let char_is_upper =
self.module
.llvm_module()
.add_function("bhc_char_is_upper", u32_to_bool, None);
self.functions.insert(VarId::new(1000034), char_is_upper);
// bhc_char_is_lower(u32) -> bool
let char_is_lower =
self.module
.llvm_module()
.add_function("bhc_char_is_lower", u32_to_bool, None);
self.functions.insert(VarId::new(1000035), char_is_lower);
// bhc_char_to_upper(u32) -> u32
let char_to_upper =
self.module
.llvm_module()
.add_function("bhc_char_to_upper", u32_to_u32, None);
self.functions.insert(VarId::new(1000036), char_to_upper);
// bhc_char_to_lower(u32) -> u32
let char_to_lower =
self.module
.llvm_module()
.add_function("bhc_char_to_lower", u32_to_u32, None);
self.functions.insert(VarId::new(1000037), char_to_lower);
// bhc_char_is_print(u32) -> bool
let char_is_print =
self.module
.llvm_module()
.add_function("bhc_char_is_print", u32_to_bool, None);
self.functions.insert(VarId::new(1000038), char_is_print);
// bhc_char_is_ascii(u32) -> bool
let char_is_ascii =
self.module
.llvm_module()
.add_function("bhc_char_is_ascii", u32_to_bool, None);
self.functions.insert(VarId::new(1000039), char_is_ascii);
// bhc_char_is_control(u32) -> bool
let char_is_control =
self.module
.llvm_module()
.add_function("bhc_char_is_control", u32_to_bool, None);
self.functions.insert(VarId::new(1000040), char_is_control);
// bhc_char_is_hex_digit(u32) -> bool
let char_is_hex =
self.module
.llvm_module()
.add_function("bhc_char_is_hex_digit", u32_to_bool, None);
self.functions.insert(VarId::new(1000041), char_is_hex);
// bhc_char_is_letter(u32) -> bool
let char_is_letter =
self.module
.llvm_module()
.add_function("bhc_char_is_letter", u32_to_bool, None);
self.functions.insert(VarId::new(1000042), char_is_letter);
// bhc_char_is_number(u32) -> bool
let char_is_number =
self.module
.llvm_module()
.add_function("bhc_char_is_number", u32_to_bool, None);
self.functions.insert(VarId::new(1000043), char_is_number);
// bhc_char_is_punctuation(u32) -> bool
let char_is_punct =
self.module
.llvm_module()
.add_function("bhc_char_is_punctuation", u32_to_bool, None);
self.functions.insert(VarId::new(1000044), char_is_punct);
// bhc_char_is_symbol(u32) -> bool
let char_is_symbol =
self.module
.llvm_module()
.add_function("bhc_char_is_symbol", u32_to_bool, None);
self.functions.insert(VarId::new(1000045), char_is_symbol);
// bhc_char_digit_to_int(u32) -> i32
let char_digit_to_int =
self.module
.llvm_module()
.add_function("bhc_char_digit_to_int", u32_to_i32, None);
self.functions
.insert(VarId::new(1000046), char_digit_to_int);
// bhc_char_int_to_digit(i32) -> u32
let char_int_to_digit =
self.module
.llvm_module()
.add_function("bhc_char_int_to_digit", i32_to_u32, None);
self.functions
.insert(VarId::new(1000047), char_int_to_digit);
// bhc_char_ord(u32) -> u32
let char_ord = self
.module
.llvm_module()
.add_function("bhc_char_ord", u32_to_u32, None);
self.functions.insert(VarId::new(1000048), char_ord);
// bhc_char_chr(u32) -> u32
let char_chr = self
.module
.llvm_module()
.add_function("bhc_char_chr", u32_to_u32, None);
self.functions.insert(VarId::new(1000049), char_chr);
// === IO/System RTS functions ===
// bhc_readFile(*i8) -> *i8
let ptr_to_ptr = i8_ptr_type.fn_type(&[i8_ptr_type.into()], false);
let read_file = self
.module
.llvm_module()
.add_function("bhc_readFile", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000050), read_file);
// bhc_writeFile(*i8, *i8) -> void
let ptr_ptr_to_void = void_type.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into()], false);
let write_file =
self.module
.llvm_module()
.add_function("bhc_writeFile", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000051), write_file);
// bhc_appendFile(*i8, *i8) -> void
let append_file =
self.module
.llvm_module()
.add_function("bhc_appendFile", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000052), append_file);
// bhc_open_file(*i8, i32) -> *i8 (handle)
let ptr_i32_to_ptr = i8_ptr_type.fn_type(&[i8_ptr_type.into(), i32_type.into()], false);
let open_file =
self.module
.llvm_module()
.add_function("bhc_open_file", ptr_i32_to_ptr, None);
self.functions.insert(VarId::new(1000053), open_file);
// bhc_close_handle(*i8) -> void
let ptr_to_void = void_type.fn_type(&[i8_ptr_type.into()], false);
let close_handle =
self.module
.llvm_module()
.add_function("bhc_close_handle", ptr_to_void, None);
self.functions.insert(VarId::new(1000054), close_handle);
// bhc_hGetChar(*i8) -> i32
let ptr_to_i32 = i32_type.fn_type(&[i8_ptr_type.into()], false);
let h_get_char = self
.module
.llvm_module()
.add_function("bhc_hGetChar", ptr_to_i32, None);
self.functions.insert(VarId::new(1000055), h_get_char);
// bhc_hGetLine(*i8) -> *i8
let h_get_line = self
.module
.llvm_module()
.add_function("bhc_hGetLine", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000056), h_get_line);
// bhc_hPutStr(*i8, *i8) -> void
let h_put_str =
self.module
.llvm_module()
.add_function("bhc_hPutStr", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000057), h_put_str);
// bhc_hFlush(*i8) -> void
let h_flush = self
.module
.llvm_module()
.add_function("bhc_hFlush", ptr_to_void, None);
self.functions.insert(VarId::new(1000058), h_flush);
// bhc_hIsEOF(*i8) -> i32 (bool)
let h_is_eof = self
.module
.llvm_module()
.add_function("bhc_hIsEOF", ptr_to_i32, None);
self.functions.insert(VarId::new(1000059), h_is_eof);
// bhc_stdin() -> *i8
let void_to_ptr = i8_ptr_type.fn_type(&[], false);
let stdin_fn = self
.module
.llvm_module()
.add_function("bhc_stdin", void_to_ptr, None);
self.functions.insert(VarId::new(1000060), stdin_fn);
// bhc_stdout() -> *i8
let stdout_fn = self
.module
.llvm_module()
.add_function("bhc_stdout", void_to_ptr, None);
self.functions.insert(VarId::new(1000061), stdout_fn);
// bhc_stderr() -> *i8
let stderr_fn = self
.module
.llvm_module()
.add_function("bhc_stderr", void_to_ptr, None);
self.functions.insert(VarId::new(1000062), stderr_fn);
// bhc_exists(*i8) -> i32 (bool) - doesFileExist
let does_file_exist =
self.module
.llvm_module()
.add_function("bhc_exists", ptr_to_i32, None);
self.functions.insert(VarId::new(1000063), does_file_exist);
// bhc_is_directory(*i8) -> i32 (bool) - doesDirectoryExist
let does_dir_exist =
self.module
.llvm_module()
.add_function("bhc_is_directory", ptr_to_i32, None);
self.functions.insert(VarId::new(1000064), does_dir_exist);
// bhc_exit(i32) -> void
let i32_to_void = void_type.fn_type(&[i32_type.into()], false);
let exit_fn = self
.module
.llvm_module()
.add_function("bhc_exit", i32_to_void, None);
self.functions.insert(VarId::new(1000065), exit_fn);
// bhc_exit_success() -> void
let void_to_void = void_type.fn_type(&[], false);
let exit_success =
self.module
.llvm_module()
.add_function("bhc_exit_success", void_to_void, None);
self.functions.insert(VarId::new(1000066), exit_success);
// bhc_exit_failure() -> void
let exit_failure =
self.module
.llvm_module()
.add_function("bhc_exit_failure", void_to_void, None);
self.functions.insert(VarId::new(1000067), exit_failure);
// bhc_get_env(*i8) -> *i8
let get_env = self
.module
.llvm_module()
.add_function("bhc_get_env", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000068), get_env);
// bhc_argc() -> i32
let void_to_i32 = i32_type.fn_type(&[], false);
let argc_fn = self
.module
.llvm_module()
.add_function("bhc_argc", void_to_i32, None);
self.functions.insert(VarId::new(1000069), argc_fn);
// bhc_argv() -> *i8 (returns ptr to argv array)
let argv_fn = self
.module
.llvm_module()
.add_function("bhc_argv", void_to_ptr, None);
self.functions.insert(VarId::new(1000070), argv_fn);
// bhc_getLine() -> *i8
let get_line = self
.module
.llvm_module()
.add_function("bhc_getLine", void_to_ptr, None);
self.functions.insert(VarId::new(1000071), get_line);
// bhc_show_int(i64) -> *i8
let i64_to_ptr = i8_ptr_type.fn_type(&[i64_type.into()], false);
let show_int = self
.module
.llvm_module()
.add_function("bhc_show_int", i64_to_ptr, None);
self.functions.insert(VarId::new(1000072), show_int);
// bhc_show_double(f64) -> *i8
let f64_to_ptr = i8_ptr_type.fn_type(&[f64_type.into()], false);
let show_double =
self.module
.llvm_module()
.add_function("bhc_show_double", f64_to_ptr, None);
self.functions.insert(VarId::new(1000073), show_double);
// bhc_show_bool(i64) -> *i8
let show_bool = self
.module
.llvm_module()
.add_function("bhc_show_bool", i64_to_ptr, None);
self.functions.insert(VarId::new(1000091), show_bool);
// bhc_show_ordering(i64) -> *i8
let show_ordering =
self.module
.llvm_module()
.add_function("bhc_show_ordering", i64_to_ptr, None);
self.functions.insert(VarId::new(1000098), show_ordering);
// bhc_show_char(i32) -> *i8
let i32_to_ptr = i8_ptr_type.fn_type(&[i32_type.into()], false);
let show_char = self
.module
.llvm_module()
.add_function("bhc_show_char", i32_to_ptr, None);
self.functions.insert(VarId::new(1000074), show_char);
// bhc_show_float(f32) -> *i8
let f32_to_ptr = i8_ptr_type.fn_type(&[f32_type.into()], false);
let show_float = self
.module
.llvm_module()
.add_function("bhc_show_float", f32_to_ptr, None);
self.functions.insert(VarId::new(1000090), show_float);
// bhc_show_string(ptr) -> *i8
let ptr_to_ptr = i8_ptr_type.fn_type(&[i8_ptr_type.into()], false);
let show_string =
self.module
.llvm_module()
.add_function("bhc_show_string", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000092), show_string);
// bhc_show_list(ptr, i64) -> *i8
let ptr_i64_to_ptr = i8_ptr_type.fn_type(&[i8_ptr_type.into(), i64_type.into()], false);
let show_list =
self.module
.llvm_module()
.add_function("bhc_show_list", ptr_i64_to_ptr, None);
self.functions.insert(VarId::new(1000093), show_list);
// bhc_show_maybe(ptr, i64) -> *i8
let show_maybe =
self.module
.llvm_module()
.add_function("bhc_show_maybe", ptr_i64_to_ptr, None);
self.functions.insert(VarId::new(1000094), show_maybe);
// bhc_show_either(ptr, i64, i64) -> *i8
let ptr_i64_i64_to_ptr = i8_ptr_type.fn_type(
&[i8_ptr_type.into(), i64_type.into(), i64_type.into()],
false,
);
let show_either =
self.module
.llvm_module()
.add_function("bhc_show_either", ptr_i64_i64_to_ptr, None);
self.functions.insert(VarId::new(1000095), show_either);
// bhc_show_tuple2(ptr, i64, i64) -> *i8
let show_tuple2 =
self.module
.llvm_module()
.add_function("bhc_show_tuple2", ptr_i64_i64_to_ptr, None);
self.functions.insert(VarId::new(1000096), show_tuple2);
// bhc_show_unit(ptr) -> *i8
let show_unit = self
.module
.llvm_module()
.add_function("bhc_show_unit", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000097), show_unit);
// bhc_show_with_desc(ptr, ptr) -> *i8
let ptr_ptr_to_ptr = i8_ptr_type.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into()], false);
let show_with_desc =
self.module
.llvm_module()
.add_function("bhc_show_with_desc", ptr_ptr_to_ptr, None);
self.functions.insert(VarId::new(1000099), show_with_desc);
// bhc_gcd(i64, i64) -> i64
let i64_i64_to_i64 = i64_type.fn_type(&[i64_type.into(), i64_type.into()], false);
let gcd_fn = self
.module
.llvm_module()
.add_function("bhc_gcd", i64_i64_to_i64, None);
self.functions.insert(VarId::new(1000500), gcd_fn);
// bhc_lcm(i64, i64) -> i64
let lcm_fn = self
.module
.llvm_module()
.add_function("bhc_lcm", i64_i64_to_i64, None);
self.functions.insert(VarId::new(1000501), lcm_fn);
// bhc_new_ioref(ptr) -> ptr
let new_ioref = self
.module
.llvm_module()
.add_function("bhc_new_ioref", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000502), new_ioref);
// bhc_read_ioref(ptr) -> ptr
let read_ioref = self
.module
.llvm_module()
.add_function("bhc_read_ioref", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000503), read_ioref);
// bhc_write_ioref(ptr, ptr) -> void
let ptr_ptr_to_void = self
.llvm_context()
.void_type()
.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into()], false);
let write_ioref =
self.module
.llvm_module()
.add_function("bhc_write_ioref", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000504), write_ioref);
// bhc_char_to_int(u32) -> i64 (ord)
let u32_to_i64 = i64_type.fn_type(&[u32_type.into()], false);
let char_to_int =
self.module
.llvm_module()
.add_function("bhc_char_to_int", u32_to_i64, None);
self.functions.insert(VarId::new(1000075), char_to_int);
// bhc_int_to_char(i64) -> u32 (chr)
let i64_to_u32 = u32_type.fn_type(&[i64_type.into()], false);
let int_to_char =
self.module
.llvm_module()
.add_function("bhc_int_to_char", i64_to_u32, None);
self.functions.insert(VarId::new(1000076), int_to_char);
// bhc_negate_double(f64) -> f64
let negate_double =
self.module
.llvm_module()
.add_function("bhc_negate_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000077), negate_double);
// bhc_abs_double(f64) -> f64
let abs_double = self
.module
.llvm_module()
.add_function("bhc_abs_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000078), abs_double);
// bhc_signum_double(f64) -> f64
let signum_double =
self.module
.llvm_module()
.add_function("bhc_signum_double", f64_to_f64, None);
self.functions.insert(VarId::new(1000079), signum_double);
// Container RTS functions (Map, Set, IntMap, IntSet)
let ptr_type = i8_ptr_type;
// Map operations
let map_empty = self.module.llvm_module().add_function(
"bhc_map_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000100), map_empty);
let map_singleton = self.module.llvm_module().add_function(
"bhc_map_singleton",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000101), map_singleton);
let map_null = self.module.llvm_module().add_function(
"bhc_map_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000102), map_null);
let map_size = self.module.llvm_module().add_function(
"bhc_map_size",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000103), map_size);
let map_member = self.module.llvm_module().add_function(
"bhc_map_member",
i64_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000104), map_member);
let map_lookup = self.module.llvm_module().add_function(
"bhc_map_lookup",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000105), map_lookup);
let map_fwd = self.module.llvm_module().add_function(
"bhc_map_find_with_default",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000106), map_fwd);
let map_insert = self.module.llvm_module().add_function(
"bhc_map_insert",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000107), map_insert);
let map_delete = self.module.llvm_module().add_function(
"bhc_map_delete",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000108), map_delete);
let map_union = self.module.llvm_module().add_function(
"bhc_map_union",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000109), map_union);
let map_intersection = self.module.llvm_module().add_function(
"bhc_map_intersection",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000110), map_intersection);
let map_difference = self.module.llvm_module().add_function(
"bhc_map_difference",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000111), map_difference);
let map_is_submap = self.module.llvm_module().add_function(
"bhc_map_is_submap_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000112), map_is_submap);
// Set operations
let set_empty = self.module.llvm_module().add_function(
"bhc_set_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000120), set_empty);
let set_singleton = self.module.llvm_module().add_function(
"bhc_set_singleton",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000121), set_singleton);
let set_null = self.module.llvm_module().add_function(
"bhc_set_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000122), set_null);
let set_size = self.module.llvm_module().add_function(
"bhc_set_size",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000123), set_size);
let set_member = self.module.llvm_module().add_function(
"bhc_set_member",
i64_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000124), set_member);
let set_insert = self.module.llvm_module().add_function(
"bhc_set_insert",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000125), set_insert);
let set_delete = self.module.llvm_module().add_function(
"bhc_set_delete",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000126), set_delete);
let set_union = self.module.llvm_module().add_function(
"bhc_set_union",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000127), set_union);
let set_intersection = self.module.llvm_module().add_function(
"bhc_set_intersection",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000128), set_intersection);
let set_difference = self.module.llvm_module().add_function(
"bhc_set_difference",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000129), set_difference);
let set_is_subset = self.module.llvm_module().add_function(
"bhc_set_is_subset_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000130), set_is_subset);
let set_is_proper_subset = self.module.llvm_module().add_function(
"bhc_set_is_proper_subset_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000131), set_is_proper_subset);
let set_find_min = self.module.llvm_module().add_function(
"bhc_set_find_min",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000132), set_find_min);
let set_find_max = self.module.llvm_module().add_function(
"bhc_set_find_max",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000133), set_find_max);
let set_delete_min = self.module.llvm_module().add_function(
"bhc_set_delete_min",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000134), set_delete_min);
let set_delete_max = self.module.llvm_module().add_function(
"bhc_set_delete_max",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000135), set_delete_max);
// IntMap operations
let intmap_empty = self.module.llvm_module().add_function(
"bhc_intmap_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000140), intmap_empty);
let intmap_singleton = self.module.llvm_module().add_function(
"bhc_intmap_singleton",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000141), intmap_singleton);
let intmap_null = self.module.llvm_module().add_function(
"bhc_intmap_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000142), intmap_null);
let intmap_insert = self.module.llvm_module().add_function(
"bhc_intmap_insert",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000143), intmap_insert);
let intmap_delete = self.module.llvm_module().add_function(
"bhc_intmap_delete",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000144), intmap_delete);
let intmap_lookup = self.module.llvm_module().add_function(
"bhc_intmap_lookup",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000145), intmap_lookup);
let intmap_union = self.module.llvm_module().add_function(
"bhc_intmap_union",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000146), intmap_union);
// IntSet operations
let intset_empty = self.module.llvm_module().add_function(
"bhc_intset_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000150), intset_empty);
let intset_singleton = self.module.llvm_module().add_function(
"bhc_intset_singleton",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000151), intset_singleton);
let intset_null = self.module.llvm_module().add_function(
"bhc_intset_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000152), intset_null);
let intset_insert = self.module.llvm_module().add_function(
"bhc_intset_insert",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000153), intset_insert);
let intset_delete = self.module.llvm_module().add_function(
"bhc_intset_delete",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000154), intset_delete);
let intset_union = self.module.llvm_module().add_function(
"bhc_intset_union",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000155), intset_union);
let intset_member = self.module.llvm_module().add_function(
"bhc_intset_member",
i64_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000156), intset_member);
let intset_intersection = self.module.llvm_module().add_function(
"bhc_intset_intersection",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000157), intset_intersection);
let intset_difference = self.module.llvm_module().add_function(
"bhc_intset_difference",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000158), intset_difference);
let intset_is_subset = self.module.llvm_module().add_function(
"bhc_intset_is_subset_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000159), intset_is_subset);
// Sequence operations (VarId 1000800-1000829)
let seq_empty = self.module.llvm_module().add_function(
"bhc_seq_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000800), seq_empty);
let seq_singleton = self.module.llvm_module().add_function(
"bhc_seq_singleton",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000801), seq_singleton);
let seq_null = self.module.llvm_module().add_function(
"bhc_seq_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000802), seq_null);
let seq_length = self.module.llvm_module().add_function(
"bhc_seq_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000803), seq_length);
let seq_index = self.module.llvm_module().add_function(
"bhc_seq_index",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000804), seq_index);
let seq_lookup = self.module.llvm_module().add_function(
"bhc_seq_lookup",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000805), seq_lookup);
let seq_cons = self.module.llvm_module().add_function(
"bhc_seq_cons",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000806), seq_cons);
let seq_snoc = self.module.llvm_module().add_function(
"bhc_seq_snoc",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000807), seq_snoc);
let seq_append = self.module.llvm_module().add_function(
"bhc_seq_append",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000808), seq_append);
let seq_take = self.module.llvm_module().add_function(
"bhc_seq_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000809), seq_take);
let seq_drop = self.module.llvm_module().add_function(
"bhc_seq_drop",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000810), seq_drop);
let seq_reverse = self.module.llvm_module().add_function(
"bhc_seq_reverse",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000811), seq_reverse);
let seq_update = self.module.llvm_module().add_function(
"bhc_seq_update",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000812), seq_update);
let seq_insert_at = self.module.llvm_module().add_function(
"bhc_seq_insert_at",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000813), seq_insert_at);
let seq_delete_at = self.module.llvm_module().add_function(
"bhc_seq_delete_at",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000814), seq_delete_at);
let seq_elem_count = self.module.llvm_module().add_function(
"bhc_seq_elem_count",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000815), seq_elem_count);
let seq_elem_at = self.module.llvm_module().add_function(
"bhc_seq_elem_at",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000816), seq_elem_at);
let seq_replicate = self.module.llvm_module().add_function(
"bhc_seq_replicate",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000817), seq_replicate);
let seq_viewl_tag = self.module.llvm_module().add_function(
"bhc_seq_viewl_tag",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000818), seq_viewl_tag);
let seq_viewl_head = self.module.llvm_module().add_function(
"bhc_seq_viewl_head",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000819), seq_viewl_head);
let seq_viewl_tail = self.module.llvm_module().add_function(
"bhc_seq_viewl_tail",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000820), seq_viewl_tail);
let seq_viewr_tag = self.module.llvm_module().add_function(
"bhc_seq_viewr_tag",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000821), seq_viewr_tag);
let seq_viewr_last = self.module.llvm_module().add_function(
"bhc_seq_viewr_last",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000822), seq_viewr_last);
let seq_viewr_init = self.module.llvm_module().add_function(
"bhc_seq_viewr_init",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000823), seq_viewr_init);
// Container iteration helpers (for toList/fromList)
// bhc_map_keys_count(map_ptr) -> i64
let map_keys_count = self.module.llvm_module().add_function(
"bhc_map_keys_count",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000160), map_keys_count);
// bhc_map_key_at(map_ptr, index) -> i64
let map_key_at = self.module.llvm_module().add_function(
"bhc_map_key_at",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000161), map_key_at);
// bhc_map_value_at(map_ptr, index) -> ptr
let map_value_at = self.module.llvm_module().add_function(
"bhc_map_value_at",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000162), map_value_at);
// bhc_set_elem_count(set_ptr) -> i64
let set_elem_count = self.module.llvm_module().add_function(
"bhc_set_elem_count",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000163), set_elem_count);
// bhc_set_elem_at(set_ptr, index) -> i64
let set_elem_at = self.module.llvm_module().add_function(
"bhc_set_elem_at",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000164), set_elem_at);
// ---- List RTS functions (VarId 1170-1175) ----
// bhc_list_sort(list_ptr) -> ptr
let list_sort = self.module.llvm_module().add_function(
"bhc_list_sort",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000170), list_sort);
// bhc_list_sort_by(closure_ptr, list_ptr) -> ptr
let list_sort_by = self.module.llvm_module().add_function(
"bhc_list_sort_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000171), list_sort_by);
// bhc_list_nub(list_ptr) -> ptr
let list_nub = self.module.llvm_module().add_function(
"bhc_list_nub",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000172), list_nub);
// bhc_list_group(list_ptr) -> ptr
let list_group = self.module.llvm_module().add_function(
"bhc_list_group",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000173), list_group);
// bhc_list_intercalate(sep_ptr, list_ptr) -> ptr
let list_intercalate = self.module.llvm_module().add_function(
"bhc_list_intercalate",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000174), list_intercalate);
// bhc_list_transpose(list_ptr) -> ptr
let list_transpose = self.module.llvm_module().add_function(
"bhc_list_transpose",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000175), list_transpose);
// ---- E.26 List RTS functions (VarId 1000550-1000559) ----
// bhc_list_sort_on(key_fn, list) -> ptr
let list_sort_on = self.module.llvm_module().add_function(
"bhc_list_sort_on",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000550), list_sort_on);
// bhc_list_nub_by(eq_fn, list) -> ptr
let list_nub_by = self.module.llvm_module().add_function(
"bhc_list_nub_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000551), list_nub_by);
// bhc_list_group_by(eq_fn, list) -> ptr
let list_group_by = self.module.llvm_module().add_function(
"bhc_list_group_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000552), list_group_by);
// bhc_list_delete_by(eq_fn, val, list) -> ptr
let list_delete_by = self.module.llvm_module().add_function(
"bhc_list_delete_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000553), list_delete_by);
// bhc_list_union_by(eq_fn, xs, ys) -> ptr
let list_union_by = self.module.llvm_module().add_function(
"bhc_list_union_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000554), list_union_by);
// bhc_list_intersect_by(eq_fn, xs, ys) -> ptr
let list_intersect_by = self.module.llvm_module().add_function(
"bhc_list_intersect_by",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000555), list_intersect_by);
// bhc_list_strip_prefix(prefix, list) -> ptr
let list_strip_prefix = self.module.llvm_module().add_function(
"bhc_list_strip_prefix",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000556), list_strip_prefix);
// bhc_list_insert(val, list) -> ptr
let list_insert = self.module.llvm_module().add_function(
"bhc_list_insert",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000557), list_insert);
// bhc_list_map_accum_l(f, acc, list) -> ptr
let list_map_accum_l = self.module.llvm_module().add_function(
"bhc_list_map_accum_l",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000558), list_map_accum_l);
// bhc_list_map_accum_r(f, acc, list) -> ptr
let list_map_accum_r = self.module.llvm_module().add_function(
"bhc_list_map_accum_r",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000559), list_map_accum_r);
// ---- E.28 IO input RTS functions (VarId 1000560-1000562) ----
let get_char_fn = self.module.llvm_module().add_function(
"bhc_getChar",
i64_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000560), get_char_fn);
let is_eof_fn =
self.module
.llvm_module()
.add_function("bhc_isEOF", i64_type.fn_type(&[], false), None);
self.functions.insert(VarId::new(1000561), is_eof_fn);
let get_contents_fn = self.module.llvm_module().add_function(
"bhc_getContents",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000562), get_contents_fn);
// ---- E.45: Integer (arbitrary precision) RTS functions (VarId 1000600-1000619) ----
// bhc_integer_from_i64(i64) -> ptr
let int_from_i64 = self.module.llvm_module().add_function(
"bhc_integer_from_i64",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000600), int_from_i64);
// bhc_integer_from_str(ptr) -> ptr
let int_from_str = self.module.llvm_module().add_function(
"bhc_integer_from_str",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000601), int_from_str);
// bhc_integer_to_i64(ptr) -> i64
let int_to_i64 = self.module.llvm_module().add_function(
"bhc_integer_to_i64",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000602), int_to_i64);
// bhc_integer_add(ptr, ptr) -> ptr
let int_add = self.module.llvm_module().add_function(
"bhc_integer_add",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000603), int_add);
// bhc_integer_sub(ptr, ptr) -> ptr
let int_sub = self.module.llvm_module().add_function(
"bhc_integer_sub",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000604), int_sub);
// bhc_integer_mul(ptr, ptr) -> ptr
let int_mul = self.module.llvm_module().add_function(
"bhc_integer_mul",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000605), int_mul);
// bhc_integer_div(ptr, ptr) -> ptr
let int_div = self.module.llvm_module().add_function(
"bhc_integer_div",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000606), int_div);
// bhc_integer_mod(ptr, ptr) -> ptr
let int_mod = self.module.llvm_module().add_function(
"bhc_integer_mod",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000607), int_mod);
// bhc_integer_negate(ptr) -> ptr
let int_negate = self.module.llvm_module().add_function(
"bhc_integer_negate",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000608), int_negate);
// bhc_integer_abs(ptr) -> ptr
let int_abs = self.module.llvm_module().add_function(
"bhc_integer_abs",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000609), int_abs);
// bhc_integer_signum(ptr) -> ptr
let int_signum = self.module.llvm_module().add_function(
"bhc_integer_signum",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000610), int_signum);
// bhc_integer_gcd(ptr, ptr) -> ptr
let int_gcd = self.module.llvm_module().add_function(
"bhc_integer_gcd",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000611), int_gcd);
// bhc_integer_eq(ptr, ptr) -> i64
let int_eq = self.module.llvm_module().add_function(
"bhc_integer_eq",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000612), int_eq);
// bhc_integer_lt(ptr, ptr) -> i64
let int_lt = self.module.llvm_module().add_function(
"bhc_integer_lt",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000613), int_lt);
// bhc_integer_le(ptr, ptr) -> i64
let int_le = self.module.llvm_module().add_function(
"bhc_integer_le",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000614), int_le);
// bhc_integer_gt(ptr, ptr) -> i64
let int_gt = self.module.llvm_module().add_function(
"bhc_integer_gt",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000615), int_gt);
// bhc_integer_ge(ptr, ptr) -> i64
let int_ge = self.module.llvm_module().add_function(
"bhc_integer_ge",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000616), int_ge);
// bhc_integer_compare(ptr, ptr) -> i64
let int_compare = self.module.llvm_module().add_function(
"bhc_integer_compare",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000617), int_compare);
// bhc_integer_show(ptr) -> ptr
let int_show = self.module.llvm_module().add_function(
"bhc_integer_show",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000618), int_show);
// ---- String RTS functions (VarId 1176-1179) ----
// bhc_string_lines(str_ptr) -> ptr
let string_lines = self.module.llvm_module().add_function(
"bhc_string_lines",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000176), string_lines);
// bhc_string_unlines(list_ptr) -> ptr
let string_unlines = self.module.llvm_module().add_function(
"bhc_string_unlines",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000177), string_unlines);
// bhc_string_words(str_ptr) -> ptr
let string_words = self.module.llvm_module().add_function(
"bhc_string_words",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000178), string_words);
// bhc_string_unwords(list_ptr) -> ptr
let string_unwords = self.module.llvm_module().add_function(
"bhc_string_unwords",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000179), string_unwords);
// bhc_string_length(str_ptr) -> i64 (strlen wrapper for C strings)
let string_length = self.module.llvm_module().add_function(
"bhc_string_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000180), string_length);
// ---- Text RTS functions (VarId 1200-1227) ----
// bhc_text_empty() -> ptr
let text_empty = self.module.llvm_module().add_function(
"bhc_text_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000200), text_empty);
// bhc_text_singleton(i64) -> ptr
let text_singleton = self.module.llvm_module().add_function(
"bhc_text_singleton",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000201), text_singleton);
// bhc_text_null(ptr) -> i64
let text_null = self.module.llvm_module().add_function(
"bhc_text_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000202), text_null);
// bhc_text_length(ptr) -> i64
let text_length = self.module.llvm_module().add_function(
"bhc_text_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000203), text_length);
// bhc_text_eq(ptr, ptr) -> i64
let text_eq = self.module.llvm_module().add_function(
"bhc_text_eq",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000204), text_eq);
// bhc_text_compare(ptr, ptr) -> i64
let text_compare = self.module.llvm_module().add_function(
"bhc_text_compare",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000205), text_compare);
// bhc_text_head(ptr) -> i64
let text_head = self.module.llvm_module().add_function(
"bhc_text_head",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000206), text_head);
// bhc_text_last(ptr) -> i64
let text_last = self.module.llvm_module().add_function(
"bhc_text_last",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000207), text_last);
// bhc_text_tail(ptr) -> ptr
let text_tail = self.module.llvm_module().add_function(
"bhc_text_tail",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000208), text_tail);
// bhc_text_init(ptr) -> ptr
let text_init = self.module.llvm_module().add_function(
"bhc_text_init",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000209), text_init);
// bhc_text_append(ptr, ptr) -> ptr
let text_append = self.module.llvm_module().add_function(
"bhc_text_append",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000210), text_append);
// bhc_text_reverse(ptr) -> ptr
let text_reverse = self.module.llvm_module().add_function(
"bhc_text_reverse",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000211), text_reverse);
// bhc_text_take(i64, ptr) -> ptr
let text_take = self.module.llvm_module().add_function(
"bhc_text_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000212), text_take);
// bhc_text_take_end(i64, ptr) -> ptr
let text_take_end = self.module.llvm_module().add_function(
"bhc_text_take_end",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000213), text_take_end);
// bhc_text_drop(i64, ptr) -> ptr
let text_drop = self.module.llvm_module().add_function(
"bhc_text_drop",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000214), text_drop);
// bhc_text_drop_end(i64, ptr) -> ptr
let text_drop_end = self.module.llvm_module().add_function(
"bhc_text_drop_end",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000215), text_drop_end);
// bhc_text_is_prefix_of(ptr, ptr) -> i64
let text_is_prefix = self.module.llvm_module().add_function(
"bhc_text_is_prefix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000216), text_is_prefix);
// bhc_text_is_suffix_of(ptr, ptr) -> i64
let text_is_suffix = self.module.llvm_module().add_function(
"bhc_text_is_suffix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000217), text_is_suffix);
// bhc_text_is_infix_of(ptr, ptr) -> i64
let text_is_infix = self.module.llvm_module().add_function(
"bhc_text_is_infix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000218), text_is_infix);
// bhc_text_to_lower(ptr) -> ptr
let text_to_lower = self.module.llvm_module().add_function(
"bhc_text_to_lower",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000219), text_to_lower);
// bhc_text_to_upper(ptr) -> ptr
let text_to_upper = self.module.llvm_module().add_function(
"bhc_text_to_upper",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000220), text_to_upper);
// bhc_text_to_case_fold(ptr) -> ptr
let text_case_fold = self.module.llvm_module().add_function(
"bhc_text_to_case_fold",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000221), text_case_fold);
// bhc_text_to_title(ptr) -> ptr
let text_to_title = self.module.llvm_module().add_function(
"bhc_text_to_title",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000222), text_to_title);
// bhc_text_pack(ptr) -> ptr (list -> Text)
let text_pack = self.module.llvm_module().add_function(
"bhc_text_pack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000223), text_pack);
// bhc_text_char_count(ptr) -> i64 (for unpack iteration)
let text_char_count = self.module.llvm_module().add_function(
"bhc_text_char_count",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000224), text_char_count);
// bhc_text_char_at(ptr, i64) -> i64 (for unpack iteration)
let text_char_at = self.module.llvm_module().add_function(
"bhc_text_char_at",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000225), text_char_at);
// bhc_text_map(fn_ptr, env_ptr, text_ptr) -> ptr
let text_map = self.module.llvm_module().add_function(
"bhc_text_map",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000226), text_map);
// ---- Additional Text RTS functions (VarId 1000227-1000236) ----
// bhc_text_filter(fn_ptr, env_ptr, text_ptr) -> ptr
let text_filter = self.module.llvm_module().add_function(
"bhc_text_filter",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000227), text_filter);
// bhc_text_foldl(fn_ptr, env_ptr, i64, text_ptr) -> i64
let text_foldl = self.module.llvm_module().add_function(
"bhc_text_foldl",
i64_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
i64_type.into(),
ptr_type.into(),
],
false,
),
None,
);
self.functions.insert(VarId::new(1000228), text_foldl);
// bhc_text_concat(list_ptr) -> ptr
let text_concat = self.module.llvm_module().add_function(
"bhc_text_concat",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000229), text_concat);
// bhc_text_intercalate(sep_ptr, list_ptr) -> ptr
let text_intercalate = self.module.llvm_module().add_function(
"bhc_text_intercalate",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000230), text_intercalate);
// bhc_text_strip(text_ptr) -> ptr
let text_strip = self.module.llvm_module().add_function(
"bhc_text_strip",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000231), text_strip);
// bhc_text_words(text_ptr) -> ptr (returns cons-list of Text)
let text_words = self.module.llvm_module().add_function(
"bhc_text_words",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000232), text_words);
// bhc_text_lines(text_ptr) -> ptr (returns cons-list of Text)
let text_lines = self.module.llvm_module().add_function(
"bhc_text_lines",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000233), text_lines);
// bhc_text_split_on(needle_ptr, haystack_ptr) -> ptr (returns cons-list of Text)
let text_split_on = self.module.llvm_module().add_function(
"bhc_text_split_on",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000234), text_split_on);
// bhc_text_replace(needle_ptr, replacement_ptr, haystack_ptr) -> ptr
let text_replace = self.module.llvm_module().add_function(
"bhc_text_replace",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000235), text_replace);
// bhc_text_encode_utf8(text_ptr) -> ptr (Text -> ByteString)
let text_encode_utf8 = self.module.llvm_module().add_function(
"bhc_text_encode_utf8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000236), text_encode_utf8);
// bhc_text_decode_utf8(bs_ptr) -> ptr (ByteString -> Text)
let text_decode_utf8 = self.module.llvm_module().add_function(
"bhc_text_decode_utf8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000237), text_decode_utf8);
// ---- Data.Text.IO RTS functions (VarId 1000240-1000246) ----
// bhc_text_read_file(path_cstr) -> ptr (Text)
let text_read_file = self.module.llvm_module().add_function(
"bhc_text_read_file",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000240), text_read_file);
// bhc_text_write_file(path_cstr, text_ptr) -> void
let text_write_file = self.module.llvm_module().add_function(
"bhc_text_write_file",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000241), text_write_file);
// bhc_text_append_file(path_cstr, text_ptr) -> void
let text_append_file = self.module.llvm_module().add_function(
"bhc_text_append_file",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000242), text_append_file);
// bhc_text_h_get_contents(handle_ptr) -> ptr (Text)
let text_h_get_contents = self.module.llvm_module().add_function(
"bhc_text_h_get_contents",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000243), text_h_get_contents);
// bhc_text_h_get_line(handle_ptr) -> ptr (Text)
let text_h_get_line = self.module.llvm_module().add_function(
"bhc_text_h_get_line",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000244), text_h_get_line);
// bhc_text_h_put_str(handle_ptr, text_ptr) -> void
let text_h_put_str = self.module.llvm_module().add_function(
"bhc_text_h_put_str",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000245), text_h_put_str);
// bhc_text_h_put_str_ln(handle_ptr, text_ptr) -> void
let text_h_put_str_ln = self.module.llvm_module().add_function(
"bhc_text_h_put_str_ln",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000246), text_h_put_str_ln);
// ---- ByteString RTS functions (VarId 1000400-1000425) ----
// bhc_bs_empty() -> ptr
let bs_empty = self.module.llvm_module().add_function(
"bhc_bs_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000400), bs_empty);
// bhc_bs_singleton(i64) -> ptr
let bs_singleton = self.module.llvm_module().add_function(
"bhc_bs_singleton",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000401), bs_singleton);
// bhc_bs_pack(ptr) -> ptr (list -> ByteString)
let bs_pack = self.module.llvm_module().add_function(
"bhc_bs_pack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000402), bs_pack);
// bhc_bs_length(ptr) -> i64
let bs_length = self.module.llvm_module().add_function(
"bhc_bs_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000403), bs_length);
// bhc_bs_null(ptr) -> i64
let bs_null = self.module.llvm_module().add_function(
"bhc_bs_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000404), bs_null);
// bhc_bs_head(ptr) -> i64
let bs_head = self.module.llvm_module().add_function(
"bhc_bs_head",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000405), bs_head);
// bhc_bs_last(ptr) -> i64
let bs_last = self.module.llvm_module().add_function(
"bhc_bs_last",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000406), bs_last);
// bhc_bs_tail(ptr) -> ptr
let bs_tail = self.module.llvm_module().add_function(
"bhc_bs_tail",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000407), bs_tail);
// bhc_bs_init(ptr) -> ptr
let bs_init = self.module.llvm_module().add_function(
"bhc_bs_init",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000408), bs_init);
// bhc_bs_append(ptr, ptr) -> ptr
let bs_append = self.module.llvm_module().add_function(
"bhc_bs_append",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000409), bs_append);
// bhc_bs_cons(i64, ptr) -> ptr
let bs_cons = self.module.llvm_module().add_function(
"bhc_bs_cons",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000410), bs_cons);
// bhc_bs_snoc(ptr, i64) -> ptr
let bs_snoc = self.module.llvm_module().add_function(
"bhc_bs_snoc",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000411), bs_snoc);
// bhc_bs_take(i64, ptr) -> ptr
let bs_take = self.module.llvm_module().add_function(
"bhc_bs_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000412), bs_take);
// bhc_bs_drop(i64, ptr) -> ptr
let bs_drop = self.module.llvm_module().add_function(
"bhc_bs_drop",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000413), bs_drop);
// bhc_bs_reverse(ptr) -> ptr
let bs_reverse = self.module.llvm_module().add_function(
"bhc_bs_reverse",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000414), bs_reverse);
// bhc_bs_elem(i64, ptr) -> i64
let bs_elem = self.module.llvm_module().add_function(
"bhc_bs_elem",
i64_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000415), bs_elem);
// bhc_bs_index(ptr, i64) -> i64
let bs_index = self.module.llvm_module().add_function(
"bhc_bs_index",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000416), bs_index);
// bhc_bs_eq(ptr, ptr) -> i64
let bs_eq = self.module.llvm_module().add_function(
"bhc_bs_eq",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000417), bs_eq);
// bhc_bs_compare(ptr, ptr) -> i64
let bs_compare = self.module.llvm_module().add_function(
"bhc_bs_compare",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000418), bs_compare);
// bhc_bs_is_prefix_of(ptr, ptr) -> i64
let bs_is_prefix = self.module.llvm_module().add_function(
"bhc_bs_is_prefix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000419), bs_is_prefix);
// bhc_bs_is_suffix_of(ptr, ptr) -> i64
let bs_is_suffix = self.module.llvm_module().add_function(
"bhc_bs_is_suffix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000420), bs_is_suffix);
// bhc_bs_unpack(ptr, i64) -> i64 (for unpack iteration)
let bs_unpack = self.module.llvm_module().add_function(
"bhc_bs_unpack",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000421), bs_unpack);
// bhc_bs_read_file(path_ptr) -> ptr
let bs_read_file = self.module.llvm_module().add_function(
"bhc_bs_read_file",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000422), bs_read_file);
// bhc_bs_write_file(path_ptr, bs_ptr) -> void
let bs_write_file = self.module.llvm_module().add_function(
"bhc_bs_write_file",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000423), bs_write_file);
// ---- ByteArray RTS functions (VarId 1250-1258) ----
// bhc_bytearray_malloc(i64) -> ptr
let ba_malloc = self.module.llvm_module().add_function(
"bhc_bytearray_malloc",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000250), ba_malloc);
// bhc_bytearray_contents(ptr) -> ptr
let ba_contents = self.module.llvm_module().add_function(
"bhc_bytearray_contents",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000251), ba_contents);
// bhc_bytearray_index(ptr, i64) -> i64
let ba_index = self.module.llvm_module().add_function(
"bhc_bytearray_index",
i64_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000252), ba_index);
// bhc_bytearray_copy(ptr, ptr, i64, i64) -> void
let ba_copy = self.module.llvm_module().add_function(
"bhc_bytearray_copy",
void_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
i64_type.into(),
i64_type.into(),
],
false,
),
None,
);
self.functions.insert(VarId::new(1000253), ba_copy);
// bhc_ptr_plus(ptr, i64) -> ptr
let ba_ptr_plus = self.module.llvm_module().add_function(
"bhc_ptr_plus",
ptr_type.fn_type(&[ptr_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000254), ba_ptr_plus);
// bhc_poke_byte(ptr, i64, i64) -> void
let ba_poke = self.module.llvm_module().add_function(
"bhc_poke_byte",
void_type.fn_type(&[ptr_type.into(), i64_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000255), ba_poke);
// bhc_cstring_length(ptr) -> i64
let ba_strlen = self.module.llvm_module().add_function(
"bhc_cstring_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000256), ba_strlen);
// bhc_peek_array(i64, ptr) -> ptr
let ba_peek = self.module.llvm_module().add_function(
"bhc_peek_array",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000257), ba_peek);
// ---- Bitcast RTS functions (VarId 1258-1261) ----
// bhc_float_to_word32(f32) -> i32
let f32_to_i32 = i32_type.fn_type(&[f32_type.into()], false);
let float_to_word32 =
self.module
.llvm_module()
.add_function("bhc_float_to_word32", f32_to_i32, None);
self.functions.insert(VarId::new(1000258), float_to_word32);
// bhc_double_to_word64(f64) -> i64
let f64_to_i64_bc = i64_type.fn_type(&[f64_type.into()], false);
let double_to_word64 =
self.module
.llvm_module()
.add_function("bhc_double_to_word64", f64_to_i64_bc, None);
self.functions.insert(VarId::new(1000259), double_to_word64);
// bhc_word32_to_float(i32) -> f32
let i32_to_f32 = f32_type.fn_type(&[i32_type.into()], false);
let word32_to_float =
self.module
.llvm_module()
.add_function("bhc_word32_to_float", i32_to_f32, None);
self.functions.insert(VarId::new(1000260), word32_to_float);
// bhc_word64_to_double(i64) -> f64
let i64_to_f64 = f64_type.fn_type(&[i64_type.into()], false);
let word64_to_double =
self.module
.llvm_module()
.add_function("bhc_word64_to_double", i64_to_f64, None);
self.functions.insert(VarId::new(1000261), word64_to_double);
// ---- Lazy Text RTS functions (VarId 1000270-1000283) ----
// bhc_lazy_text_empty() -> ptr
let lt_empty = self.module.llvm_module().add_function(
"bhc_lazy_text_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000270), lt_empty);
// bhc_lazy_text_from_strict(ptr) -> ptr
let lt_from_strict = self.module.llvm_module().add_function(
"bhc_lazy_text_from_strict",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000271), lt_from_strict);
// bhc_lazy_text_to_strict(ptr) -> ptr
let lt_to_strict = self.module.llvm_module().add_function(
"bhc_lazy_text_to_strict",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000272), lt_to_strict);
// bhc_lazy_text_pack(ptr) -> ptr
let lt_pack = self.module.llvm_module().add_function(
"bhc_lazy_text_pack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000273), lt_pack);
// bhc_lazy_text_unpack(ptr) -> ptr
let lt_unpack = self.module.llvm_module().add_function(
"bhc_lazy_text_unpack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000274), lt_unpack);
// bhc_lazy_text_null(ptr) -> i64
let lt_null = self.module.llvm_module().add_function(
"bhc_lazy_text_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000275), lt_null);
// bhc_lazy_text_length(ptr) -> i64
let lt_length = self.module.llvm_module().add_function(
"bhc_lazy_text_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000276), lt_length);
// bhc_lazy_text_append(ptr, ptr) -> ptr
let lt_append = self.module.llvm_module().add_function(
"bhc_lazy_text_append",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000277), lt_append);
// bhc_lazy_text_from_chunks(ptr) -> ptr
let lt_from_chunks = self.module.llvm_module().add_function(
"bhc_lazy_text_from_chunks",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000278), lt_from_chunks);
// bhc_lazy_text_to_chunks(ptr) -> ptr
let lt_to_chunks = self.module.llvm_module().add_function(
"bhc_lazy_text_to_chunks",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000279), lt_to_chunks);
// bhc_lazy_text_head(ptr) -> i64
let lt_head = self.module.llvm_module().add_function(
"bhc_lazy_text_head",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000280), lt_head);
// bhc_lazy_text_tail(ptr) -> ptr
let lt_tail = self.module.llvm_module().add_function(
"bhc_lazy_text_tail",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000281), lt_tail);
// bhc_lazy_text_take(i64, ptr) -> ptr
let lt_take = self.module.llvm_module().add_function(
"bhc_lazy_text_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000282), lt_take);
// bhc_lazy_text_drop(i64, ptr) -> ptr
let lt_drop = self.module.llvm_module().add_function(
"bhc_lazy_text_drop",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000283), lt_drop);
// ---- Lazy ByteString RTS functions (VarId 1000440-1000459) ----
// bhc_lazy_bs_empty() -> ptr
let lbs_empty = self.module.llvm_module().add_function(
"bhc_lazy_bs_empty",
ptr_type.fn_type(&[], false),
None,
);
self.functions.insert(VarId::new(1000440), lbs_empty);
// bhc_lazy_bs_from_strict(ptr) -> ptr
let lbs_from_strict = self.module.llvm_module().add_function(
"bhc_lazy_bs_from_strict",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000441), lbs_from_strict);
// bhc_lazy_bs_to_strict(ptr) -> ptr
let lbs_to_strict = self.module.llvm_module().add_function(
"bhc_lazy_bs_to_strict",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000442), lbs_to_strict);
// bhc_lazy_bs_from_chunks(ptr) -> ptr
let lbs_from_chunks = self.module.llvm_module().add_function(
"bhc_lazy_bs_from_chunks",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000443), lbs_from_chunks);
// bhc_lazy_bs_to_chunks(ptr) -> ptr
let lbs_to_chunks = self.module.llvm_module().add_function(
"bhc_lazy_bs_to_chunks",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000444), lbs_to_chunks);
// bhc_lazy_bs_null(ptr) -> i64
let lbs_null = self.module.llvm_module().add_function(
"bhc_lazy_bs_null",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000445), lbs_null);
// bhc_lazy_bs_length(ptr) -> i64
let lbs_length = self.module.llvm_module().add_function(
"bhc_lazy_bs_length",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000446), lbs_length);
// bhc_lazy_bs_pack(ptr) -> ptr
let lbs_pack = self.module.llvm_module().add_function(
"bhc_lazy_bs_pack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000447), lbs_pack);
// bhc_lazy_bs_append(ptr, ptr) -> ptr
let lbs_append = self.module.llvm_module().add_function(
"bhc_lazy_bs_append",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000448), lbs_append);
// bhc_lazy_bs_head(ptr) -> i64
let lbs_head = self.module.llvm_module().add_function(
"bhc_lazy_bs_head",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000449), lbs_head);
// bhc_lazy_bs_tail(ptr) -> ptr
let lbs_tail = self.module.llvm_module().add_function(
"bhc_lazy_bs_tail",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000450), lbs_tail);
// bhc_lazy_bs_take(i64, ptr) -> ptr
let lbs_take = self.module.llvm_module().add_function(
"bhc_lazy_bs_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000451), lbs_take);
// bhc_lazy_bs_drop(i64, ptr) -> ptr
let lbs_drop = self.module.llvm_module().add_function(
"bhc_lazy_bs_drop",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000452), lbs_drop);
// bhc_lazy_bs_filter(fn_ptr, env_ptr, ptr) -> ptr
let lbs_filter = self.module.llvm_module().add_function(
"bhc_lazy_bs_filter",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000453), lbs_filter);
// bhc_lazy_bs_is_prefix_of(ptr, ptr) -> i64
let lbs_is_prefix = self.module.llvm_module().add_function(
"bhc_lazy_bs_is_prefix_of",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000454), lbs_is_prefix);
// bhc_lazy_bs_read_file(ptr) -> ptr
let lbs_read_file = self.module.llvm_module().add_function(
"bhc_lazy_bs_read_file",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000455), lbs_read_file);
// bhc_lazy_bs_write_file(ptr, ptr) -> void
let lbs_write_file = self.module.llvm_module().add_function(
"bhc_lazy_bs_write_file",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000456), lbs_write_file);
// bhc_lazy_bs_put_str(ptr) -> void
let lbs_put_str = self.module.llvm_module().add_function(
"bhc_lazy_bs_put_str",
void_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000457), lbs_put_str);
// bhc_lazy_bs_h_put_str(ptr, ptr) -> void
let lbs_h_put_str = self.module.llvm_module().add_function(
"bhc_lazy_bs_h_put_str",
void_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000458), lbs_h_put_str);
// bhc_lazy_bs_h_get_contents(ptr) -> ptr
let lbs_h_get_contents = self.module.llvm_module().add_function(
"bhc_lazy_bs_h_get_contents",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000459), lbs_h_get_contents);
// ---- Lazy ByteString Char8 RTS functions (VarId 1000470-1000475) ----
// bhc_lazy_bs_char8_unpack(ptr) -> ptr
let lbs_c8_unpack = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_unpack",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000470), lbs_c8_unpack);
// bhc_lazy_bs_char8_lines(ptr) -> ptr
let lbs_c8_lines = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_lines",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000471), lbs_c8_lines);
// bhc_lazy_bs_char8_unlines(ptr) -> ptr
let lbs_c8_unlines = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_unlines",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000472), lbs_c8_unlines);
// bhc_lazy_bs_char8_take(i64, ptr) -> ptr
let lbs_c8_take = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_take",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000473), lbs_c8_take);
// bhc_lazy_bs_char8_drop_while(fn_ptr, env_ptr, ptr) -> ptr
let lbs_c8_drop_while = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_drop_while",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000474), lbs_c8_drop_while);
// bhc_lazy_bs_char8_cons(i64, ptr) -> ptr
let lbs_c8_cons = self.module.llvm_module().add_function(
"bhc_lazy_bs_char8_cons",
ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000475), lbs_c8_cons);
// ---- Lazy Text Encoding RTS functions (VarId 1000476-1000477) ----
// bhc_lazy_text_encode_utf8(ptr) -> ptr
let lt_encode_utf8 = self.module.llvm_module().add_function(
"bhc_lazy_text_encode_utf8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000476), lt_encode_utf8);
// bhc_lazy_text_decode_utf8(ptr) -> ptr
let lt_decode_utf8 = self.module.llvm_module().add_function(
"bhc_lazy_text_decode_utf8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000477), lt_decode_utf8);
// ---- ByteString Builder RTS functions (VarId 1000478-1000495) ----
// bhc_bsb_singleton(i64) -> ptr
let bsb_singleton = self.module.llvm_module().add_function(
"bhc_bsb_singleton",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000478), bsb_singleton);
// bhc_bsb_char_utf8(i64) -> ptr
let bsb_char_utf8 = self.module.llvm_module().add_function(
"bhc_bsb_char_utf8",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000479), bsb_char_utf8);
// bhc_bsb_string_utf8(ptr) -> ptr
let bsb_string_utf8 = self.module.llvm_module().add_function(
"bhc_bsb_string_utf8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000480), bsb_string_utf8);
// bhc_bsb_int_dec(i64) -> ptr
let bsb_int_dec = self.module.llvm_module().add_function(
"bhc_bsb_int_dec",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000481), bsb_int_dec);
// bhc_bsb_char7(i64) -> ptr
let bsb_char7 = self.module.llvm_module().add_function(
"bhc_bsb_char7",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000482), bsb_char7);
// bhc_bsb_char8(i64) -> ptr
let bsb_char8 = self.module.llvm_module().add_function(
"bhc_bsb_char8",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000483), bsb_char8);
// bhc_bsb_string7(ptr) -> ptr
let bsb_string7 = self.module.llvm_module().add_function(
"bhc_bsb_string7",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000484), bsb_string7);
// bhc_bsb_string8(ptr) -> ptr
let bsb_string8 = self.module.llvm_module().add_function(
"bhc_bsb_string8",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000485), bsb_string8);
// bhc_bsb_word16_be(i64) -> ptr
let bsb_word16_be = self.module.llvm_module().add_function(
"bhc_bsb_word16_be",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000486), bsb_word16_be);
// bhc_bsb_word32_be(i64) -> ptr
let bsb_word32_be = self.module.llvm_module().add_function(
"bhc_bsb_word32_be",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000487), bsb_word32_be);
// bhc_bsb_word64_be(i64) -> ptr
let bsb_word64_be = self.module.llvm_module().add_function(
"bhc_bsb_word64_be",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000488), bsb_word64_be);
// bhc_bsb_word16_le(i64) -> ptr
let bsb_word16_le = self.module.llvm_module().add_function(
"bhc_bsb_word16_le",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000489), bsb_word16_le);
// bhc_bsb_word32_le(i64) -> ptr
let bsb_word32_le = self.module.llvm_module().add_function(
"bhc_bsb_word32_le",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000490), bsb_word32_le);
// bhc_bsb_word64_le(i64) -> ptr
let bsb_word64_le = self.module.llvm_module().add_function(
"bhc_bsb_word64_le",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000491), bsb_word64_le);
// bhc_bsb_word_hex(i64) -> ptr
let bsb_word_hex = self.module.llvm_module().add_function(
"bhc_bsb_word_hex",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000492), bsb_word_hex);
// bhc_bsb_word8_hex_fixed(i64) -> ptr
let bsb_word8_hex_fixed = self.module.llvm_module().add_function(
"bhc_bsb_word8_hex_fixed",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions
.insert(VarId::new(1000493), bsb_word8_hex_fixed);
// Note: bhc_float_to_word32 and bhc_double_to_word64 are declared above
// at VarIds 1000258 and 1000259 (in bytearray section).
// ---- Exception RTS functions (VarId 1080-1091) ----
// bhc_throw(ptr) -> ptr (stores exception in TLS, returns sentinel null)
let throw_fn = self.module.llvm_module().add_function(
"bhc_throw",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000080), throw_fn);
// bhc_catch(action_fn, action_env, handler_fn, handler_env) -> ptr
let catch_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let catch_fn = self
.module
.llvm_module()
.add_function("bhc_catch", catch_type, None);
self.functions.insert(VarId::new(1000081), catch_fn);
// bhc_evaluate(ptr) -> ptr
let evaluate_fn = self.module.llvm_module().add_function(
"bhc_evaluate",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000082), evaluate_fn);
// bhc_mask(fn_ptr, env_ptr) -> ptr (simple mask for mask_)
let mask_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let mask_fn = self
.module
.llvm_module()
.add_function("bhc_mask", mask_type, None);
self.functions.insert(VarId::new(1000083), mask_fn);
// bhc_unmask(fn_ptr, env_ptr) -> ptr
let unmask_fn = self
.module
.llvm_module()
.add_function("bhc_unmask", mask_type, None);
self.functions.insert(VarId::new(1000084), unmask_fn);
// bhc_mask_with_restore(action_fn, action_env) -> ptr (mask with restore closure)
let mask_restore_fn =
self.module
.llvm_module()
.add_function("bhc_mask_with_restore", mask_type, None);
self.functions.insert(VarId::new(1000085), mask_restore_fn);
// bhc_uninterruptible_mask(fn_ptr, env_ptr) -> ptr (simple uninterruptible mask)
let uninterruptible_mask_fn =
self.module
.llvm_module()
.add_function("bhc_uninterruptible_mask", mask_type, None);
self.functions
.insert(VarId::new(1000086), uninterruptible_mask_fn);
// bhc_uninterruptible_mask_with_restore(action_fn, action_env) -> ptr
let uninterruptible_mask_restore_fn = self.module.llvm_module().add_function(
"bhc_uninterruptible_mask_with_restore",
mask_type,
None,
);
self.functions
.insert(VarId::new(1000087), uninterruptible_mask_restore_fn);
// bhc_get_masking_state() -> i64
let get_mask_type = i64_type.fn_type(&[], false);
let get_mask_fn =
self.module
.llvm_module()
.add_function("bhc_get_masking_state", get_mask_type, None);
self.functions.insert(VarId::new(1000824), get_mask_fn);
// bhc_finally(action_fn, action_env, cleanup_fn, cleanup_env) -> ptr
let finally_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let finally_fn = self
.module
.llvm_module()
.add_function("bhc_finally", finally_type, None);
self.functions.insert(VarId::new(1000088), finally_fn);
// bhc_on_exception(action_fn, action_env, handler_fn, handler_env) -> ptr
let on_exception_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let on_exception_fn =
self.module
.llvm_module()
.add_function("bhc_on_exception", on_exception_type, None);
self.functions.insert(VarId::new(1000089), on_exception_fn);
// bhc_bracket(acquire_fn, acquire_env, release_fn, release_env, use_fn, use_env) -> ptr
let bracket_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let bracket_fn = self
.module
.llvm_module()
.add_function("bhc_bracket", bracket_type, None);
self.functions.insert(VarId::new(1000825), bracket_fn);
// ---- SomeException / typed catch RTS functions (VarId 1000700-1000704) ----
// bhc_make_some_exception(i64, ptr) -> ptr
let make_exc_type = ptr_type.fn_type(&[i64_type.into(), ptr_type.into()], false);
let make_exc_fn =
self.module
.llvm_module()
.add_function("bhc_make_some_exception", make_exc_type, None);
self.functions.insert(VarId::new(1000700), make_exc_fn);
// bhc_exc_get_tag(ptr) -> i64
let get_tag_type = i64_type.fn_type(&[ptr_type.into()], false);
let get_tag_fn =
self.module
.llvm_module()
.add_function("bhc_exc_get_tag", get_tag_type, None);
self.functions.insert(VarId::new(1000701), get_tag_fn);
// bhc_exc_get_payload(ptr) -> ptr
let get_payload_fn = self.module.llvm_module().add_function(
"bhc_exc_get_payload",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000702), get_payload_fn);
// bhc_catch_typed(action_fn, action_env, handler_fn, handler_env, type_tag: i64) -> ptr
let catch_typed_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
i64_type.into(),
],
false,
);
let catch_typed_fn =
self.module
.llvm_module()
.add_function("bhc_catch_typed", catch_typed_type, None);
self.functions.insert(VarId::new(1000703), catch_typed_fn);
// bhc_show_exception(ptr) -> ptr
let show_exc_fn = self.module.llvm_module().add_function(
"bhc_show_exception",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000704), show_exc_fn);
// ---- Additional IO RTS functions (VarId 1300-1314) ----
// NOTE: Previously used VarId 1085-1099 which collided with exception
// functions (finally=1088, on_exception=1089, bracket=1090). Moved to
// 1300+ range to avoid overwriting those entries.
// bhc_hGetContents(*i8) -> *i8
let h_get_contents =
self.module
.llvm_module()
.add_function("bhc_hGetContents", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000300), h_get_contents);
// bhc_hSeek(*i8, i32, i64) -> void
let h_seek_type =
void_type.fn_type(&[ptr_type.into(), i32_type.into(), i64_type.into()], false);
let h_seek = self
.module
.llvm_module()
.add_function("bhc_hSeek", h_seek_type, None);
self.functions.insert(VarId::new(1000301), h_seek);
// bhc_hTell(*i8) -> i64
let h_tell = self.module.llvm_module().add_function(
"bhc_hTell",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000302), h_tell);
// bhc_hIsOpen(*i8) -> i32
let h_is_open = self
.module
.llvm_module()
.add_function("bhc_hIsOpen", ptr_to_i32, None);
self.functions.insert(VarId::new(1000303), h_is_open);
// bhc_hIsClosed(*i8) -> i32
let h_is_closed = self
.module
.llvm_module()
.add_function("bhc_hIsClosed", ptr_to_i32, None);
self.functions.insert(VarId::new(1000304), h_is_closed);
// bhc_hIsReadable(*i8) -> i32
let h_is_readable =
self.module
.llvm_module()
.add_function("bhc_hIsReadable", ptr_to_i32, None);
self.functions.insert(VarId::new(1000305), h_is_readable);
// bhc_hIsWritable(*i8) -> i32
let h_is_writable =
self.module
.llvm_module()
.add_function("bhc_hIsWritable", ptr_to_i32, None);
self.functions.insert(VarId::new(1000306), h_is_writable);
// bhc_hIsSeekable(*i8) -> i32
let h_is_seekable =
self.module
.llvm_module()
.add_function("bhc_hIsSeekable", ptr_to_i32, None);
self.functions.insert(VarId::new(1000307), h_is_seekable);
// bhc_hSetBuffering(*i8, i32) -> void
let h_set_buf_type = void_type.fn_type(&[ptr_type.into(), i32_type.into()], false);
let h_set_buffering =
self.module
.llvm_module()
.add_function("bhc_hSetBuffering", h_set_buf_type, None);
self.functions.insert(VarId::new(1000308), h_set_buffering);
// bhc_hGetBuffering(*i8) -> i32
let h_get_buffering =
self.module
.llvm_module()
.add_function("bhc_hGetBuffering", ptr_to_i32, None);
self.functions.insert(VarId::new(1000309), h_get_buffering);
// bhc_hPutChar(*i8, i32) -> void
let h_put_char_type = void_type.fn_type(&[ptr_type.into(), i32_type.into()], false);
let h_put_char =
self.module
.llvm_module()
.add_function("bhc_hPutChar", h_put_char_type, None);
self.functions.insert(VarId::new(1000310), h_put_char);
// bhc_create_directory(*i8) -> void
let create_dir =
self.module
.llvm_module()
.add_function("bhc_create_directory", ptr_to_void, None);
self.functions.insert(VarId::new(1000311), create_dir);
// bhc_remove_file(*i8) -> void
let remove_file =
self.module
.llvm_module()
.add_function("bhc_remove_file", ptr_to_void, None);
self.functions.insert(VarId::new(1000312), remove_file);
// bhc_list_directory(*i8) -> *i8
let list_dir =
self.module
.llvm_module()
.add_function("bhc_list_directory", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000313), list_dir);
// bhc_lookupEnv(*i8) -> *i8
let lookup_env = self
.module
.llvm_module()
.add_function("bhc_lookupEnv", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000314), lookup_env);
// bhc_get_args() -> *i8 (returns cons-list of C strings)
let get_args = self
.module
.llvm_module()
.add_function("bhc_get_args", void_to_ptr, None);
self.functions.insert(VarId::new(1000315), get_args);
// bhc_get_prog_name() -> *i8
let get_prog_name =
self.module
.llvm_module()
.add_function("bhc_get_prog_name", void_to_ptr, None);
self.functions.insert(VarId::new(1000316), get_prog_name);
// bhc_get_current_directory() -> *i8
let get_cur_dir =
self.module
.llvm_module()
.add_function("bhc_get_current_directory", void_to_ptr, None);
self.functions.insert(VarId::new(1000317), get_cur_dir);
// E.19: System.FilePath + System.Directory FFI functions
let ptr_ptr_to_ptr_type =
i8_ptr_type.fn_type(&[i8_ptr_type.into(), i8_ptr_type.into()], false);
// FilePath: </> (combine two paths)
let combine_fn =
self.module
.llvm_module()
.add_function("bhc_combine", ptr_ptr_to_ptr_type, None);
self.functions.insert(VarId::new(1000534), combine_fn);
// FilePath: String -> String (5 functions)
let take_file_name =
self.module
.llvm_module()
.add_function("bhc_take_file_name", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000520), take_file_name);
let take_directory =
self.module
.llvm_module()
.add_function("bhc_take_directory", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000521), take_directory);
let take_extension =
self.module
.llvm_module()
.add_function("bhc_take_extension", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000522), take_extension);
let drop_extension =
self.module
.llvm_module()
.add_function("bhc_drop_extension", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000523), drop_extension);
let take_base_name =
self.module
.llvm_module()
.add_function("bhc_take_base_name", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000524), take_base_name);
// FilePath: String -> String -> String
let replace_extension = self.module.llvm_module().add_function(
"bhc_replace_extension",
ptr_ptr_to_ptr_type,
None,
);
self.functions
.insert(VarId::new(1000525), replace_extension);
// FilePath: String -> Bool (i32)
let is_absolute =
self.module
.llvm_module()
.add_function("bhc_is_absolute", ptr_to_i32, None);
self.functions.insert(VarId::new(1000526), is_absolute);
let is_relative =
self.module
.llvm_module()
.add_function("bhc_is_relative", ptr_to_i32, None);
self.functions.insert(VarId::new(1000527), is_relative);
let has_extension =
self.module
.llvm_module()
.add_function("bhc_has_extension", ptr_to_i32, None);
self.functions.insert(VarId::new(1000528), has_extension);
// Directory: String -> IO ()
let set_cur_dir =
self.module
.llvm_module()
.add_function("bhc_set_current_directory", ptr_to_void, None);
self.functions.insert(VarId::new(1000529), set_cur_dir);
let remove_dir =
self.module
.llvm_module()
.add_function("bhc_remove_directory", ptr_to_void, None);
self.functions.insert(VarId::new(1000530), remove_dir);
// Directory: String -> String -> IO ()
let rename_file =
self.module
.llvm_module()
.add_function("bhc_rename_file", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000531), rename_file);
let copy_file =
self.module
.llvm_module()
.add_function("bhc_copy_file", ptr_ptr_to_void, None);
self.functions.insert(VarId::new(1000532), copy_file);
// E.25: String read functions
// bhc_read_int(ptr) -> i64 (read :: String -> Int)
let ptr_to_i64 = i64_type.fn_type(&[i8_ptr_type.into()], false);
let read_int = self
.module
.llvm_module()
.add_function("bhc_read_int", ptr_to_i64, None);
self.functions.insert(VarId::new(1000540), read_int);
// bhc_try_read_int(ptr) -> ptr (readMaybe :: String -> Maybe Int)
let try_read_int =
self.module
.llvm_module()
.add_function("bhc_try_read_int", ptr_to_ptr, None);
self.functions.insert(VarId::new(1000541), try_read_int);
// ---- Rational RTS functions (VarId 1000900-1000915) ----
// bhc_rational_make(i64, i64) -> ptr
let rat_make = self.module.llvm_module().add_function(
"bhc_rational_make",
ptr_type.fn_type(&[i64_type.into(), i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000900), rat_make);
// bhc_rational_numerator(ptr) -> i64
let rat_num = self.module.llvm_module().add_function(
"bhc_rational_numerator",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000901), rat_num);
// bhc_rational_denominator(ptr) -> i64
let rat_den = self.module.llvm_module().add_function(
"bhc_rational_denominator",
i64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000902), rat_den);
// bhc_rational_add(ptr, ptr) -> ptr
let rat_add = self.module.llvm_module().add_function(
"bhc_rational_add",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000903), rat_add);
// bhc_rational_sub(ptr, ptr) -> ptr
let rat_sub = self.module.llvm_module().add_function(
"bhc_rational_sub",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000904), rat_sub);
// bhc_rational_mul(ptr, ptr) -> ptr
let rat_mul = self.module.llvm_module().add_function(
"bhc_rational_mul",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000905), rat_mul);
// bhc_rational_div(ptr, ptr) -> ptr
let rat_div = self.module.llvm_module().add_function(
"bhc_rational_div",
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000906), rat_div);
// bhc_rational_negate(ptr) -> ptr
let rat_neg = self.module.llvm_module().add_function(
"bhc_rational_negate",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000907), rat_neg);
// bhc_rational_abs(ptr) -> ptr
let rat_abs = self.module.llvm_module().add_function(
"bhc_rational_abs",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000908), rat_abs);
// bhc_rational_signum(ptr) -> ptr
let rat_signum = self.module.llvm_module().add_function(
"bhc_rational_signum",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000909), rat_signum);
// bhc_rational_eq(ptr, ptr) -> i64
let rat_eq = self.module.llvm_module().add_function(
"bhc_rational_eq",
i64_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000910), rat_eq);
// bhc_rational_compare(ptr, ptr) -> i32
let rat_cmp = self.module.llvm_module().add_function(
"bhc_rational_compare",
i32_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000911), rat_cmp);
// bhc_rational_from_int(i64) -> ptr
let rat_from_int = self.module.llvm_module().add_function(
"bhc_rational_from_int",
ptr_type.fn_type(&[i64_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000912), rat_from_int);
// bhc_rational_to_double(ptr) -> f64
let rat_to_dbl = self.module.llvm_module().add_function(
"bhc_rational_to_double",
f64_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000913), rat_to_dbl);
// bhc_rational_recip(ptr) -> ptr
let rat_recip = self.module.llvm_module().add_function(
"bhc_rational_recip",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000914), rat_recip);
// bhc_rational_show(ptr) -> ptr
let rat_show = self.module.llvm_module().add_function(
"bhc_rational_show",
ptr_type.fn_type(&[ptr_type.into()], false),
None,
);
self.functions.insert(VarId::new(1000915), rat_show);
}
// ========================================================================
// Foreign Import Support
// ========================================================================
/// Declare foreign imports: create C function declarations and BHC wrapper functions.
///
/// For each foreign import:
/// 1. Declare the C function with its native signature
/// 2. Create a BHC wrapper that follows the uniform calling convention
/// (env_ptr + ptr args, returns ptr)
/// 3. Register the wrapper in `self.functions` keyed by the foreign import's VarId
fn declare_foreign_imports(&mut self, foreign_imports: &[ForeignImport]) -> CodegenResult<()> {
for fi in foreign_imports {
self.declare_single_foreign_import(fi)?;
}
Ok(())
}
/// Walk a Haskell type to extract the C parameter types and return type.
///
/// Unwraps `IO a` to get the return type `a`.
/// Collects function arrow parameter types.
fn extract_ffi_signature(&self, ty: &Ty) -> (Vec<FfiType>, FfiType) {
let mut params = Vec::new();
let mut current = ty;
// Unwrap foralls
while let Ty::Forall(_, body) = current {
current = body;
}
// Collect parameter types from function arrows
while let Ty::Fun(arg, ret) = current {
params.push(Self::haskell_type_to_ffi(arg));
current = ret;
}
// The return type: unwrap IO if present
let ret_type = Self::unwrap_io_type(current);
(params, ret_type)
}
/// Map a Haskell type to an FFI type for C interop.
fn haskell_type_to_ffi(ty: &Ty) -> FfiType {
match ty {
Ty::Con(con) => match con.name.as_str() {
"Int" | "CInt" | "CLong" | "Int64" | "CSize" | "CPtrdiff" => FfiType::Int64,
"Int32" | "CShort" => FfiType::Int32,
"Int8" | "CChar" | "CSChar" => FfiType::Int8,
"Word" | "CUInt" | "CULong" | "Word64" => FfiType::Int64,
"Word32" | "CUShort" => FfiType::Int32,
"Word8" | "CUChar" => FfiType::Int8,
"Double" | "CDouble" => FfiType::Double,
"Float" | "CFloat" => FfiType::Float,
"Char" => FfiType::Int64, // BHC boxes Char as i64
"Bool" => FfiType::Int64,
"()" => FfiType::Void,
_ => FfiType::Ptr, // Unknown types passed as pointers
},
Ty::Prim(prim) => {
use bhc_types::PrimTy;
match prim {
PrimTy::I32 => FfiType::Int32,
PrimTy::I64 => FfiType::Int64,
PrimTy::U32 => FfiType::Int32,
PrimTy::U64 => FfiType::Int64,
PrimTy::F32 => FfiType::Float,
PrimTy::F64 => FfiType::Double,
PrimTy::Char => FfiType::Int64,
PrimTy::Addr => FfiType::Ptr,
}
}
Ty::App(f, _arg) => {
// Check for Ptr a, FunPtr a, StablePtr a, etc.
if let Ty::Con(con) = f.as_ref() {
match con.name.as_str() {
"Ptr" | "FunPtr" | "StablePtr" | "ForeignPtr" => FfiType::Ptr,
"IO" => {
// IO a -> just use the inner type
Self::haskell_type_to_ffi(_arg)
}
_ => FfiType::Ptr,
}
} else {
FfiType::Ptr
}
}
_ => FfiType::Ptr,
}
}
/// Unwrap IO type: `IO a` -> `a`, or just return the type unchanged.
fn unwrap_io_type(ty: &Ty) -> FfiType {
match ty {
Ty::App(f, arg) => {
if let Ty::Con(con) = f.as_ref() {
if con.name.as_str() == "IO" {
return Self::haskell_type_to_ffi(arg);
}
}
Self::haskell_type_to_ffi(ty)
}
Ty::Con(con) if con.name.as_str() == "IO" => {
// bare IO without type arg — treat as IO ()
FfiType::Void
}
_ => Self::haskell_type_to_ffi(ty),
}
}
/// Convert an FfiType to an LLVM type.
fn ffi_type_to_llvm(&self, ffi_ty: &FfiType) -> Option<BasicTypeEnum<'ctx>> {
let tm = self.type_mapper();
match ffi_ty {
FfiType::Int8 => Some(self.llvm_ctx.i8_type().into()),
FfiType::Int32 => Some(tm.i32_type().into()),
FfiType::Int64 => Some(tm.i64_type().into()),
FfiType::Float => Some(tm.f32_type().into()),
FfiType::Double => Some(tm.f64_type().into()),
FfiType::Ptr => Some(tm.ptr_type().into()),
FfiType::Void => None,
}
}
/// Declare a single foreign import: C declaration + BHC wrapper.
fn declare_single_foreign_import(&mut self, fi: &ForeignImport) -> CodegenResult<()> {
let (param_types, ret_type) = self.extract_ffi_signature(&fi.var.ty);
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
// 1. Build the C function's LLVM type
let c_param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = param_types
.iter()
.filter_map(|t| self.ffi_type_to_llvm(t).map(|t| t.into()))
.collect();
let c_fn_type = match self.ffi_type_to_llvm(&ret_type) {
Some(ret_llvm) => ret_llvm.fn_type(&c_param_types, false),
None => self.llvm_ctx.void_type().fn_type(&c_param_types, false),
};
// Declare the C function
let c_fn = self.module.llvm_module().add_function(
fi.c_name.as_str(),
c_fn_type,
Some(inkwell::module::Linkage::External),
);
// 2. Create the BHC wrapper function
// Wrapper signature: (env_ptr, arg1, arg2, ...) -> ptr
// where all args are ptr (boxed values)
let wrapper_param_count = param_types.len();
let mut wrapper_param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
wrapper_param_types.push(ptr_type.into()); // env pointer (unused)
for _ in 0..wrapper_param_count {
wrapper_param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(&wrapper_param_types, false);
let wrapper_name = format!("$ffi_wrapper_{}", fi.haskell_name.as_str());
let wrapper_fn =
self.module
.llvm_module()
.add_function(&wrapper_name, wrapper_fn_type, None);
// Build the wrapper body
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
self.builder().position_at_end(entry_bb);
// Unbox each argument from ptr to native type and call C function
let mut c_args: Vec<inkwell::values::BasicMetadataValueEnum<'ctx>> = Vec::new();
for (i, param_ty) in param_types.iter().enumerate() {
// Parameter i+1 (0 is the env pointer)
let boxed_arg = wrapper_fn
.get_nth_param((i + 1) as u32)
.unwrap()
.into_pointer_value();
let unboxed = match param_ty {
FfiType::Int64 | FfiType::Int32 | FfiType::Int8 => {
// Unbox: ptr_to_int
let int_val = self
.builder()
.build_ptr_to_int(boxed_arg, i64_type, &format!("unbox_arg_{i}"))
.map_err(|e| {
CodegenError::Internal(format!("FFI unbox int failed: {e:?}"))
})?;
match param_ty {
FfiType::Int32 => {
let truncated = self
.builder()
.build_int_truncate(
int_val,
self.type_mapper().i32_type(),
&format!("trunc_arg_{i}"),
)
.map_err(|e| {
CodegenError::Internal(format!("FFI trunc failed: {e:?}"))
})?;
truncated.into()
}
FfiType::Int8 => {
let truncated = self
.builder()
.build_int_truncate(
int_val,
self.llvm_ctx.i8_type(),
&format!("trunc_arg_{i}"),
)
.map_err(|e| {
CodegenError::Internal(format!("FFI trunc failed: {e:?}"))
})?;
truncated.into()
}
_ => int_val.into(), // Int64
}
}
FfiType::Double => {
// Unbox: ptr -> i64 -> f64 (bitcast)
let int_val = self
.builder()
.build_ptr_to_int(boxed_arg, i64_type, &format!("unbox_arg_{i}_bits"))
.map_err(|e| {
CodegenError::Internal(format!("FFI unbox double failed: {e:?}"))
})?;
let f64_val = self
.builder()
.build_bit_cast(
int_val,
self.type_mapper().f64_type(),
&format!("unbox_arg_{i}_f64"),
)
.map_err(|e| {
CodegenError::Internal(format!("FFI bitcast failed: {e:?}"))
})?;
f64_val.into()
}
FfiType::Float => {
// Unbox: ptr -> i64 -> f64 -> f32 (truncate)
let int_val = self
.builder()
.build_ptr_to_int(boxed_arg, i64_type, &format!("unbox_arg_{i}_bits"))
.map_err(|e| {
CodegenError::Internal(format!("FFI unbox float failed: {e:?}"))
})?;
let f64_val = self
.builder()
.build_bit_cast(
int_val,
self.type_mapper().f64_type(),
&format!("unbox_arg_{i}_f64"),
)
.map_err(|e| CodegenError::Internal(format!("FFI bitcast failed: {e:?}")))?
.into_float_value();
let f32_val = self
.builder()
.build_float_trunc(
f64_val,
self.type_mapper().f32_type(),
&format!("unbox_arg_{i}_f32"),
)
.map_err(|e| CodegenError::Internal(format!("FFI ftrunc failed: {e:?}")))?;
f32_val.into()
}
FfiType::Ptr => {
// Pass pointer through as-is
boxed_arg.into()
}
FfiType::Void => continue, // Shouldn't happen for a parameter
};
c_args.push(unboxed);
}
// Call the C function
let c_call = self
.builder()
.build_call(c_fn, &c_args, &format!("ffi_call_{}", fi.c_name.as_str()))
.map_err(|e| CodegenError::Internal(format!("FFI call failed: {e:?}")))?;
// Box the return value
let boxed_result: PointerValue<'ctx> = match ret_type {
FfiType::Int64 | FfiType::Int32 | FfiType::Int8 => {
let ret_val = c_call
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"FFI call returned void but expected int".to_string(),
)
})?
.into_int_value();
// Extend to i64 if needed, then box
let i64_val = if ret_val.get_type().get_bit_width() < 64 {
self.builder()
.build_int_s_extend(ret_val, i64_type, "box_extend")
.map_err(|e| CodegenError::Internal(format!("FFI sext failed: {e:?}")))?
} else {
ret_val
};
self.builder()
.build_int_to_ptr(i64_val, ptr_type, "box_ret")
.map_err(|e| CodegenError::Internal(format!("FFI box int failed: {e:?}")))?
}
FfiType::Double => {
let ret_val = c_call
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"FFI call returned void but expected double".to_string(),
)
})?
.into_float_value();
// Box: f64 -> i64 bits -> ptr
let bits = self
.builder()
.build_bit_cast(ret_val, i64_type, "box_ret_bits")
.map_err(|e| {
CodegenError::Internal(format!("FFI box double bitcast failed: {e:?}"))
})?
.into_int_value();
self.builder()
.build_int_to_ptr(bits, ptr_type, "box_ret")
.map_err(|e| CodegenError::Internal(format!("FFI box double failed: {e:?}")))?
}
FfiType::Float => {
let ret_val = c_call
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"FFI call returned void but expected float".to_string(),
)
})?
.into_float_value();
// Extend to f64, then box
let f64_val = self
.builder()
.build_float_ext(ret_val, self.type_mapper().f64_type(), "box_ret_ext")
.map_err(|e| {
CodegenError::Internal(format!("FFI box float ext failed: {e:?}"))
})?;
let bits = self
.builder()
.build_bit_cast(f64_val, i64_type, "box_ret_bits")
.map_err(|e| {
CodegenError::Internal(format!("FFI box float bitcast failed: {e:?}"))
})?
.into_int_value();
self.builder()
.build_int_to_ptr(bits, ptr_type, "box_ret")
.map_err(|e| CodegenError::Internal(format!("FFI box float failed: {e:?}")))?
}
FfiType::Ptr => {
let ret_val = c_call
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"FFI call returned void but expected ptr".to_string(),
)
})?
.into_pointer_value();
ret_val
}
FfiType::Void => {
// Return unit (null pointer)
ptr_type.const_null()
}
};
self.builder()
.build_return(Some(&boxed_result))
.map_err(|e| CodegenError::Internal(format!("FFI return failed: {e:?}")))?;
// 3. Register the wrapper in the functions map
self.functions.insert(fi.var.id, wrapper_fn);
Ok(())
}
// ========================================================================
// ADT (Algebraic Data Type) Value Representation
// ========================================================================
//
// ADT values are represented as heap-allocated structs:
//
// struct ADTValue {
// i64 tag; // Constructor tag (0, 1, 2, ...)
// ptr fields[]; // Variable-length array of field pointers
// }
//
// For example, `Just 42` would be:
// { tag: 1, fields: [ptr_to_42] }
//
// And `Nothing` would be:
// { tag: 0, fields: [] }
// ========================================================================
/// Get the LLVM struct type for an ADT value with the given arity.
fn adt_type(&self, arity: u32) -> inkwell::types::StructType<'ctx> {
let tm = self.type_mapper();
let tag_type = tm.i64_type();
let ptr_type = tm.ptr_type();
// Create array type for fields
let fields_type = ptr_type.array_type(arity);
// Struct: { i64 tag, [arity x ptr] fields }
self.llvm_ctx
.struct_type(&[tag_type.into(), fields_type.into()], false)
}
/// Allocate an ADT value with the given tag and arity.
fn alloc_adt(&self, tag: u32, arity: u32) -> CodegenResult<PointerValue<'ctx>> {
let tm = self.type_mapper();
let adt_ty = self.adt_type(arity);
// Calculate size: sizeof(i64) + arity * sizeof(ptr)
let size = 8 + (arity as u64) * 8;
// Call bhc_alloc
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size_val = tm.i64_type().const_int(size, false);
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], "adt_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call bhc_alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bhc_alloc returned void".to_string()))?;
let ptr = raw_ptr.into_pointer_value();
// Store the tag at offset 0
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, ptr, 0, "tag_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to get tag ptr: {:?}", e)))?;
let tag_val = tm.i64_type().const_int(tag as u64, false);
self.builder()
.build_store(tag_ptr, tag_val)
.map_err(|e| CodegenError::Internal(format!("failed to store tag: {:?}", e)))?;
Ok(ptr)
}
/// Store a field value into an ADT at the given index.
fn store_adt_field(
&self,
adt_ptr: PointerValue<'ctx>,
arity: u32,
field_index: u32,
value: BasicValueEnum<'ctx>,
) -> CodegenResult<()> {
let adt_ty = self.adt_type(arity);
let tm = self.type_mapper();
// Get pointer to fields array
let fields_ptr = self
.builder()
.build_struct_gep(adt_ty, adt_ptr, 1, "fields_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to get fields ptr: {:?}", e)))?;
// Get pointer to specific field
let field_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
tm.ptr_type().array_type(arity),
fields_ptr,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(field_index as u64, false),
],
&format!("field_{}", field_index),
)
.map_err(|e| CodegenError::Internal(format!("failed to get field ptr: {:?}", e)))?
};
// Convert value to pointer if needed
let ptr_val = self.value_to_ptr(value)?;
self.builder()
.build_store(field_ptr, ptr_val)
.map_err(|e| CodegenError::Internal(format!("failed to store field: {:?}", e)))?;
Ok(())
}
/// Convert a basic value to a pointer (boxing primitives if needed).
fn value_to_ptr(&self, value: BasicValueEnum<'ctx>) -> CodegenResult<PointerValue<'ctx>> {
match value {
BasicValueEnum::PointerValue(p) => Ok(p),
BasicValueEnum::IntValue(i) => {
// Box the integer: cast to pointer
self.builder()
.build_int_to_ptr(i, self.type_mapper().ptr_type(), "box_int")
.map_err(|e| CodegenError::Internal(format!("failed to box int: {:?}", e)))
}
BasicValueEnum::FloatValue(f) => {
// Box the float: ensure f64, cast bits to i64, then to pointer
let f64_val = if f.get_type() == self.type_mapper().f32_type() {
self.builder()
.build_float_ext(f, self.type_mapper().f64_type(), "f32_to_f64")
.map_err(|e| {
CodegenError::Internal(format!("failed to extend f32: {:?}", e))
})?
} else {
f
};
let bits = self
.builder()
.build_bit_cast(f64_val, self.type_mapper().i64_type(), "float_bits")
.map_err(|e| CodegenError::Internal(format!("failed to cast float: {:?}", e)))?
.into_int_value();
self.builder()
.build_int_to_ptr(bits, self.type_mapper().ptr_type(), "box_float")
.map_err(|e| CodegenError::Internal(format!("failed to box float: {:?}", e)))
}
_ => Err(CodegenError::Unsupported(
"cannot box this value type".to_string(),
)),
}
}
/// Coerce a value to match a target type.
/// Used to ensure PHI node operands have consistent types.
fn coerce_to_type(
&self,
value: BasicValueEnum<'ctx>,
target_type: inkwell::types::BasicTypeEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let value_type = value.get_type();
// If types already match, no coercion needed
if value_type == target_type {
return Ok(value);
}
let tm = self.type_mapper();
match (value, target_type) {
// Pointer to integer: unbox (ptr_to_int)
(BasicValueEnum::PointerValue(p), inkwell::types::BasicTypeEnum::IntType(int_ty)) => {
let int_val = self
.builder()
.build_ptr_to_int(p, int_ty, "coerce_ptr_to_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce ptr to int: {:?}", e))
})?;
Ok(int_val.into())
}
// Integer to pointer: box (int_to_ptr)
(BasicValueEnum::IntValue(i), inkwell::types::BasicTypeEnum::PointerType(_)) => {
// First extend/truncate to i64 if needed, then convert to pointer
let i64_val = if i.get_type().get_bit_width() < 64 {
self.builder()
.build_int_z_extend(i, tm.i64_type(), "extend_to_i64")
.map_err(|e| {
CodegenError::Internal(format!("failed to extend int: {:?}", e))
})?
} else if i.get_type().get_bit_width() > 64 {
self.builder()
.build_int_truncate(i, tm.i64_type(), "truncate_to_i64")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate int: {:?}", e))
})?
} else {
i
};
let ptr_val = self
.builder()
.build_int_to_ptr(i64_val, tm.ptr_type(), "coerce_int_to_ptr")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce int to ptr: {:?}", e))
})?;
Ok(ptr_val.into())
}
// Pointer to float: unbox (ptr_to_int then bit_cast)
(
BasicValueEnum::PointerValue(p),
inkwell::types::BasicTypeEnum::FloatType(float_ty),
) => {
let bits = self
.builder()
.build_ptr_to_int(p, tm.i64_type(), "coerce_ptr_to_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce ptr to bits: {:?}", e))
})?;
// For f32, truncate to i32 first, then bit_cast
let float_val = if float_ty == tm.f32_type() {
let bits32 = self
.builder()
.build_int_truncate(bits, tm.i32_type(), "truncate_bits_f32")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate bits: {:?}", e))
})?;
self.builder()
.build_bit_cast(bits32, float_ty, "coerce_to_f32")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce to f32: {:?}", e))
})?
} else {
self.builder()
.build_bit_cast(bits, float_ty, "coerce_to_f64")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce to f64: {:?}", e))
})?
};
Ok(float_val)
}
// Float to pointer: box (extend f32→f64 if needed, bit_cast then int_to_ptr)
(BasicValueEnum::FloatValue(f), inkwell::types::BasicTypeEnum::PointerType(_)) => {
// Ensure f64 before bitcasting to i64 (f32 is only 32 bits)
let f64_val = if f.get_type() == tm.f32_type() {
self.builder()
.build_float_ext(f, tm.f64_type(), "coerce_f32_to_f64")
.map_err(|e| {
CodegenError::Internal(format!("failed to extend f32: {:?}", e))
})?
} else {
f
};
let bits = self
.builder()
.build_bit_cast(f64_val, tm.i64_type(), "float_to_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast float to bits: {:?}", e))
})?
.into_int_value();
let ptr_val = self
.builder()
.build_int_to_ptr(bits, tm.ptr_type(), "coerce_float_to_ptr")
.map_err(|e| {
CodegenError::Internal(format!("failed to coerce float to ptr: {:?}", e))
})?;
Ok(ptr_val.into())
}
// Integer width conversion
(BasicValueEnum::IntValue(i), inkwell::types::BasicTypeEnum::IntType(int_ty)) => {
let src_bits = i.get_type().get_bit_width();
let dst_bits = int_ty.get_bit_width();
let result = if src_bits < dst_bits {
self.builder()
.build_int_s_extend(i, int_ty, "sext")
.map_err(|e| {
CodegenError::Internal(format!("failed to sign extend: {:?}", e))
})?
} else {
self.builder()
.build_int_truncate(i, int_ty, "trunc")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate: {:?}", e))
})?
};
Ok(result.into())
}
_ => {
// Types don't match and we don't know how to coerce
Err(CodegenError::Internal(format!(
"cannot coerce {:?} to {:?}",
value_type, target_type
)))
}
}
}
/// Extract the tag from an ADT value.
fn extract_adt_tag(&self, adt_ptr: PointerValue<'ctx>) -> CodegenResult<IntValue<'ctx>> {
// We need to use a generic adt type for reading - use arity 0 since tag is always at offset 0
let adt_ty = self.adt_type(0);
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, adt_ptr, 0, "tag_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to get tag ptr: {:?}", e)))?;
let tag = self
.builder()
.build_load(self.type_mapper().i64_type(), tag_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("failed to load tag: {:?}", e)))?;
Ok(tag.into_int_value())
}
/// Extract a boolean tag from either a tagged-int-as-pointer (0/1) or a Bool ADT.
/// Comparison operators return tagged-int-as-pointer, while functions like `even`/`odd`
/// return proper Bool ADT structs. This helper handles both representations.
fn extract_bool_tag(&self, bool_ptr: PointerValue<'ctx>) -> CodegenResult<IntValue<'ctx>> {
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("extract_bool_tag: no current function".to_string())
})?;
let raw_int = self
.builder()
.build_ptr_to_int(bool_ptr, i64_type, "bool_raw")
.map_err(|e| {
CodegenError::Internal(format!("extract_bool_tag: ptr_to_int: {:?}", e))
})?;
// If raw value <= 1, it's a tagged-int-as-pointer (0=False, 1=True)
// If raw value > 1, it's a heap pointer to a Bool ADT struct
let is_tagged = self
.builder()
.build_int_compare(
inkwell::IntPredicate::ULE,
raw_int,
i64_type.const_int(1, false),
"is_tagged_bool",
)
.map_err(|e| CodegenError::Internal(format!("extract_bool_tag: cmp: {:?}", e)))?;
let tagged_block = self.llvm_ctx.append_basic_block(current_fn, "bool_tagged");
let adt_block = self.llvm_ctx.append_basic_block(current_fn, "bool_adt");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "bool_merge");
self.builder()
.build_conditional_branch(is_tagged, tagged_block, adt_block)
.map_err(|e| CodegenError::Internal(format!("extract_bool_tag: branch: {:?}", e)))?;
// Tagged path: raw value IS the tag (0 or 1)
self.builder().position_at_end(tagged_block);
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("extract_bool_tag: branch: {:?}", e)))?;
// ADT path: load tag from struct
self.builder().position_at_end(adt_block);
let adt_tag = self.extract_adt_tag(bool_ptr)?;
let adt_end_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("extract_bool_tag: no block".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("extract_bool_tag: branch: {:?}", e)))?;
// Merge: phi between tagged value and ADT tag
self.builder().position_at_end(merge_block);
let result = self
.builder()
.build_phi(i64_type, "bool_tag")
.map_err(|e| CodegenError::Internal(format!("extract_bool_tag: phi: {:?}", e)))?;
result.add_incoming(&[(&raw_int, tagged_block), (&adt_tag, adt_end_block)]);
Ok(result.as_basic_value().into_int_value())
}
/// Extract a field from an ADT value.
fn extract_adt_field(
&self,
adt_ptr: PointerValue<'ctx>,
arity: u32,
field_index: u32,
) -> CodegenResult<PointerValue<'ctx>> {
let adt_ty = self.adt_type(arity);
let tm = self.type_mapper();
// Get pointer to fields array
let fields_ptr = self
.builder()
.build_struct_gep(adt_ty, adt_ptr, 1, "fields_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to get fields ptr: {:?}", e)))?;
// Get pointer to specific field
let field_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
tm.ptr_type().array_type(arity),
fields_ptr,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(field_index as u64, false),
],
&format!("field_ptr_{}", field_index),
)
.map_err(|e| CodegenError::Internal(format!("failed to get field ptr: {:?}", e)))?
};
// Load the field value (which is a pointer)
let field_val = self
.builder()
.build_load(tm.ptr_type(), field_ptr, &format!("field_{}", field_index))
.map_err(|e| CodegenError::Internal(format!("failed to load field: {:?}", e)))?;
Ok(field_val.into_pointer_value())
}
/// Get the RTS function ID for a builtin name.
fn rts_function_id(&self, name: &str) -> Option<VarId> {
match name {
"print" => Some(VarId::new(1000000)), // bhc_print_int_ln for Int
"putStrLn" => Some(VarId::new(1000002)), // bhc_print_string_ln
"putStr" => Some(VarId::new(1000004)), // bhc_print_string
_ => None,
}
}
// ========================================================================
// Builtin Functions
// ========================================================================
//
// These are common Haskell functions that we implement directly in LLVM
// for performance (avoiding function call overhead) or because they need
// special handling.
// ========================================================================
/// Check if a name is a builtin function and return its arity.
fn builtin_info(&self, name: &str) -> Option<u32> {
match name {
// List operations
"head" => Some(1),
"tail" => Some(1),
"null" => Some(1),
"length" => Some(1),
"take" => Some(2),
"drop" => Some(2),
"reverse" => Some(1),
"append" | "++" => Some(2),
"enumFromTo" => Some(2),
"replicate" => Some(2),
"sum" => Some(1),
"product" => Some(1),
"map" => Some(2),
"filter" => Some(2),
"foldr" => Some(3),
"foldl" => Some(3),
"foldl'" => Some(3),
"zipWith" => Some(3),
"zip" => Some(2),
"last" => Some(1),
"init" => Some(1),
"!!" => Some(2),
"concatMap" => Some(2),
"concat" => Some(1),
// Tuple operations
"fst" => Some(1),
"snd" => Some(1),
"swap" => Some(1),
"curry" => Some(3),
"uncurry" => Some(2),
// Maybe operations
"fromJust" => Some(1),
"isJust" => Some(1),
"isNothing" => Some(1),
"fromMaybe" => Some(2),
"maybe" => Some(3),
"listToMaybe" => Some(1),
"maybeToList" => Some(1),
"catMaybes" => Some(1),
"mapMaybe" => Some(2),
// Either operations
"isLeft" => Some(1),
"isRight" => Some(1),
"either" => Some(3),
"fromLeft" => Some(2),
"fromRight" => Some(2),
"lefts" => Some(1),
"rights" => Some(1),
"partitionEithers" => Some(1),
// Control
"guard" => Some(1),
// Error / Exception handling
"error" => Some(1),
"undefined" => Some(0),
"throw" | "throwIO" => Some(1),
"catch" => Some(2),
"try" => Some(1),
"bracket" => Some(3),
"finally" | "onException" => Some(2),
"mask" | "mask_" | "uninterruptibleMask" | "uninterruptibleMask_" => Some(1),
"getMaskingState" => Some(0),
"toException" => Some(1),
"fromException" => Some(1),
"displayException" => Some(1),
"userError" => Some(1),
"ioError" => Some(1),
// Misc
"seq" => Some(2),
"id" => Some(1),
"const" => Some(2),
"not" => Some(1),
"otherwise" => Some(0),
// E.63: DeepSeq stubs (no-ops in strict runtime)
"rnf" => Some(1),
"deepseq" => Some(2),
// GHC.Generics from/to
"from" => Some(1),
"to" => Some(1),
"force" => Some(1),
// IO operations
"putStrLn" => Some(1),
"putStr" => Some(1),
"putChar" => Some(1),
"print" => Some(1),
"getLine" => Some(0),
// Monadic operations
">>=" => Some(2),
">>" => Some(2),
"return" => Some(1),
"pure" => Some(1),
// Numeric / math operations
"negate" => Some(1),
"abs" => Some(1),
"signum" => Some(1),
"sqrt" => Some(1),
"exp" => Some(1),
"log" => Some(1),
"sin" => Some(1),
"cos" => Some(1),
"tan" => Some(1),
"asin" => Some(1),
"acos" => Some(1),
"atan" => Some(1),
"atan2" => Some(2),
"ceiling" => Some(1),
"floor" => Some(1),
"round" => Some(1),
"truncate" => Some(1),
"fromIntegral" => Some(1),
"toInteger" => Some(1),
"fromInteger" => Some(1),
"compare" => Some(2),
"even" => Some(1),
"odd" => Some(1),
"gcd" => Some(2),
"lcm" => Some(2),
"divMod" => Some(2),
"quotRem" => Some(2),
"%" => Some(2),
"numerator" => Some(1),
"denominator" => Some(1),
"toRational" => Some(1),
"fromRational" => Some(1),
"recip" => Some(1),
"newIORef" => Some(1),
"readIORef" => Some(1),
"writeIORef" => Some(2),
"modifyIORef" => Some(2),
"modifyIORef'" => Some(2),
"atomicModifyIORef" => Some(2),
"atomicModifyIORef'" => Some(2),
// Character operations
"ord" => Some(1),
"chr" => Some(1),
"isAlpha" => Some(1),
"isAlphaNum" => Some(1),
"isAscii" => Some(1),
"isControl" => Some(1),
"isDigit" => Some(1),
"isHexDigit" => Some(1),
"isLetter" => Some(1),
"isLower" => Some(1),
"isNumber" => Some(1),
"isPrint" => Some(1),
"isPunctuation" => Some(1),
"isSpace" => Some(1),
"isSymbol" => Some(1),
"isUpper" => Some(1),
"toLower" => Some(1),
"toUpper" => Some(1),
"digitToInt" => Some(1),
"intToDigit" => Some(1),
// Advanced list operations
"scanl" => Some(3),
"scanl'" => Some(3),
"scanl1" => Some(2),
"scanr" => Some(3),
"scanr1" => Some(2),
"find" => Some(2),
"elem" => Some(2),
"notElem" => Some(2),
"lookup" => Some(2),
"partition" => Some(2),
"span" => Some(2),
"lines" => Some(1),
"unlines" => Some(1),
"words" => Some(1),
"unwords" => Some(1),
"nub" => Some(1),
"delete" => Some(2),
"union" => Some(2),
"intersect" => Some(2),
"sort" => Some(1),
"sortBy" => Some(2),
"intercalate" => Some(2),
"intersperse" => Some(2),
"transpose" => Some(1),
"group" => Some(1),
"sortOn" => Some(2),
"nubBy" => Some(2),
"groupBy" => Some(2),
"deleteBy" => Some(3),
"unionBy" => Some(3),
"intersectBy" => Some(3),
"stripPrefix" => Some(2),
"insert" => Some(2),
"mapAccumL" => Some(3),
"mapAccumR" => Some(3),
"splitAt" => Some(2),
"break" => Some(2),
"any" => Some(2),
"all" => Some(2),
"and" => Some(1),
"or" => Some(1),
"maximum" => Some(1),
"minimum" => Some(1),
"maximumBy" => Some(2),
"minimumBy" => Some(2),
"elemIndex" => Some(2),
"findIndex" => Some(2),
"isPrefixOf" => Some(2),
"isSuffixOf" => Some(2),
"isInfixOf" => Some(2),
"tails" => Some(1),
"inits" => Some(1),
"foldMap" => Some(2),
"iterate" => Some(2),
"unfoldr" => Some(2),
"cycle" => Some(1),
"repeat" => Some(1),
"takeWhile" => Some(2),
"dropWhile" => Some(2),
"zipWith3" => Some(4),
"zip3" => Some(3),
"unzip" => Some(1),
// IO & System operations
"readFile" => Some(1),
"writeFile" => Some(2),
"appendFile" => Some(2),
"openFile" => Some(2),
"hClose" => Some(1),
"hGetChar" => Some(1),
"hGetLine" => Some(1),
"hPutStr" => Some(2),
"hPutStrLn" => Some(2),
"hFlush" => Some(1),
"hIsEOF" => Some(1),
"hSetBuffering" => Some(2),
"stdin" => Some(0),
"stdout" => Some(0),
"stderr" => Some(0),
"doesFileExist" => Some(1),
"doesDirectoryExist" => Some(1),
"removeFile" => Some(1),
"getArgs" => Some(0),
"getProgName" => Some(0),
"getEnv" => Some(1),
"lookupEnv" => Some(1),
"exitSuccess" => Some(0),
"exitFailure" => Some(0),
"exitWith" => Some(1),
"hGetContents" => Some(1),
"getCurrentDirectory" => Some(0),
"createDirectory" => Some(1),
"listDirectory" => Some(1),
// E.19: FilePath + Directory operations
"</>" => Some(2),
"takeFileName" => Some(1),
"takeDirectory" => Some(1),
"takeExtension" => Some(1),
"dropExtension" => Some(1),
"takeBaseName" => Some(1),
"replaceExtension" => Some(2),
"isAbsolute" => Some(1),
"isRelative" => Some(1),
"hasExtension" => Some(1),
"splitExtension" => Some(1),
"setCurrentDirectory" => Some(1),
"removeDirectory" => Some(1),
"renameFile" => Some(2),
"copyFile" => Some(2),
// Monadic / higher-order operations
"fmap" => Some(2),
"<$>" => Some(2),
"<*>" => Some(2),
"join" => Some(1),
"=<<" => Some(2),
"when" => Some(2),
"unless" => Some(2),
"void" => Some(1),
"mapM" => Some(2),
"mapM_" => Some(2),
"forM" => Some(2),
"forM_" => Some(2),
"sequence" => Some(1),
"sequence_" => Some(1),
"traverse" => Some(2),
"traverse_" => Some(2),
"for" => Some(2),
"for_" => Some(2),
"sequenceA" => Some(1),
"sequenceA_" => Some(1),
"forever" => Some(1),
"filterM" => Some(2),
"foldM" => Some(3),
"foldM_" => Some(3),
"replicateM" => Some(2),
"replicateM_" => Some(2),
"zipWithM" => Some(3),
"zipWithM_" => Some(3),
// Data.Function
"flip" => Some(3),
"on" => Some(4),
"fix" => Some(1),
"$" => Some(2),
"&" => Some(2),
"." => Some(3),
"succ" => Some(1),
"pred" => Some(1),
"fromEnum" => Some(1),
"toEnum" => Some(1),
"minBound" => Some(0),
"maxBound" => Some(0),
// E.28: Arithmetic, enum, folds, higher-order, IO input
"min" => Some(2),
"max" => Some(2),
"subtract" => Some(2),
"enumFrom" => Some(1),
"enumFromThen" => Some(2),
"enumFromThenTo" => Some(3),
"foldl1" => Some(2),
"foldr1" => Some(2),
"comparing" => Some(3),
"until" => Some(3),
"getChar" => Some(0),
"isEOF" => Some(0),
"getContents" => Some(0),
"interact" => Some(1),
// E.25: String type class methods
"fromString" => Some(1),
// E.64: OverloadedLists
"fromList" => Some(1),
"read" => Some(1),
"readMaybe" => Some(1),
// Show
"show" => Some(1),
"showInt" => Some(1),
"showDouble" => Some(1),
"showFloat" => Some(1),
"showBool" => Some(1),
"showChar" => Some(1),
"showString" => Some(1),
"showList" => Some(1),
"showMaybe" => Some(1),
"showEither" => Some(1),
"showTuple2" => Some(1),
"showUnit" => Some(1),
// Data.Map operations
"Data.Map.empty" => Some(0),
"Data.Map.singleton" => Some(2),
"Data.Map.null" => Some(1),
"Data.Map.size" => Some(1),
"Data.Map.member" => Some(2),
"Data.Map.notMember" => Some(2),
"Data.Map.lookup" => Some(2),
"Data.Map.findWithDefault" => Some(3),
"Data.Map.!" => Some(2),
"Data.Map.insert" => Some(3),
"Data.Map.insertWith" => Some(4),
"Data.Map.delete" => Some(2),
"Data.Map.adjust" => Some(3),
"Data.Map.update" => Some(3),
"Data.Map.alter" => Some(3),
"Data.Map.union" => Some(2),
"Data.Map.unionWith" => Some(3),
"Data.Map.unionWithKey" => Some(4),
"Data.Map.unions" => Some(1),
"Data.Map.intersection" => Some(2),
"Data.Map.intersectionWith" => Some(3),
"Data.Map.difference" => Some(2),
"Data.Map.differenceWith" => Some(3),
"Data.Map.map" => Some(2),
"Data.Map.mapWithKey" => Some(2),
"Data.Map.mapKeys" => Some(2),
"Data.Map.filter" => Some(2),
"Data.Map.filterWithKey" => Some(2),
"Data.Map.mapMaybe" => Some(2),
"Data.Map.mapMaybeWithKey" => Some(2),
"Data.Map.foldr" => Some(3),
"Data.Map.foldl" => Some(3),
"Data.Map.foldrWithKey" => Some(3),
"Data.Map.foldlWithKey" => Some(3),
"Data.Map.keys" => Some(1),
"Data.Map.elems" => Some(1),
"Data.Map.assocs" => Some(1),
"Data.Map.toList" => Some(1),
"Data.Map.toAscList" => Some(1),
"Data.Map.toDescList" => Some(1),
"Data.Map.fromList" => Some(1),
"Data.Map.fromListWith" => Some(2),
"Data.Map.keysSet" => Some(1),
"Data.Map.isSubmapOf" => Some(2),
// Data.Set operations
"Data.Set.empty" => Some(0),
"Data.Set.singleton" => Some(1),
"Data.Set.null" => Some(1),
"Data.Set.size" => Some(1),
"Data.Set.member" => Some(2),
"Data.Set.notMember" => Some(2),
"Data.Set.insert" => Some(2),
"Data.Set.delete" => Some(2),
"Data.Set.union" => Some(2),
"Data.Set.unions" => Some(1),
"Data.Set.intersection" => Some(2),
"Data.Set.difference" => Some(2),
"Data.Set.isSubsetOf" => Some(2),
"Data.Set.isProperSubsetOf" => Some(2),
"Data.Set.map" => Some(2),
"Data.Set.filter" => Some(2),
"Data.Set.partition" => Some(2),
"Data.Set.foldr" => Some(3),
"Data.Set.foldl" => Some(3),
"Data.Set.toList" => Some(1),
"Data.Set.toAscList" => Some(1),
"Data.Set.toDescList" => Some(1),
"Data.Set.fromList" => Some(1),
"Data.Set.findMin" => Some(1),
"Data.Set.findMax" => Some(1),
"Data.Set.deleteMin" => Some(1),
"Data.Set.deleteMax" => Some(1),
"Data.Set.elems" => Some(1),
"Data.Set.lookupMin" => Some(1),
"Data.Set.lookupMax" => Some(1),
// Data.IntMap operations
"Data.IntMap.empty" => Some(0),
"Data.IntMap.singleton" => Some(2),
"Data.IntMap.null" => Some(1),
"Data.IntMap.size" => Some(1),
"Data.IntMap.member" => Some(2),
"Data.IntMap.lookup" => Some(2),
"Data.IntMap.findWithDefault" => Some(3),
"Data.IntMap.insert" => Some(3),
"Data.IntMap.insertWith" => Some(4),
"Data.IntMap.delete" => Some(2),
"Data.IntMap.adjust" => Some(3),
"Data.IntMap.union" => Some(2),
"Data.IntMap.unionWith" => Some(3),
"Data.IntMap.intersection" => Some(2),
"Data.IntMap.difference" => Some(2),
"Data.IntMap.map" => Some(2),
"Data.IntMap.mapWithKey" => Some(2),
"Data.IntMap.filter" => Some(2),
"Data.IntMap.foldr" => Some(3),
"Data.IntMap.foldlWithKey" => Some(3),
"Data.IntMap.keys" => Some(1),
"Data.IntMap.elems" => Some(1),
"Data.IntMap.toList" => Some(1),
"Data.IntMap.toAscList" => Some(1),
"Data.IntMap.fromList" => Some(1),
// Data.IntSet operations
"Data.IntSet.empty" => Some(0),
"Data.IntSet.singleton" => Some(1),
"Data.IntSet.null" => Some(1),
"Data.IntSet.size" => Some(1),
"Data.IntSet.member" => Some(2),
"Data.IntSet.insert" => Some(2),
"Data.IntSet.delete" => Some(2),
"Data.IntSet.union" => Some(2),
"Data.IntSet.intersection" => Some(2),
"Data.IntSet.difference" => Some(2),
"Data.IntSet.isSubsetOf" => Some(2),
"Data.IntSet.filter" => Some(2),
"Data.IntSet.foldr" => Some(3),
"Data.IntSet.toList" => Some(1),
"Data.IntSet.fromList" => Some(1),
// Data.Sequence operations
"Data.Sequence.empty" => Some(0),
"Data.Sequence.singleton" => Some(1),
"Data.Sequence.null" => Some(1),
"Data.Sequence.length" => Some(1),
"Data.Sequence.index" => Some(2),
"Data.Sequence.lookup" => Some(2),
"Data.Sequence.<|" => Some(2),
"Data.Sequence.|>" => Some(2),
"Data.Sequence.><" => Some(2),
"Data.Sequence.take" => Some(2),
"Data.Sequence.drop" => Some(2),
"Data.Sequence.reverse" => Some(1),
"Data.Sequence.update" => Some(3),
"Data.Sequence.insertAt" => Some(3),
"Data.Sequence.deleteAt" => Some(2),
"Data.Sequence.fromList" => Some(1),
"Data.Sequence.toList" => Some(1),
"Data.Sequence.replicate" => Some(2),
"Data.Sequence.viewl" => Some(1),
"Data.Sequence.viewr" => Some(1),
"Data.Sequence.filter" => Some(2),
// Data.Text operations
"Data.Text.empty" => Some(0),
"Data.Text.singleton" => Some(1),
"Data.Text.pack" => Some(1),
"Data.Text.unpack" => Some(1),
"Data.Text.null" => Some(1),
"Data.Text.length" => Some(1),
"Data.Text.head" => Some(1),
"Data.Text.last" => Some(1),
"Data.Text.tail" => Some(1),
"Data.Text.init" => Some(1),
"Data.Text.append" | "Data.Text.<>" => Some(2),
"Data.Text.reverse" => Some(1),
"Data.Text.take" => Some(2),
"Data.Text.takeEnd" => Some(2),
"Data.Text.drop" => Some(2),
"Data.Text.dropEnd" => Some(2),
"Data.Text.isPrefixOf" => Some(2),
"Data.Text.isSuffixOf" => Some(2),
"Data.Text.isInfixOf" => Some(2),
"Data.Text.toLower" => Some(1),
"Data.Text.toUpper" => Some(1),
"Data.Text.toCaseFold" => Some(1),
"Data.Text.toTitle" => Some(1),
"Data.Text.map" => Some(2),
"Data.Text.eq" | "Data.Text.==" => Some(2),
"Data.Text.compare" => Some(2),
// Additional Data.Text operations
"Data.Text.filter" => Some(2),
"Data.Text.foldl'" => Some(3),
"Data.Text.concat" => Some(1),
"Data.Text.intercalate" => Some(2),
"Data.Text.strip" => Some(1),
"Data.Text.words" => Some(1),
"Data.Text.lines" => Some(1),
"Data.Text.splitOn" => Some(2),
"Data.Text.replace" => Some(3),
// Data.Text.Encoding operations
"Data.Text.Encoding.encodeUtf8" => Some(1),
"Data.Text.Encoding.decodeUtf8" => Some(1),
// Data.Text.IO operations
"Data.Text.IO.readFile" => Some(1),
"Data.Text.IO.writeFile" => Some(2),
"Data.Text.IO.appendFile" => Some(2),
"Data.Text.IO.hGetContents" => Some(1),
"Data.Text.IO.hGetLine" => Some(1),
"Data.Text.IO.hPutStr" => Some(2),
"Data.Text.IO.hPutStrLn" => Some(2),
"Data.Text.IO.putStr" => Some(1),
"Data.Text.IO.putStrLn" => Some(1),
"Data.Text.IO.getLine" => Some(0),
"Data.Text.IO.getContents" => Some(0),
// Data.ByteString operations
"Data.ByteString.empty" => Some(0),
"Data.ByteString.singleton" => Some(1),
"Data.ByteString.pack" => Some(1),
"Data.ByteString.unpack" => Some(1),
"Data.ByteString.null" => Some(1),
"Data.ByteString.length" => Some(1),
"Data.ByteString.head" => Some(1),
"Data.ByteString.last" => Some(1),
"Data.ByteString.tail" => Some(1),
"Data.ByteString.init" => Some(1),
"Data.ByteString.append" => Some(2),
"Data.ByteString.cons" => Some(2),
"Data.ByteString.snoc" => Some(2),
"Data.ByteString.take" => Some(2),
"Data.ByteString.drop" => Some(2),
"Data.ByteString.reverse" => Some(1),
"Data.ByteString.elem" => Some(2),
"Data.ByteString.index" => Some(2),
"Data.ByteString.eq" => Some(2),
"Data.ByteString.compare" => Some(2),
"Data.ByteString.isPrefixOf" => Some(2),
"Data.ByteString.isSuffixOf" => Some(2),
"Data.ByteString.readFile" => Some(1),
"Data.ByteString.writeFile" => Some(2),
// Data.Text.Lazy operations
"Data.Text.Lazy.empty" => Some(0),
"Data.Text.Lazy.fromStrict" => Some(1),
"Data.Text.Lazy.toStrict" => Some(1),
"Data.Text.Lazy.pack" => Some(1),
"Data.Text.Lazy.unpack" => Some(1),
"Data.Text.Lazy.null" => Some(1),
"Data.Text.Lazy.length" => Some(1),
"Data.Text.Lazy.append" | "Data.Text.Lazy.<>" => Some(2),
"Data.Text.Lazy.fromChunks" => Some(1),
"Data.Text.Lazy.toChunks" => Some(1),
"Data.Text.Lazy.head" => Some(1),
"Data.Text.Lazy.tail" => Some(1),
"Data.Text.Lazy.take" => Some(2),
"Data.Text.Lazy.drop" => Some(2),
// Data.ByteString.Lazy operations
"Data.ByteString.Lazy.empty" => Some(0),
"Data.ByteString.Lazy.fromStrict" => Some(1),
"Data.ByteString.Lazy.toStrict" => Some(1),
"Data.ByteString.Lazy.fromChunks" => Some(1),
"Data.ByteString.Lazy.toChunks" => Some(1),
"Data.ByteString.Lazy.null" => Some(1),
"Data.ByteString.Lazy.length" => Some(1),
"Data.ByteString.Lazy.pack" => Some(1),
"Data.ByteString.Lazy.append" | "Data.ByteString.Lazy.<>" => Some(2),
"Data.ByteString.Lazy.head" => Some(1),
"Data.ByteString.Lazy.tail" => Some(1),
"Data.ByteString.Lazy.take" => Some(2),
"Data.ByteString.Lazy.drop" => Some(2),
"Data.ByteString.Lazy.filter" => Some(2),
"Data.ByteString.Lazy.isPrefixOf" => Some(2),
"Data.ByteString.Lazy.readFile" => Some(1),
"Data.ByteString.Lazy.writeFile" => Some(2),
"Data.ByteString.Lazy.putStr" => Some(1),
"Data.ByteString.Lazy.hPut" | "Data.ByteString.Lazy.hPutStr" => Some(2),
"Data.ByteString.Lazy.hGetContents" => Some(1),
// Data.ByteString.Lazy.Char8 operations
"Data.ByteString.Lazy.Char8.unpack" => Some(1),
"Data.ByteString.Lazy.Char8.lines" => Some(1),
"Data.ByteString.Lazy.Char8.unlines" => Some(1),
"Data.ByteString.Lazy.Char8.take" => Some(2),
"Data.ByteString.Lazy.Char8.dropWhile" => Some(2),
"Data.ByteString.Lazy.Char8.cons" => Some(2),
// Data.Text.Lazy.Encoding operations
"Data.Text.Lazy.Encoding.encodeUtf8" => Some(1),
"Data.Text.Lazy.Encoding.decodeUtf8" => Some(1),
// Data.ByteString.Builder operations
"Data.ByteString.Builder.empty" => Some(0),
"Data.ByteString.Builder.singleton" | "Data.ByteString.Builder.word8" => Some(1),
"Data.ByteString.Builder.byteString" | "Data.ByteString.Builder.shortByteString" => {
Some(1)
}
"Data.ByteString.Builder.lazyByteString"
| "Data.ByteString.Builder.toLazyByteString" => Some(1),
"Data.ByteString.Builder.append" | "Data.ByteString.Builder.<>" => Some(2),
"Data.ByteString.Builder.toStrictByteString" => Some(1),
"Data.ByteString.Builder.hPutBuilder" => Some(2),
"Data.ByteString.Builder.charUtf8" => Some(1),
"Data.ByteString.Builder.stringUtf8" => Some(1),
"Data.ByteString.Builder.intDec"
| "Data.ByteString.Builder.int8Dec"
| "Data.ByteString.Builder.int16Dec"
| "Data.ByteString.Builder.int32Dec"
| "Data.ByteString.Builder.int64Dec"
| "Data.ByteString.Builder.integerDec"
| "Data.ByteString.Builder.wordDec"
| "Data.ByteString.Builder.word8Dec"
| "Data.ByteString.Builder.word16Dec"
| "Data.ByteString.Builder.word32Dec"
| "Data.ByteString.Builder.word64Dec" => Some(1),
"Data.ByteString.Builder.char7" => Some(1),
"Data.ByteString.Builder.char8" => Some(1),
"Data.ByteString.Builder.string7" | "Data.ByteString.Builder.string8" => Some(1),
"Data.ByteString.Builder.word16BE"
| "Data.ByteString.Builder.int16BE"
| "Data.ByteString.Builder.word32BE"
| "Data.ByteString.Builder.int32BE"
| "Data.ByteString.Builder.word64BE"
| "Data.ByteString.Builder.int64BE" => Some(1),
"Data.ByteString.Builder.word16LE"
| "Data.ByteString.Builder.int16LE"
| "Data.ByteString.Builder.word32LE"
| "Data.ByteString.Builder.int32LE"
| "Data.ByteString.Builder.word64LE"
| "Data.ByteString.Builder.int64LE"
| "Data.ByteString.Builder.word16Host"
| "Data.ByteString.Builder.int16Host"
| "Data.ByteString.Builder.word32Host"
| "Data.ByteString.Builder.int32Host"
| "Data.ByteString.Builder.word64Host"
| "Data.ByteString.Builder.int64Host" => Some(1),
"Data.ByteString.Builder.wordHex"
| "Data.ByteString.Builder.word8Hex"
| "Data.ByteString.Builder.word16Hex"
| "Data.ByteString.Builder.word32Hex"
| "Data.ByteString.Builder.word64Hex" => Some(1),
"Data.ByteString.Builder.word8HexFixed" => Some(1),
"Data.ByteString.Builder.word16HexFixed"
| "Data.ByteString.Builder.word32HexFixed"
| "Data.ByteString.Builder.word64HexFixed" => Some(1),
"Data.ByteString.Builder.floatBE"
| "Data.ByteString.Builder.doubleBE"
| "Data.ByteString.Builder.floatLE"
| "Data.ByteString.Builder.doubleLE"
| "Data.ByteString.Builder.floatHost"
| "Data.ByteString.Builder.doubleHost" => Some(1),
// Identity operations
"Identity" => Some(1),
"runIdentity" => Some(1),
"Identity.fmap" => Some(2),
"Identity.pure" => Some(1),
"Identity.<*>" => Some(2),
"Identity.>>=" => Some(2),
"Identity.>>" => Some(2),
// ReaderT operations
"ReaderT" => Some(1),
"runReaderT" => Some(2),
"ReaderT.fmap" => Some(2),
"ReaderT.pure" => Some(1),
"ReaderT.<*>" => Some(2),
"ReaderT.>>=" => Some(2),
"ReaderT.>>" => Some(2),
"ReaderT.lift" => Some(1),
"ReaderT.liftIO" => Some(1),
"ask" => Some(0),
"asks" => Some(1),
"local" => Some(2),
// StateT operations
"StateT" => Some(1),
"runStateT" => Some(2),
"StateT.fmap" => Some(2),
"StateT.pure" => Some(1),
"StateT.<*>" => Some(2),
"StateT.>>=" => Some(2),
"StateT.>>" => Some(2),
"StateT.lift" => Some(1),
"StateT.liftIO" => Some(1),
"get" => Some(0),
"put" => Some(1),
"modify" => Some(1),
"gets" => Some(1),
"evalStateT" => Some(2),
"execStateT" => Some(2),
// ExceptT operations
"ExceptT" => Some(1),
"runExceptT" => Some(1),
"ExceptT.fmap" => Some(2),
"ExceptT.pure" => Some(1),
"ExceptT.<*>" => Some(2),
"ExceptT.>>=" => Some(2),
"ExceptT.>>" => Some(2),
"ExceptT.lift" => Some(1),
"ExceptT.liftIO" => Some(1),
"throwE" => Some(1),
"catchE" => Some(2),
// MonadError standard names (mtl-style aliases)
"throwError" => Some(1),
"catchError" => Some(2),
// WriterT operations
"WriterT" => Some(1),
"runWriterT" => Some(1),
"WriterT.fmap" => Some(2),
"WriterT.pure" => Some(1),
"WriterT.<*>" => Some(2),
"WriterT.>>=" => Some(2),
"WriterT.>>" => Some(2),
"WriterT.lift" => Some(1),
"WriterT.liftIO" => Some(1),
"tell" => Some(1),
"execWriterT" => Some(1),
// Generic lift/liftIO (dispatched based on transformer context)
"lift" => Some(1),
"liftIO" => Some(1),
_ => {
// Check for field selector pattern: $sel_N where N is a digit
if name.starts_with("$sel_") && name[5..].parse::<usize>().is_ok() {
return Some(1); // Field selectors take one argument (the tuple/dict)
}
None
}
}
}
/// Check if an expression is a saturated builtin function application.
fn is_saturated_builtin<'a>(&self, expr: &'a Expr) -> Option<(&'a str, Vec<&'a Expr>)> {
// Collect arguments while unwrapping applications
let mut args = Vec::new();
let mut current = expr;
while let Expr::App(func, arg, _) = current {
args.push(arg.as_ref());
current = func.as_ref();
}
// Skip through type applications (erased at runtime)
while let Expr::TyApp(inner, _, _) = current {
current = inner.as_ref();
}
// Check if the head is a builtin function
if let Expr::Var(var, _) = current {
let name = var.name.as_str();
if let Some(arity) = self.builtin_info(name) {
args.reverse();
if args.len() == arity as usize {
return Some((name, args));
}
}
}
None
}
/// Lower a builtin function application.
fn lower_builtin(
&mut self,
name: &str,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
match name {
// List operations
"head" => self.lower_builtin_head(args[0]),
"tail" => self.lower_builtin_tail(args[0]),
"null" => self.lower_builtin_null(args[0]),
"length" => self.lower_builtin_length(args[0]),
"take" => self.lower_builtin_take(args[0], args[1]),
"drop" => self.lower_builtin_drop(args[0], args[1]),
"reverse" => self.lower_builtin_reverse(args[0]),
"append" | "++" => self.lower_builtin_append(args[0], args[1]),
"enumFromTo" => self.lower_builtin_enum_from_to(args[0], args[1]),
"replicate" => self.lower_builtin_replicate(args[0], args[1]),
"sum" => self.lower_builtin_sum(args[0]),
"product" => self.lower_builtin_product(args[0]),
"map" => self.lower_builtin_map(args[0], args[1]),
"filter" => self.lower_builtin_filter(args[0], args[1]),
"foldr" => self.lower_builtin_foldr(args[0], args[1], args[2]),
"foldl" => self.lower_builtin_foldl(args[0], args[1], args[2]),
"foldl'" => self.lower_builtin_foldl_strict(args[0], args[1], args[2]),
"zipWith" => self.lower_builtin_zipwith(args[0], args[1], args[2]),
"zip" => self.lower_builtin_zip(args[0], args[1]),
"last" => self.lower_builtin_last(args[0]),
"init" => self.lower_builtin_init(args[0]),
"!!" => self.lower_builtin_index(args[0], args[1]),
"concatMap" => self.lower_builtin_concat_map(args[0], args[1]),
"concat" => self.lower_builtin_concat(args[0]),
// Tuple operations
"fst" => self.lower_builtin_fst(args[0]),
"snd" => self.lower_builtin_snd(args[0]),
"swap" => self.lower_builtin_swap(args[0]),
// Maybe operations
"fromJust" => self.lower_builtin_from_just(args[0]),
"isJust" => self.lower_builtin_is_just(args[0]),
"isNothing" => self.lower_builtin_is_nothing(args[0]),
"fromMaybe" => self.lower_builtin_from_maybe(args[0], args[1]),
"maybe" => self.lower_builtin_maybe(args[0], args[1], args[2]),
"listToMaybe" => self.lower_builtin_list_to_maybe(args[0]),
"maybeToList" => self.lower_builtin_maybe_to_list(args[0]),
"catMaybes" => self.lower_builtin_cat_maybes(args[0]),
"mapMaybe" => self.lower_builtin_map_maybe(args[0], args[1]),
// Either operations
"isLeft" => self.lower_builtin_is_left(args[0]),
"isRight" => self.lower_builtin_is_right(args[0]),
"either" => self.lower_builtin_either(args[0], args[1], args[2]),
"fromLeft" => self.lower_builtin_from_left(args[0], args[1]),
"fromRight" => self.lower_builtin_from_right(args[0], args[1]),
"lefts" => self.lower_builtin_lefts(args[0]),
"rights" => self.lower_builtin_rights(args[0]),
"partitionEithers" => self.lower_builtin_partition_eithers(args[0]),
// Control
"guard" => self.lower_builtin_guard(args[0]),
// Error / Exception handling
"error" => self.lower_builtin_error(args[0]),
"undefined" => self.lower_builtin_undefined(),
"throw" | "throwIO" => self.lower_builtin_throw(args[0]),
"catch" => self.lower_builtin_catch(args[0], args[1]),
"try" => self.lower_builtin_try(args[0]),
"bracket" => self.lower_builtin_bracket(args[0], args[1], args[2]),
"finally" => self.lower_builtin_finally(args[0], args[1]),
"onException" => self.lower_builtin_on_exception(args[0], args[1]),
"mask" if args.len() == 1 => self.lower_builtin_mask(args[0]),
"mask_" if args.len() == 1 => self.lower_builtin_mask_simple(args[0], false),
"uninterruptibleMask" if args.len() == 1 => {
self.lower_builtin_uninterruptible_mask(args[0])
}
"uninterruptibleMask_" if args.len() == 1 => {
self.lower_builtin_mask_simple(args[0], true)
}
"getMaskingState" => self.lower_builtin_get_masking_state(),
"toException" => self.lower_builtin_to_exception(args[0]),
"fromException" => self.lower_builtin_from_exception(args[0]),
"displayException" => self.lower_builtin_display_exception(args[0]),
"userError" => self.lower_builtin_user_error(args[0]),
"ioError" => self.lower_builtin_throw(args[0]),
// Misc
"seq" => self.lower_builtin_seq(args[0], args[1]),
"id" => self.lower_expr(args[0]),
"const" => self.lower_expr(args[0]),
"not" => self.lower_builtin_not(args[0]),
// E.63: DeepSeq stubs (no-ops in strict runtime)
"rnf" => {
// rnf evaluates arg (already done since BHC is strict), returns unit
let _val = self.lower_expr(args[0])?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
"deepseq" => {
// deepseq evaluates first arg (already done), returns second arg
let _val = self.lower_expr(args[0])?;
self.lower_expr(args[1])
}
"force" => {
// force is identity — value is already in normal form
self.lower_expr(args[0])
}
// GHC.Generics from/to — dispatch to derived functions
"from" => self.lower_builtin_generic_from(args[0]),
"to" => self.lower_builtin_generic_to(args[0]),
"otherwise" => Ok(Some(
self.type_mapper().i64_type().const_int(1, false).into(),
)),
// IO operations
"putStrLn" => self.lower_builtin_put_str_ln(args[0]),
"putStr" => self.lower_builtin_put_str(args[0]),
"putChar" => self.lower_builtin_put_char(args[0]),
"print" => self.lower_builtin_print(args[0]),
"getLine" => self.lower_builtin_get_line(),
// Monadic operations — dispatch based on transformer stack.
// Detects the operation's native layer and inserts lifts if needed.
">>=" => {
// For ReaderT-over-StateT, bypass auto-lift and route directly
// to nested ReaderT bind which threads state through 3-arg closures
if self.transformer_stack.is_reader_t_over_state_t() {
return self.lower_builtin_reader_t_bind(args[0], args[1]);
}
// For ExceptT over StateT/ReaderT, bypass auto-lift and route
// to nested ExceptT bind which threads state/reader-env
if self.transformer_stack.is_except_t_over_state_t()
|| self.transformer_stack.is_except_t_over_reader_t()
{
return self.lower_builtin_except_t_bind(args[0], args[1]);
}
// For WriterT over StateT/ReaderT, bypass auto-lift and route
// to nested WriterT bind which threads state/reader-env
if self.transformer_stack.is_writer_t_over_state_t()
|| self.transformer_stack.is_writer_t_over_reader_t()
{
return self.lower_builtin_writer_t_bind(args[0], args[1]);
}
let op_layer = self.detect_operation_layer(args[0]);
let current = self.current_transformer_layer();
// Check if we need to lift the LHS operation to the current layer
let needs_lift = op_layer != current
&& self.transformer_stack.contains(op_layer)
&& self.transformer_stack.lifts_to_reach(op_layer).unwrap_or(0) > 0;
if needs_lift {
// Lift the inner operation to the current stack layer
self.lower_bind_with_auto_lift(args[0], args[1], op_layer, current)
} else {
// No lifting needed, dispatch based on detected operation layer
self.push_transformer_layer(op_layer);
let result = match op_layer {
TransformerLayer::ReaderT => {
self.lower_builtin_reader_t_bind(args[0], args[1])
}
TransformerLayer::StateT => {
self.lower_builtin_state_t_bind(args[0], args[1])
}
TransformerLayer::ExceptT => {
self.lower_builtin_except_t_bind(args[0], args[1])
}
TransformerLayer::WriterT => {
self.lower_builtin_writer_t_bind(args[0], args[1])
}
TransformerLayer::IO => self.lower_builtin_bind(args[0], args[1]),
};
self.pop_transformer_layer();
result
}
}
">>" => {
// For ReaderT-over-StateT, bypass auto-lift and route directly
// to nested ReaderT then which threads state through 3-arg closures
if self.transformer_stack.is_reader_t_over_state_t() {
return self.lower_builtin_reader_t_then(args[0], args[1]);
}
// For ExceptT over StateT/ReaderT, bypass auto-lift
if self.transformer_stack.is_except_t_over_state_t()
|| self.transformer_stack.is_except_t_over_reader_t()
{
return self.lower_builtin_except_t_then(args[0], args[1]);
}
// For WriterT over StateT/ReaderT, bypass auto-lift
if self.transformer_stack.is_writer_t_over_state_t()
|| self.transformer_stack.is_writer_t_over_reader_t()
{
return self.lower_builtin_writer_t_then(args[0], args[1]);
}
let op_layer = self.detect_operation_layer(args[0]);
let current = self.current_transformer_layer();
// Check if we need to lift the LHS operation to the current layer
let needs_lift = op_layer != current
&& self.transformer_stack.contains(op_layer)
&& self.transformer_stack.lifts_to_reach(op_layer).unwrap_or(0) > 0;
if needs_lift {
// Lift the inner operation to the current stack layer
self.lower_then_with_auto_lift(args[0], args[1], op_layer, current)
} else {
// No lifting needed, dispatch based on detected operation layer
self.push_transformer_layer(op_layer);
let result = match op_layer {
TransformerLayer::ReaderT => {
self.lower_builtin_reader_t_then(args[0], args[1])
}
TransformerLayer::StateT => {
self.lower_builtin_state_t_then(args[0], args[1])
}
TransformerLayer::ExceptT => {
self.lower_builtin_except_t_then(args[0], args[1])
}
TransformerLayer::WriterT => {
self.lower_builtin_writer_t_then(args[0], args[1])
}
TransformerLayer::IO => self.lower_builtin_then(args[0], args[1]),
};
self.pop_transformer_layer();
result
}
}
"return" | "pure" => {
// For ReaderT-over-StateT, route to nested ReaderT pure
if self.transformer_stack.is_reader_t_over_state_t() {
return self.lower_builtin_reader_t_pure(args[0]);
}
// For ExceptT over StateT/ReaderT, route to nested ExceptT pure
if self.transformer_stack.is_except_t_over_state_t()
|| self.transformer_stack.is_except_t_over_reader_t()
{
return self.lower_builtin_except_t_pure(args[0]);
}
// For WriterT over StateT, route to nested WriterT pure
// (WriterT over ReaderT: r is absorbed by unused second arg, same as plain)
if self.transformer_stack.is_writer_t_over_state_t() {
return self.lower_builtin_writer_t_pure(args[0]);
}
match self.current_transformer_layer() {
TransformerLayer::ReaderT => self.lower_builtin_reader_t_pure(args[0]),
TransformerLayer::StateT => self.lower_builtin_state_t_pure(args[0]),
TransformerLayer::ExceptT => self.lower_builtin_except_t_pure(args[0]),
TransformerLayer::WriterT => self.lower_builtin_writer_t_pure(args[0]),
TransformerLayer::IO => self.lower_builtin_return(args[0]),
}
}
// Numeric / Math operations
"negate" => self.lower_builtin_negate(args[0]),
"abs" => self.lower_builtin_abs(args[0]),
"signum" => self.lower_builtin_signum(args[0]),
"sqrt" => self.lower_builtin_math_unary(args[0], 1013, "sqrt"),
"exp" => self.lower_builtin_math_unary(args[0], 1014, "exp"),
"log" => self.lower_builtin_math_unary(args[0], 1015, "log"),
"sin" => self.lower_builtin_math_unary(args[0], 1016, "sin"),
"cos" => self.lower_builtin_math_unary(args[0], 1017, "cos"),
"tan" => self.lower_builtin_math_unary(args[0], 1018, "tan"),
"asin" => self.lower_builtin_math_unary(args[0], 1019, "asin"),
"acos" => self.lower_builtin_math_unary(args[0], 1020, "acos"),
"atan" => self.lower_builtin_math_unary(args[0], 1021, "atan"),
"atan2" => self.lower_builtin_math_binary(args[0], args[1], 1022, "atan2"),
"ceiling" => self.lower_builtin_float_to_int(args[0], 1000023, "ceiling"),
"floor" => self.lower_builtin_float_to_int(args[0], 1000024, "floor"),
"round" => self.lower_builtin_float_to_int(args[0], 1000025, "round"),
"truncate" => self.lower_builtin_float_to_int(args[0], 1000026, "truncate"),
// Rational operations
"%" => self.lower_builtin_rational_make(args[0], args[1]),
"numerator" => self.lower_builtin_rational_unary(args[0], 1000901, "numerator"),
"denominator" => self.lower_builtin_rational_unary(args[0], 1000902, "denominator"),
"toRational" => self.lower_builtin_to_rational(args[0]),
"fromRational" => self.lower_builtin_from_rational(args[0]),
"recip" => self.lower_builtin_rational_unary(args[0], 1000914, "recip"),
// Numeric conversions (identity for BHC's single Int type)
"fromIntegral" | "toInteger" => self.lower_expr(args[0]),
"fromInteger" => {
// Check if the context expects Rational
// We detect this by examining the expression's type annotation
let expr_ty = args[0].ty();
if self.is_rational_type(&expr_ty) {
// Already Rational - identity
self.lower_expr(args[0])
} else {
// Default: identity (Int -> Int)
self.lower_expr(args[0])
}
}
// String type class methods (E.25)
"fromString" => self.lower_expr(args[0]), // identity: String = [Char]
// OverloadedLists (E.64)
"fromList" => self.lower_expr(args[0]), // identity: list stays as list
"read" => self.lower_builtin_read(args[0]),
"readMaybe" => self.lower_builtin_read_maybe(args[0]),
// Ordering
"compare" => self.lower_builtin_compare(args[0], args[1]),
// Integer predicates
"even" => self.lower_builtin_even(args[0]),
"odd" => self.lower_builtin_odd(args[0]),
// Integer operations via RTS
"gcd" => self.lower_builtin_int_binop_rts(args[0], args[1], 1000500, "gcd"),
"lcm" => self.lower_builtin_int_binop_rts(args[0], args[1], 1000501, "lcm"),
// Division with tuples
"divMod" => self.lower_builtin_divmod(args[0], args[1]),
"quotRem" => self.lower_builtin_quotrem(args[0], args[1]),
// IORef operations
"newIORef" => self.lower_builtin_new_ioref(args[0]),
"readIORef" => self.lower_builtin_read_ioref(args[0]),
"writeIORef" => self.lower_builtin_write_ioref(args[0], args[1]),
"modifyIORef" | "modifyIORef'" => self.lower_builtin_modify_ioref(args[0], args[1]),
"atomicModifyIORef" | "atomicModifyIORef'" => {
self.lower_builtin_atomic_modify_ioref(args[0], args[1])
}
// Character operations
"ord" => self.lower_builtin_ord(args[0]),
"chr" => self.lower_builtin_chr(args[0]),
"isAlpha" => self.lower_builtin_char_pred(args[0], 1000030, "is_alpha"),
"isDigit" => self.lower_builtin_char_pred(args[0], 1000031, "is_digit"),
"isAlphaNum" => self.lower_builtin_char_pred(args[0], 1000032, "is_alnum"),
"isSpace" => self.lower_builtin_char_pred(args[0], 1000033, "is_space"),
"isUpper" => self.lower_builtin_char_pred(args[0], 1000034, "is_upper"),
"isLower" => self.lower_builtin_char_pred(args[0], 1000035, "is_lower"),
"isPrint" => self.lower_builtin_char_pred(args[0], 1000038, "is_print"),
"isAscii" => self.lower_builtin_char_pred(args[0], 1000039, "is_ascii"),
"isControl" => self.lower_builtin_char_pred(args[0], 1000040, "is_control"),
"isHexDigit" => self.lower_builtin_char_pred(args[0], 1000041, "is_hex"),
"isLetter" => self.lower_builtin_char_pred(args[0], 1000042, "is_letter"),
"isNumber" => self.lower_builtin_char_pred(args[0], 1000043, "is_number"),
"isPunctuation" => self.lower_builtin_char_pred(args[0], 1000044, "is_punct"),
"isSymbol" => self.lower_builtin_char_pred(args[0], 1000045, "is_symbol"),
"toLower" => self.lower_builtin_char_conv(args[0], 1000037, "to_lower"),
"toUpper" => self.lower_builtin_char_conv(args[0], 1000036, "to_upper"),
"digitToInt" => self.lower_builtin_digit_to_int(args[0]),
"intToDigit" => self.lower_builtin_int_to_digit(args[0]),
// IO & System operations
"readFile" => self.lower_builtin_read_file(args[0]),
"writeFile" => self.lower_builtin_write_file(args[0], args[1]),
"appendFile" => self.lower_builtin_append_file(args[0], args[1]),
"openFile" => self.lower_builtin_open_file(args[0], args[1]),
"hClose" => self.lower_builtin_hclose(args[0]),
"hGetChar" => self.lower_builtin_hgetchar(args[0]),
"hGetLine" => self.lower_builtin_hgetline(args[0]),
"hPutStr" => self.lower_builtin_hputstr(args[0], args[1]),
"hPutStrLn" => self.lower_builtin_hputstrln(args[0], args[1]),
"hFlush" => self.lower_builtin_hflush(args[0]),
"hIsEOF" => self.lower_builtin_hiseof(args[0]),
"hSetBuffering" => self.lower_builtin_hset_buffering(args[0], args[1]),
"stdin" => self.lower_builtin_std_handle(1060, "stdin"),
"stdout" => self.lower_builtin_std_handle(1061, "stdout"),
"stderr" => self.lower_builtin_std_handle(1062, "stderr"),
"doesFileExist" => self.lower_builtin_file_pred(args[0], 1000063, "exists"),
"doesDirectoryExist" => self.lower_builtin_file_pred(args[0], 1000064, "is_dir"),
"removeFile" => self.lower_builtin_remove_file(args[0]),
"getArgs" => self.lower_builtin_get_args(),
"getProgName" => self.lower_builtin_get_prog_name(),
"getEnv" => self.lower_builtin_get_env(args[0]),
"lookupEnv" => self.lower_builtin_lookup_env(args[0]),
"exitSuccess" => self.lower_builtin_exit_success(),
"exitFailure" => self.lower_builtin_exit_failure(),
"exitWith" => self.lower_builtin_exit_with(args[0]),
"hGetContents" => self.lower_builtin_hgetcontents(args[0]),
"getCurrentDirectory" => self.lower_builtin_get_current_directory(),
"createDirectory" => self.lower_builtin_create_directory(args[0]),
"listDirectory" => self.lower_builtin_list_directory(args[0]),
// E.19: FilePath operations
"</>" => {
self.lower_builtin_filepath_two_str_to_str(args[0], args[1], 1000534, "combine")
}
"takeFileName" => {
self.lower_builtin_filepath_str_to_str(args[0], 1000520, "takeFileName")
}
"takeDirectory" => {
self.lower_builtin_filepath_str_to_str(args[0], 1000521, "takeDirectory")
}
"takeExtension" => {
self.lower_builtin_filepath_str_to_str(args[0], 1000522, "takeExtension")
}
"dropExtension" => {
self.lower_builtin_filepath_str_to_str(args[0], 1000523, "dropExtension")
}
"takeBaseName" => {
self.lower_builtin_filepath_str_to_str(args[0], 1000524, "takeBaseName")
}
"replaceExtension" => self.lower_builtin_filepath_two_str_to_str(
args[0],
args[1],
1000525,
"replaceExtension",
),
"isAbsolute" => self.lower_builtin_file_pred(args[0], 1000526, "is_absolute"),
"isRelative" => self.lower_builtin_file_pred(args[0], 1000527, "is_relative"),
"hasExtension" => self.lower_builtin_file_pred(args[0], 1000528, "has_extension"),
"splitExtension" => self.lower_builtin_split_extension(args[0]),
// E.19: Directory operations
"setCurrentDirectory" => {
self.lower_builtin_dir_str_to_io_unit(args[0], 1000529, "setCurrentDirectory")
}
"removeDirectory" => {
self.lower_builtin_dir_str_to_io_unit(args[0], 1000530, "removeDirectory")
}
"renameFile" => {
self.lower_builtin_dir_two_str_to_io_unit(args[0], args[1], 1000531, "renameFile")
}
"copyFile" => {
self.lower_builtin_dir_two_str_to_io_unit(args[0], args[1], 1000532, "copyFile")
}
// Higher-order / Monadic operations
"fmap" | "<$>" => self.lower_builtin_fmap(args[0], args[1]),
"<*>" => self.lower_builtin_ap(args[0], args[1]),
"join" => self.lower_builtin_join(args[0]),
"=<<" => self.lower_builtin_bind_flipped(args[0], args[1]),
"when" => self.lower_builtin_when(args[0], args[1]),
"unless" => self.lower_builtin_unless(args[0], args[1]),
"void" => self.lower_builtin_void(args[0]),
"mapM" => self.lower_builtin_mapm(args[0], args[1]),
"mapM_" => self.lower_builtin_mapm_(args[0], args[1]),
"forM" => self.lower_builtin_mapm(args[1], args[0]),
"forM_" => self.lower_builtin_mapm_(args[1], args[0]),
"sequence" => self.lower_builtin_sequence(args[0]),
"sequence_" => self.lower_builtin_sequence_(args[0]),
"traverse" => self.lower_builtin_traverse(args[0], args[1]),
"traverse_" => self.lower_builtin_traverse_(args[0], args[1]),
"for" => self.lower_builtin_traverse(args[1], args[0]), // flipped
"for_" => self.lower_builtin_traverse_(args[1], args[0]), // flipped
"sequenceA" => self.lower_builtin_sequence_a(args[0]),
"sequenceA_" => self.lower_builtin_sequence_a_(args[0]),
"forever" => self.lower_builtin_forever(args[0]),
"filterM" => self.lower_builtin_filter_m(args[0], args[1]),
"foldM" => self.lower_builtin_fold_m(args[0], args[1], args[2]),
"foldM_" => self.lower_builtin_fold_m_(args[0], args[1], args[2]),
"replicateM" => self.lower_builtin_replicate_m(args[0], args[1]),
"replicateM_" => self.lower_builtin_replicate_m_(args[0], args[1]),
"zipWithM" => self.lower_builtin_zipwith_m(args[0], args[1], args[2]),
"zipWithM_" => self.lower_builtin_zipwith_m_(args[0], args[1], args[2]),
// Data.Function
"flip" => self.lower_builtin_flip(args[0], args[1], args[2]),
"on" => self.lower_builtin_on(args[0], args[1], args[2], args[3]),
"fix" => self.lower_builtin_fix(args[0]),
"$" => self.lower_builtin_apply(args[0], args[1]),
"&" => self.lower_builtin_reverse_apply(args[0], args[1]),
"." => self.lower_builtin_compose(args[0], args[1], args[2]),
"curry" => self.lower_builtin_curry(args[0], args[1], args[2]),
"uncurry" => self.lower_builtin_uncurry(args[0], args[1]),
"succ" => self.lower_builtin_succ(args[0]),
"pred" => self.lower_builtin_pred(args[0]),
"fromEnum" => self.lower_builtin_from_enum(args[0]),
"toEnum" => self.lower_builtin_to_enum(args[0]),
"minBound" => self.lower_builtin_min_bound(),
"maxBound" => self.lower_builtin_max_bound(),
// E.28: Arithmetic, enum, folds, higher-order, IO input
"min" => self.lower_builtin_min(args[0], args[1]),
"max" => self.lower_builtin_max(args[0], args[1]),
"subtract" => self.lower_builtin_subtract(args[0], args[1]),
"enumFrom" => self.lower_builtin_enum_from(args[0]),
"enumFromThen" => self.lower_builtin_enum_from_then(args[0], args[1]),
"enumFromThenTo" => self.lower_builtin_enum_from_then_to(args[0], args[1], args[2]),
"foldl1" => self.lower_builtin_foldl1(args[0], args[1]),
"foldr1" => self.lower_builtin_foldr1(args[0], args[1]),
"comparing" => self.lower_builtin_comparing(args[0], args[1], args[2]),
"until" => self.lower_builtin_until(args[0], args[1], args[2]),
"getChar" => self.lower_builtin_get_char(),
"isEOF" => self.lower_builtin_is_eof(),
"getContents" => self.lower_builtin_get_contents(),
"interact" => self.lower_builtin_interact(args[0]),
// Show
"show" => self.lower_builtin_show(args[0]),
"showInt" => {
self.lower_builtin_show_typed(args[0], 1000072, "show_int", ShowCoerce::Int)
}
"showDouble" => {
self.lower_builtin_show_typed(args[0], 1000073, "show_double", ShowCoerce::Double)
}
"showFloat" => {
self.lower_builtin_show_typed(args[0], 1000090, "show_float", ShowCoerce::Float)
}
"showBool" => {
self.lower_builtin_show_typed(args[0], 1000091, "show_bool", ShowCoerce::Bool)
}
"showChar" => {
self.lower_builtin_show_typed(args[0], 1000074, "show_char", ShowCoerce::Char)
}
"showString" => self.lower_builtin_show_typed(
args[0],
1000092,
"show_string",
ShowCoerce::StringList,
),
"showList" => {
self.lower_builtin_show_typed(args[0], 1000093, "show_list", ShowCoerce::List)
}
"showMaybe" => {
self.lower_builtin_show_typed(args[0], 1000094, "show_maybe", ShowCoerce::MaybeOf)
}
"showEither" => {
self.lower_builtin_show_typed(args[0], 1000095, "show_either", ShowCoerce::EitherOf)
}
"showTuple2" => {
self.lower_builtin_show_typed(args[0], 1000096, "show_tuple2", ShowCoerce::Tuple2Of)
}
"showUnit" => {
self.lower_builtin_show_typed(args[0], 1000097, "show_unit", ShowCoerce::Unit)
}
// Advanced list operations (delegate to existing patterns)
"any" => self.lower_builtin_any(args[0], args[1]),
"all" => self.lower_builtin_all(args[0], args[1]),
"and" => self.lower_builtin_and(args[0]),
"or" => self.lower_builtin_or(args[0]),
"maximum" => self.lower_builtin_maximum(args[0]),
"minimum" => self.lower_builtin_minimum(args[0]),
"elem" => self.lower_builtin_elem(args[0], args[1]),
"notElem" => self.lower_builtin_not_elem(args[0], args[1]),
"iterate" => self.lower_builtin_iterate(args[0], args[1]),
"repeat" => self.lower_builtin_repeat(args[0]),
"cycle" => self.lower_builtin_cycle(args[0]),
"takeWhile" => self.lower_builtin_take_while(args[0], args[1]),
"dropWhile" => self.lower_builtin_drop_while(args[0], args[1]),
"span" => self.lower_builtin_span(args[0], args[1]),
"break" => self.lower_builtin_break(args[0], args[1]),
"splitAt" => self.lower_builtin_split_at(args[0], args[1]),
"find" => self.lower_builtin_find(args[0], args[1]),
"lookup" => self.lower_builtin_lookup(args[0], args[1]),
"partition" => self.lower_builtin_partition(args[0], args[1]),
"intersperse" => self.lower_builtin_intersperse(args[0], args[1]),
"intercalate" => self.lower_builtin_intercalate(args[0], args[1]),
"transpose" => self.lower_builtin_transpose(args[0]),
"nub" => self.lower_builtin_nub(args[0]),
"sort" => self.lower_builtin_sort(args[0]),
"sortBy" => self.lower_builtin_sort_by(args[0], args[1]),
"group" => self.lower_builtin_group(args[0]),
"delete" => self.lower_builtin_delete(args[0], args[1]),
"union" => self.lower_builtin_union(args[0], args[1]),
"intersect" => self.lower_builtin_intersect(args[0], args[1]),
"sortOn" => self.lower_builtin_sort_on(args[0], args[1]),
"nubBy" => self.lower_builtin_nub_by(args[0], args[1]),
"groupBy" => self.lower_builtin_group_by(args[0], args[1]),
"deleteBy" => self.lower_builtin_delete_by(args[0], args[1], args[2]),
"unionBy" => self.lower_builtin_union_by(args[0], args[1], args[2]),
"intersectBy" => self.lower_builtin_intersect_by(args[0], args[1], args[2]),
"stripPrefix" => self.lower_builtin_strip_prefix(args[0], args[1]),
"insert" => self.lower_builtin_insert(args[0], args[1]),
"mapAccumL" => self.lower_builtin_map_accum_l(args[0], args[1], args[2]),
"mapAccumR" => self.lower_builtin_map_accum_r(args[0], args[1], args[2]),
"scanl" => self.lower_builtin_scanl(args[0], args[1], args[2]),
"scanl'" => self.lower_builtin_scanl(args[0], args[1], args[2]),
"scanl1" => self.lower_builtin_scanl1(args[0], args[1]),
"scanr" => self.lower_builtin_scanr(args[0], args[1], args[2]),
"scanr1" => self.lower_builtin_scanr1(args[0], args[1]),
"unfoldr" => self.lower_builtin_unfoldr(args[0], args[1]),
"lines" => self.lower_builtin_lines(args[0]),
"unlines" => self.lower_builtin_unlines(args[0]),
"words" => self.lower_builtin_words(args[0]),
"unwords" => self.lower_builtin_unwords(args[0]),
"zip3" => self.lower_builtin_zip3(args[0], args[1], args[2]),
"zipWith3" => self.lower_builtin_zipwith3(args[0], args[1], args[2], args[3]),
"unzip" => self.lower_builtin_unzip(args[0]),
// E.16: New list operations
"elemIndex" => self.lower_builtin_elem_index(args[0], args[1]),
"findIndex" => self.lower_builtin_find_index(args[0], args[1]),
"isPrefixOf" => self.lower_builtin_is_prefix_of(args[0], args[1]),
"isSuffixOf" => self.lower_builtin_is_suffix_of(args[0], args[1]),
"isInfixOf" => self.lower_builtin_is_infix_of(args[0], args[1]),
"tails" => self.lower_builtin_tails(args[0]),
"inits" => self.lower_builtin_inits(args[0]),
"maximumBy" => self.lower_builtin_maximum_by(args[0], args[1]),
"minimumBy" => self.lower_builtin_minimum_by(args[0], args[1]),
"foldMap" => self.lower_builtin_concat_map(args[0], args[1]),
// Data.Map operations
"Data.Map.empty" => self.lower_builtin_map_empty(),
"Data.Map.singleton" => self.lower_builtin_map_singleton(args[0], args[1]),
"Data.Map.null" => self.lower_builtin_map_null(args[0]),
"Data.Map.size" => self.lower_builtin_map_size(args[0]),
"Data.Map.member" => self.lower_builtin_map_member(args[0], args[1]),
"Data.Map.notMember" => {
// Call member, then invert the Bool result
let member_result = self
.lower_builtin_map_member(args[0], args[1])?
.ok_or_else(|| {
CodegenError::Internal("notMember: member returned None".to_string())
})?;
let member_ptr = member_result.into_pointer_value();
let member_tag = self.extract_adt_tag(member_ptr)?;
let i64_type = self.type_mapper().i64_type();
let one = i64_type.const_int(1, false);
let inverted_tag = self
.builder()
.build_xor(member_tag, one, "not_member_tag")
.map_err(|e| CodegenError::Internal(format!("notMember: xor: {:?}", e)))?;
self.allocate_bool_adt(inverted_tag, "not_member")
}
"Data.Map.lookup" => self.lower_builtin_map_lookup(args[0], args[1]),
"Data.Map.findWithDefault" => {
self.lower_builtin_map_find_with_default(args[0], args[1], args[2])
}
"Data.Map.!" => self.lower_builtin_map_find_with_default(args[1], args[0], args[1]),
"Data.Map.insert" => self.lower_builtin_map_insert(args[0], args[1], args[2]),
"Data.Map.delete" => self.lower_builtin_map_delete(args[0], args[1]),
"Data.Map.union" => self.lower_builtin_map_union(args[0], args[1]),
"Data.Map.intersection" => self.lower_builtin_map_intersection(args[0], args[1]),
"Data.Map.difference" => self.lower_builtin_map_difference(args[0], args[1]),
"Data.Map.isSubmapOf" => self.lower_builtin_map_is_submap_of(args[0], args[1]),
"Data.Map.map" => self.lower_builtin_map_map(args[0], args[1]),
"Data.Map.filter" => self.lower_builtin_map_filter(args[0], args[1]),
"Data.Map.foldr" => self.lower_builtin_map_foldr(args[0], args[1], args[2]),
"Data.Map.foldl" => self.lower_builtin_map_foldl(args[0], args[1], args[2]),
"Data.Map.mapWithKey" => self.lower_builtin_map_map_with_key(args[0], args[1]),
"Data.Map.filterWithKey" => self.lower_builtin_map_filter_with_key(args[0], args[1]),
"Data.Map.mapMaybe" => self.lower_builtin_data_map_maybe(args[0], args[1]),
"Data.Map.mapMaybeWithKey" => {
self.lower_builtin_data_map_maybe_with_key(args[0], args[1])
}
"Data.Map.foldlWithKey" | "Data.Map.foldlWithKey'" => {
self.lower_builtin_map_foldl_with_key(args[0], args[1], args[2])
}
"Data.Map.foldrWithKey" => {
self.lower_builtin_map_foldr_with_key(args[0], args[1], args[2])
}
"Data.Map.unionWith" | "Data.Map.unionWithKey" => {
self.lower_builtin_map_union_with(args[0], args[1], args[2])
}
"Data.Map.intersectionWith" => {
self.lower_builtin_map_intersection_with(args[0], args[1], args[2])
}
"Data.Map.differenceWith" => {
self.lower_builtin_map_difference_with(args[0], args[1], args[2])
}
"Data.Map.insertWith" => {
self.lower_builtin_map_insert_with(args[0], args[1], args[2], args[3])
}
"Data.Map.adjust" => self.lower_builtin_map_adjust(args[0], args[1], args[2]),
"Data.Map.mapKeys" => self.lower_builtin_map_map_keys(args[0], args[1]),
"Data.Map.fromListWith" => self.lower_builtin_map_from_list_with(args[0], args[1]),
"Data.Map.update" => self.lower_builtin_map_update(args[0], args[1], args[2]),
"Data.Map.alter" => self.lower_builtin_map_alter(args[0], args[1], args[2]),
"Data.Map.unions" => self.lower_builtin_map_unions(args[0]),
"Data.Map.keys" => self.lower_builtin_map_keys(args[0]),
"Data.Map.elems" => self.lower_builtin_map_elems(args[0]),
"Data.Map.assocs"
| "Data.Map.toList"
| "Data.Map.toAscList"
| "Data.Map.toDescList" => self.lower_builtin_map_to_list(args[0]),
"Data.Map.keysSet" => self.lower_builtin_map_keys_set(args[0]),
"Data.Map.fromList" => self.lower_builtin_map_from_list(args[0]),
// Data.Set operations
"Data.Set.empty" => self.lower_builtin_set_empty(),
"Data.Set.singleton" => self.lower_builtin_set_singleton(args[0]),
"Data.Set.null" => self.lower_builtin_set_null(args[0]),
"Data.Set.size" => self.lower_builtin_set_size(args[0]),
"Data.Set.member" | "Data.Set.notMember" => {
self.lower_builtin_set_member(args[0], args[1])
}
"Data.Set.insert" => self.lower_builtin_set_insert(args[0], args[1]),
"Data.Set.delete" => self.lower_builtin_set_delete(args[0], args[1]),
"Data.Set.union" => {
self.lower_builtin_set_binary(args[0], args[1], 1000127, "set_union")
}
"Data.Set.intersection" => {
self.lower_builtin_set_binary(args[0], args[1], 1000128, "set_intersection")
}
"Data.Set.difference" => {
self.lower_builtin_set_binary(args[0], args[1], 1000129, "set_difference")
}
"Data.Set.isSubsetOf" => {
self.lower_builtin_set_predicate(args[0], args[1], 1000130, "set_is_subset_of")
}
"Data.Set.isProperSubsetOf" => self.lower_builtin_set_predicate(
args[0],
args[1],
1000131,
"set_is_proper_subset_of",
),
"Data.Set.findMin" => {
self.lower_builtin_set_find_extremum(args[0], 1132, "set_find_min")
}
"Data.Set.findMax" => {
self.lower_builtin_set_find_extremum(args[0], 1133, "set_find_max")
}
"Data.Set.deleteMin" => {
self.lower_builtin_set_delete_extremum(args[0], 1000134, "set_delete_min")
}
"Data.Set.deleteMax" => {
self.lower_builtin_set_delete_extremum(args[0], 1000135, "set_delete_max")
}
"Data.Set.lookupMin" => {
self.lower_builtin_set_lookup_extremum(args[0], 1000132, "set_lookup_min")
}
"Data.Set.lookupMax" => {
self.lower_builtin_set_lookup_extremum(args[0], 1000133, "set_lookup_max")
}
"Data.Set.map" => self.lower_builtin_set_map(args[0], args[1]),
"Data.Set.filter" => self.lower_builtin_set_filter(args[0], args[1]),
"Data.Set.foldr" => self.lower_builtin_set_foldr(args[0], args[1], args[2]),
"Data.Set.foldl" => self.lower_builtin_set_foldl(args[0], args[1], args[2]),
"Data.Set.unions" => self.lower_builtin_set_unions(args[0]),
"Data.Set.partition" => self.lower_builtin_set_partition(args[0], args[1]),
"Data.Set.toList" | "Data.Set.toAscList" | "Data.Set.toDescList" | "Data.Set.elems" => {
self.lower_builtin_set_to_list(args[0])
}
"Data.Set.fromList" => self.lower_builtin_set_from_list(args[0]),
// Data.IntMap operations
"Data.IntMap.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000140)).ok_or_else(|| {
CodegenError::Internal("bhc_intmap_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "intmap_empty")
.map_err(|e| CodegenError::Internal(format!("intmap_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("intmap_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.IntMap.singleton" => self.lower_builtin_map_singleton(args[0], args[1]),
"Data.IntMap.null" => self.lower_builtin_map_null(args[0]),
"Data.IntMap.size" => self.lower_builtin_map_size(args[0]),
"Data.IntMap.member" => self.lower_builtin_map_member(args[0], args[1]),
"Data.IntMap.lookup" => self.lower_builtin_map_lookup(args[0], args[1]),
"Data.IntMap.findWithDefault" => {
self.lower_builtin_map_find_with_default(args[0], args[1], args[2])
}
"Data.IntMap.insert" => self.lower_builtin_map_insert(args[0], args[1], args[2]),
"Data.IntMap.delete" => self.lower_builtin_map_delete(args[0], args[1]),
"Data.IntMap.union" => self.lower_builtin_map_union(args[0], args[1]),
"Data.IntMap.intersection" => self.lower_builtin_map_intersection(args[0], args[1]),
"Data.IntMap.difference" => self.lower_builtin_map_difference(args[0], args[1]),
"Data.IntMap.map" => self.lower_builtin_map_map(args[0], args[1]),
"Data.IntMap.filter" => self.lower_builtin_map_filter(args[0], args[1]),
"Data.IntMap.foldr" => self.lower_builtin_map_foldr(args[0], args[1], args[2]),
"Data.IntMap.mapWithKey" => self.lower_builtin_map_map_with_key(args[0], args[1]),
"Data.IntMap.foldlWithKey" | "Data.IntMap.foldlWithKey'" => {
self.lower_builtin_map_foldl_with_key(args[0], args[1], args[2])
}
"Data.IntMap.insertWith" => {
self.lower_builtin_map_insert_with(args[0], args[1], args[2], args[3])
}
"Data.IntMap.adjust" => self.lower_builtin_map_adjust(args[0], args[1], args[2]),
"Data.IntMap.unionWith" => self.lower_builtin_map_union_with(args[0], args[1], args[2]),
"Data.IntMap.keys" => self.lower_builtin_map_keys(args[0]),
"Data.IntMap.elems" => self.lower_builtin_map_elems(args[0]),
"Data.IntMap.toList" | "Data.IntMap.toAscList" => {
self.lower_builtin_map_to_list(args[0])
}
"Data.IntMap.fromList" => self.lower_builtin_map_from_list(args[0]),
// Data.IntSet operations
"Data.IntSet.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000150)).ok_or_else(|| {
CodegenError::Internal("bhc_intset_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "intset_empty")
.map_err(|e| CodegenError::Internal(format!("intset_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("intset_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.IntSet.singleton" => self.lower_builtin_set_singleton(args[0]),
"Data.IntSet.null" => self.lower_builtin_set_null(args[0]),
"Data.IntSet.size" => self.lower_builtin_set_size(args[0]),
"Data.IntSet.member" => self.lower_builtin_set_member(args[0], args[1]),
"Data.IntSet.insert" => self.lower_builtin_set_insert(args[0], args[1]),
"Data.IntSet.delete" => self.lower_builtin_set_delete(args[0], args[1]),
"Data.IntSet.union" => {
self.lower_builtin_set_binary(args[0], args[1], 1000155, "intset_union")
}
"Data.IntSet.intersection" => {
self.lower_builtin_set_binary(args[0], args[1], 1000157, "intset_intersection")
}
"Data.IntSet.difference" => {
self.lower_builtin_set_binary(args[0], args[1], 1000158, "intset_difference")
}
"Data.IntSet.isSubsetOf" => {
self.lower_builtin_set_predicate(args[0], args[1], 1000159, "intset_is_subset_of")
}
"Data.IntSet.filter" => self.lower_builtin_set_filter(args[0], args[1]),
"Data.IntSet.foldr" => self.lower_builtin_set_foldr(args[0], args[1], args[2]),
"Data.IntSet.toList" => self.lower_builtin_set_to_list(args[0]),
"Data.IntSet.fromList" => self.lower_builtin_set_from_list(args[0]),
// Data.Sequence operations (VarIds 1000800-1000823)
"Data.Sequence.empty" => self.lower_builtin_seq_empty(),
"Data.Sequence.singleton" => self.lower_builtin_seq_singleton(args[0]),
"Data.Sequence.null" => self.lower_builtin_seq_null(args[0]),
"Data.Sequence.length" => self.lower_builtin_seq_length(args[0]),
"Data.Sequence.index" => self.lower_builtin_seq_index(args[0], args[1]),
"Data.Sequence.lookup" => self.lower_builtin_seq_lookup(args[0], args[1]),
"Data.Sequence.<|" => self.lower_builtin_seq_cons(args[0], args[1]),
"Data.Sequence.|>" => self.lower_builtin_seq_snoc(args[0], args[1]),
"Data.Sequence.><" => self.lower_builtin_seq_append(args[0], args[1]),
"Data.Sequence.take" => self.lower_builtin_seq_take(args[0], args[1]),
"Data.Sequence.drop" => self.lower_builtin_seq_drop(args[0], args[1]),
"Data.Sequence.reverse" => self.lower_builtin_seq_reverse(args[0]),
"Data.Sequence.update" => self.lower_builtin_seq_update(args[0], args[1], args[2]),
"Data.Sequence.insertAt" => self.lower_builtin_seq_insert_at(args[0], args[1], args[2]),
"Data.Sequence.deleteAt" => self.lower_builtin_seq_delete_at(args[0], args[1]),
"Data.Sequence.fromList" => self.lower_builtin_seq_from_list(args[0]),
"Data.Sequence.toList" => self.lower_builtin_seq_to_list(args[0]),
"Data.Sequence.replicate" => self.lower_builtin_seq_replicate(args[0], args[1]),
"Data.Sequence.viewl" => self.lower_builtin_seq_viewl(args[0]),
"Data.Sequence.viewr" => self.lower_builtin_seq_viewr(args[0]),
"Data.Sequence.filter" => self.lower_builtin_seq_filter(args[0], args[1]),
// Data.Text operations (VarIds 1000200-1000226)
"Data.Text.empty" => self.lower_builtin_text_nullary(1000200, "text_empty"),
"Data.Text.singleton" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000201, "text_singleton")
}
"Data.Text.null" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000202, "text_null")
}
"Data.Text.length" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000203, "text_length")
}
"Data.Text.eq" | "Data.Text.==" => {
self.lower_builtin_text_binary_ptr_to_int(args[0], args[1], 1000204, "text_eq")
}
"Data.Text.compare" => {
self.lower_builtin_text_binary_ptr_to_int(args[0], args[1], 1000205, "text_compare")
}
"Data.Text.head" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000206, "text_head")
}
"Data.Text.last" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000207, "text_last")
}
"Data.Text.tail" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000208, "text_tail")
}
"Data.Text.init" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000209, "text_init")
}
"Data.Text.append" | "Data.Text.<>" => {
self.lower_builtin_text_binary_ptr_to_ptr(args[0], args[1], 1000210, "text_append")
}
"Data.Text.reverse" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000211, "text_reverse")
}
"Data.Text.take" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000212, "text_take")
}
"Data.Text.takeEnd" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000213, "text_take_end")
}
"Data.Text.drop" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000214, "text_drop")
}
"Data.Text.dropEnd" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000215, "text_drop_end")
}
"Data.Text.isPrefixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000216,
"text_is_prefix_of",
),
"Data.Text.isSuffixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000217,
"text_is_suffix_of",
),
"Data.Text.isInfixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000218,
"text_is_infix_of",
),
"Data.Text.toLower" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000219, "text_to_lower")
}
"Data.Text.toUpper" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000220, "text_to_upper")
}
"Data.Text.toCaseFold" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000221, "text_to_case_fold")
}
"Data.Text.toTitle" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000222, "text_to_title")
}
"Data.Text.pack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000223, "text_pack")
}
"Data.Text.unpack" => self.lower_builtin_text_unpack(args[0]),
"Data.Text.map" => self.lower_builtin_text_map(args[0], args[1]),
// Additional Data.Text operations (VarIds 1000227-1000235)
"Data.Text.filter" => self.lower_builtin_text_filter(args[0], args[1]),
"Data.Text.foldl'" => self.lower_builtin_text_foldl(args[0], args[1], args[2]),
"Data.Text.concat" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000229, "text_concat")
}
"Data.Text.intercalate" => self.lower_builtin_text_binary_ptr_to_ptr(
args[0],
args[1],
1000230,
"text_intercalate",
),
"Data.Text.strip" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000231, "text_strip")
}
"Data.Text.words" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000232, "text_words")
}
"Data.Text.lines" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000233, "text_lines")
}
"Data.Text.splitOn" => self.lower_builtin_text_binary_ptr_to_ptr(
args[0],
args[1],
1000234,
"text_split_on",
),
"Data.Text.replace" => self.lower_builtin_text_ternary_ptr(
args[0],
args[1],
args[2],
1000235,
"text_replace",
),
// Data.Text.Encoding (VarIds 1000236-1000237)
"Data.Text.Encoding.encodeUtf8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000236, "text_encode_utf8")
}
"Data.Text.Encoding.decodeUtf8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000237, "text_decode_utf8")
}
// Data.Text.IO operations (VarIds 1000240-1000246)
"Data.Text.IO.readFile" => self.lower_builtin_text_io_read_file(args[0]),
"Data.Text.IO.writeFile" => self.lower_builtin_text_io_write_file(args[0], args[1]),
"Data.Text.IO.appendFile" => self.lower_builtin_text_io_append_file(args[0], args[1]),
"Data.Text.IO.hGetContents" => {
self.lower_builtin_text_io_handle_to_text(args[0], 1000243, "text_h_get_contents")
}
"Data.Text.IO.hGetLine" => {
self.lower_builtin_text_io_handle_to_text(args[0], 1000244, "text_h_get_line")
}
"Data.Text.IO.hPutStr" => self.lower_builtin_text_io_handle_text_void(
args[0],
args[1],
1000245,
"text_h_put_str",
),
"Data.Text.IO.hPutStrLn" => self.lower_builtin_text_io_handle_text_void(
args[0],
args[1],
1000246,
"text_h_put_str_ln",
),
"Data.Text.IO.putStr" => {
self.lower_builtin_text_io_stdout_text(args[0], 1000245, "text_io_put_str")
}
"Data.Text.IO.putStrLn" => {
self.lower_builtin_text_io_stdout_text(args[0], 1000246, "text_io_put_str_ln")
}
"Data.Text.IO.getLine" => {
self.lower_builtin_text_io_stdin_to_text(1000244, "text_io_get_line")
}
"Data.Text.IO.getContents" => {
self.lower_builtin_text_io_stdin_to_text(1000243, "text_io_get_contents")
}
// Data.ByteString operations (VarIds 1000400-1000423)
"Data.ByteString.empty" => self.lower_builtin_text_nullary(1000400, "bs_empty"),
"Data.ByteString.singleton" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000401, "bs_singleton")
}
"Data.ByteString.pack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000402, "bs_pack")
}
"Data.ByteString.null" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000404, "bs_null")
}
"Data.ByteString.length" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000403, "bs_length")
}
"Data.ByteString.head" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000405, "bs_head")
}
"Data.ByteString.last" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000406, "bs_last")
}
"Data.ByteString.tail" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000407, "bs_tail")
}
"Data.ByteString.init" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000408, "bs_init")
}
"Data.ByteString.append" => {
self.lower_builtin_text_binary_ptr_to_ptr(args[0], args[1], 1000409, "bs_append")
}
"Data.ByteString.cons" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000410, "bs_cons")
}
"Data.ByteString.snoc" => {
self.lower_builtin_text_snoc(args[0], args[1], 1000411, "bs_snoc")
}
"Data.ByteString.take" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000412, "bs_take")
}
"Data.ByteString.drop" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000413, "bs_drop")
}
"Data.ByteString.reverse" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000414, "bs_reverse")
}
"Data.ByteString.elem" => {
self.lower_builtin_text_int_ptr_to_int(args[0], args[1], 1000415, "bs_elem")
}
"Data.ByteString.index" => {
self.lower_builtin_text_ptr_int_to_int(args[0], args[1], 1000416, "bs_index")
}
"Data.ByteString.eq" => {
self.lower_builtin_text_binary_ptr_to_int(args[0], args[1], 1000417, "bs_eq")
}
"Data.ByteString.compare" => {
self.lower_builtin_text_binary_ptr_to_int(args[0], args[1], 1000418, "bs_compare")
}
"Data.ByteString.isPrefixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000419,
"bs_is_prefix_of",
),
"Data.ByteString.isSuffixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000420,
"bs_is_suffix_of",
),
"Data.ByteString.unpack" => self.lower_builtin_bs_unpack(args[0]),
"Data.ByteString.readFile" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000422, "bs_read_file")
}
"Data.ByteString.writeFile" => self.lower_builtin_bs_write_file(args[0], args[1]),
// ByteArray operations (used by Data.ByteString internals)
"Data.ByteString.bhc_bytearray_malloc" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1250, "ba_malloc")
}
"Data.ByteString.bhc_bytearray_contents" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1251, "ba_contents")
}
"Data.ByteString.bhc_bytearray_index" => {
self.lower_builtin_text_ptr_int_to_int(args[0], args[1], 1252, "ba_index")
}
"Data.ByteString.bhc_bytearray_copy" => {
self.lower_builtin_ba_copy(args[0], args[1], args[2], args[3])
}
"Data.ByteString.bhc_ptr_plus" => {
self.lower_builtin_text_ptr_int_to_ptr(args[0], args[1], 1254, "ba_ptr_plus")
}
"Data.ByteString.bhc_poke_byte" => {
self.lower_builtin_ba_poke(args[0], args[1], args[2])
}
"Data.ByteString.bhc_cstring_length" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1256, "ba_strlen")
}
"Data.ByteString.bhc_peek_array" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1257, "ba_peek_array")
}
// Data.Text.Lazy operations (VarIds 1000270-1000283)
"Data.Text.Lazy.empty" => self.lower_builtin_text_nullary(1000270, "lt_empty"),
"Data.Text.Lazy.fromStrict" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000271, "lt_from_strict")
}
"Data.Text.Lazy.toStrict" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000272, "lt_to_strict")
}
"Data.Text.Lazy.pack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000273, "lt_pack")
}
"Data.Text.Lazy.unpack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000274, "lt_unpack")
}
"Data.Text.Lazy.null" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000275, "lt_null")
}
"Data.Text.Lazy.length" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000276, "lt_length")
}
"Data.Text.Lazy.append" | "Data.Text.Lazy.<>" => {
self.lower_builtin_text_binary_ptr_to_ptr(args[0], args[1], 1000277, "lt_append")
}
"Data.Text.Lazy.fromChunks" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000278, "lt_from_chunks")
}
"Data.Text.Lazy.toChunks" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000279, "lt_to_chunks")
}
"Data.Text.Lazy.head" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000280, "lt_head")
}
"Data.Text.Lazy.tail" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000281, "lt_tail")
}
"Data.Text.Lazy.take" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000282, "lt_take")
}
"Data.Text.Lazy.drop" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000283, "lt_drop")
}
// Data.ByteString.Lazy operations (VarIds 1000440-1000459)
"Data.ByteString.Lazy.empty" => self.lower_builtin_text_nullary(1000440, "lbs_empty"),
"Data.ByteString.Lazy.fromStrict" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000441, "lbs_from_strict")
}
"Data.ByteString.Lazy.toStrict" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000442, "lbs_to_strict")
}
"Data.ByteString.Lazy.fromChunks" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000443, "lbs_from_chunks")
}
"Data.ByteString.Lazy.toChunks" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000444, "lbs_to_chunks")
}
"Data.ByteString.Lazy.null" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000445, "lbs_null")
}
"Data.ByteString.Lazy.length" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000446, "lbs_length")
}
"Data.ByteString.Lazy.pack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000447, "lbs_pack")
}
"Data.ByteString.Lazy.append" | "Data.ByteString.Lazy.<>" => {
self.lower_builtin_text_binary_ptr_to_ptr(args[0], args[1], 1000448, "lbs_append")
}
"Data.ByteString.Lazy.head" => {
self.lower_builtin_text_unary_ptr_to_int(args[0], 1000449, "lbs_head")
}
"Data.ByteString.Lazy.tail" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000450, "lbs_tail")
}
"Data.ByteString.Lazy.take" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000451, "lbs_take")
}
"Data.ByteString.Lazy.drop" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000452, "lbs_drop")
}
"Data.ByteString.Lazy.filter" => self.lower_builtin_lazy_bs_filter(args[0], args[1]),
"Data.ByteString.Lazy.isPrefixOf" => self.lower_builtin_text_binary_ptr_to_int(
args[0],
args[1],
1000454,
"lbs_is_prefix_of",
),
"Data.ByteString.Lazy.readFile" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000455, "lbs_read_file")
}
"Data.ByteString.Lazy.writeFile" => {
self.lower_builtin_lazy_bs_write_file(args[0], args[1])
}
"Data.ByteString.Lazy.putStr" => {
self.lower_builtin_text_io_stdout_text(args[0], 1000457, "lbs_put_str")
}
"Data.ByteString.Lazy.hPut" | "Data.ByteString.Lazy.hPutStr" => self
.lower_builtin_text_io_handle_text_void(args[0], args[1], 1000458, "lbs_h_put_str"),
"Data.ByteString.Lazy.hGetContents" => {
self.lower_builtin_text_io_handle_to_text(args[0], 1000459, "lbs_h_get_contents")
}
// Data.ByteString.Lazy.Char8 operations (VarIds 1000470-1000475)
"Data.ByteString.Lazy.Char8.unpack" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000470, "lbs_c8_unpack")
}
"Data.ByteString.Lazy.Char8.lines" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000471, "lbs_c8_lines")
}
"Data.ByteString.Lazy.Char8.unlines" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000472, "lbs_c8_unlines")
}
"Data.ByteString.Lazy.Char8.take" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000473, "lbs_c8_take")
}
"Data.ByteString.Lazy.Char8.dropWhile" => {
self.lower_builtin_lazy_bs_drop_while(args[0], args[1])
}
"Data.ByteString.Lazy.Char8.cons" => {
self.lower_builtin_text_int_ptr_to_ptr(args[0], args[1], 1000475, "lbs_c8_cons")
}
// Data.Text.Lazy.Encoding operations (VarIds 1000476-1000477)
"Data.Text.Lazy.Encoding.encodeUtf8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000476, "lt_encode_utf8")
}
"Data.Text.Lazy.Encoding.decodeUtf8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000477, "lt_decode_utf8")
}
// Data.ByteString.Builder operations (VarIds 1000478-1000495 + reuse lazy BS)
"Data.ByteString.Builder.empty" => {
self.lower_builtin_text_nullary(1000440, "bsb_empty")
}
"Data.ByteString.Builder.singleton" | "Data.ByteString.Builder.word8" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000478, "bsb_singleton")
}
"Data.ByteString.Builder.byteString" | "Data.ByteString.Builder.shortByteString" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000441, "bsb_from_strict")
}
"Data.ByteString.Builder.lazyByteString"
| "Data.ByteString.Builder.toLazyByteString" => self.lower_expr(args[0]),
"Data.ByteString.Builder.append" | "Data.ByteString.Builder.<>" => {
self.lower_builtin_text_binary_ptr_to_ptr(args[0], args[1], 1000448, "bsb_append")
}
"Data.ByteString.Builder.toStrictByteString" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000442, "bsb_to_strict")
}
"Data.ByteString.Builder.hPutBuilder" => {
self.lower_builtin_text_io_handle_text_void(args[0], args[1], 1000458, "bsb_h_put")
}
"Data.ByteString.Builder.charUtf8" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000479, "bsb_char_utf8")
}
"Data.ByteString.Builder.stringUtf8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000480, "bsb_string_utf8")
}
"Data.ByteString.Builder.intDec"
| "Data.ByteString.Builder.int8Dec"
| "Data.ByteString.Builder.int16Dec"
| "Data.ByteString.Builder.int32Dec"
| "Data.ByteString.Builder.int64Dec"
| "Data.ByteString.Builder.integerDec"
| "Data.ByteString.Builder.wordDec"
| "Data.ByteString.Builder.word8Dec"
| "Data.ByteString.Builder.word16Dec"
| "Data.ByteString.Builder.word32Dec"
| "Data.ByteString.Builder.word64Dec" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000481, "bsb_int_dec")
}
"Data.ByteString.Builder.char7" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000482, "bsb_char7")
}
"Data.ByteString.Builder.char8" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000483, "bsb_char8")
}
"Data.ByteString.Builder.string7" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000484, "bsb_string7")
}
"Data.ByteString.Builder.string8" => {
self.lower_builtin_text_unary_ptr_to_ptr(args[0], 1000485, "bsb_string8")
}
"Data.ByteString.Builder.word16BE" | "Data.ByteString.Builder.int16BE" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000486, "bsb_word16_be")
}
"Data.ByteString.Builder.word32BE" | "Data.ByteString.Builder.int32BE" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000487, "bsb_word32_be")
}
"Data.ByteString.Builder.word64BE" | "Data.ByteString.Builder.int64BE" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000488, "bsb_word64_be")
}
"Data.ByteString.Builder.word16LE"
| "Data.ByteString.Builder.int16LE"
| "Data.ByteString.Builder.word16Host"
| "Data.ByteString.Builder.int16Host" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000489, "bsb_word16_le")
}
"Data.ByteString.Builder.word32LE"
| "Data.ByteString.Builder.int32LE"
| "Data.ByteString.Builder.word32Host"
| "Data.ByteString.Builder.int32Host" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000490, "bsb_word32_le")
}
"Data.ByteString.Builder.word64LE"
| "Data.ByteString.Builder.int64LE"
| "Data.ByteString.Builder.word64Host"
| "Data.ByteString.Builder.int64Host" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000491, "bsb_word64_le")
}
"Data.ByteString.Builder.wordHex"
| "Data.ByteString.Builder.word8Hex"
| "Data.ByteString.Builder.word16Hex"
| "Data.ByteString.Builder.word32Hex"
| "Data.ByteString.Builder.word64Hex" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000492, "bsb_word_hex")
}
"Data.ByteString.Builder.word8HexFixed" => {
self.lower_builtin_text_unary_int_to_ptr(args[0], 1000493, "bsb_word8_hex_fixed")
}
"Data.ByteString.Builder.word16HexFixed" => {
self.lower_builtin_bsb_word16_hex_fixed(args[0])
}
"Data.ByteString.Builder.word32HexFixed" => {
self.lower_builtin_bsb_word32_hex_fixed(args[0])
}
"Data.ByteString.Builder.word64HexFixed" => {
self.lower_builtin_bsb_word64_hex_fixed(args[0])
}
"Data.ByteString.Builder.floatBE" => self.lower_builtin_bsb_float_be(args[0]),
"Data.ByteString.Builder.doubleBE" => self.lower_builtin_bsb_double_be(args[0]),
"Data.ByteString.Builder.floatLE" | "Data.ByteString.Builder.floatHost" => {
self.lower_builtin_bsb_float_le(args[0])
}
"Data.ByteString.Builder.doubleLE" | "Data.ByteString.Builder.doubleHost" => {
self.lower_builtin_bsb_double_le(args[0])
}
// Identity operations (newtype = pass through)
"Identity" | "runIdentity" => self.lower_expr(args[0]),
"Identity.fmap" => {
// fmap f (Identity x) = Identity (f x) => just apply f to x
self.lower_builtin_fmap(args[0], args[1])
}
"Identity.pure" => self.lower_expr(args[0]),
"Identity.<*>" => self.lower_builtin_ap(args[0], args[1]),
"Identity.>>=" => self.lower_builtin_bind(args[0], args[1]),
"Identity.>>" => self.lower_builtin_then(args[0], args[1]),
// ReaderT operations
"ReaderT" => self.lower_expr(args[0]), // newtype wrap = identity
"runReaderT" => self.lower_builtin_run_reader_t(args[0], args[1]),
"ReaderT.pure" => self.lower_builtin_reader_t_pure(args[0]),
"ReaderT.>>=" => self.lower_builtin_reader_t_bind(args[0], args[1]),
"ReaderT.>>" => self.lower_builtin_reader_t_then(args[0], args[1]),
"ReaderT.fmap" => self.lower_builtin_reader_t_fmap(args[0], args[1]),
"ReaderT.<*>" => self.lower_builtin_reader_t_ap(args[0], args[1]),
"ReaderT.lift" | "ReaderT.liftIO" => self.lower_builtin_reader_t_lift(args[0]),
"ask" => self.lower_builtin_ask(),
"asks" => self.lower_builtin_asks(args[0]),
"local" => self.lower_builtin_local(args[0], args[1]),
// StateT operations
"StateT" => self.lower_expr(args[0]), // newtype wrap = identity
"runStateT" => self.lower_builtin_run_state_t(args[0], args[1]),
"StateT.pure" => self.lower_builtin_state_t_pure(args[0]),
"StateT.>>=" => self.lower_builtin_state_t_bind(args[0], args[1]),
"StateT.>>" => self.lower_builtin_state_t_then(args[0], args[1]),
"StateT.fmap" => self.lower_builtin_state_t_fmap(args[0], args[1]),
"StateT.<*>" => self.lower_builtin_state_t_ap(args[0], args[1]),
"StateT.lift" | "StateT.liftIO" => self.lower_builtin_state_t_lift(args[0]),
"get" => self.lower_builtin_get(),
"put" => self.lower_builtin_put(args[0]),
"modify" => self.lower_builtin_modify(args[0]),
"gets" => self.lower_builtin_gets(args[0]),
"evalStateT" => self.lower_builtin_eval_state_t(args[0], args[1]),
"execStateT" => self.lower_builtin_exec_state_t(args[0], args[1]),
// ExceptT operations
"ExceptT" => self.lower_expr(args[0]), // newtype wrap = identity
"runExceptT" => self.lower_builtin_run_except_t(args[0]),
"ExceptT.pure" => self.lower_builtin_except_t_pure(args[0]),
"ExceptT.>>=" => self.lower_builtin_except_t_bind(args[0], args[1]),
"ExceptT.>>" => self.lower_builtin_except_t_then(args[0], args[1]),
"ExceptT.fmap" => self.lower_builtin_except_t_fmap(args[0], args[1]),
"ExceptT.<*>" => self.lower_builtin_except_t_ap(args[0], args[1]),
"ExceptT.lift" | "ExceptT.liftIO" => self.lower_builtin_except_t_lift(args[0]),
"throwE" => self.lower_builtin_throw_e(args[0]),
"catchE" => self.lower_builtin_catch_e(args[0], args[1]),
// MonadError standard names (mtl-style aliases)
"throwError" => self.lower_builtin_throw_e(args[0]),
"catchError" => self.lower_builtin_catch_e(args[0], args[1]),
// Generic lift/liftIO — dispatched based on current transformer context
"lift" | "liftIO" => {
match self.current_transformer_layer() {
TransformerLayer::ExceptT => self.lower_builtin_except_t_lift(args[0]),
TransformerLayer::StateT => self.lower_builtin_state_t_lift(args[0]),
TransformerLayer::ReaderT => self.lower_builtin_reader_t_lift(args[0]),
TransformerLayer::WriterT => self.lower_builtin_writer_t_lift(args[0]),
TransformerLayer::IO => {
// IO base: lift is identity
let val = self.lower_expr(args[0])?.ok_or_else(|| {
CodegenError::Internal("lift: arg has no value".to_string())
})?;
Ok(Some(val))
}
}
}
// WriterT operations
"WriterT" => self.lower_expr(args[0]), // newtype wrap = identity
"runWriterT" => self.lower_builtin_run_writer_t(args[0]),
"WriterT.pure" => self.lower_builtin_writer_t_pure(args[0]),
"WriterT.>>=" => self.lower_builtin_writer_t_bind(args[0], args[1]),
"WriterT.>>" => self.lower_builtin_writer_t_then(args[0], args[1]),
"WriterT.fmap" => self.lower_builtin_writer_t_fmap(args[0], args[1]),
"WriterT.<*>" => self.lower_builtin_writer_t_ap(args[0], args[1]),
"WriterT.lift" | "WriterT.liftIO" => self.lower_builtin_writer_t_lift(args[0]),
"tell" => self.lower_builtin_tell(args[0]),
"execWriterT" => self.lower_builtin_exec_writer_t(args[0]),
_ => {
// Check for field selector pattern: $sel_N
if let Some(index_str) = name.strip_prefix("$sel_") {
if let Ok(field_index) = index_str.parse::<u32>() {
return self.lower_builtin_field_selector(args[0], field_index);
}
}
Err(CodegenError::Internal(format!("unknown builtin: {}", name)))
}
}
}
/// Lower `head` - extract first element of a list.
/// head [] = error "empty list"
/// head (x:_) = x
fn lower_builtin_head(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("head: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("head expects a list".to_string())),
};
// Extract tag to check if empty
let tag = self.extract_adt_tag(list_ptr)?;
let tm = self.type_mapper();
// Create blocks for empty check
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let empty_block = self
.llvm_context()
.append_basic_block(current_fn, "head_empty");
let cons_block = self
.llvm_context()
.append_basic_block(current_fn, "head_cons");
// Branch on tag (0 = [], 1 = :)
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, empty_block, cons_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Empty case: call error
self.builder().position_at_end(empty_block);
let error_msg = self
.module
.add_global_string("head_empty_error", "head: empty list");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Cons case: extract head (field 0)
self.builder().position_at_end(cons_block);
let head_ptr = self.extract_adt_field(list_ptr, 2, 0)?; // arity=2 for (:)
Ok(Some(head_ptr.into()))
}
/// Lower `tail` - extract rest of a list.
/// tail [] = error "empty list"
/// tail (_:xs) = xs
fn lower_builtin_tail(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("tail: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("tail expects a list".to_string())),
};
// Extract tag to check if empty
let tag = self.extract_adt_tag(list_ptr)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let empty_block = self
.llvm_context()
.append_basic_block(current_fn, "tail_empty");
let cons_block = self
.llvm_context()
.append_basic_block(current_fn, "tail_cons");
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, empty_block, cons_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Empty case: call error
self.builder().position_at_end(empty_block);
let error_msg = self
.module
.add_global_string("tail_empty_error", "tail: empty list");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Cons case: extract tail (field 1)
self.builder().position_at_end(cons_block);
let tail_ptr = self.extract_adt_field(list_ptr, 2, 1)?;
Ok(Some(tail_ptr.into()))
}
/// Lower `null` - check if list is empty.
/// null [] = True
/// null (_:_) = False
fn lower_builtin_null(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("null: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("null expects a list".to_string())),
};
// Extract tag: 0 = [], 1 = (:)
let tag = self.extract_adt_tag(list_ptr)?;
let tm = self.type_mapper();
// null = (tag == 0)
let is_null = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_null",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// Extend i1 to i64 for Bool representation
let result = self
.builder()
.build_int_z_extend(is_null, tm.i64_type(), "null_result")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `length` - compute list length.
/// Handles both cons-cell lists (tag 0=nil, 1=cons) and raw C strings
/// (from readFile etc.) by checking the tag value at runtime.
fn lower_builtin_length(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("length: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("length expects a list".to_string())),
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Read the first i64 from the pointer to determine representation.
// Cons-cell lists have tag 0 (nil) or 1 (cons).
// C strings have arbitrary byte content (interpreted as i64, usually > 1).
let tag = self.extract_adt_tag(list_ptr)?;
// Create blocks for dispatch
let is_cons_block = self
.llvm_context()
.append_basic_block(current_fn, "length_cons");
let is_cstring_block = self
.llvm_context()
.append_basic_block(current_fn, "length_cstring");
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "length_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "length_body");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "length_merge");
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Check if tag == 0 (nil list — length 0)
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// For tag==0, jump directly to merge with result 0.
// For tag!=0, check if tag==1 (cons) or >1 (C string).
let not_nil_block = self
.llvm_context()
.append_basic_block(current_fn, "length_not_nil");
self.builder()
.build_conditional_branch(is_nil, merge_block, not_nil_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// not_nil: check tag == 1 (cons) vs > 1 (C string)
self.builder().position_at_end(not_nil_block);
let is_cons = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_cons",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_cons, is_cons_block, is_cstring_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// ---- C string path: call bhc_string_length ----
self.builder().position_at_end(is_cstring_block);
let strlen_fn = self
.functions
.get(&VarId::new(1000180))
.ok_or_else(|| CodegenError::Internal("bhc_string_length not declared".to_string()))?;
let strlen_result = self
.builder()
.build_call(*strlen_fn, &[list_ptr.into()], "strlen_result")
.map_err(|e| CodegenError::Internal(format!("strlen call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("strlen returned void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let cstring_exit_block = self.builder().get_insert_block().unwrap();
// ---- Cons-cell path: iterative loop ----
self.builder().position_at_end(is_cons_block);
let init_count = tm.i64_type().const_int(1, false); // already know first cell is cons
let first_tail = self.extract_adt_field(list_ptr, 2, 1)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "count")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let current_list = list_phi.as_basic_value().into_pointer_value();
let cur_tag = self.extract_adt_tag(current_list)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
cur_tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, merge_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let loop_header_exit = self.builder().get_insert_block().unwrap();
// Loop body
self.builder().position_at_end(loop_body);
let new_count = self
.builder()
.build_int_add(
count_phi.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"new_count",
)
.map_err(|e| CodegenError::Internal(format!("failed to add: {:?}", e)))?;
let tail_ptr = self.extract_adt_field(current_list, 2, 1)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Wire up phi nodes
count_phi.add_incoming(&[(&init_count, is_cons_block), (&new_count, loop_body)]);
list_phi.add_incoming(&[(&first_tail, is_cons_block), (&tail_ptr, loop_body)]);
// ---- Merge block: phi to combine all paths ----
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(tm.i64_type(), "length_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let zero = tm.i64_type().const_zero();
result_phi.add_incoming(&[
(&zero, entry_block), // nil path
(&strlen_result.into_int_value(), cstring_exit_block), // C string path
(
&count_phi.as_basic_value().into_int_value(),
loop_header_exit,
), // cons loop done
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `take` - take first n elements of a list.
/// Implemented iteratively: collect n elements in reverse, then reverse the result.
/// Includes take-fusion for infinite generators: iterate, repeat, cycle.
fn lower_builtin_take(
&mut self,
n_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Take-fusion: detect iterate/repeat/cycle and fuse into bounded loop
if let Some((inner_name, inner_args)) = self.is_saturated_builtin(list_expr) {
match inner_name {
"iterate" => return self.lower_take_iterate(n_expr, inner_args[0], inner_args[1]),
"repeat" => return self.lower_take_repeat(n_expr, inner_args[0]),
"cycle" => return self.lower_take_cycle(n_expr, inner_args[0]),
_ => {}
}
}
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("take: n has no value".to_string()))?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("take: list has no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("take expects a list".to_string())),
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Phase 1: Collect n elements into a reversed accumulator
let collect_header = self
.llvm_context()
.append_basic_block(current_fn, "take_collect_header");
let collect_body = self
.llvm_context()
.append_basic_block(current_fn, "take_collect_body");
let collect_exit = self
.llvm_context()
.append_basic_block(current_fn, "take_collect_exit");
// Phase 2: Reverse the accumulator
let rev_header = self
.llvm_context()
.append_basic_block(current_fn, "take_rev_header");
let rev_body = self
.llvm_context()
.append_basic_block(current_fn, "take_rev_body");
let rev_exit = self
.llvm_context()
.append_basic_block(current_fn, "take_rev_exit");
// Build nil in entry block before branching
let nil = self.build_nil()?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(collect_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Collect loop header
self.builder().position_at_end(collect_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "count")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if count <= 0 (taken enough)
let count = count_phi.as_basic_value().into_int_value();
let count_le_0 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
count,
tm.i64_type().const_zero(),
"count_le_0",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// Check if list is empty
let current_list = list_phi.as_basic_value().into_pointer_value();
let tag = self.extract_adt_tag(current_list)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// Exit if count <= 0 OR list is empty
let should_exit = self
.builder()
.build_or(count_le_0, is_empty, "should_exit")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
self.builder()
.build_conditional_branch(should_exit, collect_exit, collect_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Collect loop body: cons head onto accumulator (in reverse), advance
self.builder().position_at_end(collect_body);
let head_ptr = self.extract_adt_field(current_list, 2, 0)?;
let tail_ptr = self.extract_adt_field(current_list, 2, 1)?;
let new_acc = self.build_cons(head_ptr.into(), acc_phi.as_basic_value())?;
let new_count = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "new_count")
.map_err(|e| CodegenError::Internal(format!("failed to sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(collect_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, collect_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, collect_body)]);
count_phi.add_incoming(&[(&n, entry_block), (&new_count, collect_body)]);
// collect_exit: build nil for reverse phase and branch to rev_header
self.builder().position_at_end(collect_exit);
let nil2 = self.build_nil()?;
// Save the collected accumulator (will be used as rev_list input)
let collected_acc = acc_phi.as_basic_value().into_pointer_value();
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse loop header
self.builder().position_at_end(rev_header);
let rev_acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list_phi = self
.builder()
.build_phi(tm.ptr_type(), "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_current = rev_list_phi.as_basic_value().into_pointer_value();
let rev_tag = self.extract_adt_tag(rev_current)?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse loop body
self.builder().position_at_end(rev_body);
let rev_head = self.extract_adt_field(rev_current, 2, 0)?;
let rev_tail = self.extract_adt_field(rev_current, 2, 1)?;
let new_rev_acc = self.build_cons(rev_head.into(), rev_acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming edges
rev_acc_phi.add_incoming(&[(&nil2, collect_exit), (&new_rev_acc, rev_body)]);
rev_list_phi.add_incoming(&[(&collected_acc, collect_exit), (&rev_tail, rev_body)]);
// Return result
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc_phi.as_basic_value()))
}
/// Lower `take n (iterate f seed)` — fused: loop n times applying f.
fn lower_take_iterate(
&mut self,
n_expr: &Expr,
func_expr: &Expr,
seed_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("take_iterate: n no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("take_iterate: func no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"take_iterate: func must be closure".to_string(),
))
}
};
let seed_val = self
.lower_expr(seed_expr)?
.ok_or_else(|| CodegenError::Internal("take_iterate: seed no value".to_string()))?;
let seed_ptr = self.value_to_ptr(seed_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "ti_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "ti_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "ti_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "ti_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "ti_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "ti_count")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let count = count_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
count,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Body: cons acc onto result, then acc = f(acc)
self.builder().position_at_end(loop_body);
let new_cons = self.build_cons(acc_phi.as_basic_value(), result_phi.as_basic_value())?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), acc_phi.as_basic_value().into()],
"ti_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("take_iterate: f returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
let new_count = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "ti_dec")
.map_err(|e| CodegenError::Internal(format!("sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&seed_ptr, entry_block), (&new_acc_ptr, loop_body)]);
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
count_phi.add_incoming(&[(&n, entry_block), (&new_count, loop_body)]);
self.builder().position_at_end(loop_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `take n (repeat val)` — fused: cons val n times.
fn lower_take_repeat(
&mut self,
n_expr: &Expr,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("take_repeat: n no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("take_repeat: val no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "tr_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "tr_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "tr_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "tr_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "tr_count")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let count = count_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
count,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let new_cons = self.build_cons(val_ptr.into(), result_phi.as_basic_value())?;
let new_count = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "tr_dec")
.map_err(|e| CodegenError::Internal(format!("sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
count_phi.add_incoming(&[(&n, entry_block), (&new_count, loop_body)]);
// No need to reverse — all elements are the same value, order doesn't matter.
// But we build in reverse, so reverse for consistency.
self.builder().position_at_end(loop_exit);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `take n (cycle xs)` — fused: cycle through source list n times.
/// Uses select to reset source pointer when exhausted.
fn lower_take_cycle(
&mut self,
n_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("take_cycle: n no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("take_cycle: list no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"take_cycle expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let cyc_header = self.llvm_ctx.append_basic_block(current_fn, "cyc_header");
let cyc_body = self.llvm_ctx.append_basic_block(current_fn, "cyc_body");
let cyc_exit = self.llvm_ctx.append_basic_block(current_fn, "cyc_exit");
self.builder()
.build_unconditional_branch(cyc_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(cyc_header);
let c_result = self
.builder()
.build_phi(ptr_type, "cyc_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let c_count = self
.builder()
.build_phi(tm.i64_type(), "cyc_count")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let c_src = self
.builder()
.build_phi(ptr_type, "cyc_src")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let cnt = c_count.as_basic_value().into_int_value();
let cnt_done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
cnt,
tm.i64_type().const_zero(),
"cyc_done",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(cnt_done, cyc_exit, cyc_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// cyc_body: if src is exhausted, reset to list_ptr via select; then consume one element
self.builder().position_at_end(cyc_body);
let s_tag = self.extract_adt_tag(c_src.as_basic_value().into_pointer_value())?;
let s_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
s_tag,
tm.i64_type().const_zero(),
"s_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
// Use select: if src empty, use list_ptr (reset); else use current src
let list_as_basic: BasicValueEnum = list_ptr.into();
let src_as_basic: BasicValueEnum = c_src.as_basic_value();
let actual_src = self
.builder()
.build_select(s_empty, list_as_basic, src_as_basic, "actual_src")
.map_err(|e| CodegenError::Internal(format!("select: {:?}", e)))?
.into_pointer_value();
// Extract head and tail (actual_src is guaranteed non-empty since original list is non-empty)
let cyc_head = self.extract_adt_field(actual_src, 2, 0)?;
let cyc_tail = self.extract_adt_field(actual_src, 2, 1)?;
let cyc_cons = self.build_cons(cyc_head.into(), c_result.as_basic_value())?;
let cyc_new_count = self
.builder()
.build_int_sub(cnt, tm.i64_type().const_int(1, false), "cyc_dec")
.map_err(|e| CodegenError::Internal(format!("sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(cyc_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
c_result.add_incoming(&[(&nil, entry_block), (&cyc_cons, cyc_body)]);
c_count.add_incoming(&[(&n, entry_block), (&cyc_new_count, cyc_body)]);
c_src.add_incoming(&[(&list_ptr, entry_block), (&cyc_tail, cyc_body)]);
// Reverse the result
self.builder().position_at_end(cyc_exit);
let reversed =
self.build_inline_reverse(c_result.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `drop` - drop first n elements of a list.
/// drop n xs | n <= 0 = xs
/// drop _ [] = []
/// drop n (_:xs) = drop (n-1) xs
fn lower_builtin_drop(
&mut self,
n_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("drop: n has no value".to_string()))?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("drop: list has no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("drop expects a list".to_string())),
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Create loop blocks for iterative drop
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "drop_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "drop_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "drop_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Loop header: phi for remaining count and current list
self.builder().position_at_end(loop_header);
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "count")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if count <= 0 (done dropping)
let count_le_0 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
count_phi.as_basic_value().into_int_value(),
tm.i64_type().const_zero(),
"count_le_0",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// Check if list is empty
let current_list = list_phi.as_basic_value().into_pointer_value();
let tag = self.extract_adt_tag(current_list)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
// Exit if count <= 0 OR list is empty
let should_exit = self
.builder()
.build_or(count_le_0, is_empty, "should_exit")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
self.builder()
.build_conditional_branch(should_exit, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: decrement count, advance to tail
self.builder().position_at_end(loop_body);
let new_count = self
.builder()
.build_int_sub(
count_phi.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"new_count",
)
.map_err(|e| CodegenError::Internal(format!("failed to sub: {:?}", e)))?;
let tail_ptr = self.extract_adt_field(current_list, 2, 1)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming edges
count_phi.add_incoming(&[(&n, entry_block), (&new_count, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Loop exit: return current list
self.builder().position_at_end(loop_exit);
Ok(Some(list_phi.as_basic_value()))
}
/// Lower `reverse` - reverse a list.
fn lower_builtin_reverse(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("reverse: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"reverse expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Create loop blocks for iterative reverse
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "rev_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "rev_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "rev_exit");
// Start with empty accumulator
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty
let current_list = list_phi.as_basic_value().into_pointer_value();
let tag = self.extract_adt_tag(current_list)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: cons head onto accumulator, advance to tail
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(current_list, 2, 0)?;
let tail_ptr = self.extract_adt_field(current_list, 2, 1)?;
let new_acc = self.build_cons(head_ptr.into(), acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming edges
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Loop exit: return accumulator
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `append` / `++` - concatenate two lists.
fn lower_builtin_append(
&mut self,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("append: list1 has no value".to_string()))?;
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("append: list2 has no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("append expects a list".to_string())),
};
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("append expects a list".to_string())),
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// First reverse list1, then fold onto list2
// This is: append xs ys = foldl (flip (:)) ys (reverse xs)
// Or equivalently: go [] xs where go acc [] = acc ++ ys; go acc (x:xs) = go (x:acc) xs
// Step 1: Reverse list1
let rev_header = self
.llvm_context()
.append_basic_block(current_fn, "app_rev_header");
let rev_body = self
.llvm_context()
.append_basic_block(current_fn, "app_rev_body");
let rev_exit = self
.llvm_context()
.append_basic_block(current_fn, "app_rev_exit");
let fold_header = self
.llvm_context()
.append_basic_block(current_fn, "app_fold_header");
let fold_body = self
.llvm_context()
.append_basic_block(current_fn, "app_fold_body");
let fold_exit = self
.llvm_context()
.append_basic_block(current_fn, "app_fold_exit");
// Start with empty accumulator for reverse
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Reverse loop header
self.builder().position_at_end(rev_header);
let rev_acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list_phi = self
.builder()
.build_phi(tm.ptr_type(), "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_current = rev_list_phi.as_basic_value().into_pointer_value();
let rev_tag = self.extract_adt_tag(rev_current)?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse loop body
self.builder().position_at_end(rev_body);
let rev_head = self.extract_adt_field(rev_current, 2, 0)?;
let rev_tail = self.extract_adt_field(rev_current, 2, 1)?;
let rev_new_acc = self.build_cons(rev_head.into(), rev_acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc_phi.add_incoming(&[(&nil, entry_block), (&rev_new_acc, rev_body)]);
rev_list_phi.add_incoming(&[(&list1_ptr, entry_block), (&rev_tail, rev_body)]);
// After reversing, fold reversed list onto list2
self.builder().position_at_end(rev_exit);
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Fold loop header
self.builder().position_at_end(fold_header);
let fold_acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "fold_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let fold_list_phi = self
.builder()
.build_phi(tm.ptr_type(), "fold_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let fold_current = fold_list_phi.as_basic_value().into_pointer_value();
let fold_tag = self.extract_adt_tag(fold_current)?;
let fold_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
fold_tag,
tm.i64_type().const_zero(),
"fold_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(fold_is_empty, fold_exit, fold_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Fold loop body: cons head of reversed list onto accumulator
self.builder().position_at_end(fold_body);
let fold_head = self.extract_adt_field(fold_current, 2, 0)?;
let fold_tail = self.extract_adt_field(fold_current, 2, 1)?;
let fold_new_acc = self.build_cons(fold_head.into(), fold_acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
fold_acc_phi.add_incoming(&[(&list2_ptr, rev_exit), (&fold_new_acc, fold_body)]);
fold_list_phi.add_incoming(&[
(&rev_acc_phi.as_basic_value().into_pointer_value(), rev_exit),
(&fold_tail, fold_body),
]);
// Return result
self.builder().position_at_end(fold_exit);
Ok(Some(fold_acc_phi.as_basic_value()))
}
/// Lower `enumFromTo` - generate a list [from..to].
fn lower_builtin_enum_from_to(
&mut self,
from_expr: &Expr,
to_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if arguments are user enum constructors
if let Some(type_name) = self.infer_adt_type_from_expr(from_expr) {
if self.derived_from_enum_fns.contains_key(&type_name)
&& self.derived_to_enum_fns.contains_key(&type_name)
{
return self.lower_enum_from_to_user_enum(from_expr, to_expr, &type_name);
}
}
let from_val = self
.lower_expr(from_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromTo: from has no value".to_string()))?;
let to_val = self
.lower_expr(to_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromTo: to has no value".to_string()))?;
let from = self.to_int_value(from_val)?;
let to = self.to_int_value(to_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Build list backwards from to down to from, then we have [from..to]
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "enum_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "enum_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "enum_exit");
// Start with empty list, current = to
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let current_phi = self
.builder()
.build_phi(tm.i64_type(), "current")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if current < from (done)
let current = current_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, current, from, "done")
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: cons current onto accumulator, decrement current
self.builder().position_at_end(loop_body);
let boxed_current = self.box_int(current)?;
let new_acc = self.build_cons(boxed_current.into(), acc_phi.as_basic_value())?;
let prev_current = self
.builder()
.build_int_sub(current, tm.i64_type().const_int(1, false), "prev")
.map_err(|e| CodegenError::Internal(format!("failed to sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
current_phi.add_incoming(&[(&to, entry_block), (&prev_current, loop_body)]);
// Return result
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `enumFromTo` for user-defined enum types.
/// Converts from/to to Int via fromEnum, iterates, converts each back via toEnum.
fn lower_enum_from_to_user_enum(
&mut self,
from_expr: &Expr,
to_expr: &Expr,
type_name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let from_val = self
.lower_expr(from_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromTo: from has no value".to_string()))?;
let to_val = self
.lower_expr(to_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromTo: to has no value".to_string()))?;
// Convert from/to to Int tags via fromEnum
let from_tag = self.call_derived_from_enum(type_name, from_val)?;
let to_tag = self.call_derived_from_enum(type_name, to_val)?;
let ptr_ty = self.type_mapper().ptr_type();
let i64_ty = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Build list backwards from to_tag down to from_tag
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "uenum_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "uenum_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "uenum_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("uenum: branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_ty, "acc")
.map_err(|e| CodegenError::Internal(format!("uenum: phi: {:?}", e)))?;
let current_phi = self
.builder()
.build_phi(i64_ty, "current")
.map_err(|e| CodegenError::Internal(format!("uenum: phi: {:?}", e)))?;
// Check if current < from_tag (done)
let current = current_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, current, from_tag, "done")
.map_err(|e| CodegenError::Internal(format!("uenum: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("uenum: branch: {:?}", e)))?;
// Loop body: convert current tag to enum constructor, cons onto accumulator
self.builder().position_at_end(loop_body);
let enum_val = self.call_derived_to_enum(type_name, current)?;
let new_acc = self.build_cons(enum_val, acc_phi.as_basic_value())?;
let prev = self
.builder()
.build_int_sub(current, i64_ty.const_int(1, false), "prev")
.map_err(|e| CodegenError::Internal(format!("uenum: sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("uenum: branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
current_phi.add_incoming(&[(&to_tag, entry_block), (&prev, loop_body)]);
// Return result
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `replicate` - create a list with n copies of an element.
fn lower_builtin_replicate(
&mut self,
n_expr: &Expr,
elem_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("replicate: n has no value".to_string()))?;
let elem_val = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("replicate: elem has no value".to_string()))?;
let n = self.to_int_value(n_val)?;
let elem_ptr = self.value_to_ptr(elem_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Build list by consing n times
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "rep_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "rep_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "rep_exit");
// Start with empty list
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let count_phi = self
.builder()
.build_phi(tm.i64_type(), "count")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if count <= 0
let count = count_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
count,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: cons elem onto accumulator, decrement count
self.builder().position_at_end(loop_body);
let new_acc = self.build_cons(elem_ptr.into(), acc_phi.as_basic_value())?;
let new_count = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "new_count")
.map_err(|e| CodegenError::Internal(format!("failed to sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
count_phi.add_incoming(&[(&n, entry_block), (&new_count, loop_body)]);
// Return result
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Build an empty list (Nil / []).
fn build_nil(&self) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
// Nil is represented as ADT with tag 0 and no fields
// Allocate space: tag (i64) only
let size = tm.i64_type().const_int(8, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let alloc_call = self
.builder()
.build_call(*alloc_fn, &[size.into()], "nil_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call alloc: {:?}", e)))?;
let nil_ptr = alloc_call
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("alloc returned void".to_string()))?
.into_pointer_value();
// Store tag = 0
self.builder()
.build_store(nil_ptr, tm.i64_type().const_zero())
.map_err(|e| CodegenError::Internal(format!("failed to store tag: {:?}", e)))?;
Ok(nil_ptr)
}
/// Build a cons cell (x : xs).
fn build_cons(
&self,
head: BasicValueEnum<'ctx>,
tail: BasicValueEnum<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
// Cons is represented as ADT with tag 1 and 2 fields (head, tail)
// Allocate space: tag (i64) + head (ptr) + tail (ptr) = 24 bytes
let size = tm.i64_type().const_int(24, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let alloc_call = self
.builder()
.build_call(*alloc_fn, &[size.into()], "cons_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call alloc: {:?}", e)))?;
let cons_ptr = alloc_call
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("alloc returned void".to_string()))?
.into_pointer_value();
// Store tag = 1
self.builder()
.build_store(cons_ptr, tm.i64_type().const_int(1, false))
.map_err(|e| CodegenError::Internal(format!("failed to store tag: {:?}", e)))?;
// Store head at offset 8
let head_ptr = unsafe {
self.builder()
.build_gep(
tm.i64_type(),
cons_ptr,
&[tm.i64_type().const_int(1, false)],
"head_ptr",
)
.map_err(|e| CodegenError::Internal(format!("failed to build gep: {:?}", e)))?
};
let head_as_ptr = self.value_to_ptr(head)?;
self.builder()
.build_store(head_ptr, head_as_ptr)
.map_err(|e| CodegenError::Internal(format!("failed to store head: {:?}", e)))?;
// Store tail at offset 16
let tail_slot = unsafe {
self.builder()
.build_gep(
tm.i64_type(),
cons_ptr,
&[tm.i64_type().const_int(2, false)],
"tail_ptr",
)
.map_err(|e| CodegenError::Internal(format!("failed to build gep: {:?}", e)))?
};
let tail_as_ptr = self.value_to_ptr(tail)?;
self.builder()
.build_store(tail_slot, tail_as_ptr)
.map_err(|e| CodegenError::Internal(format!("failed to store tail: {:?}", e)))?;
Ok(cons_ptr)
}
/// Convert a C-string (null-terminated i8 pointer) into a [Char] linked list.
/// Emits LLVM IR that loops over the C-string bytes and builds cons cells.
fn cstring_to_char_list(
&self,
cstr_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("no current function for cstring_to_char_list".to_string())
})?;
// Guard: if the C-string pointer is null (e.g. exception sentinel from bhc_throw),
// return nil (empty list) so execution can continue back to bhc_catch.
let null_path_block = self
.llvm_context()
.append_basic_block(current_fn, "c2l_null_path");
let convert_block = self
.llvm_context()
.append_basic_block(current_fn, "c2l_convert");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "c2l_merge");
let is_null_ptr = self
.builder()
.build_is_null(cstr_ptr, "c2l_ptr_null")
.map_err(|e| CodegenError::Internal(format!("c2l is_null: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null_ptr, null_path_block, convert_block)
.map_err(|e| CodegenError::Internal(format!("c2l null branch: {:?}", e)))?;
// Null path: build nil and jump to merge
self.builder().position_at_end(null_path_block);
let null_nil = self.build_nil()?;
let null_nil_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("c2l null nil branch: {:?}", e)))?;
// Non-null path: do the conversion
self.builder().position_at_end(convert_block);
// Strategy: compute strlen, then iterate backwards building the list.
// --- Compute strlen ---
let strlen_header = self
.llvm_context()
.append_basic_block(current_fn, "c2l_strlen_hdr");
let strlen_body = self
.llvm_context()
.append_basic_block(current_fn, "c2l_strlen_body");
let strlen_done = self
.llvm_context()
.append_basic_block(current_fn, "c2l_strlen_done");
// Capture the block we're in BEFORE branching
let pre_strlen_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(strlen_header)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
self.builder().position_at_end(strlen_header);
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "c2l_idx")
.map_err(|e| CodegenError::Internal(format!("c2l phi: {:?}", e)))?;
idx_phi.add_incoming(&[(&tm.i64_type().const_zero(), pre_strlen_block)]);
let byte_ptr = unsafe {
self.builder()
.build_gep(
tm.i8_type(),
cstr_ptr,
&[idx_phi.as_basic_value().into_int_value()],
"c2l_byte_ptr",
)
.map_err(|e| CodegenError::Internal(format!("c2l gep: {:?}", e)))?
};
let byte_val = self
.builder()
.build_load(tm.i8_type(), byte_ptr, "c2l_byte")
.map_err(|e| CodegenError::Internal(format!("c2l load: {:?}", e)))?
.into_int_value();
let is_null = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
byte_val,
tm.i8_type().const_zero(),
"c2l_is_null",
)
.map_err(|e| CodegenError::Internal(format!("c2l cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, strlen_done, strlen_body)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
self.builder().position_at_end(strlen_body);
let next_idx = self
.builder()
.build_int_add(
idx_phi.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"c2l_next",
)
.map_err(|e| CodegenError::Internal(format!("c2l add: {:?}", e)))?;
idx_phi.add_incoming(&[(&next_idx, strlen_body)]);
self.builder()
.build_unconditional_branch(strlen_header)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
// strlen_done: idx_phi contains the length
self.builder().position_at_end(strlen_done);
let len = idx_phi.as_basic_value().into_int_value();
// --- Build list backwards from len-1 to 0 ---
let build_header = self
.llvm_context()
.append_basic_block(current_fn, "c2l_build_hdr");
let build_body = self
.llvm_context()
.append_basic_block(current_fn, "c2l_build_body");
let build_done = self
.llvm_context()
.append_basic_block(current_fn, "c2l_build_done");
let nil = self.build_nil()?;
// Capture block before branching (may differ from strlen_done due to build_nil alloc)
let pre_build_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(build_header)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
self.builder().position_at_end(build_header);
let build_idx = self
.builder()
.build_phi(tm.i64_type(), "c2l_bidx")
.map_err(|e| CodegenError::Internal(format!("c2l phi: {:?}", e)))?;
let build_list = self
.builder()
.build_phi(tm.ptr_type(), "c2l_list")
.map_err(|e| CodegenError::Internal(format!("c2l phi: {:?}", e)))?;
build_idx.add_incoming(&[(&len, pre_build_block)]);
build_list.add_incoming(&[(&nil, pre_build_block)]);
let is_done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
build_idx.as_basic_value().into_int_value(),
tm.i64_type().const_zero(),
"c2l_done",
)
.map_err(|e| CodegenError::Internal(format!("c2l cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_done, build_done, build_body)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
self.builder().position_at_end(build_body);
let prev_idx = self
.builder()
.build_int_sub(
build_idx.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"c2l_prev",
)
.map_err(|e| CodegenError::Internal(format!("c2l sub: {:?}", e)))?;
let char_ptr = unsafe {
self.builder()
.build_gep(tm.i8_type(), cstr_ptr, &[prev_idx], "c2l_char_ptr")
.map_err(|e| CodegenError::Internal(format!("c2l gep: {:?}", e)))?
};
let char_byte = self
.builder()
.build_load(tm.i8_type(), char_ptr, "c2l_char")
.map_err(|e| CodegenError::Internal(format!("c2l load: {:?}", e)))?
.into_int_value();
let char_i64 = self
.builder()
.build_int_z_extend(char_byte, tm.i64_type(), "c2l_char_ext")
.map_err(|e| CodegenError::Internal(format!("c2l ext: {:?}", e)))?;
let char_as_ptr = self.int_to_ptr(char_i64)?;
let new_cons = self.build_cons(char_as_ptr.into(), build_list.as_basic_value())?;
// Capture the current block AFTER build_cons (which creates new blocks)
let build_body_end = self.builder().get_insert_block().unwrap();
build_idx.add_incoming(&[(&prev_idx, build_body_end)]);
build_list.add_incoming(&[(&new_cons, build_body_end)]);
self.builder()
.build_unconditional_branch(build_header)
.map_err(|e| CodegenError::Internal(format!("c2l branch: {:?}", e)))?;
self.builder().position_at_end(build_done);
let converted_list = build_list.as_basic_value().into_pointer_value();
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("c2l done branch: {:?}", e)))?;
// Merge: PHI between null path (nil) and convert path (converted list)
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(tm.ptr_type(), "c2l_result")
.map_err(|e| CodegenError::Internal(format!("c2l merge phi: {:?}", e)))?;
result_phi.add_incoming(&[(&null_nil, null_nil_block), (&converted_list, build_done)]);
Ok(result_phi.as_basic_value().into_pointer_value())
}
/// Convert a [Char] linked list into a C-string (null-terminated i8 pointer).
/// Emits LLVM IR that first counts the list length, allocates a buffer, then fills it.
fn char_list_to_cstring(
&self,
list_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("no current function for char_list_to_cstring".to_string())
})?;
// --- Count list length ---
let len_header = self
.llvm_context()
.append_basic_block(current_fn, "l2c_len_hdr");
let len_body = self
.llvm_context()
.append_basic_block(current_fn, "l2c_len_body");
let len_done = self
.llvm_context()
.append_basic_block(current_fn, "l2c_len_done");
let pre_len_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(len_header)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
self.builder().position_at_end(len_header);
let len_node = self
.builder()
.build_phi(tm.ptr_type(), "l2c_node")
.map_err(|e| CodegenError::Internal(format!("l2c phi: {:?}", e)))?;
let len_count = self
.builder()
.build_phi(tm.i64_type(), "l2c_count")
.map_err(|e| CodegenError::Internal(format!("l2c phi: {:?}", e)))?;
len_node.add_incoming(&[(&list_ptr, pre_len_block)]);
len_count.add_incoming(&[(&tm.i64_type().const_zero(), pre_len_block)]);
let tag = self.extract_adt_tag(len_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"l2c_nil",
)
.map_err(|e| CodegenError::Internal(format!("l2c cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, len_done, len_body)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
self.builder().position_at_end(len_body);
let next_count = self
.builder()
.build_int_add(
len_count.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"l2c_inc",
)
.map_err(|e| CodegenError::Internal(format!("l2c add: {:?}", e)))?;
let tail = self.extract_adt_field(len_node.as_basic_value().into_pointer_value(), 2, 1)?;
len_node.add_incoming(&[(&tail, len_body)]);
len_count.add_incoming(&[(&next_count, len_body)]);
self.builder()
.build_unconditional_branch(len_header)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
// --- Allocate buffer (len + 1 for null terminator) ---
self.builder().position_at_end(len_done);
let total_len = len_count.as_basic_value().into_int_value();
let buf_size = self
.builder()
.build_int_add(total_len, tm.i64_type().const_int(1, false), "l2c_bufsize")
.map_err(|e| CodegenError::Internal(format!("l2c add: {:?}", e)))?;
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let buf = self
.builder()
.build_call(*alloc_fn, &[buf_size.into()], "l2c_buf")
.map_err(|e| CodegenError::Internal(format!("l2c alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("l2c alloc void".to_string()))?
.into_pointer_value();
// --- Fill buffer ---
let fill_header = self
.llvm_context()
.append_basic_block(current_fn, "l2c_fill_hdr");
let fill_body = self
.llvm_context()
.append_basic_block(current_fn, "l2c_fill_body");
let fill_done = self
.llvm_context()
.append_basic_block(current_fn, "l2c_fill_done");
let pre_fill_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(fill_header)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
self.builder().position_at_end(fill_header);
let fill_node = self
.builder()
.build_phi(tm.ptr_type(), "l2c_fnode")
.map_err(|e| CodegenError::Internal(format!("l2c phi: {:?}", e)))?;
let fill_idx = self
.builder()
.build_phi(tm.i64_type(), "l2c_fidx")
.map_err(|e| CodegenError::Internal(format!("l2c phi: {:?}", e)))?;
fill_node.add_incoming(&[(&list_ptr, pre_fill_block)]);
fill_idx.add_incoming(&[(&tm.i64_type().const_zero(), pre_fill_block)]);
let ftag = self.extract_adt_tag(fill_node.as_basic_value().into_pointer_value())?;
let fis_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
ftag,
tm.i64_type().const_zero(),
"l2c_fnil",
)
.map_err(|e| CodegenError::Internal(format!("l2c cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(fis_nil, fill_done, fill_body)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
self.builder().position_at_end(fill_body);
let head = self.extract_adt_field(fill_node.as_basic_value().into_pointer_value(), 2, 0)?;
let char_val = self
.builder()
.build_ptr_to_int(head, tm.i64_type(), "l2c_char")
.map_err(|e| CodegenError::Internal(format!("l2c ptr2int: {:?}", e)))?;
let char_i8 = self
.builder()
.build_int_truncate(char_val, tm.i8_type(), "l2c_i8")
.map_err(|e| CodegenError::Internal(format!("l2c trunc: {:?}", e)))?;
let dst = unsafe {
self.builder()
.build_gep(
tm.i8_type(),
buf,
&[fill_idx.as_basic_value().into_int_value()],
"l2c_dst",
)
.map_err(|e| CodegenError::Internal(format!("l2c gep: {:?}", e)))?
};
self.builder()
.build_store(dst, char_i8)
.map_err(|e| CodegenError::Internal(format!("l2c store: {:?}", e)))?;
let next_fidx = self
.builder()
.build_int_add(
fill_idx.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"l2c_fnext",
)
.map_err(|e| CodegenError::Internal(format!("l2c add: {:?}", e)))?;
let ftail =
self.extract_adt_field(fill_node.as_basic_value().into_pointer_value(), 2, 1)?;
fill_node.add_incoming(&[(&ftail, fill_body)]);
fill_idx.add_incoming(&[(&next_fidx, fill_body)]);
self.builder()
.build_unconditional_branch(fill_header)
.map_err(|e| CodegenError::Internal(format!("l2c branch: {:?}", e)))?;
// --- Null-terminate ---
self.builder().position_at_end(fill_done);
let null_dst = unsafe {
self.builder()
.build_gep(
tm.i8_type(),
buf,
&[fill_idx.as_basic_value().into_int_value()],
"l2c_null",
)
.map_err(|e| CodegenError::Internal(format!("l2c gep: {:?}", e)))?
};
self.builder()
.build_store(null_dst, tm.i8_type().const_zero())
.map_err(|e| CodegenError::Internal(format!("l2c store: {:?}", e)))?;
Ok(buf)
}
/// Convert a cons-cell list of C-strings into a cons-cell list of [Char] lists.
/// Used for converting the result of RTS functions like bhc_string_lines/words.
fn cstring_list_to_char_list_list(
&self,
list_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let walk_header = self
.llvm_context()
.append_basic_block(current_fn, "csll_walk_hdr");
let walk_body = self
.llvm_context()
.append_basic_block(current_fn, "csll_walk_body");
let walk_done = self
.llvm_context()
.append_basic_block(current_fn, "csll_walk_done");
let init_nil = self.build_nil()?;
let pre_walk_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(walk_header)
.map_err(|e| CodegenError::Internal(format!("csll branch: {:?}", e)))?;
self.builder().position_at_end(walk_header);
let w_node = self
.builder()
.build_phi(tm.ptr_type(), "csll_node")
.map_err(|e| CodegenError::Internal(format!("csll phi: {:?}", e)))?;
let w_acc = self
.builder()
.build_phi(tm.ptr_type(), "csll_acc")
.map_err(|e| CodegenError::Internal(format!("csll phi: {:?}", e)))?;
w_node.add_incoming(&[(&list_ptr, pre_walk_block)]);
w_acc.add_incoming(&[(&init_nil, pre_walk_block)]);
let tag = self.extract_adt_tag(w_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"csll_nil",
)
.map_err(|e| CodegenError::Internal(format!("csll cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, walk_done, walk_body)
.map_err(|e| CodegenError::Internal(format!("csll branch: {:?}", e)))?;
self.builder().position_at_end(walk_body);
let cstr_head =
self.extract_adt_field(w_node.as_basic_value().into_pointer_value(), 2, 0)?;
let char_list = self.cstring_to_char_list(cstr_head)?;
let new_acc = self.build_cons(char_list.into(), w_acc.as_basic_value())?;
let w_tail = self.extract_adt_field(w_node.as_basic_value().into_pointer_value(), 2, 1)?;
let walk_body_end = self.builder().get_insert_block().unwrap();
w_node.add_incoming(&[(&w_tail, walk_body_end)]);
w_acc.add_incoming(&[(&new_acc, walk_body_end)]);
self.builder()
.build_unconditional_branch(walk_header)
.map_err(|e| CodegenError::Internal(format!("csll branch: {:?}", e)))?;
self.builder().position_at_end(walk_done);
let reversed = w_acc.as_basic_value().into_pointer_value();
self.reverse_list(reversed)
}
/// Reverse a cons-cell linked list (builds a new reversed list).
fn reverse_list(
&self,
list_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("no current function for reverse_list".to_string())
})?;
let rev_header = self
.llvm_context()
.append_basic_block(current_fn, "rev_hdr");
let rev_body = self
.llvm_context()
.append_basic_block(current_fn, "rev_body");
let rev_done = self
.llvm_context()
.append_basic_block(current_fn, "rev_done");
let nil = self.build_nil()?;
let pre_rev_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("rev branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let r_node = self
.builder()
.build_phi(tm.ptr_type(), "rev_node")
.map_err(|e| CodegenError::Internal(format!("rev phi: {:?}", e)))?;
let r_acc = self
.builder()
.build_phi(tm.ptr_type(), "rev_acc")
.map_err(|e| CodegenError::Internal(format!("rev phi: {:?}", e)))?;
r_node.add_incoming(&[(&list_ptr, pre_rev_block)]);
r_acc.add_incoming(&[(&nil, pre_rev_block)]);
let tag = self.extract_adt_tag(r_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"rev_nil",
)
.map_err(|e| CodegenError::Internal(format!("rev cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, rev_done, rev_body)
.map_err(|e| CodegenError::Internal(format!("rev branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let head = self.extract_adt_field(r_node.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(r_node.as_basic_value().into_pointer_value(), 2, 1)?;
let new_cons = self.build_cons(head.into(), r_acc.as_basic_value())?;
let rev_body_end = self.builder().get_insert_block().unwrap();
r_node.add_incoming(&[(&tail, rev_body_end)]);
r_acc.add_incoming(&[(&new_cons, rev_body_end)]);
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("rev branch: {:?}", e)))?;
self.builder().position_at_end(rev_done);
Ok(r_acc.as_basic_value().into_pointer_value())
}
/// Convert a [Char] list of [Char] lists to a cons-cell list of C-strings.
/// Used for passing list-of-strings to RTS functions like bhc_string_unlines/unwords.
fn char_list_list_to_cstring_list(
&self,
list_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let walk_header = self
.llvm_context()
.append_basic_block(current_fn, "lccs_walk_hdr");
let walk_body = self
.llvm_context()
.append_basic_block(current_fn, "lccs_walk_body");
let walk_done = self
.llvm_context()
.append_basic_block(current_fn, "lccs_walk_done");
let init_nil = self.build_nil()?;
let pre_walk_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(walk_header)
.map_err(|e| CodegenError::Internal(format!("lccs branch: {:?}", e)))?;
self.builder().position_at_end(walk_header);
let w_node = self
.builder()
.build_phi(tm.ptr_type(), "lccs_node")
.map_err(|e| CodegenError::Internal(format!("lccs phi: {:?}", e)))?;
let w_acc = self
.builder()
.build_phi(tm.ptr_type(), "lccs_acc")
.map_err(|e| CodegenError::Internal(format!("lccs phi: {:?}", e)))?;
w_node.add_incoming(&[(&list_ptr, pre_walk_block)]);
w_acc.add_incoming(&[(&init_nil, pre_walk_block)]);
let tag = self.extract_adt_tag(w_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"lccs_nil",
)
.map_err(|e| CodegenError::Internal(format!("lccs cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, walk_done, walk_body)
.map_err(|e| CodegenError::Internal(format!("lccs branch: {:?}", e)))?;
self.builder().position_at_end(walk_body);
let char_list_head =
self.extract_adt_field(w_node.as_basic_value().into_pointer_value(), 2, 0)?;
let cstr = self.char_list_to_cstring(char_list_head)?;
let new_acc = self.build_cons(cstr.into(), w_acc.as_basic_value())?;
let w_tail = self.extract_adt_field(w_node.as_basic_value().into_pointer_value(), 2, 1)?;
let walk_body_end = self.builder().get_insert_block().unwrap();
w_node.add_incoming(&[(&w_tail, walk_body_end)]);
w_acc.add_incoming(&[(&new_acc, walk_body_end)]);
self.builder()
.build_unconditional_branch(walk_header)
.map_err(|e| CodegenError::Internal(format!("lccs branch: {:?}", e)))?;
self.builder().position_at_end(walk_done);
let reversed = w_acc.as_basic_value().into_pointer_value();
self.reverse_list(reversed)
}
/// Try to extract a map application from an expression.
/// Returns Some((map_fn, inner_list)) if expr is `map f xs`, None otherwise.
///
/// This is used for fusion opportunities like `sum (map f xs)`.
#[allow(unused_variables)]
fn try_extract_map_app(_expr: &Expr) -> Option<(&Expr, &Expr)> {
// TODO: Implement pattern matching for map applications
// For now, return None to skip fusion and use the non-fused path
None
}
/// Lower a fused sum(map f xs) into a single loop.
/// This avoids allocating an intermediate list.
#[allow(unused_variables)]
fn lower_fused_sum_map(
&mut self,
_map_fn: &Expr,
_inner_list: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// TODO: Implement fused sum/map lowering
// This should generate a single loop that applies map_fn to each element
// and accumulates the sum, without creating an intermediate list.
Err(CodegenError::Internal(
"fused sum/map not yet implemented".to_string(),
))
}
/// Lower `sum` - sum all elements of a list.
/// sum [] = 0
/// sum (x:xs) = x + sum xs
fn lower_builtin_sum(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check for fusion opportunity: sum (map f xs)
// This fuses into a single loop that applies f to each element and sums
if let Some((map_fn, inner_list)) = Self::try_extract_map_app(list_expr) {
return self.lower_fused_sum_map(map_fn, inner_list);
}
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("sum: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("sum expects a list".to_string())),
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "sum_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "sum_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "sum_exit");
// Jump to loop header
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header: phi for accumulator and current list pointer
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.i64_type(), "sum_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "sum_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty (tag == 0)
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head, add to accumulator, continue with tail
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let head_val = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "head_val")
.map_err(|e| CodegenError::Internal(format!("failed to ptr_to_int: {:?}", e)))?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_acc = self
.builder()
.build_int_add(
acc_phi.as_basic_value().into_int_value(),
head_val,
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("failed to add: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add incoming values to phi nodes
acc_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_acc, loop_body),
]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: return accumulator (boxed as pointer)
self.builder().position_at_end(loop_exit);
let result = self
.builder()
.build_int_to_ptr(
acc_phi.as_basic_value().into_int_value(),
tm.ptr_type(),
"result",
)
.map_err(|e| CodegenError::Internal(format!("failed to int_to_ptr: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `product` - multiply all elements of a list.
/// product [] = 1
/// product (x:xs) = x * product xs
fn lower_builtin_product(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("product: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"product expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "prod_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "prod_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "prod_exit");
// Jump to loop header
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header: phi for accumulator and current list pointer
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.i64_type(), "prod_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(tm.ptr_type(), "prod_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty (tag == 0)
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head, multiply with accumulator, continue with tail
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let head_val = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "head_val")
.map_err(|e| CodegenError::Internal(format!("failed to ptr_to_int: {:?}", e)))?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_acc = self
.builder()
.build_int_mul(
acc_phi.as_basic_value().into_int_value(),
head_val,
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("failed to mul: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add incoming values to phi nodes (start with 1 for product)
acc_phi.add_incoming(&[
(&tm.i64_type().const_int(1, false), entry_block),
(&new_acc, loop_body),
]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: return accumulator (boxed as pointer)
self.builder().position_at_end(loop_exit);
let result = self
.builder()
.build_int_to_ptr(
acc_phi.as_basic_value().into_int_value(),
tm.ptr_type(),
"result",
)
.map_err(|e| CodegenError::Internal(format!("failed to int_to_ptr: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `map` - apply a function to each element of a list.
/// map f [] = []
/// map f (x:xs) = f x : map f xs
fn lower_builtin_map(
&mut self,
fn_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the function (should be a closure)
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map: function has no value".to_string()))?;
let fn_ptr = match fn_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"map: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("map: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("map expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "map_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "map_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "map_exit");
// Build nil in entry block before branching
let nil = self.build_nil()?;
// Build result list in reverse (we'll reverse at the end)
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "map_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "map_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: apply function to head, cons result onto accumulator
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call the function closure: fn_ptr(fn_ptr, head)
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let mapped_val = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[fn_ptr.into(), head_ptr.into()],
"mapped",
)
.map_err(|e| CodegenError::Internal(format!("failed to call map fn: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map function returned void".to_string()))?;
// Build new cons cell
let new_cons = self.build_cons(mapped_val, result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values (nil was built in entry block before branching)
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: reverse the result (we built it in reverse order)
self.builder().position_at_end(loop_exit);
// Call builtin reverse - but we need to do it inline to avoid recursion issues
// For simplicity, we'll use an iterative reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
// Build nil2 before branching
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// nil2 was built in loop_exit before branching
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `filter` - keep elements that satisfy a predicate.
/// filter p [] = []
/// filter p (x:xs) = if p x then x : filter p xs else filter p xs
fn lower_builtin_filter(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the predicate (should be a closure)
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("filter: predicate has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"filter: predicate must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("filter: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("filter expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "filter_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "filter_body");
let loop_keep = self.llvm_ctx.append_basic_block(current_fn, "filter_keep");
let loop_skip = self.llvm_ctx.append_basic_block(current_fn, "filter_skip");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "filter_exit");
// Build nil in entry block before branching
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "filter_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "filter_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: check predicate
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call predicate: pred_ptr(pred_ptr, head)
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Check if predicate returned True
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_bool = self.extract_bool_tag(pred_result.into_pointer_value())?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_bool,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_keep, loop_skip)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Keep: cons head onto result
self.builder().position_at_end(loop_keep);
let new_cons = self.build_cons(head_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Skip: just continue
self.builder().position_at_end(loop_skip);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values (nil was built in entry block)
result_phi.add_incoming(&[
(&nil, entry_block),
(&new_cons, loop_keep),
(&result_phi.as_basic_value(), loop_skip),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail_ptr, loop_keep),
(&tail_ptr, loop_skip),
]);
// Exit: reverse the result
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
// Build nil2 before branching
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// nil2 was built in loop_exit before branching
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `foldl` - left fold over a list.
/// foldl f z [] = z
/// foldl f z (x:xs) = foldl f (f z x) xs
///
/// Iterative implementation: accumulates from left to right
fn lower_builtin_foldl(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the function (should be a closure)
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("foldl: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"foldl: function must be a closure".to_string(),
))
}
};
// Lower the initial value
let init_val = self.lower_expr(init_expr)?.ok_or_else(|| {
CodegenError::Internal("foldl: initial value has no value".to_string())
})?;
let init_ptr = self.value_to_ptr(init_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("foldl: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("foldl expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "foldl_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "foldl_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "foldl_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "foldl_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "foldl_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: acc = f acc head; list = tail
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f acc head - function takes (closure_ptr, acc, head) and returns new acc
// The closure stores the function pointer which expects all args at once
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
acc_phi.as_basic_value().into(),
head_ptr.into(),
],
"foldl_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("foldl: function returned void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: return accumulator
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `foldr` - right fold over a list.
/// foldr f z [] = z
/// foldr f z (x:xs) = f x (foldr f z xs)
///
/// Implementation: reverse the list first, then fold left with swapped args
fn lower_builtin_foldr(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if container is a user ADT with derived Foldable
if let Some(type_name) = self.infer_adt_type_from_expr(list_expr) {
if self.derived_foldable_fns.contains_key(&type_name) {
return self.lower_foldr_derived(func_expr, init_expr, list_expr, &type_name);
}
}
// Lower the function (should be a closure)
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("foldr: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"foldr: function must be a closure".to_string(),
))
}
};
// Lower the initial value
let init_val = self.lower_expr(init_expr)?.ok_or_else(|| {
CodegenError::Internal("foldr: initial value has no value".to_string())
})?;
let init_ptr = self.value_to_ptr(init_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("foldr: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("foldr expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// First, reverse the list
let rev_entry = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_rev_header");
let rev_body = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_rev_body");
let rev_exit = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_rev_exit");
// Build nil before branching
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse loop
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil, rev_entry), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[(&list_ptr, rev_entry), (&rev_tail, rev_body)]);
// Now fold the reversed list with f applied as f elem acc
self.builder().position_at_end(rev_exit);
let fold_header = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_fold_header");
let fold_body = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_fold_body");
let fold_exit = self
.llvm_ctx
.append_basic_block(current_fn, "foldr_fold_exit");
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Fold loop
self.builder().position_at_end(fold_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "foldr_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "foldr_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, fold_exit, fold_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(fold_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f head acc - function takes (closure_ptr, elem, acc) and returns new acc
// Note: for foldr, the function signature is (a -> b -> b), so elem comes first, then acc
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
head_ptr.into(),
acc_phi.as_basic_value().into(),
],
"foldr_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("foldr: function returned void".to_string()))?;
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
acc_phi.add_incoming(&[(&init_ptr, rev_exit), (&new_acc, fold_body)]);
list_phi.add_incoming(&[
(&rev_acc.as_basic_value(), rev_exit),
(&tail_ptr, fold_body),
]);
// Exit: return accumulator
self.builder().position_at_end(fold_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `foldl'` - strict left fold (same as foldl but forces accumulator).
/// In our implementation, foldl is already strict, so this is the same.
fn lower_builtin_foldl_strict(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Our foldl is already strict, so just delegate
self.lower_builtin_foldl(func_expr, init_expr, list_expr)
}
/// Lower `zipWith` - apply a function to pairs of elements from two lists.
/// zipWith f [] _ = []
/// zipWith f _ [] = []
/// zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
fn lower_builtin_zipwith(
&mut self,
func_expr: &Expr,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the function (should be a closure)
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWith: function must be a closure".to_string(),
))
}
};
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith: list1 has no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zipWith expects lists".to_string())),
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith: list2 has no value".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zipWith expects lists".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "zipwith_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "zipwith_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "zipwith_exit");
// Build nil in entry block
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "zipwith_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list1_phi = self
.builder()
.build_phi(ptr_type, "zipwith_list1")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list2_phi = self
.builder()
.build_phi(ptr_type, "zipwith_list2")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if either list is empty
let tag1 = self.extract_adt_tag(list1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(list2_phi.as_basic_value().into_pointer_value())?;
let is_empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
tm.i64_type().const_zero(),
"is_empty1",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let is_empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
tm.i64_type().const_zero(),
"is_empty2",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let is_empty = self
.builder()
.build_or(is_empty1, is_empty2, "is_empty")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body
self.builder().position_at_end(loop_body);
let head1_ptr =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail1_ptr =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head2_ptr =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail2_ptr =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call the function: f x y
// For binary functions (most common case with zipWith), we call with both args at once.
// The closure convention is: closure_fn(closure_ptr, arg1, arg2) -> result
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let mapped_val = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[func_ptr.into(), head1_ptr.into(), head2_ptr.into()],
"mapped",
)
.map_err(|e| CodegenError::Internal(format!("failed to call function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("zipWith: function returned void".to_string()))?;
// Build cons cell
let new_cons = self.build_cons(mapped_val, result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list1_phi.add_incoming(&[(&list1_ptr, entry_block), (&tail1_ptr, loop_body)]);
list2_phi.add_incoming(&[(&list2_ptr, entry_block), (&tail2_ptr, loop_body)]);
// Exit: reverse the result
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `zip` - combine two lists into a list of pairs.
/// zip [] _ = []
/// zip _ [] = []
/// zip (x:xs) (y:ys) = (x,y) : zip xs ys
fn lower_builtin_zip(
&mut self,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zip: list1 has no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zip expects lists".to_string())),
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zip: list2 has no value".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zip expects lists".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "zip_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "zip_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "zip_exit");
// Build nil in entry block
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "zip_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list1_phi = self
.builder()
.build_phi(ptr_type, "zip_list1")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list2_phi = self
.builder()
.build_phi(ptr_type, "zip_list2")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if either list is empty
let tag1 = self.extract_adt_tag(list1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(list2_phi.as_basic_value().into_pointer_value())?;
let is_empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
tm.i64_type().const_zero(),
"is_empty1",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let is_empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
tm.i64_type().const_zero(),
"is_empty2",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let is_empty = self
.builder()
.build_or(is_empty1, is_empty2, "is_empty")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body
self.builder().position_at_end(loop_body);
let head1_ptr =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail1_ptr =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head2_ptr =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail2_ptr =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Build tuple (x, y) - represented as ADT with tag 0 and 2 fields
let pair_ptr = self.build_pair(head1_ptr.into(), head2_ptr.into())?;
// Build cons cell
let new_cons = self.build_cons(pair_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list1_phi.add_incoming(&[(&list1_ptr, entry_block), (&tail1_ptr, loop_body)]);
list2_phi.add_incoming(&[(&list2_ptr, entry_block), (&tail2_ptr, loop_body)]);
// Exit: reverse the result
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Build a pair (tuple of 2 elements).
fn build_pair(
&self,
fst: BasicValueEnum<'ctx>,
snd: BasicValueEnum<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let tm = self.type_mapper();
// Pair is represented as ADT with tag 0 and 2 fields
// Allocate space: tag (i64) + fst (ptr) + snd (ptr) = 24 bytes
let size = tm.i64_type().const_int(24, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let alloc_call = self
.builder()
.build_call(*alloc_fn, &[size.into()], "pair_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call alloc: {:?}", e)))?;
let pair_ptr = alloc_call
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("alloc returned void".to_string()))?
.into_pointer_value();
// Store tag = 0 (tuple constructor)
self.builder()
.build_store(pair_ptr, tm.i64_type().const_zero())
.map_err(|e| CodegenError::Internal(format!("failed to store tag: {:?}", e)))?;
// Store fst at offset 8
let fst_slot = unsafe {
self.builder()
.build_gep(
tm.i64_type(),
pair_ptr,
&[tm.i64_type().const_int(1, false)],
"fst_ptr",
)
.map_err(|e| CodegenError::Internal(format!("failed to build gep: {:?}", e)))?
};
let fst_as_ptr = self.value_to_ptr(fst)?;
self.builder()
.build_store(fst_slot, fst_as_ptr)
.map_err(|e| CodegenError::Internal(format!("failed to store fst: {:?}", e)))?;
// Store snd at offset 16
let snd_slot = unsafe {
self.builder()
.build_gep(
tm.i64_type(),
pair_ptr,
&[tm.i64_type().const_int(2, false)],
"snd_ptr",
)
.map_err(|e| CodegenError::Internal(format!("failed to build gep: {:?}", e)))?
};
let snd_as_ptr = self.value_to_ptr(snd)?;
self.builder()
.build_store(snd_slot, snd_as_ptr)
.map_err(|e| CodegenError::Internal(format!("failed to store snd: {:?}", e)))?;
Ok(pair_ptr)
}
/// Build a triple (3-tuple). ADT with tag 0 and 3 fields.
fn build_triple(
&self,
fst: BasicValueEnum<'ctx>,
snd: BasicValueEnum<'ctx>,
trd: BasicValueEnum<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
let triple_ptr = self.alloc_adt(0, 3)?;
self.store_adt_field(triple_ptr, 3, 0, fst)?;
self.store_adt_field(triple_ptr, 3, 1, snd)?;
self.store_adt_field(triple_ptr, 3, 2, trd)?;
Ok(triple_ptr)
}
/// Lower `last` - get last element of a list.
/// last [] = error "empty list"
/// last [x] = x
/// last (_:xs) = last xs
fn lower_builtin_last(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("last: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("last expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Check if list is empty at start
let tag = self.extract_adt_tag(list_ptr)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let error_block = self.llvm_ctx.append_basic_block(current_fn, "last_error");
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "last_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "last_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "last_exit");
self.builder()
.build_conditional_branch(is_empty, error_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Error block: empty list
self.builder().position_at_end(error_block);
let error_msg = self
.module
.add_global_string("last_error", "last: empty list");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "last_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Extract head and tail
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Check if tail is empty (this element is the last)
let tail_tag = self.extract_adt_tag(tail_ptr)?;
let tail_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tail_tag,
tm.i64_type().const_zero(),
"tail_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(tail_is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: continue with tail
self.builder().position_at_end(loop_body);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: return head
self.builder().position_at_end(loop_exit);
Ok(Some(head_ptr.into()))
}
/// Lower `init` - get all but last element of a list.
/// init [] = error "empty list"
/// init [x] = []
/// init (x:xs) = x : init xs
fn lower_builtin_init(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("init: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("init expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Check if list is empty at start
let tag = self.extract_adt_tag(list_ptr)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
let error_block = self.llvm_ctx.append_basic_block(current_fn, "init_error");
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "init_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "init_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "init_exit");
// Build nil in entry block
let nil = self.build_nil()?;
self.builder()
.build_conditional_branch(is_empty, error_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Error block: empty list
self.builder().position_at_end(error_block);
let error_msg = self
.module
.add_global_string("init_error", "init: empty list");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "init_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "init_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Extract head and tail
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Check if tail is empty (this element is the last - don't include it)
let tail_tag = self.extract_adt_tag(tail_ptr)?;
let tail_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tail_tag,
tm.i64_type().const_zero(),
"tail_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(tail_is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: cons head onto result, continue with tail
self.builder().position_at_end(loop_body);
let new_cons = self.build_cons(head_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: reverse the result
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `!!` - index into a list.
/// [] !! _ = error "index too large"
/// (x:_) !! 0 = x
/// (_:xs) !! n = xs !! (n-1)
fn lower_builtin_index(
&mut self,
list_expr: &Expr,
index_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("!!: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("!! expects a list".to_string())),
};
let index_val = self
.lower_expr(index_expr)?
.ok_or_else(|| CodegenError::Internal("!!: index has no value".to_string()))?;
let index = self.to_int_value(index_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "index_header");
let check_empty = self
.llvm_ctx
.append_basic_block(current_fn, "index_check_empty");
let check_zero = self
.llvm_ctx
.append_basic_block(current_fn, "index_check_zero");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "index_body");
let error_block = self.llvm_ctx.append_basic_block(current_fn, "index_error");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "index_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "index_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "index_idx")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
self.builder()
.build_unconditional_branch(check_empty)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Check if list is empty
self.builder().position_at_end(check_empty);
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, error_block, check_zero)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Check if index is zero
self.builder().position_at_end(check_zero);
let is_zero = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
idx_phi.as_basic_value().into_int_value(),
tm.i64_type().const_zero(),
"is_zero",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_zero, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Error block: index out of bounds
self.builder().position_at_end(error_block);
let error_msg = self
.module
.add_global_string("index_error", "!!: index too large");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Loop body: decrement index, continue with tail
self.builder().position_at_end(loop_body);
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_idx = self
.builder()
.build_int_sub(
idx_phi.as_basic_value().into_int_value(),
tm.i64_type().const_int(1, false),
"new_idx",
)
.map_err(|e| CodegenError::Internal(format!("failed to sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Add phi incoming values
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
idx_phi.add_incoming(&[(&index, entry_block), (&new_idx, loop_body)]);
// Exit: return head
self.builder().position_at_end(loop_exit);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
Ok(Some(head_ptr.into()))
}
/// Lower `concat` - flatten a list of lists.
/// concat [] = []
/// concat (xs:xss) = xs ++ concat xss
fn lower_builtin_concat(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("concat: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"concat expects a list of lists".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Build nil in entry block
let nil = self.build_nil()?;
// Outer loop: iterate over list of lists
let outer_header = self
.llvm_ctx
.append_basic_block(current_fn, "concat_outer_header");
let outer_body = self
.llvm_ctx
.append_basic_block(current_fn, "concat_outer_body");
let outer_exit = self
.llvm_ctx
.append_basic_block(current_fn, "concat_outer_exit");
// Inner loop: iterate over current inner list
let inner_header = self
.llvm_ctx
.append_basic_block(current_fn, "concat_inner_header");
let inner_body = self
.llvm_ctx
.append_basic_block(current_fn, "concat_inner_body");
let inner_exit = self
.llvm_ctx
.append_basic_block(current_fn, "concat_inner_exit");
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer loop header
self.builder().position_at_end(outer_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "concat_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let outer_list_phi = self
.builder()
.build_phi(ptr_type, "concat_outer_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if outer list is empty
let outer_tag =
self.extract_adt_tag(outer_list_phi.as_basic_value().into_pointer_value())?;
let outer_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
outer_tag,
tm.i64_type().const_zero(),
"outer_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(outer_is_empty, outer_exit, outer_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer loop body: get current inner list and process it
self.builder().position_at_end(outer_body);
let inner_list_ptr =
self.extract_adt_field(outer_list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let outer_tail_ptr =
self.extract_adt_field(outer_list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
self.builder()
.build_unconditional_branch(inner_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner loop header
self.builder().position_at_end(inner_header);
let inner_result_phi = self
.builder()
.build_phi(ptr_type, "concat_inner_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let inner_list_phi = self
.builder()
.build_phi(ptr_type, "concat_inner_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if inner list is empty
let inner_tag =
self.extract_adt_tag(inner_list_phi.as_basic_value().into_pointer_value())?;
let inner_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
inner_tag,
tm.i64_type().const_zero(),
"inner_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(inner_is_empty, inner_exit, inner_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner loop body: cons head onto result
self.builder().position_at_end(inner_body);
let head_ptr =
self.extract_adt_field(inner_list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(inner_list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_cons = self.build_cons(head_ptr.into(), inner_result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(inner_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner phi incoming values
inner_result_phi.add_incoming(&[
(&result_phi.as_basic_value(), outer_body),
(&new_cons, inner_body),
]);
inner_list_phi.add_incoming(&[(&inner_list_ptr, outer_body), (&tail_ptr, inner_body)]);
// Inner loop exit: continue with outer loop
self.builder().position_at_end(inner_exit);
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer phi incoming values
result_phi.add_incoming(&[
(&nil, entry_block),
(&inner_result_phi.as_basic_value(), inner_exit),
]);
outer_list_phi.add_incoming(&[(&list_ptr, entry_block), (&outer_tail_ptr, inner_exit)]);
// Outer exit: reverse the result
self.builder().position_at_end(outer_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, outer_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), outer_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `concatMap` - map and concatenate.
/// concatMap f xs = concat (map f xs)
fn lower_builtin_concat_map(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For efficiency, we implement this directly rather than via concat (map f xs)
let func_val = self.lower_expr(func_expr)?.ok_or_else(|| {
CodegenError::Internal("concatMap: function has no value".to_string())
})?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"concatMap: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("concatMap: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"concatMap expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Build nil in entry block
let nil = self.build_nil()?;
// Outer loop: iterate over input list
let outer_header = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_outer_header");
let outer_body = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_outer_body");
let outer_exit = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_outer_exit");
// Inner loop: iterate over result of f applied to current element
let inner_header = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_inner_header");
let inner_body = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_inner_body");
let inner_exit = self
.llvm_ctx
.append_basic_block(current_fn, "concatmap_inner_exit");
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer loop header
self.builder().position_at_end(outer_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "concatmap_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let outer_list_phi = self
.builder()
.build_phi(ptr_type, "concatmap_outer_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if outer list is empty
let outer_tag =
self.extract_adt_tag(outer_list_phi.as_basic_value().into_pointer_value())?;
let outer_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
outer_tag,
tm.i64_type().const_zero(),
"outer_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(outer_is_empty, outer_exit, outer_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer loop body: apply f to head, process result
self.builder().position_at_end(outer_body);
let head_ptr =
self.extract_adt_field(outer_list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let outer_tail_ptr =
self.extract_adt_field(outer_list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f on head
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let inner_list_ptr = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[func_ptr.into(), head_ptr.into()],
"f_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("concatMap: function returned void".to_string())
})?;
self.builder()
.build_unconditional_branch(inner_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner loop header
self.builder().position_at_end(inner_header);
let inner_result_phi = self
.builder()
.build_phi(ptr_type, "concatmap_inner_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let inner_list_phi = self
.builder()
.build_phi(ptr_type, "concatmap_inner_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
// Check if inner list is empty
let inner_tag =
self.extract_adt_tag(inner_list_phi.as_basic_value().into_pointer_value())?;
let inner_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
inner_tag,
tm.i64_type().const_zero(),
"inner_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(inner_is_empty, inner_exit, inner_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner loop body: cons head onto result
self.builder().position_at_end(inner_body);
let inner_head_ptr =
self.extract_adt_field(inner_list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let inner_tail_ptr =
self.extract_adt_field(inner_list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_cons = self.build_cons(inner_head_ptr.into(), inner_result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(inner_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Inner phi incoming values
inner_result_phi.add_incoming(&[
(&result_phi.as_basic_value(), outer_body),
(&new_cons, inner_body),
]);
inner_list_phi
.add_incoming(&[(&inner_list_ptr, outer_body), (&inner_tail_ptr, inner_body)]);
// Inner loop exit: continue with outer loop
self.builder().position_at_end(inner_exit);
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Outer phi incoming values
result_phi.add_incoming(&[
(&nil, entry_block),
(&inner_result_phi.as_basic_value(), inner_exit),
]);
outer_list_phi.add_incoming(&[(&list_ptr, entry_block), (&outer_tail_ptr, inner_exit)]);
// Outer exit: reverse the result
self.builder().position_at_end(outer_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare tag: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, outer_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), outer_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `fst` - extract first element of a pair.
fn lower_builtin_fst(
&mut self,
pair_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pair_val = self
.lower_expr(pair_expr)?
.ok_or_else(|| CodegenError::Internal("fst: pair has no value".to_string()))?;
let pair_ptr = match pair_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("fst expects a tuple".to_string())),
};
// Extract field 0 (first element)
let fst_ptr = self.extract_adt_field(pair_ptr, 2, 0)?;
Ok(Some(fst_ptr.into()))
}
/// Lower `snd` - extract second element of a pair.
fn lower_builtin_snd(
&mut self,
pair_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pair_val = self
.lower_expr(pair_expr)?
.ok_or_else(|| CodegenError::Internal("snd: pair has no value".to_string()))?;
let pair_ptr = match pair_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("snd expects a tuple".to_string())),
};
// Extract field 1 (second element)
let snd_ptr = self.extract_adt_field(pair_ptr, 2, 1)?;
Ok(Some(snd_ptr.into()))
}
/// Lower `$sel_N` - extract Nth field from a tuple/dictionary.
///
/// Field selectors are used for type class dictionary method extraction.
/// The dictionary is represented as a tuple (ADT with tag 0 and N fields),
/// and $sel_N extracts the Nth field.
///
/// Memory layout of a tuple: { i64 tag, ptr field_0, ptr field_1, ... }
/// So field N is at byte offset: 8 + N * 8 = (1 + N) * 8
fn lower_builtin_field_selector(
&mut self,
tuple_expr: &Expr,
field_index: u32,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tuple_val = self.lower_expr(tuple_expr)?.ok_or_else(|| {
CodegenError::Internal(format!("$sel_{}: tuple has no value", field_index))
})?;
let tuple_ptr = match tuple_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(format!(
"$sel_{} expects a tuple/dictionary",
field_index
)))
}
};
let tm = self.type_mapper();
// Use raw pointer arithmetic to access the field at the correct offset.
// ADT layout: { i64 tag, ptr field_0, ptr field_1, ... }
// Field N is at index (1 + N) when treating as array of i64-sized elements.
let field_offset = 1 + field_index; // Skip tag (index 0)
let field_ptr = unsafe {
self.builder()
.build_gep(
tm.i64_type(), // Treating memory as array of 8-byte slots
tuple_ptr,
&[tm.i64_type().const_int(field_offset as u64, false)],
&format!("field_ptr_{}", field_index),
)
.map_err(|e| {
CodegenError::Internal(format!("failed to build field gep: {:?}", e))
})?
};
// Load the field value (which is a pointer)
let field_val = self
.builder()
.build_load(tm.ptr_type(), field_ptr, &format!("field_{}", field_index))
.map_err(|e| CodegenError::Internal(format!("failed to load field: {:?}", e)))?;
Ok(Some(field_val))
}
/// Lower `fromJust` - extract value from Just, error on Nothing.
fn lower_builtin_from_just(
&mut self,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("fromJust: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fromJust expects Maybe".to_string(),
))
}
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let nothing_block = self
.llvm_context()
.append_basic_block(current_fn, "fromJust_nothing");
let just_block = self
.llvm_context()
.append_basic_block(current_fn, "fromJust_just");
// Tag: Nothing=0, Just=1
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nothing, nothing_block, just_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Nothing case: error
self.builder().position_at_end(nothing_block);
let error_msg = self
.module
.add_global_string("fromJust_error", "fromJust: Nothing");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
// Just case: extract value
self.builder().position_at_end(just_block);
let val_ptr = self.extract_adt_field(maybe_ptr, 1, 0)?;
Ok(Some(val_ptr.into()))
}
/// Lower `isJust` - check if Maybe is Just.
fn lower_builtin_is_just(
&mut self,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("isJust: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("isJust expects Maybe".to_string())),
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
// isJust = (tag == 1)
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
let result = self
.builder()
.build_int_z_extend(is_just, tm.i64_type(), "isJust_result")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `isNothing` - check if Maybe is Nothing.
fn lower_builtin_is_nothing(
&mut self,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("isNothing: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isNothing expects Maybe".to_string(),
))
}
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
// isNothing = (tag == 0)
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
let result = self
.builder()
.build_int_z_extend(is_nothing, tm.i64_type(), "isNothing_result")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `isLeft` - check if Either is Left.
fn lower_builtin_is_left(
&mut self,
either_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let either_val = self
.lower_expr(either_expr)?
.ok_or_else(|| CodegenError::Internal("isLeft: either has no value".to_string()))?;
let either_ptr = match either_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("isLeft expects Either".to_string())),
};
let tag = self.extract_adt_tag(either_ptr)?;
let tm = self.type_mapper();
// isLeft = (tag == 0)
let is_left = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_left",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
let result = self
.builder()
.build_int_z_extend(is_left, tm.i64_type(), "isLeft_result")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `isRight` - check if Either is Right.
fn lower_builtin_is_right(
&mut self,
either_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let either_val = self
.lower_expr(either_expr)?
.ok_or_else(|| CodegenError::Internal("isRight: either has no value".to_string()))?;
let either_ptr = match either_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isRight expects Either".to_string(),
))
}
};
let tag = self.extract_adt_tag(either_ptr)?;
let tm = self.type_mapper();
// isRight = (tag == 1)
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
let result = self
.builder()
.build_int_z_extend(is_right, tm.i64_type(), "isRight_result")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(Some(result.into()))
}
/// Lower `fromMaybe def maybe` — extract Just value or return default.
fn lower_builtin_from_maybe(
&mut self,
def_expr: &Expr,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let def_val = self
.lower_expr(def_expr)?
.ok_or_else(|| CodegenError::Internal("fromMaybe: default has no value".to_string()))?;
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("fromMaybe: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fromMaybe expects Maybe".to_string(),
))
}
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let nothing_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromMaybe_nothing");
let just_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromMaybe_just");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromMaybe_merge");
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nothing, nothing_block, just_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Nothing → return default
self.builder().position_at_end(nothing_block);
let def_as_ptr = match def_val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "def_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"fromMaybe: unexpected default type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Just → extract payload
self.builder().position_at_end(just_block);
let val_ptr = self.extract_adt_field(maybe_ptr, 1, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "fromMaybe_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&def_as_ptr, nothing_block), (&val_ptr, just_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `maybe def f maybe_val` — case analysis on Maybe.
fn lower_builtin_maybe(
&mut self,
def_expr: &Expr,
f_expr: &Expr,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let def_val = self
.lower_expr(def_expr)?
.ok_or_else(|| CodegenError::Internal("maybe: default has no value".to_string()))?;
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("maybe: function has no value".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"maybe: f must be a closure".to_string(),
))
}
};
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("maybe: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("maybe expects Maybe".to_string())),
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let nothing_block = self
.llvm_ctx
.append_basic_block(current_fn, "maybe_nothing");
let just_block = self.llvm_ctx.append_basic_block(current_fn, "maybe_just");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "maybe_merge");
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nothing, nothing_block, just_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Nothing → return default
self.builder().position_at_end(nothing_block);
let def_as_ptr = match def_val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "def_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"maybe: unexpected default type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Just → call f(payload)
self.builder().position_at_end(just_block);
let payload = self.extract_adt_field(maybe_ptr, 1, 0)?;
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let call_result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[f_ptr.into(), payload.into()],
"maybe_call",
)
.map_err(|e| CodegenError::Internal(format!("failed to call f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("maybe: f returned void".to_string()))?;
let call_as_ptr = match call_result {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "call_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"maybe: f returned unexpected type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "maybe_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&def_as_ptr, nothing_block), (&call_as_ptr, just_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `listToMaybe list` — head of list as Maybe.
fn lower_builtin_list_to_maybe(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("listToMaybe: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"listToMaybe expects a list".to_string(),
))
}
};
let tag = self.extract_adt_tag(list_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let nil_block = self.llvm_ctx.append_basic_block(current_fn, "ltm_nil");
let cons_block = self.llvm_ctx.append_basic_block(current_fn, "ltm_cons");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "ltm_merge");
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, nil_block, cons_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Nil → Nothing (tag=0, arity=0)
self.builder().position_at_end(nil_block);
let nothing = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Cons → Just head (tag=1, arity=1)
self.builder().position_at_end(cons_block);
let head = self.extract_adt_field(list_ptr, 2, 0)?;
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, head.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "listToMaybe_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(¬hing, nil_block), (&just, cons_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `maybeToList maybe` — Maybe to singleton or empty list.
fn lower_builtin_maybe_to_list(
&mut self,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("maybeToList: maybe has no value".to_string()))?;
let maybe_ptr = match maybe_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"maybeToList expects Maybe".to_string(),
))
}
};
let tag = self.extract_adt_tag(maybe_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let nothing_block = self.llvm_ctx.append_basic_block(current_fn, "mtl_nothing");
let just_block = self.llvm_ctx.append_basic_block(current_fn, "mtl_just");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "mtl_merge");
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nothing, nothing_block, just_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Nothing → []
self.builder().position_at_end(nothing_block);
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Just → [payload]
self.builder().position_at_end(just_block);
let payload = self.extract_adt_field(maybe_ptr, 1, 0)?;
let nil2 = self.build_nil()?;
let singleton = self.build_cons(payload.into(), nil2.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "maybeToList_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&nil, nothing_block), (&singleton, just_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `guard bool` — [() | True], [| False].
fn lower_builtin_guard(
&mut self,
bool_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let bool_val = self
.lower_expr(bool_expr)?
.ok_or_else(|| CodegenError::Internal("guard: bool has no value".to_string()))?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Bool is an ADT with tag 0=False, 1=True — extract the tag
let bool_int = match bool_val {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => return Err(CodegenError::TypeError("guard expects Bool".to_string())),
};
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let true_block = self.llvm_ctx.append_basic_block(current_fn, "guard_true");
let false_block = self.llvm_ctx.append_basic_block(current_fn, "guard_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "guard_merge");
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
bool_int,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, true_block, false_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// True → [()] — unit is null pointer
self.builder().position_at_end(true_block);
let unit_val = ptr_type.const_null();
let nil = self.build_nil()?;
let singleton = self.build_cons(unit_val.into(), nil.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// False → []
self.builder().position_at_end(false_block);
let empty = self.build_nil()?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "guard_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&singleton, true_block), (&empty, false_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `fromLeft def either` — extract Left or return default.
fn lower_builtin_from_left(
&mut self,
def_expr: &Expr,
either_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let def_val = self
.lower_expr(def_expr)?
.ok_or_else(|| CodegenError::Internal("fromLeft: default has no value".to_string()))?;
let either_val = self
.lower_expr(either_expr)?
.ok_or_else(|| CodegenError::Internal("fromLeft: either has no value".to_string()))?;
let either_ptr = match either_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fromLeft expects Either".to_string(),
))
}
};
let tag = self.extract_adt_tag(either_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let left_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromLeft_left");
let right_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromLeft_right");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromLeft_merge");
// Left=0, Right=1
let is_left = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_left",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_left, left_block, right_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Left → extract payload
self.builder().position_at_end(left_block);
let payload = self.extract_adt_field(either_ptr, 1, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Right → return default
self.builder().position_at_end(right_block);
let def_as_ptr = match def_val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "def_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"fromLeft: unexpected default type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "fromLeft_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&payload, left_block), (&def_as_ptr, right_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `fromRight def either` — extract Right or return default.
fn lower_builtin_from_right(
&mut self,
def_expr: &Expr,
either_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let def_val = self
.lower_expr(def_expr)?
.ok_or_else(|| CodegenError::Internal("fromRight: default has no value".to_string()))?;
let either_val = self
.lower_expr(either_expr)?
.ok_or_else(|| CodegenError::Internal("fromRight: either has no value".to_string()))?;
let either_ptr = match either_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fromRight expects Either".to_string(),
))
}
};
let tag = self.extract_adt_tag(either_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let left_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromRight_left");
let right_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromRight_right");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "fromRight_merge");
// Right=1
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_block, left_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Right → extract payload
self.builder().position_at_end(right_block);
let payload = self.extract_adt_field(either_ptr, 1, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Left → return default
self.builder().position_at_end(left_block);
let def_as_ptr = match def_val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "def_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"fromRight: unexpected default type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "fromRight_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&payload, right_block), (&def_as_ptr, left_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `either f g either_val` — case analysis on Either.
fn lower_builtin_either(
&mut self,
f_expr: &Expr,
g_expr: &Expr,
either_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("either: f has no value".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"either: f must be a closure".to_string(),
))
}
};
let g_val = self
.lower_expr(g_expr)?
.ok_or_else(|| CodegenError::Internal("either: g has no value".to_string()))?;
let g_ptr = match g_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"either: g must be a closure".to_string(),
))
}
};
let either_val = self
.lower_expr(either_expr)?
.ok_or_else(|| CodegenError::Internal("either: either has no value".to_string()))?;
let either_ptr = match either_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("either expects Either".to_string())),
};
let tag = self.extract_adt_tag(either_ptr)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let left_block = self.llvm_ctx.append_basic_block(current_fn, "either_left");
let right_block = self.llvm_ctx.append_basic_block(current_fn, "either_right");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "either_merge");
let is_left = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_left",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_left, left_block, right_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// Left → call f(payload)
self.builder().position_at_end(left_block);
let left_payload = self.extract_adt_field(either_ptr, 1, 0)?;
let f_fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let left_result = self
.builder()
.build_indirect_call(
fn_type,
f_fn_ptr,
&[f_ptr.into(), left_payload.into()],
"either_f_call",
)
.map_err(|e| CodegenError::Internal(format!("failed to call f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("either: f returned void".to_string()))?;
let left_as_ptr = match left_result {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "left_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"either: f returned unexpected type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Right → call g(payload)
self.builder().position_at_end(right_block);
let right_payload = self.extract_adt_field(either_ptr, 1, 0)?;
let g_fn_ptr = self.extract_closure_fn_ptr(g_ptr)?;
let right_result = self
.builder()
.build_indirect_call(
fn_type,
g_fn_ptr,
&[g_ptr.into(), right_payload.into()],
"either_g_call",
)
.map_err(|e| CodegenError::Internal(format!("failed to call g: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("either: g returned void".to_string()))?;
let right_as_ptr = match right_result {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => self
.builder()
.build_int_to_ptr(i, ptr_type, "right_as_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to cast: {:?}", e)))?,
_ => {
return Err(CodegenError::TypeError(
"either: g returned unexpected type".to_string(),
))
}
};
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "either_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&left_as_ptr, left_block), (&right_as_ptr, right_block)]);
Ok(Some(phi.as_basic_value()))
}
/// Lower `catMaybes list` — filter+extract Just values from [Maybe a].
fn lower_builtin_cat_maybes(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("catMaybes: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"catMaybes expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "catM_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "catM_body");
let loop_keep = self.llvm_ctx.append_basic_block(current_fn, "catM_keep");
let loop_skip = self.llvm_ctx.append_basic_block(current_fn, "catM_skip");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "catM_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "catM_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "catM_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head (a Maybe) and tail
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Check if head is Just (tag==1) or Nothing (tag==0)
let maybe_tag = self.extract_adt_tag(head)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, loop_keep, loop_skip)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Keep: extract Just payload, cons onto acc
self.builder().position_at_end(loop_keep);
let payload = self.extract_adt_field(head, 1, 0)?;
let new_cons = self.build_cons(payload.into(), acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Skip
self.builder().position_at_end(loop_skip);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[
(&nil, entry_block),
(&new_cons, loop_keep),
(&acc_phi.as_basic_value(), loop_skip),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail, loop_keep),
(&tail, loop_skip),
]);
// Exit: reverse accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(acc_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `lefts list` — extract Left values from [Either a b].
fn lower_builtin_lefts(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_filter_either(list_expr, 0, "lefts")
}
/// Lower `rights list` — extract Right values from [Either a b].
fn lower_builtin_rights(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_filter_either(list_expr, 1, "rights")
}
/// Shared implementation for lefts/rights — filter Either list by tag.
fn lower_builtin_filter_either(
&mut self,
list_expr: &Expr,
target_tag: u64,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: list has no value", label)))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(format!("{} expects a list", label))),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_header", label));
let loop_body = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_body", label));
let loop_keep = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_keep", label));
let loop_skip = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_skip", label));
let loop_exit = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_exit", label));
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_acc", label))
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_list", label))
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head (an Either) and tail
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Check if head matches target tag
let either_tag = self.extract_adt_tag(head)?;
let matches = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
either_tag,
tm.i64_type().const_int(target_tag, false),
"tag_match",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(matches, loop_keep, loop_skip)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Keep: extract payload, cons onto acc
self.builder().position_at_end(loop_keep);
let payload = self.extract_adt_field(head, 1, 0)?;
let new_cons = self.build_cons(payload.into(), acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Skip
self.builder().position_at_end(loop_skip);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[
(&nil, entry_block),
(&new_cons, loop_keep),
(&acc_phi.as_basic_value(), loop_skip),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail, loop_keep),
(&tail, loop_skip),
]);
// Exit: reverse accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(acc_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `mapMaybe f list` — apply f, keep Just results.
fn lower_builtin_map_maybe(
&mut self,
f_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybe: f has no value".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"mapMaybe: f must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybe: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"mapMaybe expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "mapM_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "mapM_body");
let loop_keep = self.llvm_ctx.append_basic_block(current_fn, "mapM_keep");
let loop_skip = self.llvm_ctx.append_basic_block(current_fn, "mapM_skip");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "mapM_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "mapM_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "mapM_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head and tail, call f(head)
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f(head) → Maybe result
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let maybe_result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[f_ptr.into(), head.into()], "mapM_call")
.map_err(|e| CodegenError::Internal(format!("failed to call f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mapMaybe: f returned void".to_string()))?;
let maybe_ptr = match maybe_result {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"mapMaybe: f should return Maybe".to_string(),
))
}
};
// Check if result is Just
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, loop_keep, loop_skip)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Keep: extract Just payload, cons onto acc
self.builder().position_at_end(loop_keep);
let payload = self.extract_adt_field(maybe_ptr, 1, 0)?;
let new_cons = self.build_cons(payload.into(), acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Skip
self.builder().position_at_end(loop_skip);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[
(&nil, entry_block),
(&new_cons, loop_keep),
(&acc_phi.as_basic_value(), loop_skip),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail, loop_keep),
(&tail, loop_skip),
]);
// Exit: reverse accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(acc_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `partitionEithers list` — split [Either a b] into ([a], [b]).
fn lower_builtin_partition_eithers(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self.lower_expr(list_expr)?.ok_or_else(|| {
CodegenError::Internal("partitionEithers: list has no value".to_string())
})?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"partitionEithers expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "pe_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "pe_body");
let loop_left = self.llvm_ctx.append_basic_block(current_fn, "pe_left");
let loop_right = self.llvm_ctx.append_basic_block(current_fn, "pe_right");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "pe_exit");
let nil = self.build_nil()?;
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop header: two accumulators
self.builder().position_at_end(loop_header);
let lefts_phi = self
.builder()
.build_phi(ptr_type, "pe_lefts")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rights_phi = self
.builder()
.build_phi(ptr_type, "pe_rights")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "pe_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Loop body: extract head (Either) and tail
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let either_tag = self.extract_adt_tag(head)?;
let is_left = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
either_tag,
tm.i64_type().const_zero(),
"is_left",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_left, loop_left, loop_right)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Left: cons payload onto lefts_acc
self.builder().position_at_end(loop_left);
let left_payload = self.extract_adt_field(head, 1, 0)?;
let new_lefts = self.build_cons(left_payload.into(), lefts_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Right: cons payload onto rights_acc
self.builder().position_at_end(loop_right);
let right_payload = self.extract_adt_field(head, 1, 0)?;
let new_rights = self.build_cons(right_payload.into(), rights_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Phi incoming
lefts_phi.add_incoming(&[
(&nil, entry_block),
(&new_lefts, loop_left),
(&lefts_phi.as_basic_value(), loop_right),
]);
rights_phi.add_incoming(&[
(&nil2, entry_block),
(&rights_phi.as_basic_value(), loop_left),
(&new_rights, loop_right),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail, loop_left),
(&tail, loop_right),
]);
// Exit: reverse both lists, build tuple
self.builder().position_at_end(loop_exit);
let rev_lefts = self
.build_inline_reverse(lefts_phi.as_basic_value().into_pointer_value(), current_fn)?
.ok_or_else(|| CodegenError::Internal("reverse returned None".to_string()))?;
let rev_lefts_ptr = match rev_lefts {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::Internal(
"reverse should return pointer".to_string(),
))
}
};
let rev_rights = self
.build_inline_reverse(rights_phi.as_basic_value().into_pointer_value(), current_fn)?
.ok_or_else(|| CodegenError::Internal("reverse returned None".to_string()))?;
let rev_rights_ptr = match rev_rights {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::Internal(
"reverse should return pointer".to_string(),
))
}
};
// Build tuple: alloc_adt(tag=0, arity=2) with two list fields
let tuple = self.alloc_adt(0, 2)?;
self.store_adt_field(tuple, 2, 0, rev_lefts_ptr.into())?;
self.store_adt_field(tuple, 2, 1, rev_rights_ptr.into())?;
Ok(Some(tuple.into()))
}
/// Build an inline reverse loop. Returns the reversed list.
/// Assumes builder is positioned at the block that should start the reverse.
fn build_inline_reverse(
&mut self,
list_ptr: PointerValue<'ctx>,
current_fn: inkwell::values::FunctionValue<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let pre_rev_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse header
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_empty",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Reverse body: cons head onto acc
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// Phi incoming
rev_acc.add_incoming(&[(&nil, pre_rev_block), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[(&list_ptr, pre_rev_block), (&rev_tail, rev_body)]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `error` - throw an ErrorCall exception (catchable).
///
/// Wraps the error message in a SomeException with tag 2 (ErrorCall)
/// and calls bhc_throw, making `error "msg"` catchable by `catch`.
fn lower_builtin_error(
&mut self,
msg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let msg_val = self
.lower_expr(msg_expr)?
.ok_or_else(|| CodegenError::Internal("error: message has no value".to_string()))?;
let msg_ptr = match msg_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
// If it's not a pointer, use a default error message
self.module
.add_global_string("error_default", "error called")
}
};
// Convert Haskell char list to C-string for the payload
let cstr_ptr = self.char_list_to_cstring(msg_ptr)?;
// Wrap in SomeException with tag 2 (ErrorCall)
let make_exc_fn = self.functions.get(&VarId::new(1000700)).ok_or_else(|| {
CodegenError::Internal("bhc_make_some_exception not declared".to_string())
})?;
let i64_type = self.llvm_context().i64_type();
let tag_val = i64_type.const_int(2, false); // EXC_TAG_ERROR_CALL
let some_exc = self
.builder()
.build_call(
*make_exc_fn,
&[tag_val.into(), cstr_ptr.into()],
"error_exc",
)
.map_err(|e| CodegenError::Internal(format!("failed to make ErrorCall: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("make_some_exception: returned void".to_string())
})?;
// Throw the exception (returns sentinel null)
let throw_fn = self
.functions
.get(&VarId::new(1000080))
.ok_or_else(|| CodegenError::Internal("bhc_throw not declared".to_string()))?;
let result = self
.builder()
.build_call(*throw_fn, &[some_exc.into()], "error_sentinel")
.map_err(|e| CodegenError::Internal(format!("failed to call throw: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("throw: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `undefined` - always errors.
fn lower_builtin_undefined(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let error_msg = self
.module
.add_global_string("undefined_error", "undefined");
let error_fn = self
.functions
.get(&VarId::new(1000006))
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(*error_fn, &[error_msg.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("failed to build unreachable: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `throw` / `throwIO` - wrap in SomeException, call bhc_throw.
fn lower_builtin_throw(
&mut self,
msg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let msg_val = self
.lower_expr(msg_expr)?
.ok_or_else(|| CodegenError::Internal("throw: message has no value".to_string()))?;
let msg_ptr = self.value_to_ptr(msg_val)?;
// Wrap in SomeException with tag 0 (generic)
let make_exc_fn = self.functions.get(&VarId::new(1000700)).ok_or_else(|| {
CodegenError::Internal("bhc_make_some_exception not declared".to_string())
})?;
let i64_type = self.llvm_context().i64_type();
let tag_val = i64_type.const_int(0, false);
let some_exc = self
.builder()
.build_call(*make_exc_fn, &[tag_val.into(), msg_ptr.into()], "some_exc")
.map_err(|e| CodegenError::Internal(format!("failed to make SomeException: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("make_some_exception: returned void".to_string())
})?;
let throw_fn = self
.functions
.get(&VarId::new(1000080))
.ok_or_else(|| CodegenError::Internal("bhc_throw not declared".to_string()))?;
let result = self
.builder()
.build_call(*throw_fn, &[some_exc.into()], "throw_sentinel")
.map_err(|e| CodegenError::Internal(format!("failed to call throw: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("throw: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `catch` - call bhc_catch with action and handler closures.
///
/// catch :: IO a -> (SomeException -> IO a) -> IO a
///
/// Extracts fn_ptr and closure_ptr from both the action and handler,
/// then calls bhc_catch(action_fn, action_env, handler_fn, handler_env).
/// Wrap an IO action expression as a thunk (closure) for deferred execution.
///
/// This creates a new function that evaluates the expression when called,
/// and wraps it in a closure. Used for `catch`, `bracket`, and similar
/// functions that need to defer IO actions.
fn wrap_io_as_thunk(&mut self, expr: &Expr) -> CodegenResult<PointerValue<'ctx>> {
// If the expression is already a lambda/closure, just lower it normally
if matches!(expr, Expr::Lam(_, _, _)) {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("wrap_io_as_thunk: no value".to_string()))?;
return self.value_to_ptr(val);
}
// Compute free variables
let free = self.free_vars(expr);
// Collect captured variable values
let mut captured: Vec<(VarId, BasicValueEnum<'ctx>)> = Vec::new();
for var_id in &free {
if let Some(val) = self.env.get(var_id) {
captured.push((*var_id, *val));
}
}
// Save current state
let current_block = self.builder().get_insert_block();
let fn_name = self.next_closure_name();
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Create function: fn(env_ptr) -> result_ptr
let fn_type = ptr_type.fn_type(&[ptr_type.into()], false);
let lifted_fn = self.module.add_function(&fn_name, fn_type);
let entry = self.llvm_context().append_basic_block(lifted_fn, "entry");
self.builder().position_at_end(entry);
// Save and replace environment
let old_env = std::mem::take(&mut self.env);
// Restore captured variables from closure env
if !captured.is_empty() {
let closure_ptr = lifted_fn
.get_first_param()
.ok_or_else(|| CodegenError::Internal("missing closure param".to_string()))?
.into_pointer_value();
for (i, (var_id, _)) in captured.iter().enumerate() {
let elem_ptr =
self.extract_closure_env_elem(closure_ptr, captured.len() as u32, i as u32)?;
self.env.insert(*var_id, elem_ptr.into());
}
}
// Lower the expression inside the thunk
let result = self.lower_expr(expr)?;
// Return result
if let Some(val) = result {
let ret_ptr = self.value_to_ptr(val)?;
self.builder()
.build_return(Some(&ret_ptr))
.map_err(|e| CodegenError::Internal(format!("thunk return failed: {:?}", e)))?;
} else {
let null = ptr_type.const_null();
self.builder().build_return(Some(&null)).map_err(|e| {
CodegenError::Internal(format!("thunk null return failed: {:?}", e))
})?;
}
// Restore environment and insertion point
self.env = old_env;
if let Some(block) = current_block {
self.builder().position_at_end(block);
}
// Create closure
let fn_ptr = lifted_fn.as_global_value().as_pointer_value();
self.alloc_closure(fn_ptr, &captured)
}
fn lower_builtin_catch(
&mut self,
action_expr: &Expr,
handler_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Wrap the action as a thunk so it's deferred (not eagerly evaluated)
let action_closure = self.wrap_io_as_thunk(action_expr)?;
let handler_val = self
.lower_expr(handler_expr)?
.ok_or_else(|| CodegenError::Internal("catch: handler has no value".to_string()))?;
let handler_closure = self.value_to_ptr(handler_val)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let handler_fn = self.extract_closure_fn_ptr(handler_closure)?;
// Infer exception type tag from handler expression
let type_tag = self.infer_exception_tag_from_handler(handler_expr);
let i64_type = self.llvm_context().i64_type();
let tag_val = i64_type.const_int(type_tag, false);
// Use bhc_catch_typed for type-filtered catch
let catch_rts = self
.functions
.get(&VarId::new(1000703))
.ok_or_else(|| CodegenError::Internal("bhc_catch_typed not declared".to_string()))?;
let result = self
.builder()
.build_call(
*catch_rts,
&[
action_fn.into(),
action_closure.into(),
handler_fn.into(),
handler_closure.into(),
tag_val.into(),
],
"catch_result",
)
.map_err(|e| CodegenError::Internal(format!("catch call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catch: returned void".to_string()))?;
Ok(Some(result))
}
/// Infer the exception type tag from the handler expression's parameter type.
///
/// Returns 0 (catch-all) for most cases, 1 for IOException, 2 for ErrorCall.
fn infer_exception_tag_from_handler(&self, handler_expr: &Expr) -> u64 {
// Try to extract the handler's parameter type from its lambda variable
match handler_expr {
Expr::Lam(var, _, _) => {
let ty_name = format!("{:?}", var.ty);
if ty_name.contains("IOException") {
1 // EXC_TAG_IO_EXCEPTION
} else if ty_name.contains("ErrorCall") {
2 // EXC_TAG_ERROR_CALL
} else {
0 // EXC_TAG_SOME_EXCEPTION (catch-all)
}
}
_ => 0, // Default: catch-all
}
}
/// Lower `toException` - wrap a value in SomeException (tag 0).
fn lower_builtin_to_exception(
&mut self,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("toException: value has no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let make_exc_fn = self.functions.get(&VarId::new(1000700)).ok_or_else(|| {
CodegenError::Internal("bhc_make_some_exception not declared".to_string())
})?;
let i64_type = self.llvm_context().i64_type();
let tag_val = i64_type.const_int(0, false);
let result = self
.builder()
.build_call(*make_exc_fn, &[tag_val.into(), val_ptr.into()], "to_exc")
.map_err(|e| CodegenError::Internal(format!("toException failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("toException: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `fromException` - extract payload from SomeException, wrap in Just.
fn lower_builtin_from_exception(
&mut self,
exc_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let exc_val = self
.lower_expr(exc_expr)?
.ok_or_else(|| CodegenError::Internal("fromException: no value".to_string()))?;
let exc_ptr = self.value_to_ptr(exc_val)?;
let get_payload_fn = self.functions.get(&VarId::new(1000702)).ok_or_else(|| {
CodegenError::Internal("bhc_exc_get_payload not declared".to_string())
})?;
let payload = self
.builder()
.build_call(*get_payload_fn, &[exc_ptr.into()], "exc_payload")
.map_err(|e| CodegenError::Internal(format!("fromException failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fromException: returned void".to_string()))?;
// Wrap in Just (tag=1, arity=1)
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, payload)?;
Ok(Some(just.into()))
}
/// Lower `displayException` - convert exception to string.
fn lower_builtin_display_exception(
&mut self,
exc_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let exc_val = self
.lower_expr(exc_expr)?
.ok_or_else(|| CodegenError::Internal("displayException: no value".to_string()))?;
let exc_ptr = self.value_to_ptr(exc_val)?;
let show_exc_fn = self
.functions
.get(&VarId::new(1000704))
.ok_or_else(|| CodegenError::Internal("bhc_show_exception not declared".to_string()))?;
let cstr = self
.builder()
.build_call(*show_exc_fn, &[exc_ptr.into()], "exc_str")
.map_err(|e| CodegenError::Internal(format!("displayException failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("displayException: returned void".to_string()))?;
// Convert C-string to Haskell char list
let result = self.cstring_to_char_list(cstr.into_pointer_value())?;
Ok(Some(result.into()))
}
/// Lower `userError` - create an IOException from a String.
fn lower_builtin_user_error(
&mut self,
msg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let msg_val = self
.lower_expr(msg_expr)?
.ok_or_else(|| CodegenError::Internal("userError: no value".to_string()))?;
let msg_ptr = self.value_to_ptr(msg_val)?;
// Convert char list to C-string
let cstr_ptr = self.char_list_to_cstring(msg_ptr)?;
// Create IOException (tag 1)
let make_exc_fn = self.functions.get(&VarId::new(1000700)).ok_or_else(|| {
CodegenError::Internal("bhc_make_some_exception not declared".to_string())
})?;
let i64_type = self.llvm_context().i64_type();
let tag_val = i64_type.const_int(1, false); // EXC_TAG_IO_EXCEPTION
let result = self
.builder()
.build_call(
*make_exc_fn,
&[tag_val.into(), cstr_ptr.into()],
"user_error",
)
.map_err(|e| CodegenError::Internal(format!("userError failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("userError: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `try` - execute action wrapped in catch.
///
/// try :: IO a -> IO (Either SomeException a)
///
/// Uses bhc_catch with a handler that returns the exception as a Left value.
/// Simplified: wraps in catch; on success returns the result directly.
/// Full Either construction would require ADT allocation at codegen level.
fn lower_builtin_try(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For now, execute the action directly. A proper implementation would
// wrap in bhc_catch and construct Either values, but that requires
// ADT allocation infrastructure that's not yet available here.
let result = self.lower_expr(action_expr)?;
Ok(result)
}
/// Lower `bracket` - acquire/use/release with exception safety.
///
/// bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
///
/// Calls bhc_bracket(acquire_fn, acquire_env, release_fn, release_env, use_fn, use_env).
/// The RTS ensures release always runs even if use throws.
fn lower_builtin_bracket(
&mut self,
acquire_expr: &Expr,
release_expr: &Expr,
use_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Wrap acquire as a thunk (deferred IO action)
let acquire_closure = self.wrap_io_as_thunk(acquire_expr)?;
// release and use are functions (lambdas), lower normally
let release_val = self
.lower_expr(release_expr)?
.ok_or_else(|| CodegenError::Internal("bracket: release has no value".to_string()))?;
let use_val = self
.lower_expr(use_expr)?
.ok_or_else(|| CodegenError::Internal("bracket: use has no value".to_string()))?;
let release_closure = self.value_to_ptr(release_val)?;
let use_closure = self.value_to_ptr(use_val)?;
let acquire_fn = self.extract_closure_fn_ptr(acquire_closure)?;
let release_fn = self.extract_closure_fn_ptr(release_closure)?;
let use_fn = self.extract_closure_fn_ptr(use_closure)?;
let bracket_rts = self
.functions
.get(&VarId::new(1000825))
.ok_or_else(|| CodegenError::Internal("bhc_bracket not declared".to_string()))?;
let result = self
.builder()
.build_call(
*bracket_rts,
&[
acquire_fn.into(),
acquire_closure.into(),
release_fn.into(),
release_closure.into(),
use_fn.into(),
use_closure.into(),
],
"bracket_result",
)
.map_err(|e| CodegenError::Internal(format!("bracket call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bracket: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `finally` - execute action with guaranteed cleanup.
///
/// finally :: IO a -> IO b -> IO a
///
/// Calls bhc_finally(action_fn, action_env, cleanup_fn, cleanup_env).
/// The RTS ensures cleanup always runs, re-throwing any exception afterward.
fn lower_builtin_finally(
&mut self,
action_expr: &Expr,
cleanup_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Wrap action and cleanup as thunks so they're deferred (not eagerly evaluated)
let action_closure = self.wrap_io_as_thunk(action_expr)?;
let cleanup_closure = self.wrap_io_as_thunk(cleanup_expr)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let cleanup_fn = self.extract_closure_fn_ptr(cleanup_closure)?;
let finally_rts = self
.functions
.get(&VarId::new(1000088))
.ok_or_else(|| CodegenError::Internal("bhc_finally not declared".to_string()))?;
let result = self
.builder()
.build_call(
*finally_rts,
&[
action_fn.into(),
action_closure.into(),
cleanup_fn.into(),
cleanup_closure.into(),
],
"finally_result",
)
.map_err(|e| CodegenError::Internal(format!("finally call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("finally: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `onException` - execute action, run handler only on exception.
///
/// onException :: IO a -> IO b -> IO a
///
/// Calls bhc_on_exception(action_fn, action_env, handler_fn, handler_env).
/// The RTS runs the handler only if the action throws, then re-throws.
fn lower_builtin_on_exception(
&mut self,
action_expr: &Expr,
handler_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Wrap action and handler as thunks so they're deferred (not eagerly evaluated)
let action_closure = self.wrap_io_as_thunk(action_expr)?;
let handler_closure = self.wrap_io_as_thunk(handler_expr)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let handler_fn = self.extract_closure_fn_ptr(handler_closure)?;
let on_exc_rts = self
.functions
.get(&VarId::new(1000089))
.ok_or_else(|| CodegenError::Internal("bhc_on_exception not declared".to_string()))?;
let result = self
.builder()
.build_call(
*on_exc_rts,
&[
action_fn.into(),
action_closure.into(),
handler_fn.into(),
handler_closure.into(),
],
"on_exception_result",
)
.map_err(|e| CodegenError::Internal(format!("onException call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("onException: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `mask_` / `uninterruptibleMask_` — simple masking without restore.
///
/// mask_ :: IO a -> IO a
/// Wraps the action as a thunk and calls bhc_mask / bhc_uninterruptible_mask.
fn lower_builtin_mask_simple(
&mut self,
action_expr: &Expr,
uninterruptible: bool,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_closure = self.wrap_io_as_thunk(action_expr)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_id = if uninterruptible {
VarId::new(1000086) // bhc_uninterruptible_mask
} else {
VarId::new(1000083) // bhc_mask
};
let rts_fn = self.functions.get(&rts_id).ok_or_else(|| {
CodegenError::Internal("bhc_mask/bhc_uninterruptible_mask not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"mask_result",
)
.map_err(|e| CodegenError::Internal(format!("mask call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mask: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `mask` — masking with restore function.
///
/// mask :: ((IO a -> IO a) -> IO b) -> IO b
/// The action receives a restore closure created by the RTS.
fn lower_builtin_mask(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_val = self
.lower_expr(action_expr)?
.ok_or_else(|| CodegenError::Internal("mask: action has no value".to_string()))?;
let action_closure = self.value_to_ptr(action_val)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self
.functions
.get(&VarId::new(1000085)) // bhc_mask_with_restore
.ok_or_else(|| {
CodegenError::Internal("bhc_mask_with_restore not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"mask_restore_result",
)
.map_err(|e| CodegenError::Internal(format!("mask_with_restore call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("mask_with_restore: returned void".to_string())
})?;
Ok(Some(result))
}
/// Lower `uninterruptibleMask` — uninterruptible masking with restore function.
fn lower_builtin_uninterruptible_mask(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_val = self.lower_expr(action_expr)?.ok_or_else(|| {
CodegenError::Internal("uninterruptibleMask: action has no value".to_string())
})?;
let action_closure = self.value_to_ptr(action_val)?;
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self
.functions
.get(&VarId::new(1000087)) // bhc_uninterruptible_mask_with_restore
.ok_or_else(|| {
CodegenError::Internal(
"bhc_uninterruptible_mask_with_restore not declared".to_string(),
)
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"uninterruptible_mask_result",
)
.map_err(|e| {
CodegenError::Internal(format!("uninterruptible_mask_with_restore failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(
"uninterruptible_mask_with_restore: returned void".to_string(),
)
})?;
Ok(Some(result))
}
/// Lower `getMaskingState` — returns a MaskingState ADT.
///
/// Calls bhc_get_masking_state() to get i64 (0/1/2), then allocates
/// a MaskingState ADT with that tag.
fn lower_builtin_get_masking_state(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000824)) // bhc_get_masking_state
.ok_or_else(|| {
CodegenError::Internal("bhc_get_masking_state not declared".to_string())
})?;
let state_val = self
.builder()
.build_call(*rts_fn, &[], "masking_state")
.map_err(|e| CodegenError::Internal(format!("get_masking_state failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("get_masking_state: returned void".to_string())
})?;
// Allocate a MaskingState ADT with tag = state_val, 0 fields
let tm = self.type_mapper();
let adt_ty = self.adt_type(0);
let size = 8u64; // Just the tag, no fields
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size_val = tm.i64_type().const_int(size, false);
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], "masking_state_alloc")
.map_err(|e| CodegenError::Internal(format!("alloc failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("alloc returned void".to_string()))?;
// Store the tag
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, raw_ptr.into_pointer_value(), 0, "tag_slot")
.map_err(|e| CodegenError::Internal(format!("gep failed: {:?}", e)))?;
self.builder()
.build_store(tag_ptr, state_val.into_int_value())
.map_err(|e| CodegenError::Internal(format!("store failed: {:?}", e)))?;
Ok(Some(raw_ptr.into_pointer_value().into()))
}
/// Lower `seq` - force evaluation of first argument, return second.
fn lower_builtin_seq(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Evaluate first argument (for its effect of forcing evaluation)
let _a = self.lower_expr(a_expr)?;
// Return second argument
self.lower_expr(b_expr)
}
/// Lower `not` - boolean negation.
fn lower_builtin_not(
&mut self,
bool_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let bool_val = self
.lower_expr(bool_expr)?
.ok_or_else(|| CodegenError::Internal("not: argument has no value".to_string()))?;
let tm = self.type_mapper();
// Handle different representations of Bool
let result = match bool_val {
BasicValueEnum::IntValue(i) => {
// XOR with 1 to flip the boolean
let one = i.get_type().const_int(1, false);
self.builder()
.build_xor(i, one, "not_result")
.map_err(|e| CodegenError::Internal(format!("failed to build not: {:?}", e)))?
.into()
}
BasicValueEnum::PointerValue(p) => {
// Convert pointer to int, XOR with 1, convert back
let int_val = self
.builder()
.build_ptr_to_int(p, tm.i64_type(), "bool_to_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to convert bool: {:?}", e))
})?;
let one = tm.i64_type().const_int(1, false);
let xored = self
.builder()
.build_xor(int_val, one, "not_result")
.map_err(|e| CodegenError::Internal(format!("failed to build not: {:?}", e)))?;
self.builder()
.build_int_to_ptr(xored, tm.ptr_type(), "not_to_ptr")
.map_err(|e| {
CodegenError::Internal(format!("failed to convert not result: {:?}", e))
})?
.into()
}
_ => return Err(CodegenError::TypeError("not expects a boolean".to_string())),
};
Ok(Some(result))
}
// ========================================================================
// IO Builtin Functions
// ========================================================================
/// Lower `putStrLn` - print a string followed by newline.
fn lower_builtin_put_str_ln(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Print the string (which is a [Char] linked list), then print a newline
self.lower_print_char_list(str_expr)?;
// Print newline
let newline_fn = self
.functions
.get(&VarId::new(1000010))
.ok_or_else(|| CodegenError::Internal("bhc_print_newline not declared".to_string()))?;
self.builder()
.build_call(*newline_fn, &[], "")
.map_err(|e| CodegenError::Internal(format!("failed to call newline: {:?}", e)))?;
// Return unit (null pointer for IO ())
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `putStr` - print a string without newline.
fn lower_builtin_put_str(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Print the string (which is a [Char] linked list)
self.lower_print_char_list(str_expr)?;
// Return unit
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Emit a loop that walks a [Char] linked list and prints each character.
fn lower_print_char_list(&mut self, str_expr: &Expr) -> CodegenResult<()> {
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("print: string has no value".to_string()))?;
let list_ptr = match str_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"print expects a string (pointer)".to_string(),
))
}
};
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let print_char_fn = self
.functions
.get(&VarId::new(1000009))
.ok_or_else(|| CodegenError::Internal("bhc_print_char not declared".to_string()))?;
// Create loop blocks
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "print_loop");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "print_body");
let loop_end = self
.llvm_context()
.append_basic_block(current_fn, "print_done");
// Branch to loop header
let pre_loop_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Loop header: phi node for current list pointer
self.builder().position_at_end(loop_header);
let current_node = self
.builder()
.build_phi(tm.ptr_type(), "cur_node")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
current_node.add_incoming(&[(&list_ptr, pre_loop_block)]);
// Load tag from current node
let tag = self.extract_adt_tag(current_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_end, loop_body)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Loop body: extract char, print it, advance to tail
self.builder().position_at_end(loop_body);
let node_ptr = current_node.as_basic_value().into_pointer_value();
// Extract head (char) at field 0
let head_ptr = self.extract_adt_field(node_ptr, 2, 0)?;
// The char was stored via int_to_ptr, so convert back to int
let char_val = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "char_val")
.map_err(|e| CodegenError::Internal(format!("failed to get char: {:?}", e)))?;
// Truncate to i32 for bhc_print_char
let char_i32 = self
.builder()
.build_int_truncate(char_val, tm.i32_type(), "char_i32")
.map_err(|e| CodegenError::Internal(format!("failed to truncate: {:?}", e)))?;
self.builder()
.build_call(*print_char_fn, &[char_i32.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call print_char: {:?}", e)))?;
// Extract tail at field 1
let tail_ptr = self.extract_adt_field(node_ptr, 2, 1)?;
current_node.add_incoming(&[(&tail_ptr, loop_body)]);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Continue after loop
self.builder().position_at_end(loop_end);
Ok(())
}
/// Lower `putChar` - print a single character.
fn lower_builtin_put_char(
&mut self,
char_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let char_val = self
.lower_expr(char_expr)?
.ok_or_else(|| CodegenError::Internal("putChar: char has no value".to_string()))?;
let char_int = match char_val {
BasicValueEnum::IntValue(i) => i,
BasicValueEnum::PointerValue(p) => {
// Might be a boxed char - unbox it
self.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_char")
.map_err(|e| CodegenError::Internal(format!("failed to unbox char: {:?}", e)))?
}
_ => {
return Err(CodegenError::TypeError(
"putChar expects a Char".to_string(),
))
}
};
// Truncate to i32 for the RTS call
let char_i32 = self
.builder()
.build_int_truncate(char_int, self.type_mapper().i32_type(), "char_i32")
.map_err(|e| CodegenError::Internal(format!("failed to truncate char: {:?}", e)))?;
// Call bhc_print_char
let print_fn = self
.functions
.get(&VarId::new(1000009))
.ok_or_else(|| CodegenError::Internal("bhc_print_char not declared".to_string()))?;
self.builder()
.build_call(*print_fn, &[char_i32.into()], "")
.map_err(|e| CodegenError::Internal(format!("failed to call print_char: {:?}", e)))?;
// Return unit
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `print` - print a value with its Show instance (simplified).
///
/// For now, we detect the type at codegen time and call the appropriate
/// print function. This is a simplification - real Haskell uses type classes.
fn lower_builtin_print(
&mut self,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Get the type of the expression to decide how to print
let expr_ty = val_expr.ty();
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("print: value has no result".to_string()))?;
match val {
BasicValueEnum::IntValue(i) => {
// Check if this is a boolean (from comparison or boolean function)
if self.is_bool_type(&expr_ty) || self.expr_looks_like_bool(val_expr) {
// Print as boolean (True/False)
let print_fn = self.functions.get(&VarId::new(1000008)).ok_or_else(|| {
CodegenError::Internal("bhc_print_bool_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[i.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_bool: {:?}", e))
})?;
} else {
// Print as integer
let print_fn = self.functions.get(&VarId::new(1000000)).ok_or_else(|| {
CodegenError::Internal("bhc_print_int_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[i.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_int: {:?}", e))
})?;
}
}
BasicValueEnum::FloatValue(f) => {
// Print as double
let print_fn = self.functions.get(&VarId::new(1000001)).ok_or_else(|| {
CodegenError::Internal("bhc_print_double_ln not declared".to_string())
})?;
// Extend float to double if needed (check if it's f32)
let f64_type = self.type_mapper().f64_type();
let f32_type = self.type_mapper().f32_type();
let f64_val = if f.get_type() == f32_type {
self.builder()
.build_float_ext(f, f64_type, "to_f64")
.map_err(|e| {
CodegenError::Internal(format!("failed to extend float: {:?}", e))
})?
} else {
f
};
self.builder()
.build_call(*print_fn, &[f64_val.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_double: {:?}", e))
})?;
}
BasicValueEnum::PointerValue(p) => {
// Pointer could be a boxed value from closure call, string, or ADT.
// Use the expression's type to decide how to print.
// Check list first - by type or by expression structure (for when type is Error)
// Compound showable values (lists, Maybe, Either, tuples) must
// be rendered through the typed `show` machinery so their
// elements/fields show recursively instead of printing raw
// pointers — matching `print x = putStrLn (show x)`. Detect them
// structurally (via `infer_show_from_expr`) as well as by type,
// because the type is often erased to `Error` by codegen time —
// the greedy "boxed int" branch below would otherwise claim them.
// `print` on a String must show it *with quotes* (`print x =
// putStrLn (show x)`), unlike `putStrLn`/`putStr`. Route it
// through the show machinery too (the StringList descriptor adds
// the quotes), alongside the other compound types.
let compound_show = matches!(
self.infer_show_from_expr(val_expr).map(|(c, _, _)| c),
Some(
ShowCoerce::List
| ShowCoerce::MaybeOf
| ShowCoerce::EitherOf
| ShowCoerce::Tuple2Of
| ShowCoerce::StringList
)
) || self.is_list_type(&expr_ty)
|| self.expr_looks_like_list(val_expr)
|| self.is_string_type(&expr_ty)
|| self.is_maybe_type(&expr_ty).is_some()
|| self.is_either_type(&expr_ty).is_some()
|| self.is_tuple_type(&expr_ty).is_some();
if compound_show {
self.print_via_show_desc(p, val_expr)?;
} else if let Some(adt_type) = self
.infer_adt_type_from_expr(val_expr)
.filter(|tn| self.derived_show_fns.contains_key(tn))
{
// A user-defined ADT with derived Show. Detected
// structurally because its type is usually erased to `Error`
// by codegen (otherwise the boxed-int branch below would
// print its pointer).
self.print_adt_via_derived_show(p, &adt_type)?;
} else if self.is_bool_type(&expr_ty) || self.expr_looks_like_bool(val_expr) {
// Bool value - use extract_bool_tag which handles both Bool ADT
// (heap-allocated from any/all/even/odd/char predicates) and
// tagged-int-as-pointer (from comparison operators)
let bool_tag = self.extract_bool_tag(p)?;
let print_fn = self.functions.get(&VarId::new(1000008)).ok_or_else(|| {
CodegenError::Internal("bhc_print_bool_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[bool_tag.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_bool: {:?}", e))
})?;
} else if self.is_int_type(&expr_ty) || self.is_type_variable_or_error(&expr_ty) {
// Boxed integer (or polymorphic type that might be int) - unbox and print as int.
// For type variables from closures, we assume int since that's the most common case.
// This is safe because boxed ints use int_to_ptr which stores the value in the
// pointer bits, not as an actual memory reference.
let int_val = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox int: {:?}", e))
})?;
let print_fn = self.functions.get(&VarId::new(1000000)).ok_or_else(|| {
CodegenError::Internal("bhc_print_int_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[int_val.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_int: {:?}", e))
})?;
} else if self.is_float_type(&expr_ty) {
// Boxed float - unbox and print as double
let bits = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_float_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox float: {:?}", e))
})?;
let float_val = self
.builder()
.build_bit_cast(bits, self.type_mapper().f64_type(), "to_double")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to double: {:?}", e))
})?;
let print_fn = self.functions.get(&VarId::new(1000001)).ok_or_else(|| {
CodegenError::Internal("bhc_print_double_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[float_val.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_double: {:?}", e))
})?;
} else {
// Assume it's a string or other pointer type
let print_fn = self.functions.get(&VarId::new(1000002)).ok_or_else(|| {
CodegenError::Internal("bhc_print_string_ln not declared".to_string())
})?;
self.builder()
.build_call(*print_fn, &[p.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("failed to call print_string: {:?}", e))
})?;
}
}
_ => {
return Err(CodegenError::Unsupported(
"print: unsupported value type".to_string(),
));
}
}
// Return unit
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Print a value by rendering it through the typed `show` machinery
/// (`bhc_show_with_desc`) and writing the result with a trailing newline —
/// i.e. `print x = putStrLn (show x)`. Used for compound types (lists,
/// Maybe, Either, tuples) whose elements/fields must be shown recursively
/// rather than printed as raw pointers.
fn print_via_show_desc(
&mut self,
p: inkwell::values::PointerValue<'ctx>,
val_expr: &Expr,
) -> CodegenResult<()> {
let desc = self.build_show_descriptor(val_expr);
let show_fn = *self
.functions
.get(&VarId::new(1000099))
.ok_or_else(|| CodegenError::Internal("bhc_show_with_desc not declared".to_string()))?;
let cstr = self
.builder()
.build_call(show_fn, &[p.into(), desc.into()], "show_desc")
.map_err(|e| CodegenError::Internal(format!("show_with_desc call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("show_with_desc: returned void".to_string()))?;
let print_ln = *self.functions.get(&VarId::new(1000002)).ok_or_else(|| {
CodegenError::Internal("bhc_print_string_ln not declared".to_string())
})?;
self.builder()
.build_call(print_ln, &[cstr.into_pointer_value().into()], "")
.map_err(|e| CodegenError::Internal(format!("print_string_ln call failed: {:?}", e)))?;
Ok(())
}
/// Print a user-defined ADT value that has a derived `Show`: call its
/// `$derived_show_<Type>` function on the already-lowered value `p`, then
/// print the resulting `[Char]` with a trailing newline — `print x =
/// putStrLn (show x)`. Uses `p` directly (no re-lowering of the argument).
fn print_adt_via_derived_show(
&mut self,
p: inkwell::values::PointerValue<'ctx>,
type_name: &str,
) -> CodegenResult<()> {
let show_var_id = *self
.derived_show_fns
.get(type_name)
.ok_or_else(|| CodegenError::Internal(format!("no derived show for {type_name}")))?;
let show_fn = *self.functions.get(&show_var_id).ok_or_else(|| {
CodegenError::Internal(format!("derived show fn for {type_name} not declared"))
})?;
// Derived show uses the standard (env, value) -> string closure calling
// convention, returning a `[Char]`.
let ptr_type = self.type_mapper().ptr_type();
let null_env = ptr_type.const_null();
let fn_ptr = show_fn.as_global_value().as_pointer_value();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), p.into()],
"derived_show",
)
.map_err(|e| CodegenError::Internal(format!("derived show call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("derived show: returned void".to_string()))?;
let list_ptr = self.value_to_ptr(result)?;
self.lower_print_char_list_ptr(list_ptr)?;
let newline_fn = *self
.functions
.get(&VarId::new(1000010))
.ok_or_else(|| CodegenError::Internal("bhc_print_newline not declared".to_string()))?;
self.builder()
.build_call(newline_fn, &[], "")
.map_err(|e| CodegenError::Internal(format!("print newline: {:?}", e)))?;
Ok(())
}
/// Check if a type is an integer type.
fn is_int_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => {
let name = con.name.as_str();
matches!(
name,
"Int" | "Int#" | "Int64" | "Int32" | "Word" | "Word64" | "Word32"
)
}
Ty::Prim(prim) => {
use bhc_types::PrimTy;
matches!(prim, PrimTy::I32 | PrimTy::I64 | PrimTy::U32 | PrimTy::U64)
}
Ty::App(f, _) => self.is_int_type(f),
Ty::Forall(_, body) => self.is_int_type(body),
_ => false,
}
}
/// E.45: Check if a type is the Integer (arbitrary precision) type.
fn is_integer_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => con.name.as_str() == "Integer",
Ty::App(f, _) => self.is_integer_type(f),
Ty::Forall(_, body) => self.is_integer_type(body),
_ => false,
}
}
/// E.45: Check if the parameter at `param_idx` in a function type is Integer.
/// Walks through Ty::Fun(param, result) chain to find the param at the given index.
fn is_integer_param_at(&self, fn_ty: &Ty, param_idx: usize) -> bool {
let mut current = fn_ty;
// Skip through foralls
while let Ty::Forall(_, body) = current {
current = body;
}
// Walk the function arrow chain to the param_idx-th parameter
for _ in 0..param_idx {
if let Ty::Fun(_, result) = current {
current = result;
} else {
return false;
}
}
if let Ty::Fun(param, _) = current {
self.is_integer_type(param)
} else {
false
}
}
/// E.45: Extract a list of which parameter positions in a function type are Integer.
/// For `Integer -> Integer -> IO ()`, returns `[true, true]`.
fn extract_integer_param_positions(&self, fn_ty: &Ty) -> Vec<bool> {
let mut positions = Vec::new();
let mut current = fn_ty;
// Skip through foralls
while let Ty::Forall(_, body) = current {
current = body;
}
// Walk the function arrow chain
while let Ty::Fun(param, result) = current {
positions.push(self.is_integer_type(param));
current = result;
}
positions
}
/// Check if a type is a float type.
fn is_float_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => {
let name = con.name.as_str();
matches!(name, "Float" | "Float#" | "Double" | "Double#")
}
Ty::Prim(prim) => {
use bhc_types::PrimTy;
matches!(prim, PrimTy::F32 | PrimTy::F64)
}
Ty::App(f, _) => self.is_float_type(f),
Ty::Forall(_, body) => self.is_float_type(body),
_ => false,
}
}
/// Check if a type is a Bool type.
fn is_bool_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => {
let name = con.name.as_str();
matches!(name, "Bool" | "Boolean")
}
Ty::App(f, _) => self.is_bool_type(f),
Ty::Forall(_, body) => self.is_bool_type(body),
_ => false,
}
}
/// Check if a type is a type variable (polymorphic) or error type.
/// We treat error types the same as type variables since we don't know
/// the concrete type and should default to treating it as a potential integer.
fn is_type_variable_or_error(&self, ty: &Ty) -> bool {
match ty {
Ty::Var(_) => true,
Ty::Error => true, // Error type might be unresolved - treat as unknown
Ty::App(f, _) => self.is_type_variable_or_error(f),
Ty::Forall(_, body) => self.is_type_variable_or_error(body),
_ => false,
}
}
/// Check if a type is a list type.
fn is_list_type(&self, ty: &Ty) -> bool {
match ty {
// Direct list type
Ty::List(_) => true,
// Type application form: [] a
Ty::App(f, _) => {
// Check if the type constructor is []
if let Ty::Con(con) = f.as_ref() {
con.name.as_str() == "[]" || con.name.as_str() == "List"
} else {
false
}
}
// Just "[]" without argument (unlikely but handle it)
Ty::Con(con) => con.name.as_str() == "[]" || con.name.as_str() == "List",
Ty::Forall(_, body) => self.is_list_type(body),
_ => false,
}
}
/// Check if a type is a Char type.
fn is_char_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => con.name.as_str() == "Char",
Ty::App(f, _) => self.is_char_type(f),
Ty::Forall(_, body) => self.is_char_type(body),
_ => false,
}
}
/// Check if a type is specifically Double (not Float).
fn is_double_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => matches!(con.name.as_str(), "Double" | "Double#"),
Ty::Prim(prim) => matches!(prim, bhc_types::PrimTy::F64),
Ty::App(f, _) => self.is_double_type(f),
Ty::Forall(_, body) => self.is_double_type(body),
_ => false,
}
}
/// Check if a type is [Char] (String).
fn is_string_type(&self, ty: &Ty) -> bool {
match ty {
Ty::List(elem) => self.is_char_type(elem),
Ty::App(f, a) => {
if let Ty::Con(con) = f.as_ref() {
(con.name.as_str() == "[]" || con.name.as_str() == "List")
&& self.is_char_type(a)
} else {
false
}
}
Ty::Con(con) => con.name.as_str() == "String",
Ty::Forall(_, body) => self.is_string_type(body),
_ => false,
}
}
/// Check if a type is the Rational type.
fn is_rational_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Con(con) => con.name.as_str() == "Rational",
Ty::App(f, _) => self.is_rational_type(f),
Ty::Forall(_, body) => self.is_rational_type(body),
_ => false,
}
}
/// Check if a type is Maybe a, returning the inner type.
fn is_maybe_type<'t>(&self, ty: &'t Ty) -> Option<&'t Ty> {
match ty {
Ty::App(f, a) => {
if let Ty::Con(con) = f.as_ref() {
if con.name.as_str() == "Maybe" {
return Some(a);
}
}
None
}
Ty::Forall(_, body) => self.is_maybe_type(body),
_ => None,
}
}
/// Check if a type is Either a b, returning (left, right) types.
fn is_either_type<'t>(&self, ty: &'t Ty) -> Option<(&'t Ty, &'t Ty)> {
match ty {
Ty::App(f, b) => {
if let Ty::App(ff, a) = f.as_ref() {
if let Ty::Con(con) = ff.as_ref() {
if con.name.as_str() == "Either" {
return Some((a, b));
}
}
}
None
}
Ty::Forall(_, body) => self.is_either_type(body),
_ => None,
}
}
/// Check if a type is a 2-tuple (a, b), returning (fst, snd) types.
fn is_tuple_type<'t>(&self, ty: &'t Ty) -> Option<(&'t Ty, &'t Ty)> {
match ty {
Ty::Tuple(elems) if elems.len() == 2 => Some((&elems[0], &elems[1])),
Ty::App(f, b) => {
if let Ty::App(ff, a) = f.as_ref() {
if let Ty::Con(con) = ff.as_ref() {
if con.name.as_str() == "(,)" || con.name.as_str() == "Tuple2" {
return Some((a, b));
}
}
}
None
}
Ty::Forall(_, body) => self.is_tuple_type(body),
_ => None,
}
}
/// Check if a type is unit ().
fn is_unit_type(&self, ty: &Ty) -> bool {
match ty {
Ty::Tuple(elems) => elems.is_empty(),
Ty::Con(con) => con.name.as_str() == "()" || con.name.as_str() == "Unit",
Ty::Forall(_, body) => self.is_unit_type(body),
_ => false,
}
}
/// Get the element type from a list type.
// Type-inspection helper retained for list-handling lowering paths.
#[allow(dead_code)]
fn list_elem_type<'t>(&self, ty: &'t Ty) -> Option<&'t Ty> {
match ty {
Ty::List(elem) => Some(elem),
Ty::App(f, a) => {
if let Ty::Con(con) = f.as_ref() {
if con.name.as_str() == "[]" || con.name.as_str() == "List" {
return Some(a);
}
}
None
}
Ty::Forall(_, body) => self.list_elem_type(body),
_ => None,
}
}
/// Convert a type to a show type tag for RTS functions.
/// 0=Int, 1=Double, 2=Float, 3=Bool, 4=Char, 5=String
// Retained for RTS show dispatch; not currently called on all paths.
#[allow(dead_code)]
fn type_to_show_tag(&self, ty: &Ty) -> i64 {
if self.is_char_type(ty) {
return 4;
}
if self.is_bool_type(ty) {
return 3;
}
if self.is_double_type(ty) {
return 1;
}
if self.is_float_type(ty) {
// is_float_type matches both Float and Double; if we got here, it's Float only
// since Double was already checked above
return 2;
}
if self.is_string_type(ty) {
return 5;
}
if self.is_int_type(ty) {
return 0;
}
0 // default to Int
}
/// Check if a function expression (in App position) returns Bool when fully applied.
fn expr_returns_bool(&self, func_expr: &Expr) -> bool {
match func_expr {
// Unary Bool-returning functions: even, odd, not, null, isAlpha, etc.
Expr::Var(var, _) => {
let name = var.name.as_str();
matches!(
name,
"even"
| "odd"
| "not"
| "null"
| "isEOF"
| "isAlpha"
| "isAlphaNum"
| "isAscii"
| "isControl"
| "isDigit"
| "isHexDigit"
| "isLetter"
| "isLower"
| "isNumber"
| "isPrint"
| "isPunctuation"
| "isSpace"
| "isSymbol"
| "isUpper"
| "isJust"
| "isNothing"
| "isLeft"
| "isRight"
| "isAbsolute"
| "isRelative"
| "hasExtension"
| "doesFileExist"
| "doesDirectoryExist"
| "Data.Map.null"
| "Data.Set.null"
| "Data.IntMap.null"
| "Data.IntSet.null"
| "Data.Sequence.null"
)
}
// Binary Bool-returning: fully applied comparison operators
// e.g. App(App(==, x), y) — here func_expr is App(==, x)
Expr::App(f, _, _) => match f.as_ref() {
Expr::Var(var, _) => {
let name = var.name.as_str();
matches!(
name,
"==" | "/="
| "<"
| ">"
| "<="
| ">="
| "&&"
| "||"
| "and"
| "or"
| "elem"
| "notElem"
| "any"
| "all"
| "Data.Map.member"
| "Data.Map.notMember"
| "Data.Map.isSubmapOf"
| "Data.Set.member"
| "Data.Set.notMember"
| "Data.Set.isSubsetOf"
| "Data.IntMap.member"
| "Data.IntSet.member"
| "isPrefixOf"
| "isSuffixOf"
| "isInfixOf"
)
}
_ => false,
},
Expr::TyApp(inner, _, _) => self.expr_returns_bool(inner),
_ => false,
}
}
/// Check if a fully-applied expression returns Int (e.g., gcd 12 8).
fn expr_returns_int(&self, expr: &Expr) -> bool {
match expr {
Expr::App(f, _, _) => {
match f.as_ref() {
// Fully applied binary Int ops: gcd, lcm, div, mod, etc.
Expr::App(ff, _, _) => match ff.as_ref() {
Expr::Var(var, _) => {
let name = var.name.as_str();
matches!(
name,
"gcd"
| "lcm"
| "quot"
| "rem"
| "+"
| "-"
| "*"
| "div"
| "mod"
| "min"
| "max"
| "subtract"
)
}
_ => false,
},
// Unary Int ops
Expr::Var(var, _) => {
let name = var.name.as_str();
if name == "succ" || name == "pred" {
// E.54: succ/pred on user enums return ADT, not Int
if let Expr::App(_, arg, _) = expr {
return self.infer_adt_type_from_expr(arg).is_none();
}
}
if name == "read" && !self.derived_read_fns.is_empty() {
return false; // read returns ADT, not Int
}
matches!(
name,
"length"
| "ord"
| "abs"
| "signum"
| "negate"
| "fromIntegral"
| "toInteger"
| "fromInteger"
| "digitToInt"
| "read"
| "succ"
| "pred"
)
}
_ => false,
}
}
_ => false,
}
}
/// Check if a fully-applied expression returns Double/Float.
fn expr_returns_double(&self, expr: &Expr) -> bool {
match expr {
Expr::Lit(Literal::Double(_), _, _) | Expr::Lit(Literal::Float(_), _, _) => true,
Expr::App(f, _, _) => {
match f.as_ref() {
// Unary Double-returning functions
Expr::Var(var, _) => {
let n = var.name.as_str();
if matches!(
n,
"sqrt"
| "sin"
| "cos"
| "tan"
| "exp"
| "log"
| "asin"
| "acos"
| "atan"
| "sinh"
| "cosh"
| "tanh"
| "abs"
| "negate"
| "signum"
| "recip"
| "ceiling"
| "floor"
| "round"
| "truncate"
) {
return true;
}
// Type-based inference: check if function returns Double or IO Double
self.fun_returns_double(&var.ty)
}
// Binary Double-returning: App(App(op, x), y) — f is App(op, x)
Expr::App(ff, _, _) => {
match ff.as_ref() {
Expr::Var(var, _) => {
let n = var.name.as_str();
if matches!(n, "/" | "**") {
return true;
}
// Type-based: check if the partially-applied function returns Double
// For a binary function A -> B -> C, after one application we have B -> C
if let Ty::Fun(_, ret) = &var.ty {
self.fun_returns_double(ret)
} else {
false
}
}
_ => false,
}
}
_ => false,
}
}
Expr::TyApp(inner, _, _) => self.expr_returns_double(inner),
_ => false,
}
}
/// Check if a function type's return type is Double (unwrapping IO if needed).
fn fun_returns_double(&self, ty: &Ty) -> bool {
match ty {
Ty::Fun(_, ret) => self.fun_returns_double(ret),
_ => self.is_double_or_io_double(ty),
}
}
/// Check if a type is Double or IO Double.
fn is_double_or_io_double(&self, ty: &Ty) -> bool {
if self.is_double_type(ty) {
return true;
}
// Check for IO Double: App(Con("IO"), Con("Double"))
if let Ty::App(f, a) = ty {
if let Ty::Con(con) = f.as_ref() {
if con.name.as_str() == "IO" {
return self.is_double_type(a);
}
}
}
false
}
/// Check if a function expression returns Ordering (e.g., `compare`).
fn expr_returns_ordering(&self, func_expr: &Expr) -> bool {
match func_expr {
// Fully applied binary: App(compare, x) — the outer App provides the second arg
Expr::App(ff, _, _) => match ff.as_ref() {
Expr::Var(var, _) => var.name.as_str() == "compare",
_ => false,
},
Expr::TyApp(inner, _, _) => self.expr_returns_ordering(inner),
_ => false,
}
}
/// Check if an expression looks like a list based on its structure.
/// This is used when type information is unavailable (Error type).
fn expr_looks_like_list(&self, expr: &Expr) -> bool {
match expr {
// Application of a constructor or function
Expr::App(f, _, _) => self.expr_looks_like_list(f),
// Type application
Expr::TyApp(e, _, _) => self.expr_looks_like_list(e),
// Let binding - check the body
Expr::Let(_, body, _) => self.expr_looks_like_list(body),
// Variable that's a list constructor or function returning a list
Expr::Var(var, _) => {
let name = var.name.as_str();
// List constructors and functions that return lists
matches!(
name,
":" | "[]"
| "Nil"
| "Cons"
| "map"
| "filter"
| "reverse"
| "take"
| "drop"
| "enumFromTo"
| "enumFrom"
| "enumFromThen"
| "enumFromThenTo"
| "replicate"
| "append"
| "tail"
| "maybeToList"
| "catMaybes"
| "mapMaybe"
| "lefts"
| "rights"
| "guard"
| "sort"
| "sortBy"
| "sortOn"
| "nub"
| "nubBy"
| "delete"
| "deleteBy"
| "union"
| "unionBy"
| "intersect"
| "intersectBy"
| "insert"
| "concat"
| "concatMap"
| "zip"
| "zipWith"
| "zip3"
| "zipWith3"
| "transpose"
| "intersperse"
| "intercalate"
| "takeWhile"
| "dropWhile"
| "Data.Map.toList"
| "Data.Map.toAscList"
| "Data.Map.toDescList"
| "Data.Map.keys"
| "Data.Map.elems"
| "Data.Map.assocs"
| "Data.Set.toList"
| "Data.Set.toAscList"
| "Data.Set.toDescList"
| "Data.Set.elems"
| "Data.Sequence.toList"
)
}
_ => false,
}
}
/// Check if an expression looks like a boolean based on its structure.
/// This is used when type information is unavailable (Error type).
fn expr_looks_like_bool(&self, expr: &Expr) -> bool {
match expr {
// Application of comparison or boolean function
Expr::App(f, _, _) => self.expr_looks_like_bool(f),
// Type application
Expr::TyApp(e, _, _) => self.expr_looks_like_bool(e),
// Let binding - check the body
Expr::Let(_, body, _) => self.expr_looks_like_bool(body),
// Variable that's a comparison operator or boolean function
Expr::Var(var, _) => {
let name = var.name.as_str();
// Comparison operators and boolean functions
matches!(
name,
">" | "<"
| ">="
| "<="
| "=="
| "/="
| "True"
| "False"
| "not"
| "&&"
| "||"
| "and"
| "or"
| "isJust"
| "isNothing"
| "isLeft"
| "isRight"
| "null"
| "elem"
| "notElem"
| "even"
| "odd"
| "any"
| "all"
| "greaterThan"
| "lessThan"
| "equals"
| "isAlpha"
| "isAlphaNum"
| "isAscii"
| "isControl"
| "isDigit"
| "isHexDigit"
| "isLetter"
| "isLower"
| "isNumber"
| "isPrint"
| "isPunctuation"
| "isSpace"
| "isSymbol"
| "isUpper"
| "isAbsolute"
| "isRelative"
| "hasExtension"
| "doesFileExist"
| "doesDirectoryExist"
| "isPrefixOf"
| "isSuffixOf"
| "isInfixOf"
| "Data.Map.null"
| "Data.Set.null"
| "Data.IntMap.null"
| "Data.IntSet.null"
| "Data.Sequence.null"
| "Data.Map.member"
| "Data.Map.notMember"
| "Data.Map.isSubmapOf"
| "Data.Set.member"
| "Data.Set.notMember"
| "Data.Set.isSubsetOf"
| "Data.IntMap.member"
| "Data.IntSet.member"
)
}
_ => false,
}
}
/// Lower `getLine` - read a line from stdin via RTS `bhc_getLine`.
fn lower_builtin_get_line(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000071))
.ok_or_else(|| CodegenError::Internal("bhc_getLine not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "getline_result")
.map_err(|e| CodegenError::Internal(format!("getLine call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getLine: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `>>=` (bind) for monads.
/// For IO: execute first action, pass result to function, execute result.
fn lower_builtin_bind(
&mut self,
action_expr: &Expr,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Execute the first action
let action_result = self
.lower_expr(action_expr)?
.ok_or_else(|| CodegenError::Internal(">>=: action has no value".to_string()))?;
// Lower the function
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal(">>=: function has no value".to_string()))?;
// Apply the function to the action's result
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
">>=: function must be a closure".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Convert action result to pointer for uniform calling convention
let action_ptr = self.value_to_ptr(action_result)?;
// Call the function with the action result
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), action_ptr.into()],
"bind_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call bind function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(">>=: function returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `>>` (then) for monads.
/// For IO: execute first action, ignore result, execute second action.
fn lower_builtin_then(
&mut self,
action1_expr: &Expr,
action2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Execute the first action (result is discarded)
let _action1_result = self.lower_expr(action1_expr)?;
// Execute the second action and return its result
self.lower_expr(action2_expr)
}
/// Lower `return` / `pure` for monads.
/// For IO: just return the value wrapped (identity for our simple model).
fn lower_builtin_return(
&mut self,
value_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For our simple IO model, return is identity
self.lower_expr(value_expr)
}
// ========================================================================
// Monad Transformer Helpers
// ========================================================================
/// Helper to build a closure that captures values from the current scope.
/// `body_name` is a unique name for the closure function.
/// `num_captures` is how many captured values there are.
/// `build_body` receives (builder, env_ptr, arg) and should build the body + return.
// Monad-transformer closure helper retained for transformer lowering paths.
#[allow(dead_code)]
fn build_transformer_closure(
&mut self,
body_name: &str,
captures: &[BasicValueEnum<'ctx>],
) -> CodegenResult<PointerValue<'ctx>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let _num_captures = captures.len() as u32;
// Create the closure function: (ptr env, ptr arg) -> ptr
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let func = self
.module
.llvm_module()
.add_function(body_name, fn_type, None);
// Allocate closure with captures
let capture_pairs: Vec<(VarId, BasicValueEnum<'ctx>)> = captures
.iter()
.enumerate()
.map(|(i, v)| (VarId::new(900000 + i), *v))
.collect();
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &capture_pairs)?;
Ok(closure_ptr)
}
/// Helper: emit a transformer closure body function that has already been added to the module.
/// Returns the function value so the caller can build its body.
/// Takes 2 arguments: (closure_env, state)
fn get_or_create_transformer_fn(&mut self, name: &str) -> FunctionValue<'ctx> {
let ptr_type = self.type_mapper().ptr_type();
if let Some(existing) = self.module.llvm_module().get_function(name) {
existing
} else {
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
self.module.llvm_module().add_function(name, fn_type, None)
}
}
/// Helper for nested transformers (e.g., StateT over ReaderT).
/// Takes 3 arguments: (closure_env, state, reader_env)
fn get_or_create_nested_transformer_fn(&mut self, name: &str) -> FunctionValue<'ctx> {
let ptr_type = self.type_mapper().ptr_type();
if let Some(existing) = self.module.llvm_module().get_function(name) {
existing
} else {
let fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
self.module.llvm_module().add_function(name, fn_type, None)
}
}
/// Helper: get or create a list append function for use in WriterT.
/// The function has signature: (ptr, ptr) -> ptr (list1, list2) -> appended
fn get_or_create_list_append_fn(&mut self) -> CodegenResult<FunctionValue<'ctx>> {
let fn_name = "bhc_list_append";
let ptr_type = self.type_mapper().ptr_type();
if let Some(existing) = self.module.llvm_module().get_function(fn_name) {
return Ok(existing);
}
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let func = self
.module
.llvm_module()
.add_function(fn_name, fn_type, None);
// Build the function body - implements: append xs ys = foldr (:) ys xs
// which reverses xs, then conses each element onto ys
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let rev_header = self.llvm_ctx.append_basic_block(func, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(func, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(func, "rev_exit");
let fold_header = self.llvm_ctx.append_basic_block(func, "fold_header");
let fold_body = self.llvm_ctx.append_basic_block(func, "fold_body");
let fold_exit = self.llvm_ctx.append_basic_block(func, "fold_exit");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let list1 = func.get_nth_param(0).unwrap().into_pointer_value();
let list2 = func.get_nth_param(1).unwrap().into_pointer_value();
let tm = self.type_mapper();
// Start with empty accumulator for reverse
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
// Reverse loop header
self.builder().position_at_end(rev_header);
let rev_acc_phi = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("append phi: {:?}", e)))?;
let rev_list_phi = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("append phi: {:?}", e)))?;
rev_acc_phi.add_incoming(&[(&nil, entry)]);
rev_list_phi.add_incoming(&[(&list1, entry)]);
let rev_current = rev_list_phi.as_basic_value().into_pointer_value();
let rev_tag = self.extract_adt_tag(rev_current)?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("append cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
// Reverse loop body
self.builder().position_at_end(rev_body);
let rev_head = self.extract_adt_field(rev_current, 2, 0)?;
let rev_tail = self.extract_adt_field(rev_current, 2, 1)?;
let rev_new_acc = self.build_cons(rev_head.into(), rev_acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
rev_acc_phi.add_incoming(&[(&rev_new_acc, rev_body)]);
rev_list_phi.add_incoming(&[(&rev_tail, rev_body)]);
// Reverse done, start fold
self.builder().position_at_end(rev_exit);
let reversed = rev_acc_phi.as_basic_value();
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
// Fold loop header
self.builder().position_at_end(fold_header);
let fold_acc_phi = self
.builder()
.build_phi(ptr_type, "fold_acc")
.map_err(|e| CodegenError::Internal(format!("append phi: {:?}", e)))?;
let fold_list_phi = self
.builder()
.build_phi(ptr_type, "fold_list")
.map_err(|e| CodegenError::Internal(format!("append phi: {:?}", e)))?;
fold_acc_phi.add_incoming(&[(&list2, rev_exit)]);
fold_list_phi.add_incoming(&[(&reversed, rev_exit)]);
let fold_current = fold_list_phi.as_basic_value().into_pointer_value();
let fold_tag = self.extract_adt_tag(fold_current)?;
let fold_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
fold_tag,
tm.i64_type().const_zero(),
"fold_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("append cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(fold_is_empty, fold_exit, fold_body)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
// Fold loop body
self.builder().position_at_end(fold_body);
let fold_head = self.extract_adt_field(fold_current, 2, 0)?;
let fold_tail = self.extract_adt_field(fold_current, 2, 1)?;
let fold_new_acc = self.build_cons(fold_head.into(), fold_acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(fold_header)
.map_err(|e| CodegenError::Internal(format!("append branch: {:?}", e)))?;
fold_acc_phi.add_incoming(&[(&fold_new_acc, fold_body)]);
fold_list_phi.add_incoming(&[(&fold_tail, fold_body)]);
// Return result
self.builder().position_at_end(fold_exit);
self.builder()
.build_return(Some(&fold_acc_phi.as_basic_value()))
.map_err(|e| CodegenError::Internal(format!("append return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
Ok(func)
}
// ========================================================================
// ReaderT Operations
// ========================================================================
/// runReaderT m r = m(r)
///
/// For nested transformers like `ReaderT r (StateT s IO)`, this returns
/// a StateT closure instead of executing directly.
fn lower_builtin_run_reader_t(
&mut self,
m_expr: &Expr,
r_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if this is a nested transformer
let m_ty = m_expr.ty();
let inner_monad = self.extract_inner_monad_from_reader_t(&m_ty);
match inner_monad {
Some(TransformerLayer::StateT) => {
// ReaderT r (StateT s IO) - return a StateT closure
self.lower_run_reader_t_over_state_t(m_expr, r_expr)
}
Some(TransformerLayer::IO) | None => {
// ReaderT r IO - direct execution (current behavior)
self.lower_run_reader_t_direct(m_expr, r_expr)
}
Some(_other) => Err(CodegenError::Internal(
"runReaderT over non-IO/StateT inner monad not yet supported".to_string(),
)),
}
}
/// Direct runReaderT for ReaderT r IO - the current behavior.
fn lower_run_reader_t_direct(
&mut self,
m_expr: &Expr,
r_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::ReaderT);
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("runReaderT: m has no value".to_string()))?;
self.pop_transformer_layer();
let r_val = self
.lower_expr(r_expr)?
.ok_or_else(|| CodegenError::Internal("runReaderT: r has no value".to_string()))?;
let m_ptr = m_val.into_pointer_value();
let r_ptr = self.value_to_ptr(r_val)?;
let fn_ptr = self.extract_closure_fn_ptr(m_ptr)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[m_ptr.into(), r_ptr.into()],
"run_reader_t",
)
.map_err(|e| CodegenError::Internal(format!("runReaderT call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runReaderT: void".to_string()))?;
Ok(Some(result))
}
/// runReaderT for ReaderT r (StateT s IO) - returns a StateT closure.
///
/// The returned StateT closure takes (env, state) and internally calls m with 3 args:
/// m(m, reader_env, state) -> (value, final_state)
fn lower_run_reader_t_over_state_t(
&mut self,
m_expr: &Expr,
r_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower m — m is typically a top-level function reference whose body
// is already lowered with the correct transformer stack by its own handler.
// Do NOT push extra layers here — they would stack on top of the function's own
// layers if m's body is lowered inline (forward reference).
let m_val = self.lower_expr(m_expr)?.ok_or_else(|| {
CodegenError::Internal("runReaderT/StateT: m has no value".to_string())
})?;
let r_val = self.lower_expr(r_expr)?.ok_or_else(|| {
CodegenError::Internal("runReaderT/StateT: r has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_run_reader_t_over_state_t";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// Parameters: (env, state) — this is a StateT closure
// env contains: [m, reader_env]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let state = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let reader_env = self.extract_closure_env_elem(env, 2, 1)?;
// Call m with 3 args: m(m, reader_env, state) -> (value, final_state)
let fn_type_3 =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(
fn_type_3,
m_fn,
&[m.into(), reader_env.into(), state.into()],
"run_rt_st_result",
)
.map_err(|e| CodegenError::Internal(format!("runReaderT/StateT call m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runReaderT/StateT m: void".to_string()))?;
// Return the (value, final_state) pair directly — state is threaded through m
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("runReaderT/StateT return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
// Create a StateT closure that captures m and reader_env
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let r_ptr = self.value_to_ptr(r_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), r_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ask = closure \r -> r (returns env)
///
/// When in StateT context (nested), ask is lifted:
/// ask = closure \(env, s, reader_env) -> (reader_env, s)
///
/// When in ReaderT-over-StateT context:
/// ask = closure \(env, reader_env, state) -> (reader_env, state)
fn lower_builtin_ask(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ptr_type = self.type_mapper().ptr_type();
// Check if we're in a ReaderT-over-StateT context
if self.transformer_stack.is_reader_t_over_state_t() {
// Nested: \(_env, reader_env, state) -> (reader_env, state)
let fn_name = "bhc_ask_in_reader_t_over_state_t";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// param 0 = env, param 1 = reader_env, param 2 = state
let reader_env = func.get_nth_param(1).unwrap();
let state = func.get_nth_param(2).unwrap();
let pair = self.alloc_pair(reader_env, state)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("ask_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
return Ok(Some(closure_ptr.into()));
}
// Check if we're in a StateT context - if so, lift ask
if self.transformer_stack.is_state_t_over_reader_t() {
// Lifted ask: produces a StateT closure that returns reader_env
let fn_name = "bhc_lifted_ask_in_state_t";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(_env, s, reader_env) -> (reader_env, s)
let s = func.get_nth_param(1).unwrap();
let reader_env = func.get_nth_param(2).unwrap();
let pair = self.alloc_pair(reader_env, s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("lifted_ask return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
return Ok(Some(closure_ptr.into()));
}
// Normal ReaderT ask
let fn_name = "bhc_reader_t_ask";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> r
let r = func.get_nth_param(1).unwrap();
self.builder()
.build_return(Some(&r))
.map_err(|e| CodegenError::Internal(format!("ask return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
/// asks f = closure \r -> f(r)
fn lower_builtin_asks(&mut self, f_expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("asks: f has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_asks";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> f(r) where f = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let r_ptr = self.value_to_ptr(r)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), r_ptr.into()], "asks_result")
.map_err(|e| CodegenError::Internal(format!("asks call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("asks: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("asks return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// local f m = closure \r -> m(f(r))
fn lower_builtin_local(
&mut self,
f_expr: &Expr,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("local: f has no value".to_string()))?;
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("local: m has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_local";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> m(f(r)) where f = env[0], m = env[1]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// r' = f(r)
let f_fn = self.extract_closure_fn_ptr(f)?;
let r_ptr = self.value_to_ptr(r)?;
let r_prime = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), r_ptr.into()], "local_r")
.map_err(|e| CodegenError::Internal(format!("local f call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("local f: void".to_string()))?;
// result = m(r')
let m_fn = self.extract_closure_fn_ptr(m)?;
let result = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r_prime.into()], "local_result")
.map_err(|e| CodegenError::Internal(format!("local m call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("local m: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("local return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.pure x = closure \r -> x
///
/// For RtOvSt: \(env, _r, s) -> (x, s) where x = env[0]
fn lower_builtin_reader_t_pure(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.pure: x has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: return (x, s) pair
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_reader_t_pure_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _r, s) -> (x, s) where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(2).unwrap();
let x = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(x.into(), s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("reader_t_pure_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
return Ok(Some(closure_ptr.into()));
}
let fn_name = "bhc_reader_t_pure";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _r) -> x where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
self.builder()
.build_return(Some(&x))
.map_err(|e| CodegenError::Internal(format!("reader_t_pure return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.>>= m k = closure \r -> let a = m(r) in k(a)(r)
///
/// For RtOvSt: \(env, r, s) ->
/// let (a, s') = m(m, r, s)
/// let kr = k(k, a)
/// kr(kr, r, s')
fn lower_builtin_reader_t_bind(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.>>=: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.>>=: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: 3-arg closures threading state
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_reader_t_bind_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r, s) -> ...
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let s = func.get_nth_param(2).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type_3 =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let fn_type_2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// (a, s') = m(m, r, s)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(
fn_type_3,
m_fn,
&[m.into(), r.into(), s.into()],
"rt_st_bind_pair",
)
.map_err(|e| CodegenError::Internal(format!("ReaderT/StateT bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("ReaderT/StateT bind m: void".to_string())
})?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
// kr = k(k, a) — produces a new 3-arg closure
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type_2, k_fn, &[k.into(), a.into()], "rt_st_bind_kr")
.map_err(|e| CodegenError::Internal(format!("ReaderT/StateT bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("ReaderT/StateT bind k: void".to_string())
})?;
// result = kr(kr, r, s')
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type_3,
kr_fn,
&[kr_ptr.into(), r.into(), s_prime.into()],
"rt_st_bind_result",
)
.map_err(|e| {
CodegenError::Internal(format!("ReaderT/StateT bind kr: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("ReaderT/StateT bind kr: void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader_t_bind_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
return Ok(Some(closure_ptr.into()));
}
let fn_name = "bhc_reader_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> let a = m(r); kr = k(a); kr(r)
// where m = env[0], k = env[1]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// a = m(r)
let m_fn = self.extract_closure_fn_ptr(m)?;
let a = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r.into()], "bind_a")
.map_err(|e| CodegenError::Internal(format!("ReaderT bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT bind m: void".to_string()))?;
// kr = k(a)
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "bind_kr")
.map_err(|e| CodegenError::Internal(format!("ReaderT bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT bind k: void".to_string()))?;
// result = kr(r)
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(fn_type, kr_fn, &[kr_ptr.into(), r.into()], "bind_result")
.map_err(|e| CodegenError::Internal(format!("ReaderT bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT bind kr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("reader_t_bind return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.>> m1 m2 = closure \r -> m1(r); m2(r)
///
/// For RtOvSt: \(env, r, s) ->
/// let (_, s') = m1(m1, r, s)
/// m2(m2, r, s')
fn lower_builtin_reader_t_then(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.>>: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.>>: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: 3-arg closures threading state
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_reader_t_then_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r, s) -> ...
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let s = func.get_nth_param(2).unwrap();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type_3 =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
// (_, s') = m1(m1, r, s)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let pair1 = self
.builder()
.build_indirect_call(
fn_type_3,
m1_fn,
&[m1.into(), r.into(), s.into()],
"rt_st_then_pair",
)
.map_err(|e| {
CodegenError::Internal(format!("ReaderT/StateT then m1: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("ReaderT/StateT then m1: void".to_string())
})?
.into_pointer_value();
let s_prime = self.extract_pair_snd(pair1)?;
// result = m2(m2, r, s')
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type_3,
m2_fn,
&[m2.into(), r.into(), s_prime.into()],
"rt_st_then_result",
)
.map_err(|e| {
CodegenError::Internal(format!("ReaderT/StateT then m2: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("ReaderT/StateT then m2: void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader_t_then_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
return Ok(Some(closure_ptr.into()));
}
let fn_name = "bhc_reader_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, r) -> m1(r); m2(r) where m1 = env[0], m2 = env[1]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// m1(r) - discard result
let m1_fn = self.extract_closure_fn_ptr(m1)?;
self.builder()
.build_indirect_call(fn_type, m1_fn, &[m1.into(), r.into()], "then_m1")
.map_err(|e| CodegenError::Internal(format!("ReaderT then m1: {:?}", e)))?;
// result = m2(r)
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(fn_type, m2_fn, &[m2.into(), r.into()], "then_result")
.map_err(|e| CodegenError::Internal(format!("ReaderT then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT then m2: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("reader_t_then return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.fmap f m = closure \r -> f(m(r))
fn lower_builtin_reader_t_fmap(
&mut self,
f_expr: &Expr,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.fmap: f has no value".to_string()))?;
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.fmap: m has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// a = m(r)
let m_fn = self.extract_closure_fn_ptr(m)?;
let a = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r.into()], "fmap_a")
.map_err(|e| CodegenError::Internal(format!("ReaderT fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT fmap m: void".to_string()))?;
// result = f(a)
let f_fn = self.extract_closure_fn_ptr(f)?;
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "fmap_result")
.map_err(|e| CodegenError::Internal(format!("ReaderT fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT fmap f: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("reader_t_fmap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.<*> mf mx = closure \r -> mf(r)(mx(r))
fn lower_builtin_reader_t_ap(
&mut self,
mf_expr: &Expr,
mx_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Same structure as fmap but applied differently
// For simplicity, implement as: mf >>= \f -> fmap f mx
// Which compiles to same closure pattern
let mf_val = self
.lower_expr(mf_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.<*>: mf has no value".to_string()))?;
let mx_val = self
.lower_expr(mx_expr)?
.ok_or_else(|| CodegenError::Internal("ReaderT.<*>: mx has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let mx = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// f = mf(r)
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let f = self
.builder()
.build_indirect_call(fn_type, mf_fn, &[mf.into(), r.into()], "ap_f")
.map_err(|e| CodegenError::Internal(format!("ReaderT ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT ap mf: void".to_string()))?;
// x = mx(r)
let mx_fn = self.extract_closure_fn_ptr(mx)?;
let x = self
.builder()
.build_indirect_call(fn_type, mx_fn, &[mx.into(), r.into()], "ap_x")
.map_err(|e| CodegenError::Internal(format!("ReaderT ap mx: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT ap mx: void".to_string()))?;
// result = f(x)
let f_ptr = f.into_pointer_value();
let f_fn = self.extract_closure_fn_ptr(f_ptr)?;
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f_ptr.into(), x.into()], "ap_result")
.map_err(|e| CodegenError::Internal(format!("ReaderT ap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ReaderT ap f: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("reader_t_ap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let mx_ptr = self.value_to_ptr(mx_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), mx_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ReaderT.lift action = closure \r -> action (ignore r, return action)
fn lower_builtin_reader_t_lift(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_val = self.lower_expr(action_expr)?.ok_or_else(|| {
CodegenError::Internal("ReaderT.lift: action has no value".to_string())
})?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_reader_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _r) -> action where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
self.builder()
.build_return(Some(&action))
.map_err(|e| CodegenError::Internal(format!("reader_t_lift return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), action_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
// ========================================================================
// StateT Operations
// ========================================================================
/// runStateT m s = m(s)
///
/// For nested transformers like `StateT s (ReaderT r IO)`, this returns
/// a ReaderT closure that produces `(a, s)` instead of executing directly.
fn lower_builtin_run_state_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if this is a nested transformer
let m_ty = m_expr.ty();
let inner_monad = self.extract_inner_monad_from_state_t(&m_ty);
match inner_monad {
Some(TransformerLayer::ReaderT) => {
// StateT s (ReaderT r IO) - return a ReaderT closure that produces (a, s)
self.lower_run_state_t_over_reader_t(m_expr, s_expr)
}
Some(TransformerLayer::IO) | None => {
// StateT s IO - direct execution
self.lower_run_state_t_direct(m_expr, s_expr)
}
Some(_other) => Err(CodegenError::Internal(
"runStateT over non-IO/ReaderT inner monad not yet supported".to_string(),
)),
}
}
/// Direct runStateT for StateT s IO - the current behavior.
fn lower_run_state_t_direct(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::StateT);
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("runStateT: m has no value".to_string()))?;
self.pop_transformer_layer();
let s_val = self
.lower_expr(s_expr)?
.ok_or_else(|| CodegenError::Internal("runStateT: s has no value".to_string()))?;
let m_ptr = m_val.into_pointer_value();
let s_ptr = self.value_to_ptr(s_val)?;
let fn_ptr = self.extract_closure_fn_ptr(m_ptr)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[m_ptr.into(), s_ptr.into()],
"run_state_t",
)
.map_err(|e| CodegenError::Internal(format!("runStateT call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runStateT: void".to_string()))?;
Ok(Some(result))
}
/// runStateT for StateT s (ReaderT r IO) - returns a ReaderT closure that produces (a, s).
fn lower_run_state_t_over_reader_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::StateT);
let m_val = self.lower_expr(m_expr)?.ok_or_else(|| {
CodegenError::Internal("runStateT/ReaderT: m has no value".to_string())
})?;
self.pop_transformer_layer();
let s_val = self.lower_expr(s_expr)?.ok_or_else(|| {
CodegenError::Internal("runStateT/ReaderT: s has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_run_state_t_over_reader_t";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// Parameters: (env, reader_env)
// env contains: [m, init_state]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let reader_env = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let init_state = self.extract_closure_env_elem(env, 2, 1)?;
// Call m with state - returns (result, final_state)
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), init_state.into()], "run_st_pair")
.map_err(|e| CodegenError::Internal(format!("runStateT/ReaderT call m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runStateT/ReaderT m: void".to_string()))?;
// Return the pair (reader_env unused for pure StateT computations)
let _ = reader_env;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("runStateT/ReaderT return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
// Create a ReaderT closure that captures m and init_state
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), s_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// Helper: allocate a pair (a, s) as a 2-element ADT (tag=0, field0=a, field1=s)
fn alloc_pair(
&mut self,
fst_val: BasicValueEnum<'ctx>,
snd_val: BasicValueEnum<'ctx>,
) -> CodegenResult<PointerValue<'ctx>> {
let adt_ty = self.adt_type(2);
let i64_type = self.type_mapper().i64_type();
// Allocate: tag(i64) + 2 fields (ptr each) = 24 bytes
let alloc_fn = *self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size = i64_type.const_int(24, false);
let raw = self
.builder()
.build_call(alloc_fn, &[size.into()], "pair_alloc")
.map_err(|e| CodegenError::Internal(format!("pair alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("pair alloc: void".to_string()))?
.into_pointer_value();
// Tag = 0 (tuple constructor)
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, raw, 0, "pair_tag")
.map_err(|e| CodegenError::Internal(format!("pair tag gep: {:?}", e)))?;
self.builder()
.build_store(tag_ptr, i64_type.const_zero())
.map_err(|e| CodegenError::Internal(format!("pair tag store: {:?}", e)))?;
// Field 0 - use store_adt_field pattern
self.store_adt_field(raw, 2, 0, fst_val)?;
// Field 1
self.store_adt_field(raw, 2, 1, snd_val)?;
Ok(raw)
}
/// Helper: extract fst from pair (tag + 2 fields)
fn extract_pair_fst(&self, pair: PointerValue<'ctx>) -> CodegenResult<PointerValue<'ctx>> {
self.extract_adt_field(pair, 2, 0)
}
/// Helper: extract snd from pair (tag + 2 fields)
fn extract_pair_snd(&self, pair: PointerValue<'ctx>) -> CodegenResult<PointerValue<'ctx>> {
self.extract_adt_field(pair, 2, 1)
}
/// get = closure \s -> (s, s)
///
/// For nested transformers (StOvRt): \(_env, s, _reader_env) -> (s, s)
/// For nested transformers (RtOvSt): \(_env, _reader_env, s) -> (s, s)
fn lower_builtin_get(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: state is at param 2
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_state_t_get_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(_env, _reader_env, s) -> (s, s)
let s = func.get_nth_param(2).unwrap();
let pair = self.alloc_pair(s, s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("get_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
return Ok(Some(closure_ptr.into()));
}
let is_nested = self.transformer_stack.is_state_t_over_reader_t();
let fn_name = if is_nested {
"bhc_state_t_get_nested"
} else {
"bhc_state_t_get"
};
let func = if is_nested {
self.get_or_create_nested_transformer_fn(fn_name)
} else {
self.get_or_create_transformer_fn(fn_name)
};
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// Both: \(_env, s, [_reader_env]) -> (s, s)
let s = func.get_nth_param(1).unwrap();
let pair = self.alloc_pair(s, s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_get return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
/// put s' = closure \_ -> ((), s')
///
/// For RtOvSt: \(env, _reader_env, _s) -> ((), s') where s' = env[0]
fn lower_builtin_put(&mut self, s_expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let s_val = self
.lower_expr(s_expr)?
.ok_or_else(|| CodegenError::Internal("put: s has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: 3-arg closure
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_state_t_put_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _reader_env, _s) -> ((), s') where s' = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s_new = self.extract_closure_env_elem(env, 1, 0)?;
let unit = ptr_type.const_null();
let pair = self.alloc_pair(unit.into(), s_new.into())?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("put_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), s_ptr.into())])?;
return Ok(Some(closure_ptr.into()));
}
let fn_name = "bhc_state_t_put";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _s) -> ((), s') where s' = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s_new = self.extract_closure_env_elem(env, 1, 0)?;
let unit = ptr_type.const_null();
let pair = self.alloc_pair(unit.into(), s_new.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_put return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), s_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// modify f = closure \s -> ((), f(s))
///
/// For RtOvSt: \(env, _reader_env, s) -> ((), f(s)) where f = env[0]
fn lower_builtin_modify(
&mut self,
f_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("modify: f has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
// ReaderT-over-StateT: 3-arg closure
if self.transformer_stack.is_reader_t_over_state_t() {
let fn_name = "bhc_state_t_modify_rt_over_st";
let func = self.get_or_create_nested_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _reader_env, s) -> ((), f(s)) where f = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(2).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let f_fn = self.extract_closure_fn_ptr(f)?;
let s_ptr = self.value_to_ptr(s)?;
let s_new = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), s_ptr.into()], "modify_rt_st_s")
.map_err(|e| CodegenError::Internal(format!("modify_rt_over_st f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("modify_rt_over_st f: void".to_string())
})?;
let unit = ptr_type.const_null();
let pair = self.alloc_pair(unit.into(), s_new)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("modify_rt_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
return Ok(Some(closure_ptr.into()));
}
let fn_name = "bhc_state_t_modify";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> ((), f(s)) where f = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let f_fn = self.extract_closure_fn_ptr(f)?;
let s_ptr = self.value_to_ptr(s)?;
let s_new = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), s_ptr.into()], "modify_s")
.map_err(|e| CodegenError::Internal(format!("modify f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("modify f: void".to_string()))?;
let unit = ptr_type.const_null();
let pair = self.alloc_pair(unit.into(), s_new)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_modify return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// gets f = closure \s -> (f(s), s)
fn lower_builtin_gets(&mut self, f_expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("gets: f has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_gets";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let f_fn = self.extract_closure_fn_ptr(f)?;
let s_ptr = self.value_to_ptr(s)?;
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), s_ptr.into()], "gets_result")
.map_err(|e| CodegenError::Internal(format!("gets f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("gets f: void".to_string()))?;
let pair = self.alloc_pair(result, s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_gets return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// StateT.pure x = closure \s -> (x, s)
///
/// For nested transformers (StateT over ReaderT), the closure takes 3 args:
/// \(env, s, reader_env) -> (x, s)
fn lower_builtin_state_t_pure(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.pure: x has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let is_nested = self.transformer_stack.is_state_t_over_reader_t();
let fn_name = if is_nested {
"bhc_state_t_pure_nested"
} else {
"bhc_state_t_pure"
};
let func = if is_nested {
self.get_or_create_nested_transformer_fn(fn_name)
} else {
self.get_or_create_transformer_fn(fn_name)
};
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// For nested: \(env, s, reader_env) -> (x, s) where x = env[0]
// For non-nested: \(env, s) -> (x, s) where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
// reader_env is param 2 for nested, but unused for pure
let x = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(x.into(), s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_pure return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// StateT.>>= m k = closure \s -> let (a, s') = m(s) in k(a)(s')
///
/// For nested transformers (StateT over ReaderT), the closure takes 3 args:
/// \(env, s, reader_env) -> let (a, s') = m(s, reader_env) in k(a)(s')(reader_env)
fn lower_builtin_state_t_bind(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.>>=: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.>>=: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let is_nested = self.transformer_stack.is_state_t_over_reader_t();
let fn_name = if is_nested {
"bhc_state_t_bind_nested"
} else {
"bhc_state_t_bind"
};
let func = if is_nested {
self.get_or_create_nested_transformer_fn(fn_name)
} else {
self.get_or_create_transformer_fn(fn_name)
};
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
if is_nested {
// Nested: 3-arg closures with reader_env threading
let reader_env = func.get_nth_param(2).unwrap();
let fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
// pair = m(s, reader_env)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), s.into(), reader_env.into()],
"st_bind_pair",
)
.map_err(|e| CodegenError::Internal(format!("StateT bind m nested: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("StateT bind m nested: void".to_string())
})?
.into_pointer_value();
// a = fst(pair), s' = snd(pair)
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
// kr = k(a) -- k is a non-nested closure that produces a nested closure
let k_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(k_fn_type, k_fn, &[k.into(), a.into()], "st_bind_kr")
.map_err(|e| CodegenError::Internal(format!("StateT bind k nested: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("StateT bind k nested: void".to_string())
})?;
// result = kr(s', reader_env)
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr_ptr.into(), s_prime.into(), reader_env.into()],
"st_bind_result",
)
.map_err(|e| CodegenError::Internal(format!("StateT bind kr nested: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("StateT bind kr nested: void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("state_t_bind nested return: {:?}", e))
})?;
} else {
// Non-nested: original 2-arg implementation
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = m(s)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), s.into()], "st_bind_pair")
.map_err(|e| CodegenError::Internal(format!("StateT bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT bind m: void".to_string()))?
.into_pointer_value();
// a = fst(pair), s' = snd(pair)
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
// kr = k(a)
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "st_bind_kr")
.map_err(|e| CodegenError::Internal(format!("StateT bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT bind k: void".to_string()))?;
// result = kr(s')
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr_ptr.into(), s_prime.into()],
"st_bind_result",
)
.map_err(|e| CodegenError::Internal(format!("StateT bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT bind kr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("state_t_bind return: {:?}", e)))?;
}
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// StateT.>> m1 m2 = closure \s -> let (_, s') = m1(s) in m2(s')
fn lower_builtin_state_t_then(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.>>: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.>>: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = m1(s)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m1_fn, &[m1.into(), s.into()], "st_then_pair")
.map_err(|e| CodegenError::Internal(format!("StateT then m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT then m1: void".to_string()))?
.into_pointer_value();
// s' = snd(pair)
let s_prime = self.extract_pair_snd(pair)?;
// result = m2(s')
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
m2_fn,
&[m2.into(), s_prime.into()],
"st_then_result",
)
.map_err(|e| CodegenError::Internal(format!("StateT then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT then m2: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("state_t_then return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// StateT.fmap f m = closure \s -> let (a, s') = m(s) in (f(a), s')
fn lower_builtin_state_t_fmap(
&mut self,
f_expr: &Expr,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.fmap: f has no value".to_string()))?;
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.fmap: m has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), s.into()], "st_fmap_pair")
.map_err(|e| CodegenError::Internal(format!("StateT fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT fmap m: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "st_fmap_b")
.map_err(|e| CodegenError::Internal(format!("StateT fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT fmap f: void".to_string()))?;
let result_pair = self.alloc_pair(b, s_prime.into())?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("state_t_fmap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// StateT.<*> = use bind and fmap composition
fn lower_builtin_state_t_ap(
&mut self,
mf_expr: &Expr,
mx_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ap mf mx = mf >>= \f -> fmap f mx
// For simplicity, implement directly like reader_t_ap but with state threading
let mf_val = self
.lower_expr(mf_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.<*>: mf has no value".to_string()))?;
let mx_val = self
.lower_expr(mx_expr)?
.ok_or_else(|| CodegenError::Internal("StateT.<*>: mx has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let mx = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// (f, s') = mf(s)
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let pair1 = self
.builder()
.build_indirect_call(fn_type, mf_fn, &[mf.into(), s.into()], "st_ap_pair1")
.map_err(|e| CodegenError::Internal(format!("StateT ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT ap mf: void".to_string()))?
.into_pointer_value();
let f = self.extract_pair_fst(pair1)?;
let s_prime = self.extract_pair_snd(pair1)?;
// (x, s'') = mx(s')
let mx_fn = self.extract_closure_fn_ptr(mx)?;
let pair2 = self
.builder()
.build_indirect_call(fn_type, mx_fn, &[mx.into(), s_prime.into()], "st_ap_pair2")
.map_err(|e| CodegenError::Internal(format!("StateT ap mx: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT ap mx: void".to_string()))?
.into_pointer_value();
let x = self.extract_pair_fst(pair2)?;
let s_double_prime = self.extract_pair_snd(pair2)?;
// result = f(x)
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), x.into()], "st_ap_result")
.map_err(|e| CodegenError::Internal(format!("StateT ap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("StateT ap f: void".to_string()))?;
let result_pair = self.alloc_pair(b, s_double_prime.into())?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("state_t_ap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let mx_ptr = self.value_to_ptr(mx_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), mx_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// StateT.lift action = closure \s -> (action, s)
fn lower_builtin_state_t_lift(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_val = self.lower_expr(action_expr)?.ok_or_else(|| {
CodegenError::Internal("StateT.lift: action has no value".to_string())
})?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_state_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> (action, s) where action = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(action.into(), s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("state_t_lift return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), action_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// evalStateT m s = fst(m(s))
///
/// For nested transformers like `StateT s (ReaderT r IO)`, this returns
/// a ReaderT closure instead of executing directly.
fn lower_builtin_eval_state_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if this is a nested transformer by examining the type
let m_ty = m_expr.ty();
let inner_monad = self.extract_inner_monad_from_state_t(&m_ty);
match inner_monad {
Some(TransformerLayer::ReaderT) => {
// StateT s (ReaderT r IO) - return a ReaderT closure
self.lower_eval_state_t_over_reader_t(m_expr, s_expr)
}
Some(TransformerLayer::IO) | None => {
// StateT s IO - direct execution (current behavior)
let result = self.lower_builtin_run_state_t(m_expr, s_expr)?;
let pair = result
.ok_or_else(|| CodegenError::Internal("evalStateT: no result".to_string()))?
.into_pointer_value();
let fst = self.extract_pair_fst(pair)?;
Ok(Some(fst.into()))
}
Some(_other) => {
// Other nested transformers - not yet supported
Err(CodegenError::Internal(
"evalStateT over non-IO/ReaderT inner monad not yet supported".to_string(),
))
}
}
}
/// Extract the inner monad from a StateT type.
///
/// For `StateT s m a`, returns the transformer layer of `m`.
fn extract_inner_monad_from_state_t(&self, ty: &Ty) -> Option<TransformerLayer> {
// StateT s m a = App(App(App(Con(StateT), s), m), a)
// We need to get `m` and determine its transformer layer
if let Ty::App(inner, _result_type) = ty {
// inner = App(App(Con(StateT), s), m)
if let Ty::App(inner2, monad_arg) = inner.as_ref() {
// inner2 = App(Con(StateT), s)
if let Ty::App(con, _param) = inner2.as_ref() {
if let Ty::Con(tycon) = con.as_ref() {
if tycon.name.as_str() == "StateT" {
// monad_arg is the inner monad `m`
return self.get_transformer_layer_from_type(monad_arg);
}
}
}
}
}
None
}
/// Extract the inner monad from a ReaderT type.
///
/// For `ReaderT r m a`, returns the transformer layer of `m`.
fn extract_inner_monad_from_reader_t(&self, ty: &Ty) -> Option<TransformerLayer> {
// ReaderT r m a = App(App(App(Con(ReaderT), r), m), a)
if let Ty::App(inner, _result_type) = ty {
if let Ty::App(inner2, monad_arg) = inner.as_ref() {
if let Ty::App(con, _param) = inner2.as_ref() {
if let Ty::Con(tycon) = con.as_ref() {
if tycon.name.as_str() == "ReaderT" {
return self.get_transformer_layer_from_type(monad_arg);
}
}
}
}
}
None
}
/// Extract the return type from a function type, stripping away function arrows.
///
/// For `Int -> StateT Int IO Int`, returns `StateT Int IO Int`.
/// For `StateT Int IO Int` (no arrows), returns the type itself.
fn get_return_type_from_function_type<'a>(&self, ty: &'a Ty) -> &'a Ty {
match ty {
Ty::Fun(_, ret) => self.get_return_type_from_function_type(ret),
_ => ty,
}
}
/// Get the transformer layer from a monad type.
///
/// Returns IO for `IO`, StateT for `StateT s m`, ReaderT for `ReaderT r m`, etc.
fn get_transformer_layer_from_type(&self, ty: &Ty) -> Option<TransformerLayer> {
// Detect transformer types. The structure depends on whether it's:
// - IO: Con(IO)
// - IO a: App(Con(IO), a)
// - T x m (partial, 2 params): App(App(Con(T), x), m)
// - T x m a (fully applied): App(App(App(Con(T), x), m), a)
match ty {
Ty::Con(tycon) if tycon.name.as_str() == "IO" => Some(TransformerLayer::IO),
Ty::App(inner, _arg) => {
// Check if inner is IO (for IO a)
if let Ty::Con(tycon) = inner.as_ref() {
if tycon.name.as_str() == "IO" {
return Some(TransformerLayer::IO);
}
}
// For T x m (two-param transformers), inner = App(Con(T), x)
// This handles partial application: StateT s m
if let Ty::App(con, _param) = inner.as_ref() {
if let Ty::Con(tycon) = con.as_ref() {
let result = match tycon.name.as_str() {
"StateT" => Some(TransformerLayer::StateT),
"ReaderT" => Some(TransformerLayer::ReaderT),
"ExceptT" => Some(TransformerLayer::ExceptT),
"WriterT" => Some(TransformerLayer::WriterT),
_ => None,
};
if result.is_some() {
return result;
}
}
// For T x m a (fully applied), go one level deeper
// inner = App(App(Con(T), x), m), so we look at inner.inner
if let Ty::App(con2, _param2) = con.as_ref() {
if let Ty::Con(tycon) = con2.as_ref() {
return match tycon.name.as_str() {
"StateT" => Some(TransformerLayer::StateT),
"ReaderT" => Some(TransformerLayer::ReaderT),
"ExceptT" => Some(TransformerLayer::ExceptT),
"WriterT" => Some(TransformerLayer::WriterT),
_ => None,
};
}
}
}
None
}
_ => None,
}
}
/// evalStateT for StateT s (ReaderT r IO) - returns a ReaderT closure.
///
/// The returned closure takes reader_env and executes the StateT computation
/// with combined (state, reader_env) context.
fn lower_eval_state_t_over_reader_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower m — m is typically a top-level function reference whose body
// is already lowered with the correct transformer stack by its own handler.
// Do NOT push extra layers here — they would stack on top of the function's own
// layers if m's body is lowered inline (forward reference).
let m_val = self.lower_expr(m_expr)?.ok_or_else(|| {
CodegenError::Internal("evalStateT/ReaderT: m has no value".to_string())
})?;
let s_val = self.lower_expr(s_expr)?.ok_or_else(|| {
CodegenError::Internal("evalStateT/ReaderT: s has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_eval_state_t_over_reader_t";
// Create the wrapper function that takes reader_env and runs the StateT computation
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// Parameters: (env, reader_env)
// env contains: [m, init_state]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let reader_env = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let init_state = self.extract_closure_env_elem(env, 2, 1)?;
// Call m with (m, init_state, reader_env) - 3-arg nested calling convention
// m :: StateT s (ReaderT r IO) a uses nested closures that need reader_env
let fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), init_state.into(), reader_env.into()],
"eval_st_pair",
)
.map_err(|e| CodegenError::Internal(format!("evalStateT/ReaderT call m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("evalStateT/ReaderT m: void".to_string()))?
.into_pointer_value();
// Extract fst (the result)
let result = self.extract_pair_fst(pair)?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("evalStateT/ReaderT return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
// Create a ReaderT closure that captures m and init_state
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), s_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// execStateT m s = snd(m(s))
/// execStateT m s = snd(m(s))
///
/// For nested transformers like `StateT s (ReaderT r IO)`, this returns
/// a ReaderT closure that produces just the final state.
fn lower_builtin_exec_state_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if this is a nested transformer
let m_ty = m_expr.ty();
let inner_monad = self.extract_inner_monad_from_state_t(&m_ty);
match inner_monad {
Some(TransformerLayer::ReaderT) => {
// StateT s (ReaderT r IO) - return a ReaderT closure that produces final state
self.lower_exec_state_t_over_reader_t(m_expr, s_expr)
}
Some(TransformerLayer::IO) | None => {
// StateT s IO - direct execution (current behavior)
let result = self.lower_run_state_t_direct(m_expr, s_expr)?;
let pair = result
.ok_or_else(|| CodegenError::Internal("execStateT: no result".to_string()))?
.into_pointer_value();
let snd = self.extract_pair_snd(pair)?;
Ok(Some(snd.into()))
}
Some(_other) => Err(CodegenError::Internal(
"execStateT over non-IO/ReaderT inner monad not yet supported".to_string(),
)),
}
}
/// execStateT for StateT s (ReaderT r IO) - returns a ReaderT closure that produces final state.
fn lower_exec_state_t_over_reader_t(
&mut self,
m_expr: &Expr,
s_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::StateT);
let m_val = self.lower_expr(m_expr)?.ok_or_else(|| {
CodegenError::Internal("execStateT/ReaderT: m has no value".to_string())
})?;
self.pop_transformer_layer();
let s_val = self.lower_expr(s_expr)?.ok_or_else(|| {
CodegenError::Internal("execStateT/ReaderT: s has no value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_exec_state_t_over_reader_t";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let reader_env = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let init_state = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), init_state.into()],
"exec_st_pair",
)
.map_err(|e| CodegenError::Internal(format!("execStateT/ReaderT call m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("execStateT/ReaderT m: void".to_string()))?
.into_pointer_value();
// Extract snd (the final state)
let final_state = self.extract_pair_snd(pair)?;
let _ = reader_env;
self.builder()
.build_return(Some(&final_state))
.map_err(|e| {
CodegenError::Internal(format!("execStateT/ReaderT return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), s_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
// ========================================================================
// ExceptT Transformer Operations
// ========================================================================
/// runExceptT m = m(_) — runs the computation, returns Either e a
fn lower_builtin_run_except_t(
&mut self,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::ExceptT);
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("runExceptT: m has no value".to_string()))?;
self.pop_transformer_layer();
// In nested context (ExceptT over StateT/ReaderT), the m_val IS the transformer
// closure that expects state/reader-env. Return it as-is — the outer
// runStateT/runReaderT will call it with the appropriate argument.
let current = self.current_transformer_layer();
if current == TransformerLayer::StateT || current == TransformerLayer::ReaderT {
return Ok(Some(m_val));
}
let m_ptr = match m_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"runExceptT: expected closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m_ptr)?;
let null_arg = ptr_type.const_null();
let result = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m_ptr.into(), null_arg.into()],
"run_except_t",
)
.map_err(|e| CodegenError::Internal(format!("runExceptT call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runExceptT: void".to_string()))?;
Ok(Some(result))
}
/// ExceptT.pure x = closure \_ -> Right x
fn lower_builtin_except_t_pure(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ExceptT over StateT: \(env, s) -> (Right x, s)
if self.transformer_stack.is_except_t_over_state_t() {
return self.lower_builtin_except_t_pure_over_st(x_expr);
}
// ExceptT over ReaderT: same as plain ExceptT (r is ignored in pure)
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.pure: x has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_pure";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> Right(x) where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
// Allocate Right ADT: tag=1, arity=1
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, x.into())?;
self.builder()
.build_return(Some(&right_adt))
.map_err(|e| CodegenError::Internal(format!("except_t_pure return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// throwE e = closure \_ -> Left e
fn lower_builtin_throw_e(
&mut self,
e_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ExceptT over StateT: \(env, s) -> (Left e, s)
if self.transformer_stack.is_except_t_over_state_t() {
return self.lower_builtin_throw_e_over_st(e_expr);
}
// ExceptT over ReaderT: same as plain ExceptT (r is ignored in throwE)
let e_val = self
.lower_expr(e_expr)?
.ok_or_else(|| CodegenError::Internal("throwE: e has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_throw";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> Left(e) where e = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let e = self.extract_closure_env_elem(env, 1, 0)?;
// Allocate Left ADT: tag=0, arity=1
let left_adt = self.alloc_adt(0, 1)?;
self.store_adt_field(left_adt, 1, 0, e.into())?;
self.builder()
.build_return(Some(&left_adt))
.map_err(|e| CodegenError::Internal(format!("throw_e return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let e_ptr = self.value_to_ptr(e_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), e_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.>>= m k = closure \_ -> let either = m(_) in case tag of
/// 0 (Left) -> either (short-circuit)
/// 1 (Right) -> let a = field0(either); kr = k(a) in kr(_)
fn lower_builtin_except_t_bind(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ExceptT over StateT: \(env, s) -> ...threads state...
if self.transformer_stack.is_except_t_over_state_t() {
return self.lower_builtin_except_t_bind_over_st(m_expr, k_expr);
}
// ExceptT over ReaderT: \(env, r) -> ...threads reader-env...
if self.transformer_stack.is_except_t_over_reader_t() {
return self.lower_builtin_except_t_bind_over_rt(m_expr, k_expr);
}
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// either = m(_)
let m_fn = self.extract_closure_fn_ptr(m)?;
let either = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), null_arg.into()],
"except_bind_either",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind m: void".to_string()))?
.into_pointer_value();
// Check tag (0 = Left, 1 = Right)
let tag = self.extract_adt_tag(either)?;
let tm = self.type_mapper();
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("ExceptT branch: {:?}", e)))?;
// Left case: short-circuit, return either
self.builder().position_at_end(left_bb);
self.builder().build_return(Some(&either)).map_err(|e| {
CodegenError::Internal(format!("except_t_bind left return: {:?}", e))
})?;
// Right case: extract a, call k(a), call result(_)
self.builder().position_at_end(right_bb);
let a = self.extract_adt_field(either, 1, 0)?;
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "except_bind_kr")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind k: void".to_string()))?
.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr.into(), null_arg.into()],
"except_bind_result",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind kr: void".to_string()))?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("except_t_bind right return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.>> m1 m2 = m1 >>= \_ -> m2
fn lower_builtin_except_t_then(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ExceptT over StateT: \(env, s) -> ...threads state...
if self.transformer_stack.is_except_t_over_state_t() {
return self.lower_builtin_except_t_then_over_st(m1_expr, m2_expr);
}
// ExceptT over ReaderT: \(env, r) -> ...threads reader-env...
if self.transformer_stack.is_except_t_over_reader_t() {
return self.lower_builtin_except_t_then_over_rt(m1_expr, m2_expr);
}
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// either = m1(_)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let either = self
.builder()
.build_indirect_call(
fn_type,
m1_fn,
&[m1.into(), null_arg.into()],
"except_then_either",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT then m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then m1: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let tm = self.type_mapper();
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT then cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("ExceptT then branch: {:?}", e)))?;
// Left case: return either
self.builder().position_at_end(left_bb);
self.builder().build_return(Some(&either)).map_err(|e| {
CodegenError::Internal(format!("except_t_then left return: {:?}", e))
})?;
// Right case: call m2(_)
self.builder().position_at_end(right_bb);
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
m2_fn,
&[m2.into(), null_arg.into()],
"except_then_result",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then m2: void".to_string()))?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("except_t_then right return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// catchE m handler = closure \_ -> let either = m(_) in case tag of
/// 0 (Left) -> let e = field0; h = handler(e) in h(_)
/// 1 (Right) -> either
fn lower_builtin_catch_e(
&mut self,
m_expr: &Expr,
handler_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// ExceptT over StateT: \(env, s) -> ...threads state...
if self.transformer_stack.is_except_t_over_state_t() {
return self.lower_builtin_catch_e_over_st(m_expr, handler_expr);
}
// ExceptT over ReaderT: \(env, r) -> ...threads reader-env...
if self.transformer_stack.is_except_t_over_reader_t() {
return self.lower_builtin_catch_e_over_rt(m_expr, handler_expr);
}
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("catchE: m has no value".to_string()))?;
let handler_val = self
.lower_expr(handler_expr)?
.ok_or_else(|| CodegenError::Internal("catchE: handler has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_catch";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let handler = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// either = m(_)
let m_fn = self.extract_closure_fn_ptr(m)?;
let either = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), null_arg.into()], "catch_either")
.map_err(|e| CodegenError::Internal(format!("catchE m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE m: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let tm = self.type_mapper();
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("catchE tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("catchE branch: {:?}", e)))?;
// Right case: pass through
self.builder().position_at_end(right_bb);
self.builder()
.build_return(Some(&either))
.map_err(|e| CodegenError::Internal(format!("catch_e right return: {:?}", e)))?;
// Left case: run handler
self.builder().position_at_end(left_bb);
let e = self.extract_adt_field(either, 1, 0)?;
let handler_fn = self.extract_closure_fn_ptr(handler)?;
let h = self
.builder()
.build_indirect_call(
fn_type,
handler_fn,
&[handler.into(), e.into()],
"catch_handler",
)
.map_err(|e| CodegenError::Internal(format!("catchE handler: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE handler: void".to_string()))?
.into_pointer_value();
let h_fn = self.extract_closure_fn_ptr(h)?;
let result = self
.builder()
.build_indirect_call(fn_type, h_fn, &[h.into(), null_arg.into()], "catch_result")
.map_err(|e| CodegenError::Internal(format!("catchE h: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE h: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("catch_e left return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let handler_ptr = self.value_to_ptr(handler_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), handler_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.lift io = closure \_ -> let a = io in Right(a)
fn lower_builtin_except_t_lift(
&mut self,
io_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// In nested contexts, the inner action is a closure that needs
// state/reader-env threaded through. Route to nested lift functions.
if self.transformer_stack.is_except_t_over_state_t() {
let io_val = self.lower_expr(io_expr)?.ok_or_else(|| {
CodegenError::Internal("ExceptT.lift/st: io has no value".to_string())
})?;
return Ok(Some(self.apply_except_t_lift_to_value_over_st(io_val)?));
}
if self.transformer_stack.is_except_t_over_reader_t() {
let io_val = self.lower_expr(io_expr)?.ok_or_else(|| {
CodegenError::Internal("ExceptT.lift/rt: io has no value".to_string())
})?;
return Ok(Some(self.apply_except_t_lift_to_value_over_rt(io_val)?));
}
let io_val = self
.lower_expr(io_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.lift: io has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> Right(io_result) where io = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let io = self.extract_closure_env_elem(env, 1, 0)?;
// For now, IO actions are already executed, so just wrap the value
// Allocate Right ADT: tag=1, arity=1
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, io.into())?;
self.builder()
.build_return(Some(&right_adt))
.map_err(|e| CodegenError::Internal(format!("except_t_lift return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let io_ptr = self.value_to_ptr(io_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), io_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.fmap f m = closure \_ -> let either = m(_) in case tag of
/// 0 (Left) -> either
/// 1 (Right) -> Right(f(field0))
fn lower_builtin_except_t_fmap(
&mut self,
f_expr: &Expr,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.fmap: f has no value".to_string()))?;
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.fmap: m has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
let m_fn = self.extract_closure_fn_ptr(m)?;
let either = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), null_arg.into()], "fmap_either")
.map_err(|e| CodegenError::Internal(format!("ExceptT fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT fmap m: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let tm = self.type_mapper();
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT fmap cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("ExceptT fmap branch: {:?}", e)))?;
// Left case: pass through
self.builder().position_at_end(left_bb);
self.builder().build_return(Some(&either)).map_err(|e| {
CodegenError::Internal(format!("except_t_fmap left return: {:?}", e))
})?;
// Right case: apply f
self.builder().position_at_end(right_bb);
let a = self.extract_adt_field(either, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "fmap_b")
.map_err(|e| CodegenError::Internal(format!("ExceptT fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT fmap f: void".to_string()))?;
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, b)?;
self.builder().build_return(Some(&right_adt)).map_err(|e| {
CodegenError::Internal(format!("except_t_fmap right return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.<*> mf ma — applicative apply
fn lower_builtin_except_t_ap(
&mut self,
mf_expr: &Expr,
ma_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let mf_val = self
.lower_expr(mf_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.<*>: mf has no value".to_string()))?;
let ma_val = self
.lower_expr(ma_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.<*>: ma has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let mf_right_bb = self.llvm_ctx.append_basic_block(func, "mf_right");
let mf_left_bb = self.llvm_ctx.append_basic_block(func, "mf_left");
let ma_right_bb = self.llvm_ctx.append_basic_block(func, "ma_right");
let ma_left_bb = self.llvm_ctx.append_basic_block(func, "ma_left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let ma = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
let tm = self.type_mapper();
// either_f = mf(_)
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let either_f = self
.builder()
.build_indirect_call(fn_type, mf_fn, &[mf.into(), null_arg.into()], "ap_either_f")
.map_err(|e| CodegenError::Internal(format!("ExceptT ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT ap mf: void".to_string()))?
.into_pointer_value();
let tag_f = self.extract_adt_tag(either_f)?;
let is_right_f = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag_f,
tm.i64_type().const_int(1, false),
"is_right_f",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT ap tag_f cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right_f, mf_right_bb, mf_left_bb)
.map_err(|e| CodegenError::Internal(format!("ExceptT ap branch_f: {:?}", e)))?;
// mf Left: return either_f
self.builder().position_at_end(mf_left_bb);
self.builder().build_return(Some(&either_f)).map_err(|e| {
CodegenError::Internal(format!("except_t_ap mf_left return: {:?}", e))
})?;
// mf Right: run ma
self.builder().position_at_end(mf_right_bb);
let f = self.extract_adt_field(either_f, 1, 0)?;
let ma_fn = self.extract_closure_fn_ptr(ma)?;
let either_a = self
.builder()
.build_indirect_call(fn_type, ma_fn, &[ma.into(), null_arg.into()], "ap_either_a")
.map_err(|e| CodegenError::Internal(format!("ExceptT ap ma: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT ap ma: void".to_string()))?
.into_pointer_value();
let tag_a = self.extract_adt_tag(either_a)?;
let is_right_a = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag_a,
tm.i64_type().const_int(1, false),
"is_right_a",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT ap tag_a cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right_a, ma_right_bb, ma_left_bb)
.map_err(|e| CodegenError::Internal(format!("ExceptT ap branch_a: {:?}", e)))?;
// ma Left: return either_a
self.builder().position_at_end(ma_left_bb);
self.builder().build_return(Some(&either_a)).map_err(|e| {
CodegenError::Internal(format!("except_t_ap ma_left return: {:?}", e))
})?;
// Both Right: apply f to a
self.builder().position_at_end(ma_right_bb);
let a = self.extract_adt_field(either_a, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "ap_b")
.map_err(|e| CodegenError::Internal(format!("ExceptT ap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT ap f: void".to_string()))?;
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, b)?;
self.builder().build_return(Some(&right_adt)).map_err(|e| {
CodegenError::Internal(format!("except_t_ap ma_right return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let ma_ptr = self.value_to_ptr(ma_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), ma_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
// ========================================================================
// ExceptT over StateT — Nested Transformer Operations
// All closures use (env, state) -> (Either, state') convention
// ========================================================================
/// ExceptT.pure over StateT: \(env, s) -> (Right x, s)
fn lower_builtin_except_t_pure_over_st(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.pure/st: x has no value".to_string()))?;
let fn_name = "bhc_except_t_pure_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> (Right(x), s) where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, x.into())?;
let pair = self.alloc_pair(right_adt.into(), s.into())?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("except_t_pure_over_st return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// throwE over StateT: \(env, s) -> (Left e, s)
fn lower_builtin_throw_e_over_st(
&mut self,
e_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let e_val = self
.lower_expr(e_expr)?
.ok_or_else(|| CodegenError::Internal("throwE/st: e has no value".to_string()))?;
let fn_name = "bhc_except_t_throw_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, s) -> (Left(e), s) where e = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let e = self.extract_closure_env_elem(env, 1, 0)?;
let left_adt = self.alloc_adt(0, 1)?;
self.store_adt_field(left_adt, 1, 0, e.into())?;
let pair = self.alloc_pair(left_adt.into(), s.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("throw_e_over_st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let e_ptr = self.value_to_ptr(e_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), e_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.>>= over StateT:
/// \(env, s) ->
/// pair = m(m, s); either = fst(pair), s' = snd(pair)
/// Left: return (either, s')
/// Right: a = field0(either); kr = k(k, a); result_pair = kr(kr, s'); return result_pair
fn lower_builtin_except_t_bind_over_st(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=/st: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=/st: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_bind_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = m(m, s)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), s.into()], "bind_st_pair")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/st m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/st m: void".to_string()))?
.into_pointer_value();
let either = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("bind/st tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("bind/st branch: {:?}", e)))?;
// Left: return (either, s')
self.builder().position_at_end(left_bb);
let left_pair = self.alloc_pair(either.into(), s_prime.into())?;
self.builder()
.build_return(Some(&left_pair))
.map_err(|e| CodegenError::Internal(format!("bind/st left return: {:?}", e)))?;
// Right: a = field0(either); kr = k(k, a); result = kr(kr, s')
self.builder().position_at_end(right_bb);
let a = self.extract_adt_field(either, 1, 0)?;
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type2, k_fn, &[k.into(), a.into()], "bind_st_kr")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/st k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/st k: void".to_string()))?
.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let result = self
.builder()
.build_indirect_call(
fn_type2,
kr_fn,
&[kr.into(), s_prime.into()],
"bind_st_result",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/st kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/st kr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("bind/st right return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.>> over StateT:
/// \(env, s) ->
/// pair = m1(m1, s); either = fst(pair), s' = snd(pair)
/// Left: return (either, s')
/// Right: return m2(m2, s')
fn lower_builtin_except_t_then_over_st(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>/st: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>/st: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_then_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = m1(m1, s)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let pair = self
.builder()
.build_indirect_call(fn_type2, m1_fn, &[m1.into(), s.into()], "then_st_pair")
.map_err(|e| CodegenError::Internal(format!("ExceptT then/st m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then/st m1: void".to_string()))?
.into_pointer_value();
let either = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("then/st tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("then/st branch: {:?}", e)))?;
// Left: return (either, s')
self.builder().position_at_end(left_bb);
let left_pair = self.alloc_pair(either.into(), s_prime.into())?;
self.builder()
.build_return(Some(&left_pair))
.map_err(|e| CodegenError::Internal(format!("then/st left return: {:?}", e)))?;
// Right: return m2(m2, s')
self.builder().position_at_end(right_bb);
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type2,
m2_fn,
&[m2.into(), s_prime.into()],
"then_st_result",
)
.map_err(|e| CodegenError::Internal(format!("ExceptT then/st m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then/st m2: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("then/st right return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// catchE over StateT:
/// \(env, s) ->
/// pair = m(m, s); either = fst(pair), s' = snd(pair)
/// Right: return (either, s')
/// Left: e = field0(either); hr = handler(handler, e); return hr(hr, s')
fn lower_builtin_catch_e_over_st(
&mut self,
m_expr: &Expr,
handler_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("catchE/st: m has no value".to_string()))?;
let handler_val = self
.lower_expr(handler_expr)?
.ok_or_else(|| CodegenError::Internal("catchE/st: handler has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_catch_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let handler = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = m(m, s)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), s.into()], "catch_st_pair")
.map_err(|e| CodegenError::Internal(format!("catchE/st m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/st m: void".to_string()))?
.into_pointer_value();
let either = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("catch/st tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("catch/st branch: {:?}", e)))?;
// Right: return (either, s')
self.builder().position_at_end(right_bb);
let right_pair = self.alloc_pair(either.into(), s_prime.into())?;
self.builder()
.build_return(Some(&right_pair))
.map_err(|e| CodegenError::Internal(format!("catch/st right return: {:?}", e)))?;
// Left: e = field0(either); hr = handler(handler, e); return hr(hr, s')
self.builder().position_at_end(left_bb);
let e = self.extract_adt_field(either, 1, 0)?;
let handler_fn = self.extract_closure_fn_ptr(handler)?;
let hr = self
.builder()
.build_indirect_call(
fn_type2,
handler_fn,
&[handler.into(), e.into()],
"catch_st_hr",
)
.map_err(|e| CodegenError::Internal(format!("catchE/st handler: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/st handler: void".to_string()))?
.into_pointer_value();
let hr_fn = self.extract_closure_fn_ptr(hr)?;
let result = self
.builder()
.build_indirect_call(
fn_type2,
hr_fn,
&[hr.into(), s_prime.into()],
"catch_st_result",
)
.map_err(|e| CodegenError::Internal(format!("catchE/st hr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/st hr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("catch/st left return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let handler_ptr = self.value_to_ptr(handler_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), handler_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// apply_except_t_lift over StateT: \(env, s) -> (Right(fst(action(action, s))), snd(action(action, s)))
/// Lifts a StateT action into ExceptT-over-StateT by wrapping result in Right.
fn apply_except_t_lift_to_value_over_st(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let fn_name = "bhc_except_t_lift_auto_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = action(action, s) — StateT action returns (value, s')
let action_fn = self.extract_closure_fn_ptr(action)?;
let pair = self
.builder()
.build_indirect_call(
fn_type2,
action_fn,
&[action.into(), s.into()],
"lift_st_pair",
)
.map_err(|e| CodegenError::Internal(format!("except_t_lift/st action: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("except_t_lift/st: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
// Wrap a in Right
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, a.into())?;
let result = self.alloc_pair(right_adt.into(), s_prime.into())?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("except_t_lift/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900102), action_ptr.into())])?;
Ok(closure_ptr.into())
}
// ========================================================================
// ExceptT over ReaderT — Nested Transformer Operations
// All closures use (env, reader_env) -> Either convention
// ========================================================================
/// ExceptT.>>= over ReaderT:
/// \(env, r) ->
/// either = m(m, r)
/// Left: return either
/// Right: a = field0(either); kr = k(k, a); return kr(kr, r)
fn lower_builtin_except_t_bind_over_rt(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=/rt: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>=/rt: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_bind_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// either = m(m, r)
let m_fn = self.extract_closure_fn_ptr(m)?;
let either = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), r.into()], "bind_rt_either")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/rt m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/rt m: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("bind/rt tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("bind/rt branch: {:?}", e)))?;
// Left: return either
self.builder().position_at_end(left_bb);
self.builder()
.build_return(Some(&either))
.map_err(|e| CodegenError::Internal(format!("bind/rt left return: {:?}", e)))?;
// Right: a = field0(either); kr = k(k, a); return kr(kr, r)
self.builder().position_at_end(right_bb);
let a = self.extract_adt_field(either, 1, 0)?;
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type2, k_fn, &[k.into(), a.into()], "bind_rt_kr")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/rt k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/rt k: void".to_string()))?
.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let result = self
.builder()
.build_indirect_call(fn_type2, kr_fn, &[kr.into(), r.into()], "bind_rt_result")
.map_err(|e| CodegenError::Internal(format!("ExceptT bind/rt kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT bind/rt kr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("bind/rt right return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT.>> over ReaderT:
/// \(env, r) ->
/// either = m1(m1, r)
/// Left: return either
/// Right: return m2(m2, r)
fn lower_builtin_except_t_then_over_rt(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>/rt: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("ExceptT.>>/rt: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_then_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// either = m1(m1, r)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let either = self
.builder()
.build_indirect_call(fn_type2, m1_fn, &[m1.into(), r.into()], "then_rt_either")
.map_err(|e| CodegenError::Internal(format!("ExceptT then/rt m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then/rt m1: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("then/rt tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("then/rt branch: {:?}", e)))?;
// Left: return either
self.builder().position_at_end(left_bb);
self.builder()
.build_return(Some(&either))
.map_err(|e| CodegenError::Internal(format!("then/rt left return: {:?}", e)))?;
// Right: return m2(m2, r)
self.builder().position_at_end(right_bb);
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(fn_type2, m2_fn, &[m2.into(), r.into()], "then_rt_result")
.map_err(|e| CodegenError::Internal(format!("ExceptT then/rt m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("ExceptT then/rt m2: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("then/rt right return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// catchE over ReaderT:
/// \(env, r) ->
/// either = m(m, r)
/// Right: return either
/// Left: e = field0(either); hr = handler(handler, e); return hr(hr, r)
fn lower_builtin_catch_e_over_rt(
&mut self,
m_expr: &Expr,
handler_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("catchE/rt: m has no value".to_string()))?;
let handler_val = self
.lower_expr(handler_expr)?
.ok_or_else(|| CodegenError::Internal("catchE/rt: handler has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_catch_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let right_bb = self.llvm_ctx.append_basic_block(func, "right");
let left_bb = self.llvm_ctx.append_basic_block(func, "left");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let handler = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// either = m(m, r)
let m_fn = self.extract_closure_fn_ptr(m)?;
let either = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), r.into()], "catch_rt_either")
.map_err(|e| CodegenError::Internal(format!("catchE/rt m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/rt m: void".to_string()))?
.into_pointer_value();
let tag = self.extract_adt_tag(either)?;
let is_right = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
self.type_mapper().i64_type().const_int(1, false),
"is_right",
)
.map_err(|e| CodegenError::Internal(format!("catch/rt tag cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_right, right_bb, left_bb)
.map_err(|e| CodegenError::Internal(format!("catch/rt branch: {:?}", e)))?;
// Right: return either
self.builder().position_at_end(right_bb);
self.builder()
.build_return(Some(&either))
.map_err(|e| CodegenError::Internal(format!("catch/rt right return: {:?}", e)))?;
// Left: e = field0(either); hr = handler(handler, e); return hr(hr, r)
self.builder().position_at_end(left_bb);
let e = self.extract_adt_field(either, 1, 0)?;
let handler_fn = self.extract_closure_fn_ptr(handler)?;
let hr = self
.builder()
.build_indirect_call(
fn_type2,
handler_fn,
&[handler.into(), e.into()],
"catch_rt_hr",
)
.map_err(|e| CodegenError::Internal(format!("catchE/rt handler: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/rt handler: void".to_string()))?
.into_pointer_value();
let hr_fn = self.extract_closure_fn_ptr(hr)?;
let result = self
.builder()
.build_indirect_call(fn_type2, hr_fn, &[hr.into(), r.into()], "catch_rt_result")
.map_err(|e| CodegenError::Internal(format!("catchE/rt hr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("catchE/rt hr: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("catch/rt left return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let handler_ptr = self.value_to_ptr(handler_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), handler_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// apply_except_t_lift over ReaderT: \(env, r) -> Right(action(action, r))
fn apply_except_t_lift_to_value_over_rt(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let fn_name = "bhc_except_t_lift_auto_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// a = action(action, r) — ReaderT action returns value directly
let action_fn = self.extract_closure_fn_ptr(action)?;
let a = self
.builder()
.build_indirect_call(fn_type2, action_fn, &[action.into(), r.into()], "lift_rt_a")
.map_err(|e| CodegenError::Internal(format!("except_t_lift/rt action: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("except_t_lift/rt: void".to_string()))?;
// Wrap in Right
let right_adt = self.alloc_adt(1, 1)?;
self.store_adt_field(right_adt, 1, 0, a)?;
self.builder()
.build_return(Some(&right_adt))
.map_err(|e| CodegenError::Internal(format!("except_t_lift/rt return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900102), action_ptr.into())])?;
Ok(closure_ptr.into())
}
/// ExceptT bind implementation with pre-lowered values
fn lower_except_t_bind_impl(
&mut self,
m_val: BasicValueEnum<'ctx>,
k_val: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
// Function body is generated by lower_builtin_except_t_bind if not already present
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// ExceptT then implementation with pre-lowered values
fn lower_except_t_then_impl(
&mut self,
m1_val: BasicValueEnum<'ctx>,
m2_val: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_except_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
// Function body is generated by lower_builtin_except_t_then if not already present
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
// ========================================================================
// WriterT Transformer Operations
// ========================================================================
/// runWriterT m = m(_) — runs the computation, returns (a, w) pair
fn lower_builtin_run_writer_t(
&mut self,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.push_transformer_layer(TransformerLayer::WriterT);
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("runWriterT: m has no value".to_string()))?;
self.pop_transformer_layer();
// In nested context (WriterT over StateT/ReaderT), the m_val IS the transformer
// closure that expects state/reader-env. Return it as-is — the outer
// runStateT/runReaderT will call it with the appropriate argument.
let current = self.current_transformer_layer();
if current == TransformerLayer::StateT || current == TransformerLayer::ReaderT {
return Ok(Some(m_val));
}
let m_ptr = match m_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"runWriterT: expected closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m_ptr)?;
let null_arg = ptr_type.const_null();
let result = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m_ptr.into(), null_arg.into()],
"run_writer_t",
)
.map_err(|e| CodegenError::Internal(format!("runWriterT call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runWriterT: void".to_string()))?;
Ok(Some(result))
}
/// execWriterT m = snd(m(_)) — runs computation, returns just the output
fn lower_builtin_exec_writer_t(
&mut self,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let result = self.lower_builtin_run_writer_t(m_expr)?;
let pair = result
.ok_or_else(|| CodegenError::Internal("execWriterT: no result".to_string()))?
.into_pointer_value();
let snd = self.extract_pair_snd(pair)?;
Ok(Some(snd.into()))
}
/// WriterT.pure x = closure \_ -> (x, "")
fn lower_builtin_writer_t_pure(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// WriterT over StateT: \(env, s) -> ((x, []), s)
if self.transformer_stack.is_writer_t_over_state_t() {
return self.lower_builtin_writer_t_pure_over_st(x_expr);
}
// WriterT over ReaderT: r is absorbed by unused second arg, same as plain
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.pure: x has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_pure";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> (x, "") where x = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
// Empty string = nil list
let empty = self.build_nil()?;
let pair = self.alloc_pair(x.into(), empty.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_pure return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// tell w = closure \_ -> ((), w)
fn lower_builtin_tell(&mut self, w_expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// WriterT over StateT: \(env, s) -> (((), w), s)
if self.transformer_stack.is_writer_t_over_state_t() {
return self.lower_builtin_tell_over_st(w_expr);
}
// WriterT over ReaderT: r is absorbed by unused second arg, same as plain
let w_val = self
.lower_expr(w_expr)?
.ok_or_else(|| CodegenError::Internal("tell: w has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_tell";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> ((), w) where w = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let w = self.extract_closure_env_elem(env, 1, 0)?;
let unit = ptr_type.const_null();
let pair = self.alloc_pair(unit.into(), w.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_tell return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let w_ptr = self.value_to_ptr(w_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), w_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>>= m k = closure \_ ->
/// let (a, w1) = m(_)
/// let (b, w2) = k(a)(_)
/// (b, w1 ++ w2)
fn lower_builtin_writer_t_bind(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// WriterT over StateT: threads state through closures
if self.transformer_stack.is_writer_t_over_state_t() {
return self.lower_builtin_writer_t_bind_over_st(m_expr, k_expr);
}
// WriterT over ReaderT: threads reader-env through closures
if self.transformer_stack.is_writer_t_over_reader_t() {
return self.lower_builtin_writer_t_bind_over_rt(m_expr, k_expr);
}
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// pair1 = m(_)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair1 = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), null_arg.into()], "wt_bind_pair1")
.map_err(|e| CodegenError::Internal(format!("WriterT bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind m: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair1)?;
let w1 = self.extract_pair_snd(pair1)?;
// kr = k(a)
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "wt_bind_kr")
.map_err(|e| CodegenError::Internal(format!("WriterT bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind k: void".to_string()))?
.into_pointer_value();
// pair2 = kr(_)
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let pair2 = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr.into(), null_arg.into()],
"wt_bind_pair2",
)
.map_err(|e| CodegenError::Internal(format!("WriterT bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind kr: void".to_string()))?
.into_pointer_value();
let b = self.extract_pair_fst(pair2)?;
let w2 = self.extract_pair_snd(pair2)?;
// w_combined = append(w1, w2) via list append
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_bind_append")
.map_err(|e| CodegenError::Internal(format!("WriterT append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT append: void".to_string()))?;
let result_pair = self.alloc_pair(b.into(), w_combined)?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_bind return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>> m1 m2 = m1 >>= \_ -> m2
fn lower_builtin_writer_t_then(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// WriterT over StateT: threads state through closures
if self.transformer_stack.is_writer_t_over_state_t() {
return self.lower_builtin_writer_t_then_over_st(m1_expr, m2_expr);
}
// WriterT over ReaderT: threads reader-env through closures
if self.transformer_stack.is_writer_t_over_reader_t() {
return self.lower_builtin_writer_t_then_over_rt(m1_expr, m2_expr);
}
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// pair1 = m1(_)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let pair1 = self
.builder()
.build_indirect_call(
fn_type,
m1_fn,
&[m1.into(), null_arg.into()],
"wt_then_pair1",
)
.map_err(|e| CodegenError::Internal(format!("WriterT then m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then m1: void".to_string()))?
.into_pointer_value();
let w1 = self.extract_pair_snd(pair1)?;
// pair2 = m2(_)
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let pair2 = self
.builder()
.build_indirect_call(
fn_type,
m2_fn,
&[m2.into(), null_arg.into()],
"wt_then_pair2",
)
.map_err(|e| CodegenError::Internal(format!("WriterT then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then m2: void".to_string()))?
.into_pointer_value();
let b = self.extract_pair_fst(pair2)?;
let w2 = self.extract_pair_snd(pair2)?;
// w_combined = append(w1, w2) via list append
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_then_append")
.map_err(|e| CodegenError::Internal(format!("WriterT then append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then append: void".to_string()))?;
let result_pair = self.alloc_pair(b.into(), w_combined)?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_then return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT bind implementation with pre-lowered values
fn lower_writer_t_bind_impl(
&mut self,
m_val: BasicValueEnum<'ctx>,
k_val: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_name = "bhc_writer_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
// Function body is generated by lower_builtin_writer_t_bind if not already present
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT then implementation with pre-lowered values
fn lower_writer_t_then_impl(
&mut self,
m1_val: BasicValueEnum<'ctx>,
m2_val: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_name = "bhc_writer_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
// Function body is generated by lower_builtin_writer_t_then if not already present
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.lift io = closure \_ -> (io_result, "")
fn lower_builtin_writer_t_lift(
&mut self,
io_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// In nested contexts, the inner action is a closure that needs
// state/reader-env threaded through. Route to nested lift functions.
if self.transformer_stack.is_writer_t_over_state_t() {
let io_val = self.lower_expr(io_expr)?.ok_or_else(|| {
CodegenError::Internal("WriterT.lift/st: io has no value".to_string())
})?;
return Ok(Some(self.apply_writer_t_lift_to_value_over_st(io_val)?));
}
if self.transformer_stack.is_writer_t_over_reader_t() {
let io_val = self.lower_expr(io_expr)?.ok_or_else(|| {
CodegenError::Internal("WriterT.lift/rt: io has no value".to_string())
})?;
return Ok(Some(self.apply_writer_t_lift_to_value_over_rt(io_val)?));
}
let io_val = self
.lower_expr(io_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.lift: io has no value".to_string()))?;
let _ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
// \(env, _) -> (io_result, "") where io = env[0]
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let io_result = self.extract_closure_env_elem(env, 1, 0)?;
let empty = self.build_nil()?;
let pair = self.alloc_pair(io_result.into(), empty.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_lift return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let io_ptr = self.value_to_ptr(io_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), io_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.fmap f m = closure \_ -> let (a, w) = m(_) in (f(a), w)
fn lower_builtin_writer_t_fmap(
&mut self,
f_expr: &Expr,
m_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.fmap: f has no value".to_string()))?;
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.fmap: m has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), null_arg.into()], "wt_fmap_pair")
.map_err(|e| CodegenError::Internal(format!("WriterT fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT fmap m: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let w = self.extract_pair_snd(pair)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "wt_fmap_b")
.map_err(|e| CodegenError::Internal(format!("WriterT fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT fmap f: void".to_string()))?;
let result_pair = self.alloc_pair(b, w.into())?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_fmap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.<*> mf ma — applicative apply
fn lower_builtin_writer_t_ap(
&mut self,
mf_expr: &Expr,
ma_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let mf_val = self
.lower_expr(mf_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.<*>: mf has no value".to_string()))?;
let ma_val = self
.lower_expr(ma_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.<*>: ma has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let ma = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let null_arg = ptr_type.const_null();
// (f, w1) = mf(_)
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let pair1 = self
.builder()
.build_indirect_call(fn_type, mf_fn, &[mf.into(), null_arg.into()], "wt_ap_pair1")
.map_err(|e| CodegenError::Internal(format!("WriterT ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT ap mf: void".to_string()))?
.into_pointer_value();
let f = self.extract_pair_fst(pair1)?;
let w1 = self.extract_pair_snd(pair1)?;
// (a, w2) = ma(_)
let ma_fn = self.extract_closure_fn_ptr(ma)?;
let pair2 = self
.builder()
.build_indirect_call(fn_type, ma_fn, &[ma.into(), null_arg.into()], "wt_ap_pair2")
.map_err(|e| CodegenError::Internal(format!("WriterT ap ma: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT ap ma: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair2)?;
let w2 = self.extract_pair_snd(pair2)?;
// b = f(a)
let f_fn = self.extract_closure_fn_ptr(f)?;
let b = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "wt_ap_b")
.map_err(|e| CodegenError::Internal(format!("WriterT ap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT ap f: void".to_string()))?;
// w_combined = append(w1, w2) via list append
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_ap_append")
.map_err(|e| CodegenError::Internal(format!("WriterT ap append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT ap append: void".to_string()))?;
let result_pair = self.alloc_pair(b, w_combined)?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_ap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let ma_ptr = self.value_to_ptr(ma_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), ma_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
// ========================================================================
// WriterT Cross-Transformer Operations (WriterT over StateT / ReaderT)
// ========================================================================
/// WriterT.pure over StateT: \(env, s) -> ((x, []), s)
fn lower_builtin_writer_t_pure_over_st(
&mut self,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.pure/st: x has no value".to_string()))?;
let fn_name = "bhc_writer_t_pure_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
let empty = self.build_nil()?;
let inner_pair = self.alloc_pair(x.into(), empty.into())?;
let outer_pair = self.alloc_pair(inner_pair.into(), s.into())?;
self.builder()
.build_return(Some(&outer_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_pure/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// tell over StateT: \(env, s) -> (((), w), s)
fn lower_builtin_tell_over_st(
&mut self,
w_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let w_val = self
.lower_expr(w_expr)?
.ok_or_else(|| CodegenError::Internal("tell/st: w has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_tell_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let w = self.extract_closure_env_elem(env, 1, 0)?;
let unit = ptr_type.const_null();
let inner_pair = self.alloc_pair(unit.into(), w.into())?;
let outer_pair = self.alloc_pair(inner_pair.into(), s.into())?;
self.builder()
.build_return(Some(&outer_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_tell/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let w_ptr = self.value_to_ptr(w_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900000), w_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>>= over StateT: \(env, s) ->
/// outer1 = m(m, s) → ((a, w1), s')
/// a = fst(fst(outer1)), w1 = snd(fst(outer1)), s' = snd(outer1)
/// kr = k(k, a) → new closure
/// outer2 = kr(kr, s') → ((b, w2), s'')
/// b = fst(fst(outer2)), w2 = snd(fst(outer2)), s'' = snd(outer2)
/// return ((b, w1++w2), s'')
fn lower_builtin_writer_t_bind_over_st(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=/st: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=/st: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_bind_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// outer1 = m(m, s) → ((a, w1), s')
let m_fn = self.extract_closure_fn_ptr(m)?;
let outer1 = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), s.into()], "wt_bind_st_outer1")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/st m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/st m: void".to_string()))?
.into_pointer_value();
let inner1 = self.extract_pair_fst(outer1)?;
let s_prime = self.extract_pair_snd(outer1)?;
let a = self.extract_pair_fst(inner1)?;
let w1 = self.extract_pair_snd(inner1)?;
// kr = k(k, a) → new closure
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type2, k_fn, &[k.into(), a.into()], "wt_bind_st_kr")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/st k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/st k: void".to_string()))?
.into_pointer_value();
// outer2 = kr(kr, s') → ((b, w2), s'')
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let outer2 = self
.builder()
.build_indirect_call(
fn_type2,
kr_fn,
&[kr.into(), s_prime.into()],
"wt_bind_st_outer2",
)
.map_err(|e| CodegenError::Internal(format!("WriterT bind/st kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/st kr: void".to_string()))?
.into_pointer_value();
let inner2 = self.extract_pair_fst(outer2)?;
let s_double_prime = self.extract_pair_snd(outer2)?;
let b = self.extract_pair_fst(inner2)?;
let w2 = self.extract_pair_snd(inner2)?;
// w_combined = w1 ++ w2
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_bind_st_append")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/st append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("WriterT bind/st append: void".to_string())
})?;
let result_inner = self.alloc_pair(b.into(), w_combined)?;
let result_outer = self.alloc_pair(result_inner.into(), s_double_prime.into())?;
self.builder()
.build_return(Some(&result_outer))
.map_err(|e| CodegenError::Internal(format!("writer_t_bind/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>>= over ReaderT: \(env, r) ->
/// pair1 = m(m, r) → (a, w1)
/// kr = k(k, a) → new closure
/// pair2 = kr(kr, r) → (b, w2)
/// return (b, w1++w2)
fn lower_builtin_writer_t_bind_over_rt(
&mut self,
m_expr: &Expr,
k_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m_val = self
.lower_expr(m_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=/rt: m has no value".to_string()))?;
let k_val = self
.lower_expr(k_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>=/rt: k has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_bind_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair1 = m(m, r) → (a, w1)
let m_fn = self.extract_closure_fn_ptr(m)?;
let pair1 = self
.builder()
.build_indirect_call(fn_type2, m_fn, &[m.into(), r.into()], "wt_bind_rt_pair1")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/rt m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/rt m: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair1)?;
let w1 = self.extract_pair_snd(pair1)?;
// kr = k(k, a) → new closure
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type2, k_fn, &[k.into(), a.into()], "wt_bind_rt_kr")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/rt k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/rt k: void".to_string()))?
.into_pointer_value();
// pair2 = kr(kr, r) → (b, w2)
let kr_fn = self.extract_closure_fn_ptr(kr)?;
let pair2 = self
.builder()
.build_indirect_call(fn_type2, kr_fn, &[kr.into(), r.into()], "wt_bind_rt_pair2")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/rt kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT bind/rt kr: void".to_string()))?
.into_pointer_value();
let b = self.extract_pair_fst(pair2)?;
let w2 = self.extract_pair_snd(pair2)?;
// w_combined = w1 ++ w2
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_bind_rt_append")
.map_err(|e| CodegenError::Internal(format!("WriterT bind/rt append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("WriterT bind/rt append: void".to_string())
})?;
let result_pair = self.alloc_pair(b.into(), w_combined)?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_bind/rt return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>> over StateT: \(env, s) ->
/// outer1 = m1(m1, s) → ((_, w1), s')
/// outer2 = m2(m2, s') → ((b, w2), s'')
/// return ((b, w1++w2), s'')
fn lower_builtin_writer_t_then_over_st(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>/st: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>/st: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_then_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// outer1 = m1(m1, s) → ((_, w1), s')
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let outer1 = self
.builder()
.build_indirect_call(fn_type2, m1_fn, &[m1.into(), s.into()], "wt_then_st_outer1")
.map_err(|e| CodegenError::Internal(format!("WriterT then/st m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then/st m1: void".to_string()))?
.into_pointer_value();
let inner1 = self.extract_pair_fst(outer1)?;
let s_prime = self.extract_pair_snd(outer1)?;
let w1 = self.extract_pair_snd(inner1)?;
// outer2 = m2(m2, s') → ((b, w2), s'')
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let outer2 = self
.builder()
.build_indirect_call(
fn_type2,
m2_fn,
&[m2.into(), s_prime.into()],
"wt_then_st_outer2",
)
.map_err(|e| CodegenError::Internal(format!("WriterT then/st m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then/st m2: void".to_string()))?
.into_pointer_value();
let inner2 = self.extract_pair_fst(outer2)?;
let s_double_prime = self.extract_pair_snd(outer2)?;
let b = self.extract_pair_fst(inner2)?;
let w2 = self.extract_pair_snd(inner2)?;
// w_combined = w1 ++ w2
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_then_st_append")
.map_err(|e| CodegenError::Internal(format!("WriterT then/st append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("WriterT then/st append: void".to_string())
})?;
let result_inner = self.alloc_pair(b.into(), w_combined)?;
let result_outer = self.alloc_pair(result_inner.into(), s_double_prime.into())?;
self.builder()
.build_return(Some(&result_outer))
.map_err(|e| CodegenError::Internal(format!("writer_t_then/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.>> over ReaderT: \(env, r) ->
/// pair1 = m1(m1, r) → (_, w1)
/// pair2 = m2(m2, r) → (b, w2)
/// return (b, w1++w2)
fn lower_builtin_writer_t_then_over_rt(
&mut self,
m1_expr: &Expr,
m2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1_val = self
.lower_expr(m1_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>/rt: m1 has no value".to_string()))?;
let m2_val = self
.lower_expr(m2_expr)?
.ok_or_else(|| CodegenError::Internal("WriterT.>>/rt: m2 has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let fn_name = "bhc_writer_t_then_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair1 = m1(m1, r) → (_, w1)
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let pair1 = self
.builder()
.build_indirect_call(fn_type2, m1_fn, &[m1.into(), r.into()], "wt_then_rt_pair1")
.map_err(|e| CodegenError::Internal(format!("WriterT then/rt m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then/rt m1: void".to_string()))?
.into_pointer_value();
let w1 = self.extract_pair_snd(pair1)?;
// pair2 = m2(m2, r) → (b, w2)
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let pair2 = self
.builder()
.build_indirect_call(fn_type2, m2_fn, &[m2.into(), r.into()], "wt_then_rt_pair2")
.map_err(|e| CodegenError::Internal(format!("WriterT then/rt m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("WriterT then/rt m2: void".to_string()))?
.into_pointer_value();
let b = self.extract_pair_fst(pair2)?;
let w2 = self.extract_pair_snd(pair2)?;
// w_combined = w1 ++ w2
let append_fn = self.get_or_create_list_append_fn()?;
let w_combined = self
.builder()
.build_call(append_fn, &[w1.into(), w2.into()], "wt_then_rt_append")
.map_err(|e| CodegenError::Internal(format!("WriterT then/rt append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("WriterT then/rt append: void".to_string())
})?;
let result_pair = self.alloc_pair(b.into(), w_combined)?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_then/rt return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
/// WriterT.lift auto over StateT: \(env, s) -> ((action(action, s).fst, []), action(action, s).snd)
fn apply_writer_t_lift_to_value_over_st(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let fn_name = "bhc_writer_t_lift_auto_over_st";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// pair = action(action, s) → (value, s')
let action_fn = self.extract_closure_fn_ptr(action)?;
let pair = self
.builder()
.build_indirect_call(
fn_type2,
action_fn,
&[action.into(), s.into()],
"wt_lift_st_pair",
)
.map_err(|e| CodegenError::Internal(format!("writer_t_lift/st action: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("writer_t_lift/st: void".to_string()))?
.into_pointer_value();
let a = self.extract_pair_fst(pair)?;
let s_prime = self.extract_pair_snd(pair)?;
let empty = self.build_nil()?;
let inner_pair = self.alloc_pair(a.into(), empty.into())?;
let outer_pair = self.alloc_pair(inner_pair.into(), s_prime.into())?;
self.builder()
.build_return(Some(&outer_pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_lift/st return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900103), action_ptr.into())])?;
Ok(closure_ptr.into())
}
/// WriterT.lift auto over ReaderT: \(env, r) -> (action(action, r), [])
fn apply_writer_t_lift_to_value_over_rt(
&mut self,
action_val: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let fn_name = "bhc_writer_t_lift_auto_over_rt";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap().into_pointer_value();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let ptr_type = self.type_mapper().ptr_type();
let fn_type2 = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
// a = action(action, r) — ReaderT action returns value directly
let action_fn = self.extract_closure_fn_ptr(action)?;
let a = self
.builder()
.build_indirect_call(
fn_type2,
action_fn,
&[action.into(), r.into()],
"wt_lift_rt_a",
)
.map_err(|e| CodegenError::Internal(format!("writer_t_lift/rt action: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("writer_t_lift/rt: void".to_string()))?;
let empty = self.build_nil()?;
let pair = self.alloc_pair(a, empty.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("writer_t_lift/rt return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let action_ptr = self.value_to_ptr(action_val)?;
let closure_ptr = self.alloc_closure(fn_ptr, &[(VarId::new(900103), action_ptr.into())])?;
Ok(closure_ptr.into())
}
// ========================================================================
// Phase 1: Numeric & Character Operation Handlers
// ========================================================================
/// Lower `negate` - negate a numeric value.
fn lower_builtin_negate(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Rational dispatch
if self.is_rational_expr(expr) {
return self.lower_builtin_rational_unary(expr, 1000907, "negate");
}
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("negate: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let zero = self.type_mapper().i64_type().const_zero();
let result = self
.builder()
.build_int_sub(zero, int_val, "negate")
.map_err(|e| CodegenError::Internal(format!("negate failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower `abs` - absolute value.
fn lower_builtin_abs(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Rational dispatch
if self.is_rational_expr(expr) {
return self.lower_builtin_rational_unary(expr, 1000908, "abs");
}
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("abs: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let zero = self.type_mapper().i64_type().const_zero();
let is_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, int_val, zero, "is_neg")
.map_err(|e| CodegenError::Internal(format!("abs cmp failed: {:?}", e)))?;
let neg_val = self
.builder()
.build_int_sub(zero, int_val, "neg")
.map_err(|e| CodegenError::Internal(format!("abs neg failed: {:?}", e)))?;
let result = self
.builder()
.build_select(is_neg, neg_val, int_val, "abs")
.map_err(|e| CodegenError::Internal(format!("abs select failed: {:?}", e)))?
.into_int_value();
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower `signum` - sign of a value (-1, 0, or 1).
fn lower_builtin_signum(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Rational dispatch
if self.is_rational_expr(expr) {
return self.lower_builtin_rational_unary(expr, 1000909, "signum");
}
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("signum: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let tm = self.type_mapper();
let zero = tm.i64_type().const_zero();
let one = tm.i64_type().const_int(1, false);
let neg_one = tm.i64_type().const_all_ones();
let is_pos = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, int_val, zero, "is_pos")
.map_err(|e| CodegenError::Internal(format!("signum failed: {:?}", e)))?;
let is_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, int_val, zero, "is_neg")
.map_err(|e| CodegenError::Internal(format!("signum failed: {:?}", e)))?;
let step1 = self
.builder()
.build_select(is_pos, one, zero, "signum_step1")
.map_err(|e| CodegenError::Internal(format!("signum failed: {:?}", e)))?
.into_int_value();
let result = self
.builder()
.build_select(is_neg, neg_one, step1, "signum")
.map_err(|e| CodegenError::Internal(format!("signum failed: {:?}", e)))?
.into_int_value();
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower a unary math function (sqrt, exp, log, sin, cos, etc.)
fn lower_builtin_math_unary(
&mut self,
expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", name)))?;
let f64_val = self.coerce_to_f64(val)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("RTS function {} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[f64_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(self.f64_to_ptr(result.into_float_value())?.into()))
}
/// Lower a binary math function (atan2).
fn lower_builtin_math_binary(
&mut self,
expr1: &Expr,
expr2: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val1 = self
.lower_expr(expr1)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no arg1", name)))?;
let val2 = self
.lower_expr(expr2)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no arg2", name)))?;
let f1 = self.coerce_to_f64(val1)?;
let f2 = self.coerce_to_f64(val2)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("RTS function {} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[f1.into(), f2.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(self.f64_to_ptr(result.into_float_value())?.into()))
}
/// Lower float-to-int conversion (ceiling, floor, round, truncate).
fn lower_builtin_float_to_int(
&mut self,
expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", name)))?;
let f64_val = self.coerce_to_f64(val)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("RTS function {} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[f64_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `%` operator — construct a Rational from two Int args.
fn lower_builtin_rational_make(
&mut self,
num_expr: &Expr,
denom_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let num = self
.lower_expr(num_expr)?
.ok_or_else(|| CodegenError::Internal("%: no numerator".to_string()))?;
let denom = self
.lower_expr(denom_expr)?
.ok_or_else(|| CodegenError::Internal("%: no denominator".to_string()))?;
let num_int = self.coerce_to_int(num)?;
let denom_int = self.coerce_to_int(denom)?;
let rts_fn = *self
.functions
.get(&VarId::new(1000900))
.ok_or_else(|| CodegenError::Internal("bhc_rational_make not declared".to_string()))?;
let result = self
.builder()
.build_call(rts_fn, &[num_int.into(), denom_int.into()], "rational_make")
.map_err(|e| CodegenError::Internal(format!("rational_make call: {:?}", e)))?;
Ok(result.try_as_basic_value().basic())
}
/// Lower a unary Rational RTS operation (numerator, denominator, negate, abs, signum, recip).
fn lower_builtin_rational_unary(
&mut self,
expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", name)))?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = *self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!(
"RTS function {} (VarId {}) not declared",
name, rts_id
))
})?;
let result = self
.builder()
.build_call(rts_fn, &[val_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?;
Ok(result.try_as_basic_value().basic())
}
/// Lower `toRational` — convert Int to Rational via bhc_rational_from_int.
fn lower_builtin_to_rational(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// If the argument is already Rational, it's identity
if self.is_rational_expr(expr) {
return self.lower_expr(expr);
}
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("toRational: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let rts_fn = *self.functions.get(&VarId::new(1000912)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_from_int not declared".to_string())
})?;
let result = self
.builder()
.build_call(rts_fn, &[int_val.into()], "to_rational")
.map_err(|e| CodegenError::Internal(format!("toRational call: {:?}", e)))?;
Ok(result.try_as_basic_value().basic())
}
/// Lower `fromRational` — convert Rational to target type.
/// For Rational -> Rational: identity. For Rational -> Float/Double: call bhc_rational_to_double.
fn lower_builtin_from_rational(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("fromRational: no value".to_string()))?;
// Default: identity (Rational -> Rational)
Ok(Some(val))
}
/// Lower `ord` - character to integer (identity in our representation).
fn lower_builtin_ord(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(expr)
}
/// Lower `chr` - integer to character (identity in our representation).
fn lower_builtin_chr(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(expr)
}
/// Lower `even n` — returns Bool ADT (tag 0=False, 1=True).
fn lower_builtin_even(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("even: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let i64_type = self.type_mapper().i64_type();
let two = i64_type.const_int(2, false);
let rem = self
.builder()
.build_int_signed_rem(int_val, two, "even_rem")
.map_err(|e| CodegenError::Internal(format!("even: srem failed: {:?}", e)))?;
let is_even = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rem,
i64_type.const_zero(),
"is_even",
)
.map_err(|e| CodegenError::Internal(format!("even: cmp failed: {:?}", e)))?;
let tag = self
.builder()
.build_int_z_extend(is_even, i64_type, "even_tag")
.map_err(|e| CodegenError::Internal(format!("even: extend failed: {:?}", e)))?;
self.allocate_bool_adt(tag, "even")
}
/// Lower `odd n` — returns Bool ADT (tag 0=False, 1=True).
fn lower_builtin_odd(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("odd: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let i64_type = self.type_mapper().i64_type();
let two = i64_type.const_int(2, false);
let rem = self
.builder()
.build_int_signed_rem(int_val, two, "odd_rem")
.map_err(|e| CodegenError::Internal(format!("odd: srem failed: {:?}", e)))?;
let is_odd = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
rem,
i64_type.const_zero(),
"is_odd",
)
.map_err(|e| CodegenError::Internal(format!("odd: cmp failed: {:?}", e)))?;
let tag = self
.builder()
.build_int_z_extend(is_odd, i64_type, "odd_tag")
.map_err(|e| CodegenError::Internal(format!("odd: extend failed: {:?}", e)))?;
self.allocate_bool_adt(tag, "odd")
}
/// Allocate a Bool ADT with dynamic tag (0=False, 1=True).
fn allocate_bool_adt(
&mut self,
tag: inkwell::values::IntValue<'ctx>,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let adt_ty = self.adt_type(0);
let size_val = self.type_mapper().i64_type().const_int(8, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], &format!("{}_alloc", name))
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?;
let ptr = raw_ptr.into_pointer_value();
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, ptr, 0, &format!("{}_tag_ptr", name))
.map_err(|e| CodegenError::Internal(format!("{}: gep failed: {:?}", name, e)))?;
self.builder()
.build_store(tag_ptr, tag)
.map_err(|e| CodegenError::Internal(format!("{}: store failed: {:?}", name, e)))?;
Ok(Some(ptr.into()))
}
/// Allocate an Ordering ADT with dynamic tag (0=LT, 1=EQ, 2=GT).
fn allocate_ordering_adt(
&mut self,
tag: inkwell::values::IntValue<'ctx>,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Identical layout to Bool ADT — 8-byte tag-only struct
let adt_ty = self.adt_type(0);
let size_val = self.type_mapper().i64_type().const_int(8, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], &format!("{}_alloc", name))
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?;
let ptr = raw_ptr.into_pointer_value();
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, ptr, 0, &format!("{}_tag_ptr", name))
.map_err(|e| CodegenError::Internal(format!("{}: gep failed: {:?}", name, e)))?;
self.builder()
.build_store(tag_ptr, tag)
.map_err(|e| CodegenError::Internal(format!("{}: store failed: {:?}", name, e)))?;
Ok(Some(ptr.into()))
}
/// Lower `from x` (GHC.Generics) — dispatch to derived $derived_from_TypeName.
fn lower_builtin_generic_from(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Try to infer the ADT type from the argument expression
let type_name = self.infer_adt_type_from_expr(arg_expr);
// Fallback: if there's exactly one derived_from_fn, use it (common case)
let type_name = type_name.or_else(|| {
if self.derived_from_fns.len() == 1 {
Some(self.derived_from_fns.keys().next().unwrap().clone())
} else {
None
}
});
if let Some(type_name) = type_name {
if let Some(from_var_id) = self.derived_from_fns.get(&type_name).copied() {
if let Some(from_fn) = self.functions.get(&from_var_id).copied() {
let value = self.lower_expr(arg_expr)?.ok_or_else(|| {
CodegenError::Internal("from: failed to lower ADT value".to_string())
})?;
// Call derived from: fn(env_ptr, value) -> rep_ptr
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = from_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), value.into()],
"derived_from",
)
.map_err(|e| CodegenError::Internal(format!("from: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("from: void result".to_string()))?;
return Ok(Some(result));
}
}
}
// Fallback: identity (for types without Generic instance)
self.lower_expr(arg_expr)
}
/// Lower `to rep` (GHC.Generics) — dispatch to derived $derived_to_TypeName.
/// Note: `to` dispatches based on what the result type should be, which is
/// determined from the expression context. Since we can't easily infer the
/// result type, we look at the argument's rep structure to find the original type.
fn lower_builtin_generic_to(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For `to`, we need to find which type this is for.
// Strategy: check if the argument was produced by `from` of a known type,
// or look for derived_to_fns and try each one.
// Simplest approach: if there's exactly one derived_to_fn, use it.
// Otherwise, check if the argument expression gives us a hint.
// Check if argument is (from x) — then we know the type from x
if let Expr::App(f, inner_arg, _) = arg_expr {
if let Expr::Var(fv, _) = f.as_ref() {
if fv.name.as_str() == "from" {
if let Some(type_name) = self.infer_adt_type_from_expr(inner_arg) {
if let Some(to_var_id) = self.derived_to_fns.get(&type_name).copied() {
if let Some(to_fn) = self.functions.get(&to_var_id).copied() {
let value = self.lower_expr(arg_expr)?.ok_or_else(|| {
CodegenError::Internal(
"to: failed to lower rep value".to_string(),
)
})?;
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = to_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), value.into()],
"derived_to",
)
.map_err(|e| {
CodegenError::Internal(format!("to: call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("to: void result".to_string())
})?;
return Ok(Some(result));
}
}
}
}
}
}
// If there's exactly one derived_to_fn, use it (common case in tests)
if self.derived_to_fns.len() == 1 {
let (_, to_var_id) = self.derived_to_fns.iter().next().unwrap();
let to_var_id = *to_var_id;
if let Some(to_fn) = self.functions.get(&to_var_id).copied() {
let value = self.lower_expr(arg_expr)?.ok_or_else(|| {
CodegenError::Internal("to: failed to lower rep value".to_string())
})?;
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = to_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), value.into()],
"derived_to",
)
.map_err(|e| CodegenError::Internal(format!("to: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("to: void result".to_string()))?;
return Ok(Some(result));
}
}
// Fallback: identity
self.lower_expr(arg_expr)
}
/// Lower `compare a b` — returns Ordering ADT (tag 0=LT, 1=EQ, 2=GT).
fn lower_builtin_compare(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if comparing user ADTs with derived Ord
let adt_type = Self::is_user_adt_expr(a_expr, &self.constructor_metadata)
.or_else(|| Self::is_user_adt_expr(b_expr, &self.constructor_metadata));
if let Some(type_name) = adt_type {
if let Some(cmp_var_id) = self.derived_compare_fns.get(&type_name).copied() {
if let Some(cmp_fn) = self.functions.get(&cmp_var_id).copied() {
let lhs = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no lhs".to_string()))?;
let rhs = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no rhs".to_string()))?;
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = cmp_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), lhs.into(), rhs.into()],
"derived_compare",
)
.map_err(|e| CodegenError::Internal(format!("derived compare: {:?}", e)))?;
let ordering_ptr = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("derived compare returned void".to_string())
})?;
return Ok(Some(ordering_ptr));
}
}
}
// Rational comparison
if self.is_rational_expr(a_expr) || self.is_rational_expr(b_expr) {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no lhs".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no rhs".to_string()))?;
let a_ptr = self.value_to_ptr(a_val)?;
let b_ptr = self.value_to_ptr(b_val)?;
let rts_fn = *self.functions.get(&VarId::new(1000911)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_compare not declared".to_string())
})?;
let cmp_result = self
.builder()
.build_call(rts_fn, &[a_ptr.into(), b_ptr.into()], "rational_compare")
.map_err(|e| CodegenError::Internal(format!("rational compare: {:?}", e)))?;
let cmp_val = cmp_result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("rational compare returned void".to_string())
})?;
// bhc_rational_compare returns i32 (-1, 0, 1). Convert to Ordering ADT (tag 0=LT, 1=EQ, 2=GT).
let cmp_int = if cmp_val.is_int_value() {
cmp_val.into_int_value()
} else {
self.ptr_to_int(cmp_val.into_pointer_value())?
};
let i64_type = self.type_mapper().i64_type();
let zero = i64_type.const_zero();
let is_lt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, cmp_int, zero, "rat_cmp_lt")
.map_err(|e| CodegenError::Internal(format!("rat cmp lt: {:?}", e)))?;
let is_gt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, cmp_int, zero, "rat_cmp_gt")
.map_err(|e| CodegenError::Internal(format!("rat cmp gt: {:?}", e)))?;
let tag_gt_or_eq = self
.builder()
.build_select(
is_gt,
i64_type.const_int(2, false),
i64_type.const_int(1, false),
"gt_or_eq",
)
.map_err(|e| CodegenError::Internal(format!("rat compare select1: {:?}", e)))?
.into_int_value();
let tag = self
.builder()
.build_select(
is_lt,
i64_type.const_int(0, false),
tag_gt_or_eq,
"rat_cmp_tag",
)
.map_err(|e| CodegenError::Internal(format!("rat compare select2: {:?}", e)))?;
return self.allocate_ordering_adt(tag.into_int_value(), "rational_compare");
}
// Integer comparison fallback
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no lhs".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("compare: no rhs".to_string()))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
let i64_type = self.type_mapper().i64_type();
let is_lt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, a_int, b_int, "cmp_lt")
.map_err(|e| CodegenError::Internal(format!("compare: slt: {:?}", e)))?;
let is_gt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, a_int, b_int, "cmp_gt")
.map_err(|e| CodegenError::Internal(format!("compare: sgt: {:?}", e)))?;
// tag = is_lt ? 0 : (is_gt ? 2 : 1)
let tag_gt_or_eq = self
.builder()
.build_select(
is_gt,
i64_type.const_int(2, false),
i64_type.const_int(1, false),
"gt_or_eq",
)
.map_err(|e| CodegenError::Internal(format!("compare: select1: {:?}", e)))?
.into_int_value();
let tag = self
.builder()
.build_select(is_lt, i64_type.const_int(0, false), tag_gt_or_eq, "cmp_tag")
.map_err(|e| CodegenError::Internal(format!("compare: select2: {:?}", e)))?;
self.allocate_ordering_adt(tag.into_int_value(), "compare")
}
/// Lower a binary integer RTS operation (gcd, lcm).
fn lower_builtin_int_binop_rts(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no a", name)))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no b", name)))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[a_int.into(), b_int.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let result_int = result.into_int_value();
let ptr = self.int_to_ptr(result_int)?;
Ok(Some(ptr.into()))
}
/// Lower `divMod a b` — returns (Int, Int) tuple with floor-division semantics.
fn lower_builtin_divmod(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("divMod: no a".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("divMod: no b".to_string()))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
// Haskell divMod uses floor-division: div rounds toward negative infinity
// d = floor(a/b), m = a - d*b
let i64_type = self.type_mapper().i64_type();
// Compute truncated quotient and remainder
let q = self
.builder()
.build_int_signed_div(a_int, b_int, "divmod_q")
.map_err(|e| CodegenError::Internal(format!("divMod: sdiv failed: {:?}", e)))?;
let r = self
.builder()
.build_int_signed_rem(a_int, b_int, "divmod_r")
.map_err(|e| CodegenError::Internal(format!("divMod: srem failed: {:?}", e)))?;
// Adjust for floor division: if remainder != 0 and signs differ, subtract 1 from quotient and add b to remainder
let r_nonzero = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, r, i64_type.const_zero(), "r_nz")
.map_err(|e| CodegenError::Internal(format!("divMod: cmp1 failed: {:?}", e)))?;
// XOR a and b, check sign bit (if signs differ, xor is negative)
let xor_ab = self
.builder()
.build_xor(a_int, b_int, "xor_ab")
.map_err(|e| CodegenError::Internal(format!("divMod: xor failed: {:?}", e)))?;
let signs_differ = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
xor_ab,
i64_type.const_zero(),
"signs_diff",
)
.map_err(|e| CodegenError::Internal(format!("divMod: cmp2 failed: {:?}", e)))?;
let needs_adjust = self
.builder()
.build_and(r_nonzero, signs_differ, "needs_adj")
.map_err(|e| CodegenError::Internal(format!("divMod: and failed: {:?}", e)))?;
let one = i64_type.const_int(1, false);
let q_minus_1 = self
.builder()
.build_int_sub(q, one, "q_m1")
.map_err(|e| CodegenError::Internal(format!("divMod: sub failed: {:?}", e)))?;
let r_plus_b = self
.builder()
.build_int_add(r, b_int, "r_pb")
.map_err(|e| CodegenError::Internal(format!("divMod: add failed: {:?}", e)))?;
let div_result = self
.builder()
.build_select(needs_adjust, q_minus_1, q, "div_res")
.map_err(|e| CodegenError::Internal(format!("divMod: select1 failed: {:?}", e)))?
.into_int_value();
let mod_result = self
.builder()
.build_select(needs_adjust, r_plus_b, r, "mod_res")
.map_err(|e| CodegenError::Internal(format!("divMod: select2 failed: {:?}", e)))?
.into_int_value();
self.allocate_int_pair_tuple(div_result, mod_result, "divmod")
}
/// Lower `quotRem a b` — returns (Int, Int) tuple with truncation-toward-zero semantics.
fn lower_builtin_quotrem(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("quotRem: no a".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("quotRem: no b".to_string()))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
let q = self
.builder()
.build_int_signed_div(a_int, b_int, "quotrem_q")
.map_err(|e| CodegenError::Internal(format!("quotRem: sdiv failed: {:?}", e)))?;
let r = self
.builder()
.build_int_signed_rem(a_int, b_int, "quotrem_r")
.map_err(|e| CodegenError::Internal(format!("quotRem: srem failed: {:?}", e)))?;
self.allocate_int_pair_tuple(q, r, "quotrem")
}
/// Allocate a (Int, Int) tuple: 24 bytes, tag=0 at offset 0, fst at offset 8, snd at offset 16.
fn allocate_int_pair_tuple(
&mut self,
fst: inkwell::values::IntValue<'ctx>,
snd: inkwell::values::IntValue<'ctx>,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let i64_type = self.type_mapper().i64_type();
let ptr_type = self.type_mapper().ptr_type();
let size_val = i64_type.const_int(24, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], &format!("{}_alloc", name))
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?;
let tuple_ptr = raw_ptr.into_pointer_value();
// Store tag=0 at offset 0
let adt_ty = self.adt_type(0);
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, tuple_ptr, 0, &format!("{}_tag", name))
.map_err(|e| CodegenError::Internal(format!("{}: tag gep failed: {:?}", name, e)))?;
self.builder()
.build_store(tag_ptr, i64_type.const_zero())
.map_err(|e| CodegenError::Internal(format!("{}: tag store failed: {:?}", name, e)))?;
// Store fst as ptr at offset 8
let fst_ptr = self.int_to_ptr(fst)?;
let field1_ptr = self
.builder()
.build_struct_gep(adt_ty, tuple_ptr, 1, &format!("{}_fst", name))
.map_err(|e| CodegenError::Internal(format!("{}: fst gep failed: {:?}", name, e)))?;
self.builder()
.build_store(field1_ptr, fst_ptr)
.map_err(|e| CodegenError::Internal(format!("{}: fst store failed: {:?}", name, e)))?;
// Store snd as ptr at offset 16
let snd_ptr = self.int_to_ptr(snd)?;
let field2_gep = unsafe {
self.builder()
.build_gep(
ptr_type,
tuple_ptr,
&[i64_type.const_int(2, false)],
&format!("{}_snd_gep", name),
)
.map_err(|e| CodegenError::Internal(format!("{}: snd gep failed: {:?}", name, e)))?
};
self.builder()
.build_store(field2_gep, snd_ptr)
.map_err(|e| CodegenError::Internal(format!("{}: snd store failed: {:?}", name, e)))?;
Ok(Some(tuple_ptr.into()))
}
/// Lower `newIORef val` — create a new mutable reference.
fn lower_builtin_new_ioref(
&mut self,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("newIORef: no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000502))
.ok_or_else(|| CodegenError::Internal("bhc_new_ioref not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_ptr.into()], "new_ioref")
.map_err(|e| CodegenError::Internal(format!("newIORef call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("newIORef: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `readIORef ref` — read the current value.
fn lower_builtin_read_ioref(
&mut self,
ref_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ref_val = self
.lower_expr(ref_expr)?
.ok_or_else(|| CodegenError::Internal("readIORef: no ref".to_string()))?;
let ref_ptr = match ref_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"readIORef expects pointer".to_string(),
))
}
};
let rts_fn = self
.functions
.get(&VarId::new(1000503))
.ok_or_else(|| CodegenError::Internal("bhc_read_ioref not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[ref_ptr.into()], "read_ioref")
.map_err(|e| CodegenError::Internal(format!("readIORef call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("readIORef: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `writeIORef ref val` — overwrite the value.
fn lower_builtin_write_ioref(
&mut self,
ref_expr: &Expr,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ref_val = self
.lower_expr(ref_expr)?
.ok_or_else(|| CodegenError::Internal("writeIORef: no ref".to_string()))?;
let ref_ptr = match ref_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"writeIORef expects pointer".to_string(),
))
}
};
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("writeIORef: no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000504))
.ok_or_else(|| CodegenError::Internal("bhc_write_ioref not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[ref_ptr.into(), val_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("writeIORef call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `modifyIORef ref f` — read, apply f, write back.
fn lower_builtin_modify_ioref(
&mut self,
ref_expr: &Expr,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ref_val = self
.lower_expr(ref_expr)?
.ok_or_else(|| CodegenError::Internal("modifyIORef: no ref".to_string()))?;
let ref_ptr = match ref_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"modifyIORef expects pointer".to_string(),
))
}
};
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("modifyIORef: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"modifyIORef: function must be closure".to_string(),
))
}
};
// Read current value
let read_fn = self
.functions
.get(&VarId::new(1000503))
.ok_or_else(|| CodegenError::Internal("bhc_read_ioref not declared".to_string()))?;
let current_val = self
.builder()
.build_call(*read_fn, &[ref_ptr.into()], "modify_read")
.map_err(|e| CodegenError::Internal(format!("modifyIORef read failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("modifyIORef: read returned void".to_string()))?;
// Apply function: f(current_val)
let ptr_type = self.type_mapper().ptr_type();
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let new_val = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), current_val.into()],
"modify_apply",
)
.map_err(|e| CodegenError::Internal(format!("modifyIORef apply failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("modifyIORef: apply returned void".to_string())
})?;
// Write new value
let write_fn = self
.functions
.get(&VarId::new(1000504))
.ok_or_else(|| CodegenError::Internal("bhc_write_ioref not declared".to_string()))?;
let new_val_ptr = self.value_to_ptr(new_val)?;
self.builder()
.build_call(*write_fn, &[ref_ptr.into(), new_val_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("modifyIORef write failed: {:?}", e)))?;
Ok(Some(ptr_type.const_null().into()))
}
/// Lower `atomicModifyIORef ref f` — read, apply f (returns tuple), write first, return second.
/// Simplified: in our single-threaded RTS, atomic = non-atomic.
/// f :: a -> (a, b), we apply f, treat result as tuple, extract fst to write back, return snd.
fn lower_builtin_atomic_modify_ioref(
&mut self,
ref_expr: &Expr,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ref_val = self
.lower_expr(ref_expr)?
.ok_or_else(|| CodegenError::Internal("atomicModifyIORef: no ref".to_string()))?;
let ref_ptr = match ref_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"atomicModifyIORef expects pointer".to_string(),
))
}
};
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("atomicModifyIORef: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"atomicModifyIORef: function must be closure".to_string(),
))
}
};
// Read current value
let read_fn = self
.functions
.get(&VarId::new(1000503))
.ok_or_else(|| CodegenError::Internal("bhc_read_ioref not declared".to_string()))?;
let current_val = self
.builder()
.build_call(*read_fn, &[ref_ptr.into()], "atomic_read")
.map_err(|e| CodegenError::Internal(format!("atomicModifyIORef read failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("atomicModifyIORef: read returned void".to_string())
})?;
// Apply function: f(current_val) -> (new_a, result_b)
let ptr_type = self.type_mapper().ptr_type();
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let tuple_val = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), current_val.into()],
"atomic_apply",
)
.map_err(|e| {
CodegenError::Internal(format!("atomicModifyIORef apply failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("atomicModifyIORef: apply returned void".to_string())
})?;
// The result is a tuple (a, b) represented as a heap object.
// Extract field 0 (the new value) and field 1 (the result).
// Tuples in BHC are allocated as: [tag, field0, field1, ...]
let tuple_ptr = match tuple_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Ok(Some(ptr_type.const_null().into())),
};
// Treat tuple as array of pointers: [tag, fst, snd, ...]
// tag at offset 0, fst at offset 1, snd at offset 2 (each pointer-sized)
let i64_type = self.type_mapper().i64_type();
let fst_gep = unsafe {
self.builder().build_gep(
ptr_type,
tuple_ptr,
&[i64_type.const_int(1, false)],
"fst_ptr",
)
}
.map_err(|e| CodegenError::Internal(format!("atomicModifyIORef: fst gep: {:?}", e)))?;
let fst_val = self
.builder()
.build_load(ptr_type, fst_gep, "fst_val")
.map_err(|e| CodegenError::Internal(format!("atomicModifyIORef: load fst: {:?}", e)))?;
let snd_gep = unsafe {
self.builder().build_gep(
ptr_type,
tuple_ptr,
&[i64_type.const_int(2, false)],
"snd_ptr",
)
}
.map_err(|e| CodegenError::Internal(format!("atomicModifyIORef: snd gep: {:?}", e)))?;
let snd_val = self
.builder()
.build_load(ptr_type, snd_gep, "snd_val")
.map_err(|e| CodegenError::Internal(format!("atomicModifyIORef: load snd: {:?}", e)))?;
// Write fst (new value) back to the IORef
let write_fn = self
.functions
.get(&VarId::new(1000504))
.ok_or_else(|| CodegenError::Internal("bhc_write_ioref not declared".to_string()))?;
let fst_ptr = self.value_to_ptr(fst_val)?;
self.builder()
.build_call(*write_fn, &[ref_ptr.into(), fst_ptr.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("atomicModifyIORef write failed: {:?}", e))
})?;
// Return snd (the result b)
Ok(Some(snd_val))
}
/// Lower a character predicate (isAlpha, isDigit, isSpace, etc.)
/// Returns a proper Bool ADT (tag 0=False, 1=True) for consistency with True/False constructors.
fn lower_builtin_char_pred(
&mut self,
expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", name)))?;
let int_val = self.coerce_to_int(val)?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| CodegenError::Internal(format!("{}: truncate failed: {:?}", name, e)))?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("char pred {} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let bool_val = result.into_int_value();
let tag = self
.builder()
.build_int_z_extend(bool_val, self.type_mapper().i64_type(), "bool_tag")
.map_err(|e| CodegenError::Internal(format!("{}: extend failed: {:?}", name, e)))?;
// Allocate a Bool ADT with dynamic tag (0=False, 1=True)
let tm = self.type_mapper();
let adt_ty = self.adt_type(0);
let size_val = tm.i64_type().const_int(8, false); // Just tag, no fields
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], "bool_alloc")
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?;
let ptr = raw_ptr.into_pointer_value();
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, ptr, 0, "tag_ptr")
.map_err(|e| CodegenError::Internal(format!("{}: gep failed: {:?}", name, e)))?;
self.builder()
.build_store(tag_ptr, tag)
.map_err(|e| CodegenError::Internal(format!("{}: store failed: {:?}", name, e)))?;
Ok(Some(ptr.into()))
}
/// Lower a character conversion (toLower, toUpper).
fn lower_builtin_char_conv(
&mut self,
expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", name)))?;
let int_val = self.coerce_to_int(val)?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| CodegenError::Internal(format!("{}: truncate failed: {:?}", name, e)))?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("char conv {} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let u32_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(u32_val, self.type_mapper().i64_type(), "char_ext")
.map_err(|e| CodegenError::Internal(format!("{}: extend failed: {:?}", name, e)))?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
/// Lower `digitToInt` - convert digit char to integer.
fn lower_builtin_digit_to_int(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("digitToInt: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| CodegenError::Internal(format!("digitToInt: truncate failed: {:?}", e)))?;
let rts_fn = self.functions.get(&VarId::new(1000046)).ok_or_else(|| {
CodegenError::Internal("bhc_char_digit_to_int not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], "digit_to_int")
.map_err(|e| CodegenError::Internal(format!("digitToInt call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("digitToInt: returned void".to_string()))?;
let i32_val = result.into_int_value();
let extended = self
.builder()
.build_int_s_extend(i32_val, self.type_mapper().i64_type(), "digit_ext")
.map_err(|e| CodegenError::Internal(format!("digitToInt: extend failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
/// Lower `intToDigit` - convert integer to digit char.
fn lower_builtin_int_to_digit(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("intToDigit: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let i32_type = self.type_mapper().i32_type();
let truncated = self
.builder()
.build_int_truncate(int_val, i32_type, "int_val")
.map_err(|e| CodegenError::Internal(format!("intToDigit: truncate failed: {:?}", e)))?;
let rts_fn = self.functions.get(&VarId::new(1000047)).ok_or_else(|| {
CodegenError::Internal("bhc_char_int_to_digit not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[truncated.into()], "int_to_digit")
.map_err(|e| CodegenError::Internal(format!("intToDigit call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("intToDigit: returned void".to_string()))?;
let u32_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(u32_val, self.type_mapper().i64_type(), "digit_char_ext")
.map_err(|e| CodegenError::Internal(format!("intToDigit: extend failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
// ========================================================================
// Phase 4: IO & System Operation Handlers
// ========================================================================
/// Lower `readFile` - read file contents.
fn lower_builtin_read_file(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("readFile: no path".to_string()))?;
// Convert [Char] path to C-string for RTS
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000050))
.ok_or_else(|| CodegenError::Internal("bhc_readFile not declared".to_string()))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into()], "read_file")
.map_err(|e| CodegenError::Internal(format!("readFile call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("readFile: returned void".to_string()))?;
// Check for exception sentinel (null return from bhc_throw).
// If null, return null immediately from current function so bhc_catch
// can detect the exception via TLS instead of continuing execution.
let result_ptr = cstr_result.into_pointer_value();
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("readFile: no current function".to_string()))?;
let early_ret_block = self
.llvm_context()
.append_basic_block(current_fn, "readfile_exc");
let ok_block = self
.llvm_context()
.append_basic_block(current_fn, "readfile_ok");
let is_null = self
.builder()
.build_is_null(result_ptr, "readfile_null")
.map_err(|e| CodegenError::Internal(format!("readFile null check: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, early_ret_block, ok_block)
.map_err(|e| CodegenError::Internal(format!("readFile branch: {:?}", e)))?;
// Exception path: return null sentinel to propagate to bhc_catch
self.builder().position_at_end(early_ret_block);
self.builder()
.build_return(Some(&tm.ptr_type().const_null()))
.map_err(|e| CodegenError::Internal(format!("readFile exc return: {:?}", e)))?;
// OK path: convert C-string result to [Char] linked list
self.builder().position_at_end(ok_block);
let char_list = self.cstring_to_char_list(result_ptr)?;
Ok(Some(char_list.into()))
}
/// Lower `writeFile` - write string to file.
fn lower_builtin_write_file(
&mut self,
path_expr: &Expr,
content_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("writeFile: no path".to_string()))?;
let content_val = self
.lower_expr(content_expr)?
.ok_or_else(|| CodegenError::Internal("writeFile: no content".to_string()))?;
// Convert [Char] args to C-strings for RTS
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let content_ptr = self.value_to_ptr(content_val)?;
let content_cstr = self.char_list_to_cstring(content_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000051))
.ok_or_else(|| CodegenError::Internal("bhc_writeFile not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into(), content_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("writeFile call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `appendFile` - append string to file.
fn lower_builtin_append_file(
&mut self,
path_expr: &Expr,
content_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("appendFile: no path".to_string()))?;
let content_val = self
.lower_expr(content_expr)?
.ok_or_else(|| CodegenError::Internal("appendFile: no content".to_string()))?;
// Convert [Char] args to C-strings for RTS
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let content_ptr = self.value_to_ptr(content_val)?;
let content_cstr = self.char_list_to_cstring(content_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000052))
.ok_or_else(|| CodegenError::Internal("bhc_appendFile not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into(), content_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("appendFile call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `openFile` - open a file handle.
fn lower_builtin_open_file(
&mut self,
path_expr: &Expr,
mode_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("openFile: no path".to_string()))?;
let mode_val = self
.lower_expr(mode_expr)?
.ok_or_else(|| CodegenError::Internal("openFile: no mode".to_string()))?;
// Convert [Char] path to C-string for RTS
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
// mode_val is an IOMode ADT pointer — load the tag (first i64) to get the mode integer
let mode_int = match mode_val {
BasicValueEnum::PointerValue(p) => self
.builder()
.build_load(self.type_mapper().i64_type(), p, "mode_tag")
.map_err(|e| {
CodegenError::Internal(format!("openFile: load mode tag failed: {:?}", e))
})?
.into_int_value(),
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::TypeError(
"openFile: unexpected mode type".to_string(),
))
}
};
let mode_i32 = self
.builder()
.build_int_truncate(mode_int, self.type_mapper().i32_type(), "mode")
.map_err(|e| CodegenError::Internal(format!("openFile: truncate failed: {:?}", e)))?;
let rts_fn = self
.functions
.get(&VarId::new(1000053))
.ok_or_else(|| CodegenError::Internal("bhc_open_file not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into(), mode_i32.into()], "handle")
.map_err(|e| CodegenError::Internal(format!("openFile call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("openFile: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `hClose` - close a file handle.
fn lower_builtin_hclose(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hClose: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("hClose expects handle".to_string())),
};
let rts_fn = self
.functions
.get(&VarId::new(1000054))
.ok_or_else(|| CodegenError::Internal("bhc_close_handle not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("hClose call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `hGetChar` - read a character from handle.
fn lower_builtin_hgetchar(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hGetChar: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"hGetChar expects handle".to_string(),
))
}
};
let rts_fn = self
.functions
.get(&VarId::new(1000055))
.ok_or_else(|| CodegenError::Internal("bhc_hGetChar not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "hgetchar")
.map_err(|e| CodegenError::Internal(format!("hGetChar call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hGetChar: returned void".to_string()))?;
let char_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(char_val, self.type_mapper().i64_type(), "char_ext")
.map_err(|e| CodegenError::Internal(format!("hGetChar: extend failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
/// Lower `hGetLine` - read a line from handle.
fn lower_builtin_hgetline(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hGetLine: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"hGetLine expects handle".to_string(),
))
}
};
let rts_fn = self
.functions
.get(&VarId::new(1000056))
.ok_or_else(|| CodegenError::Internal("bhc_hGetLine not declared".to_string()))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "hgetline")
.map_err(|e| CodegenError::Internal(format!("hGetLine call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hGetLine: returned void".to_string()))?;
// Convert C-string result to [Char] linked list
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
}
/// Lower `hPutStr` - write string to handle.
fn lower_builtin_hputstr(
&mut self,
handle_expr: &Expr,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hPutStr: no handle".to_string()))?;
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("hPutStr: no string".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"hPutStr expects handle".to_string(),
))
}
};
// Convert [Char] string to C-string for RTS
let str_ptr = self.value_to_ptr(str_val)?;
let str_cstr = self.char_list_to_cstring(str_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000057))
.ok_or_else(|| CodegenError::Internal("bhc_hPutStr not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[handle_ptr.into(), str_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("hPutStr call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `hPutStrLn` - write string + newline to handle.
fn lower_builtin_hputstrln(
&mut self,
handle_expr: &Expr,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hPutStrLn: no handle".to_string()))?;
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("hPutStrLn: no string".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"hPutStrLn expects handle".to_string(),
))
}
};
// Convert [Char] string to C-string for RTS
let str_ptr = self.value_to_ptr(str_val)?;
let str_cstr = self.char_list_to_cstring(str_ptr)?;
let put_fn = self
.functions
.get(&VarId::new(1000057))
.ok_or_else(|| CodegenError::Internal("bhc_hPutStr not declared".to_string()))?;
self.builder()
.build_call(*put_fn, &[handle_ptr.into(), str_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("hPutStrLn put failed: {:?}", e)))?;
let nl = self.module.add_global_string("hputstrln_nl", "\n");
self.builder()
.build_call(*put_fn, &[handle_ptr.into(), nl.into()], "")
.map_err(|e| CodegenError::Internal(format!("hPutStrLn nl failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `hFlush`.
fn lower_builtin_hflush(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hFlush: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("hFlush expects handle".to_string())),
};
let rts_fn = self
.functions
.get(&VarId::new(1000058))
.ok_or_else(|| CodegenError::Internal("bhc_hFlush not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("hFlush call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `hIsEOF`.
fn lower_builtin_hiseof(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hIsEOF: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("hIsEOF expects handle".to_string())),
};
let rts_fn = self
.functions
.get(&VarId::new(1000059))
.ok_or_else(|| CodegenError::Internal("bhc_hIsEOF not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "is_eof")
.map_err(|e| CodegenError::Internal(format!("hIsEOF call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hIsEOF: returned void".to_string()))?;
let bool_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(bool_val, self.type_mapper().i64_type(), "eof_ext")
.map_err(|e| CodegenError::Internal(format!("hIsEOF: extend failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
/// Lower `hSetBuffering` (stub).
fn lower_builtin_hset_buffering(
&mut self,
handle_expr: &Expr,
mode_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_expr(handle_expr)?;
let _ = self.lower_expr(mode_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower stdin/stdout/stderr.
fn lower_builtin_std_handle(
&mut self,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("bhc_{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(result))
}
/// Lower file predicate (doesFileExist, doesDirectoryExist).
fn lower_builtin_file_pred(
&mut self,
path_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no path", name)))?;
// Convert [Char] path to C-string for RTS
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let bool_i32 = result.into_int_value();
// Convert i32 result (0/1) to a proper Bool ADT constructor
// True = tag 1, False = tag 0
let i64_type = self.type_mapper().i64_type();
let extended = self
.builder()
.build_int_z_extend(bool_i32, i64_type, "pred_ext")
.map_err(|e| CodegenError::Internal(format!("{}: extend failed: {:?}", name, e)))?;
// Allocate a Bool ADT: 8-byte tag followed by no fields
let _ptr_type = self.type_mapper().ptr_type();
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size = i64_type.const_int(8, false); // Just the tag
let adt_ptr = self
.builder()
.build_call(*alloc_fn, &[size.into()], "bool_adt")
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?
.into_pointer_value();
// Store the tag
self.builder()
.build_store(adt_ptr, extended)
.map_err(|e| CodegenError::Internal(format!("{}: store tag failed: {:?}", name, e)))?;
Ok(Some(adt_ptr.into()))
}
/// Lower `removeFile`.
fn lower_builtin_remove_file(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("removeFile: no path".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000312))
.ok_or_else(|| CodegenError::Internal("bhc_remove_file not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("removeFile call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `getArgs`: returns list of command-line arguments.
fn lower_builtin_get_args(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000315))
.ok_or_else(|| CodegenError::Internal("bhc_get_args not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "get_args")
.map_err(|e| CodegenError::Internal(format!("getArgs call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getArgs: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `getProgName`: returns program name.
fn lower_builtin_get_prog_name(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000316))
.ok_or_else(|| CodegenError::Internal("bhc_get_prog_name not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "get_prog_name")
.map_err(|e| CodegenError::Internal(format!("getProgName call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getProgName: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `getEnv`.
fn lower_builtin_get_env(
&mut self,
name_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let name_val = self
.lower_expr(name_expr)?
.ok_or_else(|| CodegenError::Internal("getEnv: no name".to_string()))?;
let name_ptr = match name_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("getEnv expects string".to_string())),
};
let rts_fn = self
.functions
.get(&VarId::new(1000068))
.ok_or_else(|| CodegenError::Internal("bhc_get_env not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[name_ptr.into()], "get_env")
.map_err(|e| CodegenError::Internal(format!("getEnv call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getEnv: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `lookupEnv` (simplified: wraps getEnv).
fn lower_builtin_lookup_env(
&mut self,
name_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_get_env(name_expr)
}
/// Lower `exitSuccess`.
fn lower_builtin_exit_success(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000066))
.ok_or_else(|| CodegenError::Internal("bhc_exit_success not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[], "")
.map_err(|e| CodegenError::Internal(format!("exitSuccess failed: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("unreachable failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `exitFailure`.
fn lower_builtin_exit_failure(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000067))
.ok_or_else(|| CodegenError::Internal("bhc_exit_failure not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[], "")
.map_err(|e| CodegenError::Internal(format!("exitFailure failed: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("unreachable failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `exitWith`.
fn lower_builtin_exit_with(
&mut self,
code_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let code_val = self
.lower_expr(code_expr)?
.ok_or_else(|| CodegenError::Internal("exitWith: no code".to_string()))?;
let int_val = self.coerce_to_int(code_val)?;
let code_i32 = self
.builder()
.build_int_truncate(int_val, self.type_mapper().i32_type(), "exit_code")
.map_err(|e| CodegenError::Internal(format!("exitWith: truncate failed: {:?}", e)))?;
let rts_fn = self
.functions
.get(&VarId::new(1000065))
.ok_or_else(|| CodegenError::Internal("bhc_exit not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[code_i32.into()], "")
.map_err(|e| CodegenError::Internal(format!("exitWith failed: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("unreachable failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `hGetContents`: read all remaining contents from a handle.
fn lower_builtin_hgetcontents(
&mut self,
handle_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal("hGetContents: no handle".to_string()))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"hGetContents expects handle".to_string(),
))
}
};
let rts_fn = self
.functions
.get(&VarId::new(1000300))
.ok_or_else(|| CodegenError::Internal("bhc_hGetContents not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[handle_ptr.into()], "hgetcontents")
.map_err(|e| CodegenError::Internal(format!("hGetContents call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hGetContents: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `getCurrentDirectory`.
fn lower_builtin_get_current_directory(
&mut self,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self.functions.get(&VarId::new(1000317)).ok_or_else(|| {
CodegenError::Internal("bhc_get_current_directory not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "getcwd")
.map_err(|e| {
CodegenError::Internal(format!("getCurrentDirectory call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("getCurrentDirectory: returned void".to_string())
})?;
Ok(Some(result))
}
/// Lower `createDirectory`.
fn lower_builtin_create_directory(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("createDirectory: no path".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self.functions.get(&VarId::new(1000311)).ok_or_else(|| {
CodegenError::Internal("bhc_create_directory not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("createDirectory call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `listDirectory`.
fn lower_builtin_list_directory(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("listDirectory: no path".to_string()))?;
let path_ptr = match path_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"listDirectory expects string".to_string(),
))
}
};
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000313))
.ok_or_else(|| CodegenError::Internal("bhc_list_directory not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into()], "listdir")
.map_err(|e| CodegenError::Internal(format!("listDirectory call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("listDirectory: returned void".to_string()))?;
Ok(Some(result))
}
// ========================================================================
// E.19: System.FilePath + System.Directory codegen
// ========================================================================
/// Lower a filepath function that takes String and returns String.
/// Pattern: char_list_to_cstring -> call RTS -> cstring_to_char_list.
fn lower_builtin_filepath_str_to_str(
&mut self,
path_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no path", name)))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let result_ptr = cstr_result.into_pointer_value();
let char_list = self.cstring_to_char_list(result_ptr)?;
Ok(Some(char_list.into()))
}
/// Lower a filepath function that takes two Strings and returns String.
/// Pattern: char_list_to_cstring on both -> call RTS -> cstring_to_char_list.
fn lower_builtin_filepath_two_str_to_str(
&mut self,
arg1_expr: &Expr,
arg2_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let arg1_val = self
.lower_expr(arg1_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no arg1", name)))?;
let arg2_val = self
.lower_expr(arg2_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no arg2", name)))?;
let arg1_ptr = self.value_to_ptr(arg1_val)?;
let arg1_cstr = self.char_list_to_cstring(arg1_ptr)?;
let arg2_ptr = self.value_to_ptr(arg2_val)?;
let arg2_cstr = self.char_list_to_cstring(arg2_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[arg1_cstr.into(), arg2_cstr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let result_ptr = cstr_result.into_pointer_value();
let char_list = self.cstring_to_char_list(result_ptr)?;
Ok(Some(char_list.into()))
}
/// Lower `splitExtension`: takes String, returns (String, String) tuple.
/// Codegen-composed: calls dropExtension and takeExtension, packs into tuple.
fn lower_builtin_split_extension(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Evaluate path once
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("splitExtension: no path".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
// Call bhc_drop_extension for the base part
let drop_ext_fn = self
.functions
.get(&VarId::new(1000523))
.ok_or_else(|| CodegenError::Internal("bhc_drop_extension not declared".to_string()))?;
let base_cstr = self
.builder()
.build_call(*drop_ext_fn, &[path_cstr.into()], "split_base")
.map_err(|e| CodegenError::Internal(format!("splitExtension base: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("splitExtension base: void".to_string()))?
.into_pointer_value();
let base_list = self.cstring_to_char_list(base_cstr)?;
// Call bhc_take_extension for the extension part
let take_ext_fn = self
.functions
.get(&VarId::new(1000522))
.ok_or_else(|| CodegenError::Internal("bhc_take_extension not declared".to_string()))?;
let ext_cstr = self
.builder()
.build_call(*take_ext_fn, &[path_cstr.into()], "split_ext")
.map_err(|e| CodegenError::Internal(format!("splitExtension ext: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("splitExtension ext: void".to_string()))?
.into_pointer_value();
let ext_list = self.cstring_to_char_list(ext_cstr)?;
// Pack into a (String, String) tuple using alloc_pair
let pair_ptr = self.alloc_pair(base_list.into(), ext_list.into())?;
Ok(Some(pair_ptr.into()))
}
/// Lower a directory function that takes String and returns IO ().
/// Pattern: char_list_to_cstring -> call void RTS -> return unit.
fn lower_builtin_dir_str_to_io_unit(
&mut self,
path_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no path", name)))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower a directory function that takes two Strings and returns IO ().
/// Pattern: char_list_to_cstring on both -> call void RTS -> return unit.
fn lower_builtin_dir_two_str_to_io_unit(
&mut self,
src_expr: &Expr,
dst_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let src_val = self
.lower_expr(src_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no src", name)))?;
let dst_val = self
.lower_expr(dst_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no dst", name)))?;
let src_ptr = self.value_to_ptr(src_val)?;
let src_cstr = self.char_list_to_cstring(src_ptr)?;
let dst_ptr = self.value_to_ptr(dst_val)?;
let dst_cstr = self.char_list_to_cstring(dst_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
self.builder()
.build_call(*rts_fn, &[src_cstr.into(), dst_cstr.into()], "")
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `show` — type-aware dispatch to the correct show function.
/// First tries expr.ty(), falls back to expression structure analysis.
fn lower_builtin_show(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ty = expr.ty();
// First try type-based dispatch (works when type info is available)
if !matches!(ty, Ty::Error) && !matches!(ty, Ty::Var(_)) {
if self.is_bool_type(&ty) {
return self.lower_builtin_show_typed(expr, 1000091, "show_bool", ShowCoerce::Bool);
}
if self.is_char_type(&ty) {
return self.lower_builtin_show_typed(expr, 1000074, "show_char", ShowCoerce::Char);
}
if self.is_double_type(&ty) {
return self.lower_builtin_show_typed(
expr,
1000073,
"show_double",
ShowCoerce::Double,
);
}
if self.is_float_type(&ty) {
return self.lower_builtin_show_typed(
expr,
1000090,
"show_float",
ShowCoerce::Float,
);
}
if self.is_string_type(&ty) {
return self.lower_builtin_show_typed(
expr,
1000092,
"show_string",
ShowCoerce::StringList,
);
}
if self.is_list_type(&ty) {
return self.lower_builtin_show_typed(expr, 1000093, "show_list", ShowCoerce::List);
}
if self.is_maybe_type(&ty).is_some() {
return self.lower_builtin_show_typed(
expr,
1000094,
"show_maybe",
ShowCoerce::MaybeOf,
);
}
if self.is_either_type(&ty).is_some() {
return self.lower_builtin_show_typed(
expr,
1000095,
"show_either",
ShowCoerce::EitherOf,
);
}
if self.is_tuple_type(&ty).is_some() {
return self.lower_builtin_show_typed(
expr,
1000096,
"show_tuple2",
ShowCoerce::Tuple2Of,
);
}
if self.is_unit_type(&ty) {
return self.lower_builtin_show_typed(expr, 1000097, "show_unit", ShowCoerce::Unit);
}
if self.is_rational_type(&ty) {
// Call bhc_rational_show (VarId 1000915) which returns a C string,
// then convert to BHC char list for putStrLn
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("show rational: no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = *self.functions.get(&VarId::new(1000915)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_show not declared".to_string())
})?;
let cstr_result = self
.builder()
.build_call(rts_fn, &[val_ptr.into()], "show_rational")
.map_err(|e| CodegenError::Internal(format!("show_rational call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("show_rational: returned void".to_string())
})?;
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
return Ok(Some(char_list.into()));
}
if self.is_integer_type(&ty) {
return self.lower_builtin_show_typed(
expr,
1000618,
"show_integer",
ShowCoerce::Integer,
);
}
if self.is_int_type(&ty) {
return self.lower_builtin_show_typed(expr, 1000072, "show_int", ShowCoerce::Int);
}
}
// Check if the expression is Rational (when type info unavailable)
if self.is_rational_expr(expr) {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("show rational: no value".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = *self.functions.get(&VarId::new(1000915)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_show not declared".to_string())
})?;
let cstr_result = self
.builder()
.build_call(rts_fn, &[val_ptr.into()], "show_rational")
.map_err(|e| CodegenError::Internal(format!("show_rational call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("show_rational: returned void".to_string())
})?;
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
return Ok(Some(char_list.into()));
}
// Fall back to expression structure analysis when type is Error/Var
if let Some((coerce, var_id, label)) = self.infer_show_from_expr(expr) {
return self.lower_builtin_show_typed(expr, var_id, label, coerce);
}
// Check if the expression is a user-defined ADT with derived Show
if let Some(type_name) = self.infer_adt_type_from_expr(expr) {
if let Some(show_var_id) = self.derived_show_fns.get(&type_name).copied() {
if let Some(show_fn) = self.functions.get(&show_var_id).copied() {
let value = self.lower_expr(expr)?.ok_or_else(|| {
CodegenError::Internal("show: failed to lower ADT value".to_string())
})?;
// Call derived show: fn(env_ptr, value) -> string_ptr
// All BHC functions use (env, args...) calling convention
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = show_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), value.into()],
"derived_show",
)
.map_err(|e| {
CodegenError::Internal(format!("derived show call: {:?}", e))
})?;
return Ok(result.try_as_basic_value().basic());
}
}
}
// Default: Int
self.lower_builtin_show_typed(expr, 1000072, "show_int", ShowCoerce::Int)
}
/// Infer the show coercion from the expression structure when type info is unavailable.
fn infer_show_from_expr(&self, expr: &Expr) -> Option<(ShowCoerce, usize, &'static str)> {
match expr {
// String literal
Expr::Lit(Literal::String(_), _, _) => {
Some((ShowCoerce::StringList, 1000092, "show_string"))
}
// Int literal
Expr::Lit(Literal::Int(_), _, _) => Some((ShowCoerce::Int, 1000072, "show_int")),
// Integer literal (arbitrary precision)
Expr::Lit(Literal::Integer(_), _, _) => {
Some((ShowCoerce::Integer, 1000618, "show_integer"))
}
// Float/Double literal
Expr::Lit(Literal::Float(_), _, _) | Expr::Lit(Literal::Double(_), _, _) => {
Some((ShowCoerce::Double, 1000073, "show_double"))
}
// Char literal
Expr::Lit(Literal::Char(_), _, _) => Some((ShowCoerce::Char, 1000074, "show_char")),
// Unit "()" or constructor named "()"
Expr::Var(var, _) if var.name.as_str() == "()" => {
Some((ShowCoerce::Unit, 1000097, "show_unit"))
}
// Bool constructors
Expr::Var(var, _) if var.name.as_str() == "True" || var.name.as_str() == "False" => {
Some((ShowCoerce::Bool, 1000091, "show_bool"))
}
// Ordering constructors
Expr::Var(var, _) if matches!(var.name.as_str(), "LT" | "EQ" | "GT") => {
Some((ShowCoerce::Ordering, 1000098, "show_ordering"))
}
// Nothing
Expr::Var(var, _) if var.name.as_str() == "Nothing" => {
Some((ShowCoerce::MaybeOf, 1000094, "show_maybe"))
}
// E.45: Integer variable (tracked from let-binding or by type)
Expr::Var(var, _)
if self.integer_vars.contains(&var.id) || self.is_integer_type(&var.ty) =>
{
Some((ShowCoerce::Integer, 1000618, "show_integer"))
}
// Empty list []
Expr::Var(var, _) if var.name.as_str() == "[]" => {
// Could be [a] for any a - use List with Int default
Some((ShowCoerce::List, 1000093, "show_list"))
}
// Function applications returning known types
Expr::App(f, _arg, _) => {
// E.63: force/id are identity functions — infer from argument
if let Expr::Var(fv, _) = f.as_ref() {
if fv.name.as_str() == "force"
|| fv.name.as_str() == "id"
|| fv.name.as_str() == "to"
{
return self.infer_show_from_expr(_arg);
}
}
// E.51: fmap/(<$>) preserves the functor type — infer from the second arg
if let Expr::App(ff, _, _) = f.as_ref() {
if let Expr::Var(fv, _) = ff.as_ref() {
if fv.name.as_str() == "fmap" || fv.name.as_str() == "<$>" {
return self.infer_show_from_expr(_arg);
}
}
}
// E.52: foldr f z x — result type equals type of z (the init value)
// Expression structure: App(App(App(foldr, step), init), container)
// At this level: f = App(App(foldr, step), init), _arg = container
if let Expr::App(ff, init_val, _) = f.as_ref() {
if let Expr::App(fff, _, _) = ff.as_ref() {
if let Expr::Var(fv, _) = fff.as_ref() {
if fv.name.as_str() == "foldr" {
return self.infer_show_from_expr(init_val);
}
}
}
}
// E.45: Check if it's a function that returns Integer
if self.is_integer_expr(expr) {
return Some((ShowCoerce::Integer, 1000618, "show_integer"));
}
// Check if it's a function that returns Bool
if self.expr_returns_bool(f) {
return Some((ShowCoerce::Bool, 1000091, "show_bool"));
}
// Check if it's a function that returns Ordering (e.g., compare x y)
if self.expr_returns_ordering(f) {
return Some((ShowCoerce::Ordering, 1000098, "show_ordering"));
}
// Check if it's a function that returns Int (fully applied binary int op)
if self.expr_returns_int(expr) {
return Some((ShowCoerce::Int, 1000072, "show_int"));
}
// Check if it's a function/expression that returns Double/Float
if self.expr_returns_double(expr) {
return Some((ShowCoerce::Double, 1000073, "show_double"));
}
// Check if it's a function that returns a list
if self.expr_looks_like_list(f) {
return Some((ShowCoerce::List, 1000093, "show_list"));
}
match f.as_ref() {
// Just x
Expr::Var(var, _) if var.name.as_str() == "Just" => {
Some((ShowCoerce::MaybeOf, 1000094, "show_maybe"))
}
// Functions that return Maybe (readMaybe, lookupEnv, find, lookup, elemIndex, findIndex, listToMaybe)
Expr::Var(var, _)
if matches!(
var.name.as_str(),
"readMaybe"
| "lookupEnv"
| "find"
| "lookup"
| "elemIndex"
| "findIndex"
| "listToMaybe"
| "Data.Map.lookup"
| "stripPrefix"
) =>
{
Some((ShowCoerce::MaybeOf, 1000094, "show_maybe"))
}
// Left x or Right x
Expr::Var(var, _)
if var.name.as_str() == "Left" || var.name.as_str() == "Right" =>
{
Some((ShowCoerce::EitherOf, 1000095, "show_either"))
}
// Cons application: (:) x xs — it's a two-arg app, so this is partially applied
// The full Cons is App(App((:), head), tail)
Expr::App(ff, _head, _) => {
match ff.as_ref() {
Expr::Var(var, _) if var.name.as_str() == ":" => {
// This is a list. Check head to determine element type.
Some((ShowCoerce::List, 1000093, "show_list"))
}
// Tuple constructor: App(App((,), fst), snd)
Expr::Var(var, _)
if var.name.as_str() == "(,)" || var.name.as_str() == "Tuple2" =>
{
Some((ShowCoerce::Tuple2Of, 1000096, "show_tuple2"))
}
_ => None,
}
}
_ => None,
}
}
// Let binding: check body
Expr::Let(_, body, _) => self.infer_show_from_expr(body),
// Type application: check inner
Expr::TyApp(inner, _, _) => self.infer_show_from_expr(inner),
_ => None,
}
}
/// Infer the show type tag for an element from expression structure.
/// Returns: 0=Int, 1=Double, 2=Float, 3=Bool, 4=Char, 5=String
// Show-tag inference helper retained for container show lowering.
#[allow(dead_code)]
fn infer_elem_tag_from_expr(&self, expr: &Expr) -> i64 {
// First try type info
let ty = expr.ty();
if !matches!(ty, Ty::Error) && !matches!(ty, Ty::Var(_)) {
return self.type_to_show_tag(&ty);
}
// Fall back to expression structure
match expr {
Expr::Lit(Literal::Int(_), _, _) => 0,
Expr::Lit(Literal::Double(_), _, _) => 1,
Expr::Lit(Literal::Float(_), _, _) => 2,
Expr::Lit(Literal::Char(_), _, _) => 4,
Expr::Lit(Literal::String(_), _, _) => 5,
Expr::Var(var, _) => match var.name.as_str() {
"True" | "False" => 3,
_ => 0,
},
_ => 0, // default Int
}
}
/// Get the first element of a Cons-list expression for type inference.
// Retained for list show-tag inference.
#[allow(dead_code)]
fn get_list_head_expr<'e>(&self, expr: &'e Expr) -> Option<&'e Expr> {
match expr {
// App(App((:), head), tail)
Expr::App(f, _tail, _) => {
if let Expr::App(ff, head, _) = f.as_ref() {
if let Expr::Var(var, _) = ff.as_ref() {
if var.name.as_str() == ":" {
return Some(head);
}
}
}
None
}
Expr::Let(_, body, _) => self.get_list_head_expr(body),
Expr::TyApp(inner, _, _) => self.get_list_head_expr(inner),
_ => None,
}
}
/// Infer element type tag for a list expression.
// Retained for list show-tag inference.
#[allow(dead_code)]
fn infer_list_elem_tag(&self, list_expr: &Expr) -> i64 {
// Try to get the head element and infer its type
if let Some(head) = self.get_list_head_expr(list_expr) {
return self.infer_elem_tag_from_expr(head);
}
0 // default to Int for empty lists
}
/// Infer the value type tag for a Maybe/Just expression.
// Retained for Maybe show-tag inference.
#[allow(dead_code)]
fn infer_maybe_elem_tag(&self, maybe_expr: &Expr) -> i64 {
// Just x → infer from x
if let Expr::App(f, arg, _) = maybe_expr {
if let Expr::Var(var, _) = f.as_ref() {
if var.name.as_str() == "Just" {
return self.infer_elem_tag_from_expr(arg);
}
}
}
0 // Nothing defaults to Int
}
/// Infer the left/right type tags for an Either expression.
// Retained for Either show-tag inference.
#[allow(dead_code)]
fn infer_either_tags(&self, either_expr: &Expr) -> (i64, i64) {
if let Expr::App(f, arg, _) = either_expr {
if let Expr::Var(var, _) = f.as_ref() {
match var.name.as_str() {
"Left" => return (self.infer_elem_tag_from_expr(arg), 0),
"Right" => return (0, self.infer_elem_tag_from_expr(arg)),
_ => {}
}
}
}
(0, 0)
}
/// Infer the fst/snd type tags for a tuple expression.
// Retained for tuple show-tag inference.
#[allow(dead_code)]
fn infer_tuple_tags(&self, tuple_expr: &Expr) -> (i64, i64) {
// App(App((,), fst), snd)
if let Expr::App(f, snd, _) = tuple_expr {
if let Expr::App(ff, fst, _) = f.as_ref() {
if let Expr::Var(var, _) = ff.as_ref() {
if var.name.as_str() == "(,)" || var.name.as_str() == "Tuple2" {
return (
self.infer_elem_tag_from_expr(fst),
self.infer_elem_tag_from_expr(snd),
);
}
}
}
}
(0, 0)
}
/// Create a ShowTypeDesc global constant in the LLVM module.
/// Returns a pointer to the global.
fn create_show_desc_global(
&mut self,
tag: i64,
child1: Option<PointerValue<'ctx>>,
child2: Option<PointerValue<'ctx>>,
) -> PointerValue<'ctx> {
let i64_type = self.type_mapper().i64_type();
let ptr_type = self.type_mapper().ptr_type();
let struct_type = self
.llvm_ctx
.struct_type(&[i64_type.into(), ptr_type.into(), ptr_type.into()], false);
let struct_val = struct_type.const_named_struct(&[
i64_type.const_int(tag as u64, false).into(),
child1.unwrap_or(ptr_type.const_null()).into(),
child2.unwrap_or(ptr_type.const_null()).into(),
]);
let name = format!("show_desc_{}", self.show_desc_counter);
self.show_desc_counter += 1;
let global = self
.module
.llvm_module()
.add_global(struct_type, None, &name);
global.set_initializer(&struct_val);
global.set_constant(true);
global.as_pointer_value()
}
/// Get or create a primitive show descriptor global (leaf types with no children).
fn get_primitive_show_desc(&mut self, tag: i64) -> PointerValue<'ctx> {
self.create_show_desc_global(tag, None, None)
}
/// Build a recursive show descriptor from a ShowCoerce + expression.
fn build_show_descriptor(&mut self, expr: &Expr) -> PointerValue<'ctx> {
// First determine the coerce type
let coerce = if let Some((c, _, _)) = self.infer_show_from_expr(expr) {
c
} else {
// Try type-based detection
let ty = expr.ty();
if !matches!(ty, Ty::Error) && !matches!(ty, Ty::Var(_)) {
if self.is_bool_type(&ty) {
ShowCoerce::Bool
} else if self.is_char_type(&ty) {
ShowCoerce::Char
} else if self.is_double_type(&ty) {
ShowCoerce::Double
} else if self.is_float_type(&ty) {
ShowCoerce::Float
} else if self.is_string_type(&ty) {
ShowCoerce::StringList
} else if self.is_list_type(&ty) {
ShowCoerce::List
} else if self.is_maybe_type(&ty).is_some() {
ShowCoerce::MaybeOf
} else if self.is_either_type(&ty).is_some() {
ShowCoerce::EitherOf
} else if self.is_tuple_type(&ty).is_some() {
ShowCoerce::Tuple2Of
} else if self.is_unit_type(&ty) {
ShowCoerce::Unit
} else {
ShowCoerce::Int
}
} else {
ShowCoerce::Int
}
};
match coerce {
ShowCoerce::Int => {
// A user-defined ADT (with derived Show) used as an element of a
// compound (e.g. `[Red, Green]`, `Just (Circle 5)`) falls through
// to the Int default here; build an Adt descriptor carrying its
// derived-show function pointer so the RTS shows it properly
// instead of printing the element pointer.
if let Some(desc) = self.try_build_adt_show_desc(expr) {
desc
} else {
self.get_primitive_show_desc(0)
}
}
ShowCoerce::Double => self.get_primitive_show_desc(1),
ShowCoerce::Float => self.get_primitive_show_desc(2),
ShowCoerce::Bool => self.get_primitive_show_desc(3),
ShowCoerce::Char => self.get_primitive_show_desc(4),
ShowCoerce::StringList => self.get_primitive_show_desc(5),
ShowCoerce::Unit => self.get_primitive_show_desc(6),
ShowCoerce::Ordering => self.get_primitive_show_desc(7),
ShowCoerce::Integer => self.get_primitive_show_desc(0), // Use Int tag (0) — Integer show is handled separately
ShowCoerce::List => {
let elem_desc = self.build_list_elem_descriptor(expr);
self.create_show_desc_global(10, Some(elem_desc), None)
}
ShowCoerce::MaybeOf => {
let elem_desc = self.build_maybe_elem_descriptor(expr);
self.create_show_desc_global(11, Some(elem_desc), None)
}
ShowCoerce::Tuple2Of => {
let (fst_d, snd_d) = self.build_tuple_elem_descriptors(expr);
self.create_show_desc_global(12, Some(fst_d), Some(snd_d))
}
ShowCoerce::EitherOf => {
let (l_d, r_d) = self.build_either_elem_descriptors(expr);
self.create_show_desc_global(13, Some(l_d), Some(r_d))
}
}
}
/// Build an `Adt` show descriptor (tag 14) for a user-defined type with
/// derived Show, carrying its derived-show function pointer in `child1`.
/// Returns `None` if `expr` isn't such an ADT.
fn try_build_adt_show_desc(&mut self, expr: &Expr) -> Option<PointerValue<'ctx>> {
let type_name = self.infer_adt_type_from_expr(expr)?;
let show_var_id = *self.derived_show_fns.get(&type_name)?;
let show_fn = *self.functions.get(&show_var_id)?;
let fn_ptr = show_fn.as_global_value().as_pointer_value();
Some(self.create_show_desc_global(14, Some(fn_ptr), None))
}
/// Build a descriptor for the element type of a list expression.
fn build_list_elem_descriptor(&mut self, list_expr: &Expr) -> PointerValue<'ctx> {
// Try to get the head element and build its descriptor recursively
if let Some(head) = self.get_list_head_expr_cloned(list_expr) {
return self.build_show_descriptor(&head);
}
self.get_primitive_show_desc(0) // default to Int
}
/// Clone the head expression from a Cons-list (needed because build_show_descriptor takes &mut self).
fn get_list_head_expr_cloned(&self, expr: &Expr) -> Option<Expr> {
match expr {
// App(App((:), head), tail)
Expr::App(f, _tail, _) => {
if let Expr::App(ff, head, _) = f.as_ref() {
if let Expr::Var(var, _) = ff.as_ref() {
if var.name.as_str() == ":" {
return Some(head.as_ref().clone());
}
}
}
None
}
Expr::Let(_, body, _) => self.get_list_head_expr_cloned(body),
Expr::TyApp(inner, _, _) => self.get_list_head_expr_cloned(inner),
_ => None,
}
}
/// Build a descriptor for the element type of a Maybe expression.
fn build_maybe_elem_descriptor(&mut self, maybe_expr: &Expr) -> PointerValue<'ctx> {
// Just x → build descriptor from x
if let Expr::App(f, arg, _) = maybe_expr {
if let Expr::Var(var, _) = f.as_ref() {
if var.name.as_str() == "Just" {
return self.build_show_descriptor(arg);
}
}
}
self.get_primitive_show_desc(0) // Nothing defaults to Int
}
/// Build descriptors for the fst/snd types of a tuple expression.
fn build_tuple_elem_descriptors(
&mut self,
tuple_expr: &Expr,
) -> (PointerValue<'ctx>, PointerValue<'ctx>) {
// App(App((,), fst), snd)
if let Expr::App(f, snd_expr, _) = tuple_expr {
if let Expr::App(ff, fst_expr, _) = f.as_ref() {
if let Expr::Var(var, _) = ff.as_ref() {
if var.name.as_str() == "(,)" || var.name.as_str() == "Tuple2" {
let fst_cloned = fst_expr.as_ref().clone();
let snd_cloned = snd_expr.as_ref().clone();
let fst_d = self.build_show_descriptor(&fst_cloned);
let snd_d = self.build_show_descriptor(&snd_cloned);
return (fst_d, snd_d);
}
}
}
}
let d0 = self.get_primitive_show_desc(0);
let d0b = self.get_primitive_show_desc(0);
(d0, d0b)
}
/// Build descriptors for the left/right types of an Either expression.
fn build_either_elem_descriptors(
&mut self,
either_expr: &Expr,
) -> (PointerValue<'ctx>, PointerValue<'ctx>) {
if let Expr::App(f, arg, _) = either_expr {
if let Expr::Var(var, _) = f.as_ref() {
let arg_cloned = arg.as_ref().clone();
match var.name.as_str() {
"Left" => {
let l_d = self.build_show_descriptor(&arg_cloned);
let r_d = self.get_primitive_show_desc(0);
return (l_d, r_d);
}
"Right" => {
let l_d = self.get_primitive_show_desc(0);
let r_d = self.build_show_descriptor(&arg_cloned);
return (l_d, r_d);
}
_ => {}
}
}
}
let d0 = self.get_primitive_show_desc(0);
let d0b = self.get_primitive_show_desc(0);
(d0, d0b)
}
/// Lower type-specialized show functions.
fn lower_builtin_show_typed(
&mut self,
expr: &Expr,
var_id: usize,
label: &str,
coerce: ShowCoerce,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", label)))?;
// For compound types, use bhc_show_with_desc with recursive descriptors
match coerce {
ShowCoerce::StringList | ShowCoerce::Unit | ShowCoerce::Integer => {
// Pass pointer directly: fn(ptr) -> ptr (simple, no nesting)
let rts_fn = *self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("bhc_{} not declared", label)))?;
let ptr = self.value_to_ptr(val)?;
let cstr_result = self
.builder()
.build_call(rts_fn, &[ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
return Ok(Some(char_list.into()));
}
ShowCoerce::List
| ShowCoerce::MaybeOf
| ShowCoerce::EitherOf
| ShowCoerce::Tuple2Of => {
// Use bhc_show_with_desc with recursive type descriptor
let ptr = self.value_to_ptr(val)?;
let desc = self.build_show_descriptor(expr);
let show_fn = *self.functions.get(&VarId::new(1000099)).ok_or_else(|| {
CodegenError::Internal("bhc_show_with_desc not declared".to_string())
})?;
let cstr_result = self
.builder()
.build_call(show_fn, &[ptr.into(), desc.into()], "show_with_desc")
.map_err(|e| {
CodegenError::Internal(format!("show_with_desc call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("show_with_desc: returned void".to_string())
})?;
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
return Ok(Some(char_list.into()));
}
_ => {} // Fall through to primitive coercion handling below
}
// For primitive types, use the specific RTS function
let rts_fn = *self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("bhc_{} not declared", label)))?;
// Primitive coercions (Int, Double, Float, Char, Bool)
let call_arg: inkwell::values::BasicMetadataValueEnum = match coerce {
ShowCoerce::Int => {
let int_val = self.coerce_to_int(val)?;
int_val.into()
}
ShowCoerce::Double => {
// Handle float values directly (e.g. from Float/Double literals)
match val {
BasicValueEnum::FloatValue(fv) => {
// Check if it's already f64
if fv.get_type() == self.type_mapper().f64_type() {
fv.into()
} else {
// f32 -> f64 extension
let f64_val = self
.builder()
.build_float_ext(fv, self.type_mapper().f64_type(), "f32_to_f64")
.map_err(|e| {
CodegenError::Internal(format!(
"{}: float ext failed: {:?}",
label, e
))
})?;
f64_val.into()
}
}
_ => {
let int_val = self.coerce_to_int(val)?;
let f64_val = self
.builder()
.build_bit_cast(int_val, self.type_mapper().f64_type(), "to_f64")
.map_err(|e| {
CodegenError::Internal(format!(
"{}: bitcast failed: {:?}",
label, e
))
})?;
f64_val.into()
}
}
}
ShowCoerce::Float => {
let int_val = self.coerce_to_int(val)?;
let truncated = self
.builder()
.build_int_truncate(int_val, self.llvm_context().i32_type(), "trunc32")
.map_err(|e| {
CodegenError::Internal(format!("{}: truncate failed: {:?}", label, e))
})?;
let f32_val = self
.builder()
.build_bit_cast(truncated, self.type_mapper().f32_type(), "to_f32")
.map_err(|e| {
CodegenError::Internal(format!("{}: bitcast failed: {:?}", label, e))
})?;
f32_val.into()
}
ShowCoerce::Char => {
let int_val = self.coerce_to_int(val)?;
let i32_val = self
.builder()
.build_int_truncate(int_val, self.llvm_context().i32_type(), "to_i32")
.map_err(|e| {
CodegenError::Internal(format!("{}: truncate failed: {:?}", label, e))
})?;
i32_val.into()
}
ShowCoerce::Bool => {
// Bool is an ADT with tag 0=False, 1=True - extract the tag
let ptr = match val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => {
// Already an int (e.g. from case branch), use directly
return {
let cstr_result = self
.builder()
.build_call(rts_fn, &[i.into()], label)
.map_err(|e| {
CodegenError::Internal(format!(
"{} call failed: {:?}",
label, e
))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(format!("{}: returned void", label))
})?;
let char_list =
self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
};
}
_ => {
return Err(CodegenError::Internal(format!(
"{}: expected pointer or int for Bool",
label
)))
}
};
let tag = self.extract_adt_tag(ptr)?;
tag.into()
}
ShowCoerce::Ordering => {
// Ordering is an ADT with tag 0=LT, 1=EQ, 2=GT - extract the tag
let ptr = match val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => {
return {
let cstr_result = self
.builder()
.build_call(rts_fn, &[i.into()], label)
.map_err(|e| {
CodegenError::Internal(format!(
"{} call failed: {:?}",
label, e
))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(format!("{}: returned void", label))
})?;
let char_list =
self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
};
}
_ => {
return Err(CodegenError::Internal(format!(
"{}: expected pointer or int for Ordering",
label
)))
}
};
let tag = self.extract_adt_tag(ptr)?;
tag.into()
}
_ => unreachable!("compound coercions handled above"),
};
let cstr_result = self
.builder()
.build_call(rts_fn, &[call_arg], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
}
// ========================================================================
// Phase 5: Monadic & Higher-Order Operation Handlers
// ========================================================================
/// Lower `fmap` / `<$>` with type dispatch.
/// Dispatches to: user ADT (derived Functor), Maybe, List, or IO (fallback).
fn lower_builtin_fmap(
&mut self,
func_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if action_expr is a user ADT with derived Functor
if let Some(type_name) = self.infer_adt_type_from_expr(action_expr) {
if self.derived_functor_fns.contains_key(&type_name) {
return self.lower_fmap_derived(func_expr, action_expr, &type_name);
}
}
// Check if action_expr looks like a Maybe value
if self.expr_looks_like_maybe(action_expr) {
return self.lower_fmap_maybe(func_expr, action_expr);
}
// Check if action_expr looks like a List value
if self.expr_looks_like_list(action_expr) {
return self.lower_builtin_map(func_expr, action_expr);
}
// Fallback: IO fmap (apply function to action result)
self.lower_fmap_io(func_expr, action_expr)
}
/// Lower fmap for a user-defined ADT with derived Functor.
/// Calls the derived fmap function: fn(env, f, x) -> result
fn lower_fmap_derived(
&mut self,
func_expr: &Expr,
value_expr: &Expr,
type_name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fmap_var_id = self
.derived_functor_fns
.get(type_name)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!("fmap: no derived fmap for {}", type_name))
})?;
let fmap_fn = self.functions.get(&fmap_var_id).copied().ok_or_else(|| {
CodegenError::Internal(format!(
"fmap: derived fmap function not found for {}",
type_name
))
})?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("fmap: no function value".to_string()))?;
let value_val = self
.lower_expr(value_expr)?
.ok_or_else(|| CodegenError::Internal("fmap: no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let null_env = ptr_type.const_null();
let func_ptr = self.value_to_ptr(func_val)?;
let value_ptr = self.value_to_ptr(value_val)?;
// Flat 3-arg call: fn(env, f, x) -> result
let fn_ptr = fmap_fn.as_global_value().as_pointer_value();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), func_ptr.into(), value_ptr.into()],
"derived_fmap",
)
.map_err(|e| CodegenError::Internal(format!("derived fmap call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("derived fmap: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower foldr for a user-defined ADT with derived Foldable.
/// Calls the derived foldr function: fn(env, f, z, x) -> result
fn lower_foldr_derived(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
value_expr: &Expr,
type_name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let foldr_var_id = self
.derived_foldable_fns
.get(type_name)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!("foldr: no derived foldr for {}", type_name))
})?;
let foldr_fn = self.functions.get(&foldr_var_id).copied().ok_or_else(|| {
CodegenError::Internal(format!(
"foldr: derived foldr function not found for {}",
type_name
))
})?;
let func_val = self.lower_expr(func_expr)?.ok_or_else(|| {
CodegenError::Internal("foldr derived: no function value".to_string())
})?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("foldr derived: no init value".to_string()))?;
let value_val = self.lower_expr(value_expr)?.ok_or_else(|| {
CodegenError::Internal("foldr derived: no container value".to_string())
})?;
let ptr_type = self.type_mapper().ptr_type();
let null_env = ptr_type.const_null();
let func_ptr = self.value_to_ptr(func_val)?;
let init_ptr = self.value_to_ptr(init_val)?;
let value_ptr = self.value_to_ptr(value_val)?;
// Flat 4-arg call: fn(env, f, z, x) -> result
let fn_ptr = foldr_fn.as_global_value().as_pointer_value();
let fn_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
null_env.into(),
func_ptr.into(),
init_ptr.into(),
value_ptr.into(),
],
"derived_foldr",
)
.map_err(|e| CodegenError::Internal(format!("derived foldr call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("derived foldr: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower fmap for Maybe: fmap f Nothing = Nothing, fmap f (Just x) = Just (f x)
fn lower_fmap_maybe(
&mut self,
func_expr: &Expr,
maybe_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let maybe_val = self
.lower_expr(maybe_expr)?
.ok_or_else(|| CodegenError::Internal("fmap Maybe: no value".to_string()))?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("fmap Maybe: no function".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let maybe_ptr = self.value_to_ptr(maybe_val)?;
// Extract tag: Nothing=0, Just=1
let tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
tag,
i64_type.const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe cmp: {:?}", e)))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("fmap Maybe: no current function".to_string()))?;
let just_bb = self.llvm_ctx.append_basic_block(current_fn, "fmap_just");
let nothing_bb = self.llvm_ctx.append_basic_block(current_fn, "fmap_nothing");
let merge_bb = self.llvm_ctx.append_basic_block(current_fn, "fmap_merge");
self.builder()
.build_conditional_branch(is_just, just_bb, nothing_bb)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe branch: {:?}", e)))?;
// Just branch: extract payload, apply f, wrap in new Just
self.builder().position_at_end(just_bb);
let adt_type = self.adt_type(1);
let payload_gep = self
.builder()
.build_struct_gep(adt_type, maybe_ptr, 1, "just_payload_ptr")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe gep: {:?}", e)))?;
let payload = self
.builder()
.build_load(ptr_type, payload_gep, "just_payload")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe load: {:?}", e)))?;
// Apply f to payload
let func_ptr_val = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fmap: function must be closure".to_string(),
))
}
};
let fn_ptr = self.extract_closure_fn_ptr(func_ptr_val)?;
let payload_ptr = self.value_to_ptr(payload)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let mapped = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr_val.into(), payload_ptr.into()],
"fmap_applied",
)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe apply: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fmap Maybe: apply returned void".to_string()))?;
// Allocate new Just(mapped): tag=1, arity=1
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.copied()
.ok_or_else(|| CodegenError::Internal("fmap: bhc_alloc not found".to_string()))?;
let size_val = i64_type.const_int(16, false); // 8 tag + 8 payload
let raw_just = self
.builder()
.build_call(alloc_fn, &[size_val.into()], "just_alloc")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fmap: alloc returned void".to_string()))?;
let just_ptr = raw_just.into_pointer_value();
// Store tag = 1
let tag_gep = self
.builder()
.build_struct_gep(adt_type, just_ptr, 0, "just_tag_ptr")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe tag gep: {:?}", e)))?;
self.builder()
.build_store(tag_gep, i64_type.const_int(1, false))
.map_err(|e| CodegenError::Internal(format!("fmap Maybe tag store: {:?}", e)))?;
// Store payload
let payload_out_gep = self
.builder()
.build_struct_gep(adt_type, just_ptr, 1, "just_payload_out_ptr")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe payload gep: {:?}", e)))?;
let mapped_ptr = self.value_to_ptr(mapped)?;
self.builder()
.build_store(payload_out_gep, mapped_ptr)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe payload store: {:?}", e)))?;
let just_result: BasicValueEnum<'ctx> = just_ptr.into();
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe just br: {:?}", e)))?;
let just_end_bb = self.builder().get_insert_block().unwrap();
// Nothing branch: return Nothing as-is
self.builder().position_at_end(nothing_bb);
let nothing_result: BasicValueEnum<'ctx> = maybe_ptr.into();
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("fmap Maybe nothing br: {:?}", e)))?;
let nothing_end_bb = self.builder().get_insert_block().unwrap();
// Merge
self.builder().position_at_end(merge_bb);
let phi = self
.builder()
.build_phi(ptr_type, "fmap_maybe_result")
.map_err(|e| CodegenError::Internal(format!("fmap Maybe phi: {:?}", e)))?;
phi.add_incoming(&[
(&just_result, just_end_bb),
(¬hing_result, nothing_end_bb),
]);
Ok(Some(phi.as_basic_value()))
}
/// Lower fmap for IO: apply function to action result (original behavior).
fn lower_fmap_io(
&mut self,
func_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let action_result = self
.lower_expr(action_expr)?
.ok_or_else(|| CodegenError::Internal("fmap: no value".to_string()))?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("fmap: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"fmap: function must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let action_ptr = self.value_to_ptr(action_result)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), action_ptr.into()],
"fmap_result",
)
.map_err(|e| CodegenError::Internal(format!("fmap call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fmap: returned void".to_string()))?;
Ok(Some(result))
}
/// Check if an expression looks like a Maybe value based on its structure.
fn expr_looks_like_maybe(&self, expr: &Expr) -> bool {
match expr {
Expr::App(f, _, _) => self.expr_looks_like_maybe(f),
Expr::TyApp(e, _, _) => self.expr_looks_like_maybe(e),
Expr::Let(_, body, _) => self.expr_looks_like_maybe(body),
Expr::Var(var, _) => {
let name = var.name.as_str();
matches!(
name,
"Just"
| "Nothing"
| "readMaybe"
| "lookupEnv"
| "find"
| "lookup"
| "elemIndex"
| "findIndex"
| "listToMaybe"
| "Data.Map.lookup"
| "stripPrefix"
)
}
_ => false,
}
}
/// Lower `<*>`.
fn lower_builtin_ap(
&mut self,
func_expr: &Expr,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_result = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("<*>: no func".to_string()))?;
let val_result = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("<*>: no val".to_string()))?;
let func_ptr = match func_result {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"<*>: function must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let val_ptr = self.value_to_ptr(val_result)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), val_ptr.into()],
"ap_result",
)
.map_err(|e| CodegenError::Internal(format!("<*> call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("<*>: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `join`.
fn lower_builtin_join(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(expr)
}
/// Lower `=<<` (reverse bind).
fn lower_builtin_bind_flipped(
&mut self,
func_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_bind(action_expr, func_expr)
}
/// Lower `when`.
fn lower_builtin_when(
&mut self,
cond_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let cond_val = self
.lower_expr(cond_expr)?
.ok_or_else(|| CodegenError::Internal("when: no cond".to_string()))?;
// Bool is an ADT with tag 0=False, 1=True — extract the tag
let bool_int = match cond_val {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => return Err(CodegenError::TypeError("when expects Bool".to_string())),
};
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
bool_int,
self.type_mapper().i64_type().const_zero(),
"when_cond",
)
.map_err(|e| CodegenError::Internal(format!("when: cmp failed: {:?}", e)))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let then_block = self
.llvm_context()
.append_basic_block(current_fn, "when_then");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "when_merge");
self.builder()
.build_conditional_branch(is_true, then_block, merge_block)
.map_err(|e| CodegenError::Internal(format!("when: branch failed: {:?}", e)))?;
self.builder().position_at_end(then_block);
let _ = self.lower_expr(action_expr)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("when: branch failed: {:?}", e)))?;
self.builder().position_at_end(merge_block);
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `unless`.
fn lower_builtin_unless(
&mut self,
cond_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let cond_val = self
.lower_expr(cond_expr)?
.ok_or_else(|| CodegenError::Internal("unless: no cond".to_string()))?;
// Bool is an ADT with tag 0=False, 1=True — extract the tag
let bool_int = match cond_val {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => return Err(CodegenError::TypeError("unless expects Bool".to_string())),
};
let is_false = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
bool_int,
self.type_mapper().i64_type().const_zero(),
"unless_cond",
)
.map_err(|e| CodegenError::Internal(format!("unless: cmp failed: {:?}", e)))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let then_block = self
.llvm_context()
.append_basic_block(current_fn, "unless_then");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "unless_merge");
self.builder()
.build_conditional_branch(is_false, then_block, merge_block)
.map_err(|e| CodegenError::Internal(format!("unless: branch failed: {:?}", e)))?;
self.builder().position_at_end(then_block);
let _ = self.lower_expr(action_expr)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("unless: branch failed: {:?}", e)))?;
self.builder().position_at_end(merge_block);
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `void`.
fn lower_builtin_void(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_expr(action_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `mapM`.
fn lower_builtin_mapm(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_map(func_expr, list_expr)
}
/// Lower `mapM_`.
fn lower_builtin_mapm_(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_builtin_map(func_expr, list_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `sequence`.
fn lower_builtin_sequence(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(list_expr)
}
/// Lower `sequence_`.
fn lower_builtin_sequence_(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_expr(list_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `traverse`. Dispatches to derived Traversable for user ADTs, or falls back to mapM for lists.
fn lower_builtin_traverse(
&mut self,
func_expr: &Expr,
container_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if container is a user ADT with derived Traversable
if let Some(type_name) = self.infer_adt_type_from_expr(container_expr) {
if self.derived_traversable_fns.contains_key(&type_name) {
return self.lower_traverse_derived(func_expr, container_expr, &type_name);
}
}
// Fall back to list traverse (= mapM = map for BHC's eager IO)
self.lower_builtin_mapm(func_expr, container_expr)
}
/// Lower traverse for a user-defined ADT with derived Traversable.
/// Calls the derived traverse function: fn(env, f, x) -> result
fn lower_traverse_derived(
&mut self,
func_expr: &Expr,
container_expr: &Expr,
type_name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let traverse_var_id = self
.derived_traversable_fns
.get(type_name)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!("traverse: no derived traverse for {}", type_name))
})?;
let traverse_fn = self
.functions
.get(&traverse_var_id)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!(
"traverse: derived traverse function not found for {}",
type_name
))
})?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("traverse: no function value".to_string()))?;
let container_val = self
.lower_expr(container_expr)?
.ok_or_else(|| CodegenError::Internal("traverse: no container value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let null_env = ptr_type.const_null();
let func_ptr = self.value_to_ptr(func_val)?;
let container_ptr = self.value_to_ptr(container_val)?;
// Flat 3-arg call: fn(env, f, x) -> result
let fn_ptr = traverse_fn.as_global_value().as_pointer_value();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), func_ptr.into(), container_ptr.into()],
"derived_traverse",
)
.map_err(|e| CodegenError::Internal(format!("derived traverse call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("derived traverse: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `traverse_`. Calls traverse and discards the result.
fn lower_builtin_traverse_(
&mut self,
func_expr: &Expr,
container_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_builtin_traverse(func_expr, container_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `sequenceA`. Delegates to sequence for lists, or traverse id for user ADTs.
fn lower_builtin_sequence_a(
&mut self,
container_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For user ADTs, we'd need to traverse with identity, but sequenceA on user ADTs
// is uncommon. Fall back to evaluating the expression (same as sequence for lists).
self.lower_expr(container_expr)
}
/// Lower `sequenceA_`. Evaluates and discards.
fn lower_builtin_sequence_a_(
&mut self,
container_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_expr(container_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `forever`.
fn lower_builtin_forever(
&mut self,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let loop_block = self
.llvm_context()
.append_basic_block(current_fn, "forever_loop");
self.builder()
.build_unconditional_branch(loop_block)
.map_err(|e| CodegenError::Internal(format!("forever: branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_block);
let _ = self.lower_expr(action_expr)?;
self.builder()
.build_unconditional_branch(loop_block)
.map_err(|e| CodegenError::Internal(format!("forever: branch failed: {:?}", e)))?;
let after_block = self
.llvm_context()
.append_basic_block(current_fn, "forever_after");
self.builder().position_at_end(after_block);
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
// E.18: Monadic combinators
/// Lower `filterM pred list` — monadic filter.
/// Walk list, call pred on each element (returns IO Bool), keep elements where True.
fn lower_builtin_filter_m(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("filterM: no predicate".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"filterM: predicate must be closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("filterM: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("filterM: expects list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "filterm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "filterm_body");
let loop_keep = self
.llvm_context()
.append_basic_block(current_fn, "filterm_keep");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "filterm_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "filterm_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("filterM: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "filterm_result")
.map_err(|e| CodegenError::Internal(format!("filterM: phi failed: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "filterm_list")
.map_err(|e| CodegenError::Internal(format!("filterM: phi failed: {:?}", e)))?;
// Check if list is empty (tag == 0 means Nil)
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("filterM: cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("filterM: branch failed: {:?}", e)))?;
// Loop body: extract head/tail, call predicate
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call predicate: pred_ptr(pred_ptr, head) -> IO Bool (in strict codegen = Bool)
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("filterM: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("filterM: predicate returned void".to_string())
})?;
// Bool is an ADT with tag 0=False, 1=True — use extract_adt_tag (not ptr_to_int!)
let bool_int = match pred_result {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::TypeError(
"filterM: predicate must return Bool".to_string(),
))
}
};
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
bool_int,
i64_type.const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("filterM: cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_keep, loop_skip)
.map_err(|e| CodegenError::Internal(format!("filterM: branch failed: {:?}", e)))?;
// Keep: cons head onto result
self.builder().position_at_end(loop_keep);
let new_cons = self.build_cons(head_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("filterM: branch failed: {:?}", e)))?;
// Skip: continue without adding
self.builder().position_at_end(loop_skip);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("filterM: branch failed: {:?}", e)))?;
// Phi incoming
result_phi.add_incoming(&[
(&nil, entry_block),
(&new_cons, loop_keep),
(&result_phi.as_basic_value(), loop_skip),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail_ptr, loop_keep),
(&tail_ptr, loop_skip),
]);
// Exit: reverse the accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `foldM f acc list` — monadic left fold.
fn lower_builtin_fold_m(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("foldM: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"foldM: function must be closure".to_string(),
))
}
};
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("foldM: no init".to_string()))?;
let init_ptr = self.value_to_ptr(init_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("foldM: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("foldM: expects list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "foldm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "foldm_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "foldm_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldM: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "foldm_acc")
.map_err(|e| CodegenError::Internal(format!("foldM: phi failed: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "foldm_list")
.map_err(|e| CodegenError::Internal(format!("foldM: phi failed: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("foldM: cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("foldM: branch failed: {:?}", e)))?;
// Loop body: call f(acc, head)
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Flat 2-arg closure call: fn_ptr(closure_ptr, acc, head) -> result
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[
func_ptr.into(),
acc_phi.as_basic_value().into_pointer_value().into(),
head_ptr.into(),
],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("foldM: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("foldM: function returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldM: branch failed: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc_ptr, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: return final accumulator
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `foldM_ f acc list` — monadic left fold, discards result.
fn lower_builtin_fold_m_(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_builtin_fold_m(func_expr, init_expr, list_expr)?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `replicateM n action` — run action n times, collect results.
fn lower_builtin_replicate_m(
&mut self,
count_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let count_val = self
.lower_expr(count_expr)?
.ok_or_else(|| CodegenError::Internal("replicateM: no count".to_string()))?;
// Coerce count to i64
let count_i64 = match count_val {
BasicValueEnum::IntValue(i) => i,
BasicValueEnum::PointerValue(p) => self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "count_int")
.map_err(|e| {
CodegenError::Internal(format!("replicateM: ptr_to_int failed: {:?}", e))
})?,
_ => {
return Err(CodegenError::TypeError(
"replicateM: count must be Int".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "repm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "repm_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "repm_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("replicateM: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let counter_phi = self
.builder()
.build_phi(i64_type, "repm_counter")
.map_err(|e| CodegenError::Internal(format!("replicateM: phi failed: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "repm_result")
.map_err(|e| CodegenError::Internal(format!("replicateM: phi failed: {:?}", e)))?;
let counter_done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
counter_phi.as_basic_value().into_int_value(),
i64_type.const_zero(),
"counter_done",
)
.map_err(|e| CodegenError::Internal(format!("replicateM: cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(counter_done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("replicateM: branch failed: {:?}", e)))?;
// Loop body: re-lower the action expression each iteration
self.builder().position_at_end(loop_body);
let action_result = self.lower_expr(action_expr)?.ok_or_else(|| {
CodegenError::Internal("replicateM: action returned nothing".to_string())
})?;
let new_cons = self.build_cons(action_result, result_phi.as_basic_value())?;
let decremented = self
.builder()
.build_int_sub(
counter_phi.as_basic_value().into_int_value(),
i64_type.const_int(1, false),
"dec",
)
.map_err(|e| CodegenError::Internal(format!("replicateM: sub failed: {:?}", e)))?;
// Get the actual current block — lower_expr may have created new blocks
let body_end_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("replicateM: no block".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("replicateM: branch failed: {:?}", e)))?;
// Phi incoming — use body_end_block (may differ from loop_body if lower_expr created blocks)
counter_phi.add_incoming(&[(&count_i64, entry_block), (&decremented, body_end_block)]);
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, body_end_block)]);
// Exit: reverse accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `replicateM_ n action` — run action n times, discard results.
fn lower_builtin_replicate_m_(
&mut self,
count_expr: &Expr,
action_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let count_val = self
.lower_expr(count_expr)?
.ok_or_else(|| CodegenError::Internal("replicateM_: no count".to_string()))?;
let count_i64 = match count_val {
BasicValueEnum::IntValue(i) => i,
BasicValueEnum::PointerValue(p) => self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "count_int")
.map_err(|e| {
CodegenError::Internal(format!("replicateM_: ptr_to_int failed: {:?}", e))
})?,
_ => {
return Err(CodegenError::TypeError(
"replicateM_: count must be Int".to_string(),
))
}
};
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "repm__header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "repm__body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "repm__exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("replicateM_: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let counter_phi = self
.builder()
.build_phi(i64_type, "repm__counter")
.map_err(|e| CodegenError::Internal(format!("replicateM_: phi failed: {:?}", e)))?;
let counter_done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLE,
counter_phi.as_basic_value().into_int_value(),
i64_type.const_zero(),
"counter_done",
)
.map_err(|e| CodegenError::Internal(format!("replicateM_: cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(counter_done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("replicateM_: branch failed: {:?}", e)))?;
// Loop body: run action, discard result
self.builder().position_at_end(loop_body);
let _ = self.lower_expr(action_expr)?;
// lower_expr may have created new blocks (e.g., for putStrLn), get the current block
let body_end_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("replicateM_: no block after action".to_string())
})?;
let decremented = self
.builder()
.build_int_sub(
counter_phi.as_basic_value().into_int_value(),
i64_type.const_int(1, false),
"dec",
)
.map_err(|e| CodegenError::Internal(format!("replicateM_: sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("replicateM_: branch failed: {:?}", e)))?;
// Phi incoming — use body_end_block, not loop_body
counter_phi.add_incoming(&[(&count_i64, entry_block), (&decremented, body_end_block)]);
// Exit: return unit
self.builder().position_at_end(loop_exit);
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Lower `zipWithM f xs ys` — monadic zipWith, collecting results.
fn lower_builtin_zipwith_m(
&mut self,
func_expr: &Expr,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM: function must be closure".to_string(),
))
}
};
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM: no list1".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM: expects list".to_string(),
))
}
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM: no list2".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM: expects list".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "zwm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "zwm_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "zwm_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("zipWithM: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "zwm_result")
.map_err(|e| CodegenError::Internal(format!("zipWithM: phi failed: {:?}", e)))?;
let list1_phi = self
.builder()
.build_phi(ptr_type, "zwm_list1")
.map_err(|e| CodegenError::Internal(format!("zipWithM: phi failed: {:?}", e)))?;
let list2_phi = self
.builder()
.build_phi(ptr_type, "zwm_list2")
.map_err(|e| CodegenError::Internal(format!("zipWithM: phi failed: {:?}", e)))?;
// Check if either list is empty
let tag1 = self.extract_adt_tag(list1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(list2_phi.as_basic_value().into_pointer_value())?;
let is_empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
i64_type.const_zero(),
"is_empty1",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM: cmp failed: {:?}", e)))?;
let is_empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
i64_type.const_zero(),
"is_empty2",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM: cmp failed: {:?}", e)))?;
let is_empty = self
.builder()
.build_or(is_empty1, is_empty2, "is_empty")
.map_err(|e| CodegenError::Internal(format!("zipWithM: or failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("zipWithM: branch failed: {:?}", e)))?;
// Loop body: extract heads/tails, call function
self.builder().position_at_end(loop_body);
let head1 =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail1 =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head2 =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail2 =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Flat 2-arg closure call: fn_ptr(closure_ptr, head1, head2) -> result
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let mapped_val = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[func_ptr.into(), head1.into(), head2.into()],
"zwm_mapped",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("zipWithM: function returned void".to_string())
})?;
let new_cons = self.build_cons(mapped_val, result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("zipWithM: branch failed: {:?}", e)))?;
// Phi incoming
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list1_phi.add_incoming(&[(&list1_ptr, entry_block), (&tail1, loop_body)]);
list2_phi.add_incoming(&[(&list2_ptr, entry_block), (&tail2, loop_body)]);
// Exit: reverse accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `zipWithM_ f xs ys` — monadic zipWith, discards results.
fn lower_builtin_zipwith_m_(
&mut self,
func_expr: &Expr,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM_: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM_: function must be closure".to_string(),
))
}
};
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM_: no list1".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM_: expects list".to_string(),
))
}
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zipWithM_: no list2".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWithM_: expects list".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "zwm__header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "zwm__body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "zwm__exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list1_phi = self
.builder()
.build_phi(ptr_type, "zwm__list1")
.map_err(|e| CodegenError::Internal(format!("zipWithM_: phi failed: {:?}", e)))?;
let list2_phi = self
.builder()
.build_phi(ptr_type, "zwm__list2")
.map_err(|e| CodegenError::Internal(format!("zipWithM_: phi failed: {:?}", e)))?;
// Check if either list is empty
let tag1 = self.extract_adt_tag(list1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(list2_phi.as_basic_value().into_pointer_value())?;
let is_empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
i64_type.const_zero(),
"is_empty1",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: cmp failed: {:?}", e)))?;
let is_empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
i64_type.const_zero(),
"is_empty2",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: cmp failed: {:?}", e)))?;
let is_empty = self
.builder()
.build_or(is_empty1, is_empty2, "is_empty")
.map_err(|e| CodegenError::Internal(format!("zipWithM_: or failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: branch failed: {:?}", e)))?;
// Loop body: extract heads/tails, call function, discard result
self.builder().position_at_end(loop_body);
let head1 =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail1 =
self.extract_adt_field(list1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head2 =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail2 =
self.extract_adt_field(list2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Flat 2-arg closure call: fn_ptr(closure_ptr, head1, head2) -> result (discarded)
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let _ = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[func_ptr.into(), head1.into(), head2.into()],
"zwm__result",
)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: call failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("zipWithM_: branch failed: {:?}", e)))?;
// Phi incoming
list1_phi.add_incoming(&[(&list1_ptr, entry_block), (&tail1, loop_body)]);
list2_phi.add_incoming(&[(&list2_ptr, entry_block), (&tail2, loop_body)]);
// Exit: return unit
self.builder().position_at_end(loop_exit);
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
// Data.Function handlers
/// Lower `flip f x y = f y x`.
/// Uses flat 3-arg call: fn(closure_env, arg2, arg1) — BHC calling convention.
fn lower_builtin_flip(
&mut self,
func_expr: &Expr,
arg1_expr: &Expr,
arg2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("flip: no function".to_string()))?;
let arg1_val = self
.lower_expr(arg1_expr)?
.ok_or_else(|| CodegenError::Internal("flip: no arg1".to_string()))?;
let arg2_val = self
.lower_expr(arg2_expr)?
.ok_or_else(|| CodegenError::Internal("flip: no arg2".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"flip: function must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let arg1_ptr = self.value_to_ptr(arg1_val)?;
let arg2_ptr = self.value_to_ptr(arg2_val)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
// Flat 3-arg call: fn(closure_env, arg2, arg1) — args swapped
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), arg2_ptr.into(), arg1_ptr.into()],
"flip_result",
)
.map_err(|e| CodegenError::Internal(format!("flip call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("flip: result returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `on f g x y = f (g x) (g y)`.
fn lower_builtin_on(
&mut self,
f_expr: &Expr,
g_expr: &Expr,
x_expr: &Expr,
y_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("on: no f".to_string()))?;
let g_val = self
.lower_expr(g_expr)?
.ok_or_else(|| CodegenError::Internal("on: no g".to_string()))?;
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("on: no x".to_string()))?;
let y_val = self
.lower_expr(y_expr)?
.ok_or_else(|| CodegenError::Internal("on: no y".to_string()))?;
let g_ptr = match g_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("on: g must be closure".to_string())),
};
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("on: f must be closure".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let x_ptr = self.value_to_ptr(x_val)?;
let g_fn_ptr = self.extract_closure_fn_ptr(g_ptr)?;
let gx = self
.builder()
.build_indirect_call(fn_type, g_fn_ptr, &[g_ptr.into(), x_ptr.into()], "gx")
.map_err(|e| CodegenError::Internal(format!("on: g(x) failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("on: g(x) void".to_string()))?;
let y_ptr = self.value_to_ptr(y_val)?;
let gy = self
.builder()
.build_indirect_call(fn_type, g_fn_ptr, &[g_ptr.into(), y_ptr.into()], "gy")
.map_err(|e| CodegenError::Internal(format!("on: g(y) failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("on: g(y) void".to_string()))?;
let f_fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let gx_ptr = self.value_to_ptr(gx)?;
let partial = self
.builder()
.build_indirect_call(fn_type, f_fn_ptr, &[f_ptr.into(), gx_ptr.into()], "f_gx")
.map_err(|e| CodegenError::Internal(format!("on: f(gx) failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("on: f(gx) void".to_string()))?;
let partial_ptr = match partial {
BasicValueEnum::PointerValue(p) => p,
_ => return Ok(Some(partial)),
};
let gy_ptr = self.value_to_ptr(gy)?;
let p_fn = self.extract_closure_fn_ptr(partial_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
p_fn,
&[partial_ptr.into(), gy_ptr.into()],
"on_result",
)
.map_err(|e| CodegenError::Internal(format!("on: result failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("on: result void".to_string()))?;
Ok(Some(result))
}
/// Lower `fix` (simplified).
fn lower_builtin_fix(
&mut self,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(func_expr)
}
/// Lower `$` - function application.
fn lower_builtin_apply(
&mut self,
func_expr: &Expr,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("$: no function".to_string()))?;
let arg_val = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("$: no argument".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"$: function must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let arg_ptr = self.value_to_ptr(arg_val)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), arg_ptr.into()],
"apply_result",
)
.map_err(|e| CodegenError::Internal(format!("$ call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("$: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `.` - function composition: (f . g) x = f (g x).
fn lower_builtin_compose(
&mut self,
f_expr: &Expr,
g_expr: &Expr,
x_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal(".: no f".to_string()))?;
let g_val = self
.lower_expr(g_expr)?
.ok_or_else(|| CodegenError::Internal(".: no g".to_string()))?;
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal(".: no x".to_string()))?;
let g_ptr = match g_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(".: g must be closure".to_string())),
};
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(".: f must be closure".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let x_ptr = self.value_to_ptr(x_val)?;
let g_fn = self.extract_closure_fn_ptr(g_ptr)?;
let gx = self
.builder()
.build_indirect_call(fn_type, g_fn, &[g_ptr.into(), x_ptr.into()], "gx")
.map_err(|e| CodegenError::Internal(format!(".: g(x) failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(".: g(x) void".to_string()))?;
let gx_ptr = self.value_to_ptr(gx)?;
let f_fn = self.extract_closure_fn_ptr(f_ptr)?;
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f_ptr.into(), gx_ptr.into()], "compose")
.map_err(|e| CodegenError::Internal(format!(".: f(g(x)) failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(".: f(g(x)) void".to_string()))?;
Ok(Some(result))
}
// ========================================================================
// E.27: succ, pred, (&), swap, curry, uncurry
// ========================================================================
/// Lower `succ` - successor function (n + 1), or user enum succ via fromEnum/toEnum.
fn lower_builtin_succ(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if argument is a user enum type
if let Some(type_name) = self.infer_adt_type_from_expr(expr) {
if self.derived_from_enum_fns.contains_key(&type_name)
&& self.derived_to_enum_fns.contains_key(&type_name)
{
// succ x = toEnum(fromEnum(x) + 1)
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("succ: no value".to_string()))?;
let tag = self.call_derived_from_enum(&type_name, val)?;
let one = self.type_mapper().i64_type().const_int(1, false);
let tag_plus_1 = self
.builder()
.build_int_add(tag, one, "succ_tag")
.map_err(|e| CodegenError::Internal(format!("succ add: {:?}", e)))?;
let result = self.call_derived_to_enum(&type_name, tag_plus_1)?;
return Ok(Some(result));
}
}
// Fallback: Int succ (n + 1)
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("succ: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let one = self.type_mapper().i64_type().const_int(1, false);
let result = self
.builder()
.build_int_add(int_val, one, "succ")
.map_err(|e| CodegenError::Internal(format!("succ failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower `pred` - predecessor function (n - 1), or user enum pred via fromEnum/toEnum.
fn lower_builtin_pred(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if argument is a user enum type
if let Some(type_name) = self.infer_adt_type_from_expr(expr) {
if self.derived_from_enum_fns.contains_key(&type_name)
&& self.derived_to_enum_fns.contains_key(&type_name)
{
// pred x = toEnum(fromEnum(x) - 1)
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("pred: no value".to_string()))?;
let tag = self.call_derived_from_enum(&type_name, val)?;
let one = self.type_mapper().i64_type().const_int(1, false);
let tag_minus_1 = self
.builder()
.build_int_sub(tag, one, "pred_tag")
.map_err(|e| CodegenError::Internal(format!("pred sub: {:?}", e)))?;
let result = self.call_derived_to_enum(&type_name, tag_minus_1)?;
return Ok(Some(result));
}
}
// Fallback: Int pred (n - 1)
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("pred: no value".to_string()))?;
let int_val = self.coerce_to_int(val)?;
let one = self.type_mapper().i64_type().const_int(1, false);
let result = self
.builder()
.build_int_sub(int_val, one, "pred")
.map_err(|e| CodegenError::Internal(format!("pred failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower `fromEnum` - convert enum value to Int.
fn lower_builtin_from_enum(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if argument is a user enum type
if let Some(type_name) = self.infer_adt_type_from_expr(expr) {
if let Some(&var_id) = self.derived_from_enum_fns.get(&type_name) {
if let Some(fn_val) = self.functions.get(&var_id).copied() {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("fromEnum: no value".to_string()))?;
// Call derived fromEnum: fn(env, x) -> Int (as tagged int pointer)
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), val.into()],
"derived_from_enum",
)
.map_err(|e| CodegenError::Internal(format!("fromEnum call: {:?}", e)))?;
return Ok(result.try_as_basic_value().basic());
}
}
}
// Fallback: identity (fromEnum on Int = identity)
self.lower_expr(expr)
}
/// Lower `toEnum` - convert Int to enum value.
fn lower_builtin_to_enum(
&mut self,
expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// If there's exactly one user enum with derived Enum, use it
if self.derived_to_enum_fns.len() == 1 {
let (type_name, &var_id) = self.derived_to_enum_fns.iter().next().unwrap();
let type_name = type_name.clone();
if let Some(fn_val) = self.functions.get(&var_id).copied() {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal("toEnum: no value".to_string()))?;
// Convert Int value to pointer for the call (derived functions use ptr calling convention)
let val_ptr = self.value_to_ptr(val)?;
// Call derived toEnum: fn(env, n) -> Constructor pointer
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), val_ptr.into()],
"derived_to_enum",
)
.map_err(|e| CodegenError::Internal(format!("toEnum call: {:?}", e)))?;
let _ = type_name;
return Ok(result.try_as_basic_value().basic());
}
}
// Fallback: identity (toEnum on Int = identity)
self.lower_expr(expr)
}
/// Lower `minBound` - return the minimum value of a Bounded type.
fn lower_builtin_min_bound(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// If there's exactly one user enum with derived Bounded, use it
if self.derived_min_bound_fns.len() == 1 {
let (type_name, &var_id) = self.derived_min_bound_fns.iter().next().unwrap();
let type_name = type_name.clone();
if let Some(fn_val) = self.functions.get(&var_id).copied() {
// Call derived minBound: fn(env) -> Constructor pointer
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self
.type_mapper()
.ptr_type()
.fn_type(&[self.type_mapper().ptr_type().into()], false);
let result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[null_env.into()], "derived_min_bound")
.map_err(|e| CodegenError::Internal(format!("minBound call: {:?}", e)))?;
let _ = type_name;
return Ok(result.try_as_basic_value().basic());
}
}
// Fallback: Int.MIN_VALUE
let min_val = self
.type_mapper()
.i64_type()
.const_int(i64::MIN as u64, true);
Ok(Some(self.int_to_ptr(min_val)?.into()))
}
/// Lower `maxBound` - return the maximum value of a Bounded type.
fn lower_builtin_max_bound(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// If there's exactly one user enum with derived Bounded, use it
if self.derived_max_bound_fns.len() == 1 {
let (type_name, &var_id) = self.derived_max_bound_fns.iter().next().unwrap();
let type_name = type_name.clone();
if let Some(fn_val) = self.functions.get(&var_id).copied() {
// Call derived maxBound: fn(env) -> Constructor pointer
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self
.type_mapper()
.ptr_type()
.fn_type(&[self.type_mapper().ptr_type().into()], false);
let result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[null_env.into()], "derived_max_bound")
.map_err(|e| CodegenError::Internal(format!("maxBound call: {:?}", e)))?;
let _ = type_name;
return Ok(result.try_as_basic_value().basic());
}
}
// Fallback: Int.MAX_VALUE
let max_val = self
.type_mapper()
.i64_type()
.const_int(i64::MAX as u64, true);
Ok(Some(self.int_to_ptr(max_val)?.into()))
}
/// Helper: Call a derived fromEnum function on a value, returning the Int tag.
fn call_derived_from_enum(
&mut self,
type_name: &str,
val: BasicValueEnum<'ctx>,
) -> CodegenResult<IntValue<'ctx>> {
let var_id = self
.derived_from_enum_fns
.get(type_name)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!("no derived fromEnum for {}", type_name))
})?;
let fn_val = self.functions.get(&var_id).copied().ok_or_else(|| {
CodegenError::Internal(format!("fromEnum function not found for {}", type_name))
})?;
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), val.into()],
"from_enum_call",
)
.map_err(|e| CodegenError::Internal(format!("fromEnum call: {:?}", e)))?;
// The derived fromEnum returns an Int value as a tagged pointer
let result_ptr = result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fromEnum: no return value".to_string()))?;
self.coerce_to_int(result_ptr)
}
/// Helper: Call a derived toEnum function with an Int tag, returning the constructor pointer.
fn call_derived_to_enum(
&mut self,
type_name: &str,
tag: IntValue<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let var_id = self
.derived_to_enum_fns
.get(type_name)
.copied()
.ok_or_else(|| {
CodegenError::Internal(format!("no derived toEnum for {}", type_name))
})?;
let fn_val = self.functions.get(&var_id).copied().ok_or_else(|| {
CodegenError::Internal(format!("toEnum function not found for {}", type_name))
})?;
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
// Convert Int tag to tagged pointer for the call
let tag_ptr = self.int_to_ptr(tag)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), tag_ptr.into()],
"to_enum_call",
)
.map_err(|e| CodegenError::Internal(format!("toEnum call: {:?}", e)))?;
result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("toEnum: no return value".to_string()))
}
// ====================================================================
// E.28: Arithmetic, enum, folds, higher-order, IO input
// ====================================================================
/// Lower `min` - returns the smaller of two Ints.
fn lower_builtin_min(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("min: a has no value".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("min: b has no value".to_string()))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
let cmp = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, a_int, b_int, "min_cmp")
.map_err(|e| CodegenError::Internal(format!("min cmp: {:?}", e)))?;
let result = self
.builder()
.build_select(cmp, a_int, b_int, "min_sel")
.map_err(|e| CodegenError::Internal(format!("min sel: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `max` - returns the larger of two Ints.
fn lower_builtin_max(
&mut self,
a_expr: &Expr,
b_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a_val = self
.lower_expr(a_expr)?
.ok_or_else(|| CodegenError::Internal("max: a has no value".to_string()))?;
let b_val = self
.lower_expr(b_expr)?
.ok_or_else(|| CodegenError::Internal("max: b has no value".to_string()))?;
let a_int = self.coerce_to_int(a_val)?;
let b_int = self.coerce_to_int(b_val)?;
let cmp = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, a_int, b_int, "max_cmp")
.map_err(|e| CodegenError::Internal(format!("max cmp: {:?}", e)))?;
let result = self
.builder()
.build_select(cmp, a_int, b_int, "max_sel")
.map_err(|e| CodegenError::Internal(format!("max sel: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `subtract` - subtract x y = y - x (flipped subtraction).
fn lower_builtin_subtract(
&mut self,
x_expr: &Expr,
y_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("subtract: x has no value".to_string()))?;
let y_val = self
.lower_expr(y_expr)?
.ok_or_else(|| CodegenError::Internal("subtract: y has no value".to_string()))?;
let x_int = self.coerce_to_int(x_val)?;
let y_int = self.coerce_to_int(y_val)?;
// subtract x y = y - x
let result = self
.builder()
.build_int_sub(y_int, x_int, "subtract")
.map_err(|e| CodegenError::Internal(format!("subtract: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
/// Lower `enumFrom` - [n..] (infinite list, capped at 10000 elements as fallback).
fn lower_builtin_enum_from(
&mut self,
from_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Build a finite list of 10000 elements as fallback
// Real programs use `take n (enumFrom x)` which the optimizer can handle
let from_val = self
.lower_expr(from_expr)?
.ok_or_else(|| CodegenError::Internal("enumFrom: from has no value".to_string()))?;
let from = self.to_int_value(from_val)?;
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// to = from + 9999
let limit = self
.builder()
.build_int_add(from, i64_type.const_int(9999, false), "enum_limit")
.map_err(|e| CodegenError::Internal(format!("enumFrom: add: {:?}", e)))?;
// Build list backwards from limit down to from (same pattern as enumFromTo)
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "enumf_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "enumf_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "enumf_exit");
let nil = self.build_nil()?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFrom: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("enumFrom: phi: {:?}", e)))?;
let current_phi = self
.builder()
.build_phi(i64_type, "current")
.map_err(|e| CodegenError::Internal(format!("enumFrom: phi: {:?}", e)))?;
let current = current_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, current, from, "done")
.map_err(|e| CodegenError::Internal(format!("enumFrom: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("enumFrom: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let boxed = self.box_int(current)?;
let new_acc = self.build_cons(boxed.into(), acc_phi.as_basic_value())?;
let prev = self
.builder()
.build_int_sub(current, i64_type.const_int(1, false), "prev")
.map_err(|e| CodegenError::Internal(format!("enumFrom: sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFrom: branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
current_phi.add_incoming(&[(&limit, entry_block), (&prev, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `enumFromThen` - [n, m..] (infinite list with step, capped at 10000).
fn lower_builtin_enum_from_then(
&mut self,
from_expr: &Expr,
then_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let from_val = self
.lower_expr(from_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromThen: from has no value".to_string()))?;
let then_val = self
.lower_expr(then_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromThen: then has no value".to_string()))?;
let from = self.to_int_value(from_val)?;
let then_v = self.to_int_value(then_val)?;
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// step = then - from
let step = self
.builder()
.build_int_sub(then_v, from, "step")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: sub: {:?}", e)))?;
// Build 10000 elements: last_val = from + step * 9999
// Build backwards from element 9999 down to 0
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "enumft_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "enumft_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "enumft_exit");
let nil = self.build_nil()?;
let count_limit = i64_type.const_int(10000, false);
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFromThen: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: phi: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(i64_type, "idx")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: phi: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
i64_type.const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("enumFromThen: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("enumFromThen: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
// current_val = from + step * idx
let step_times_idx = self
.builder()
.build_int_mul(step, idx, "step_idx")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: mul: {:?}", e)))?;
let current_val = self
.builder()
.build_int_add(from, step_times_idx, "cur_val")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: add: {:?}", e)))?;
let boxed = self.box_int(current_val)?;
let new_acc = self.build_cons(boxed.into(), acc_phi.as_basic_value())?;
let prev_idx = self
.builder()
.build_int_sub(idx, i64_type.const_int(1, false), "prev_idx")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFromThen: branch: {:?}", e)))?;
let start_idx = self
.builder()
.build_int_sub(count_limit, i64_type.const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("enumFromThen: sub: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&prev_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `enumFromThenTo` - [n, m..p] (finite list with step).
fn lower_builtin_enum_from_then_to(
&mut self,
from_expr: &Expr,
then_expr: &Expr,
to_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let from_val = self.lower_expr(from_expr)?.ok_or_else(|| {
CodegenError::Internal("enumFromThenTo: from has no value".to_string())
})?;
let then_val = self.lower_expr(then_expr)?.ok_or_else(|| {
CodegenError::Internal("enumFromThenTo: then has no value".to_string())
})?;
let to_val = self
.lower_expr(to_expr)?
.ok_or_else(|| CodegenError::Internal("enumFromThenTo: to has no value".to_string()))?;
let from = self.to_int_value(from_val)?;
let then_v = self.to_int_value(then_val)?;
let to = self.to_int_value(to_val)?;
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// step = then - from
let step = self
.builder()
.build_int_sub(then_v, from, "step")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: sub: {:?}", e)))?;
// We need to handle both positive and negative steps.
// For positive step: iterate while current <= to
// For negative step: iterate while current >= to
// We'll compute the number of elements, then build the list backwards.
// Check step direction
let step_positive = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SGT,
step,
i64_type.const_zero(),
"step_pos",
)
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: cmp: {:?}", e)))?;
// Build blocks
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "eftt_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "eftt_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "eftt_exit");
let nil = self.build_nil()?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: phi: {:?}", e)))?;
let current_phi = self
.builder()
.build_phi(i64_type, "current")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: phi: {:?}", e)))?;
let current = current_phi.as_basic_value().into_int_value();
// For positive step: done when current > to
let over_pos = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, current, to, "over_pos")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: cmp: {:?}", e)))?;
// For negative step: done when current < to
let over_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, current, to, "over_neg")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: cmp: {:?}", e)))?;
// Select based on step direction
let done = self
.builder()
.build_select(step_positive, over_pos, over_neg, "done")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: sel: {:?}", e)))?;
self.builder()
.build_conditional_branch(done.into_int_value(), loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let boxed = self.box_int(current)?;
// Build list in forward order: cons current onto end, but we need reverse.
// Actually, build forward by consing and then reversing. Or build in reverse:
// Walk forward, consing each element. The list will be in reverse order.
// Then reverse at the end.
let new_acc = self.build_cons(boxed.into(), acc_phi.as_basic_value())?;
let next = self
.builder()
.build_int_add(current, step, "next")
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: add: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("enumFromThenTo: branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
current_phi.add_incoming(&[(&from, entry_block), (&next, loop_body)]);
self.builder().position_at_end(loop_exit);
// The list is reversed (we consed in forward order), so reverse it
let reversed =
self.build_inline_reverse(acc_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `foldl1` - left fold with no initial value (uses head of list).
fn lower_builtin_foldl1(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("foldl1: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"foldl1: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("foldl1: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("foldl1 expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Extract head as initial accumulator, tail as list to fold
let head = self.extract_adt_field(list_ptr, 2, 0)?;
let tail = self.extract_adt_field(list_ptr, 2, 1)?;
// Now do standard foldl loop on tail with head as init
let entry_block = self.builder().get_insert_block().unwrap();
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "foldl1_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "foldl1_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "foldl1_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldl1: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "foldl1_acc")
.map_err(|e| CodegenError::Internal(format!("foldl1: phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "foldl1_list")
.map_err(|e| CodegenError::Internal(format!("foldl1: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("foldl1: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("foldl1: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f acc head - flat 3-arg call: fn_ptr(closure_ptr, acc, head)
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
acc_phi.as_basic_value().into(),
head_ptr.into(),
],
"foldl1_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("foldl1: call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("foldl1: function returned void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldl1: branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&head, entry_block), (&new_acc, loop_body)]);
list_phi.add_incoming(&[(&tail, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `foldr1` - right fold with no initial value.
/// Implementation: reverse list, then foldl1.
fn lower_builtin_foldr1(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("foldr1: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"foldr1: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("foldr1: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("foldr1 expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Reverse the list first
let reversed = self
.build_inline_reverse(list_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("foldr1: reverse returned None".to_string()))?;
let reversed_ptr = reversed.into_pointer_value();
// Extract head of reversed list as init, tail as list to fold
let head = self.extract_adt_field(reversed_ptr, 2, 0)?;
let tail = self.extract_adt_field(reversed_ptr, 2, 1)?;
// foldl on reversed list with flipped function args
let entry_block = self.builder().get_insert_block().unwrap();
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "foldr1_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "foldr1_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "foldr1_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldr1: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "foldr1_acc")
.map_err(|e| CodegenError::Internal(format!("foldr1: phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "foldr1_list")
.map_err(|e| CodegenError::Internal(format!("foldr1: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_empty")
.map_err(|e| CodegenError::Internal(format!("foldr1: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("foldr1: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// For foldr1: f elem acc (not f acc elem like foldl)
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
head_ptr.into(),
acc_phi.as_basic_value().into(),
],
"foldr1_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("foldr1: call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("foldr1: function returned void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("foldr1: branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&head, entry_block), (&new_acc, loop_body)]);
list_phi.add_incoming(&[(&tail, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `comparing` - comparing f x y = compare (f x) (f y).
fn lower_builtin_comparing(
&mut self,
f_expr: &Expr,
x_expr: &Expr,
y_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("comparing: f has no value".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"comparing: f must be a closure".to_string(),
))
}
};
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("comparing: x has no value".to_string()))?;
let y_val = self
.lower_expr(y_expr)?
.ok_or_else(|| CodegenError::Internal("comparing: y has no value".to_string()))?;
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let x_ptr = self.value_to_ptr(x_val)?;
let y_ptr = self.value_to_ptr(y_val)?;
// Call f(x) and f(y)
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let fx = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[f_ptr.into(), x_ptr.into()], "fx")
.map_err(|e| CodegenError::Internal(format!("comparing: fx call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("comparing: fx returned void".to_string()))?;
let fy = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[f_ptr.into(), y_ptr.into()], "fy")
.map_err(|e| CodegenError::Internal(format!("comparing: fy call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("comparing: fy returned void".to_string()))?;
// Inline compare: coerce to int, then SLT/SGT chain -> Ordering ADT
let fx_int = self.coerce_to_int(fx)?;
let fy_int = self.coerce_to_int(fy)?;
let is_lt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, fx_int, fy_int, "is_lt")
.map_err(|e| CodegenError::Internal(format!("comparing: lt: {:?}", e)))?;
let is_gt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, fx_int, fy_int, "is_gt")
.map_err(|e| CodegenError::Internal(format!("comparing: gt: {:?}", e)))?;
let tag_gt_or_eq = self
.builder()
.build_select(
is_gt,
i64_type.const_int(2, false),
i64_type.const_int(1, false),
"gt_or_eq",
)
.map_err(|e| CodegenError::Internal(format!("comparing: sel1: {:?}", e)))?;
let tag = self
.builder()
.build_select(
is_lt,
i64_type.const_int(0, false),
tag_gt_or_eq.into_int_value(),
"cmp_tag",
)
.map_err(|e| CodegenError::Internal(format!("comparing: sel2: {:?}", e)))?;
self.allocate_ordering_adt(tag.into_int_value(), "comparing")
}
/// Lower `until` - until p f x = if p x then x else until p f (f x).
fn lower_builtin_until(
&mut self,
pred_expr: &Expr,
func_expr: &Expr,
init_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("until: pred has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"until: pred must be a closure".to_string(),
))
}
};
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("until: func has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"until: func must be a closure".to_string(),
))
}
};
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("until: init has no value".to_string()))?;
let init_ptr = self.value_to_ptr(init_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self.builder().get_insert_block().unwrap();
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "until_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "until_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "until_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("until: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let val_phi = self
.builder()
.build_phi(ptr_type, "until_val")
.map_err(|e| CodegenError::Internal(format!("until: phi: {:?}", e)))?;
// Call p(val) -> Bool ADT
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let pred_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
pred_fn_type,
pred_fn_ptr,
&[pred_ptr.into(), val_phi.as_basic_value().into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("until: pred call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("until: pred returned void".to_string()))?;
// Check if predicate returned True (non-zero value)
// Comparison operators return tagged-int-as-pointer, so use ptr_to_int
let pred_bool = self
.builder()
.build_ptr_to_int(pred_result.into_pointer_value(), tm.i64_type(), "pred_bool")
.map_err(|e| CodegenError::Internal(format!("until: ptr_to_int: {:?}", e)))?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_bool,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("until: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("until: branch: {:?}", e)))?;
// Loop body: apply f to current val
self.builder().position_at_end(loop_body);
let func_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let func_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let new_val = self
.builder()
.build_indirect_call(
func_fn_type,
func_fn_ptr,
&[func_ptr.into(), val_phi.as_basic_value().into()],
"new_val",
)
.map_err(|e| CodegenError::Internal(format!("until: func call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("until: func returned void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("until: branch: {:?}", e)))?;
val_phi.add_incoming(&[(&init_ptr, entry_block), (&new_val, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(val_phi.as_basic_value()))
}
/// Lower `getChar` - read a single character from stdin.
fn lower_builtin_get_char(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000560))
.ok_or_else(|| CodegenError::Internal("bhc_getChar not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "getchar_result")
.map_err(|e| CodegenError::Internal(format!("getChar call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getChar: returned void".to_string()))?;
// Result is i64 char code, convert to pointer (Char representation)
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `isEOF` - check if stdin is at EOF.
fn lower_builtin_is_eof(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000561))
.ok_or_else(|| CodegenError::Internal("bhc_isEOF not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "iseof_result")
.map_err(|e| CodegenError::Internal(format!("isEOF call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("isEOF: returned void".to_string()))?;
// Result is i64 (0 or 1), wrap as Bool ADT
self.allocate_bool_adt(result.into_int_value(), "iseof")
}
/// Lower `getContents` - read all of stdin.
fn lower_builtin_get_contents(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000562))
.ok_or_else(|| CodegenError::Internal("bhc_getContents not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "getcontents_result")
.map_err(|e| CodegenError::Internal(format!("getContents call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("getContents: returned void".to_string()))?;
// Result is *mut c_char, convert to [Char] list
let char_list = self.cstring_to_char_list(result.into_pointer_value())?;
Ok(Some(char_list.into()))
}
/// Lower `interact` - interact f = getContents >>= putStr . f
fn lower_builtin_interact(
&mut self,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("interact: func has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"interact: func must be a closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
// Step 1: Call getContents to get input as [Char]
let rts_fn = self
.functions
.get(&VarId::new(1000562))
.ok_or_else(|| CodegenError::Internal("bhc_getContents not declared".to_string()))?;
let input_cstr = self
.builder()
.build_call(*rts_fn, &[], "interact_input")
.map_err(|e| CodegenError::Internal(format!("interact: getContents: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("interact: getContents returned void".to_string())
})?;
let input_list = self.cstring_to_char_list(input_cstr.into_pointer_value())?;
// Step 2: Call f(input) via closure call
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let output = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), input_list.into()],
"interact_output",
)
.map_err(|e| CodegenError::Internal(format!("interact: f call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("interact: f returned void".to_string()))?;
// Step 3: Print the output string (putStr pattern)
let output_ptr = match output {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"interact: output must be string".to_string(),
))
}
};
self.lower_print_char_list_ptr(output_ptr)?;
// Return unit
Ok(Some(ptr_type.const_null().into()))
}
/// Emit a loop that walks a [Char] linked list and prints each character.
/// Takes a pre-evaluated PointerValue instead of an Expr.
fn lower_print_char_list_ptr(
&mut self,
list_ptr: inkwell::values::PointerValue<'ctx>,
) -> CodegenResult<()> {
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let print_char_fn = self
.functions
.get(&VarId::new(1000009))
.ok_or_else(|| CodegenError::Internal("bhc_print_char not declared".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "iprint_loop");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "iprint_body");
let loop_end = self.llvm_ctx.append_basic_block(current_fn, "iprint_done");
let pre_loop_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("iprint: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let current_node = self
.builder()
.build_phi(tm.ptr_type(), "cur_node")
.map_err(|e| CodegenError::Internal(format!("iprint: phi: {:?}", e)))?;
current_node.add_incoming(&[(&list_ptr, pre_loop_block)]);
let tag = self.extract_adt_tag(current_node.as_basic_value().into_pointer_value())?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("iprint: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_end, loop_body)
.map_err(|e| CodegenError::Internal(format!("iprint: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
// Extract head (Char) at field 0. The char is stored boxed (via
// int_to_ptr), so convert back to an int and truncate to i32 — which is
// what bhc_print_char expects. (Passing the i64 directly fails LLVM
// verification.)
let head =
self.extract_adt_field(current_node.as_basic_value().into_pointer_value(), 2, 0)?;
let char_val = self
.builder()
.build_ptr_to_int(head, tm.i64_type(), "char_val")
.map_err(|e| CodegenError::Internal(format!("iprint: char ptr_to_int: {:?}", e)))?;
let char_i32 = self
.builder()
.build_int_truncate(char_val, tm.i32_type(), "char_i32")
.map_err(|e| CodegenError::Internal(format!("iprint: char truncate: {:?}", e)))?;
self.builder()
.build_call(*print_char_fn, &[char_i32.into()], "")
.map_err(|e| CodegenError::Internal(format!("iprint: call: {:?}", e)))?;
let next =
self.extract_adt_field(current_node.as_basic_value().into_pointer_value(), 2, 1)?;
current_node.add_incoming(&[(&next, loop_body)]);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("iprint: branch: {:?}", e)))?;
self.builder().position_at_end(loop_end);
Ok(())
}
/// Lower `(&)` - reverse function application: (&) x f = f x.
fn lower_builtin_reverse_apply(
&mut self,
val_expr: &Expr,
func_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("&: no value".to_string()))?;
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("&: no function".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"&: function must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let val_ptr = self.value_to_ptr(val)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), val_ptr.into()],
"rev_apply_result",
)
.map_err(|e| CodegenError::Internal(format!("& call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("&: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `swap` - swap elements of a pair: swap (a, b) = (b, a).
fn lower_builtin_swap(
&mut self,
pair_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pair_val = self
.lower_expr(pair_expr)?
.ok_or_else(|| CodegenError::Internal("swap: pair has no value".to_string()))?;
let pair_ptr = match pair_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("swap expects a tuple".to_string())),
};
// Extract field 0 (fst) and field 1 (snd)
let fst_val = self.extract_adt_field(pair_ptr, 2, 0)?;
let snd_val = self.extract_adt_field(pair_ptr, 2, 1)?;
// Allocate new tuple with swapped fields: (snd, fst)
self.allocate_ptr_pair_tuple(snd_val, fst_val, "swap")
}
/// Allocate a (ptr, ptr) tuple: 24 bytes, tag=0, fst at offset 8, snd at offset 16.
/// Like allocate_int_pair_tuple but takes pointer values instead of int values.
fn allocate_ptr_pair_tuple(
&mut self,
fst: PointerValue<'ctx>,
snd: PointerValue<'ctx>,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let i64_type = self.type_mapper().i64_type();
let ptr_type = self.type_mapper().ptr_type();
let size_val = i64_type.const_int(24, false);
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], &format!("{}_alloc", name))
.map_err(|e| CodegenError::Internal(format!("{}: alloc failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: alloc returned void", name)))?;
let tuple_ptr = raw_ptr.into_pointer_value();
// Store tag=0 at offset 0
let adt_ty = self.adt_type(0);
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, tuple_ptr, 0, &format!("{}_tag", name))
.map_err(|e| CodegenError::Internal(format!("{}: tag gep: {:?}", name, e)))?;
self.builder()
.build_store(tag_ptr, i64_type.const_zero())
.map_err(|e| CodegenError::Internal(format!("{}: tag store: {:?}", name, e)))?;
// Store fst at offset 8
let field1_ptr = self
.builder()
.build_struct_gep(adt_ty, tuple_ptr, 1, &format!("{}_fst", name))
.map_err(|e| CodegenError::Internal(format!("{}: fst gep: {:?}", name, e)))?;
self.builder()
.build_store(field1_ptr, fst)
.map_err(|e| CodegenError::Internal(format!("{}: fst store: {:?}", name, e)))?;
// Store snd at offset 16 (raw GEP, same pattern as allocate_int_pair_tuple)
let field2_gep = unsafe {
self.builder()
.build_gep(
ptr_type,
tuple_ptr,
&[i64_type.const_int(2, false)],
&format!("{}_snd_gep", name),
)
.map_err(|e| CodegenError::Internal(format!("{}: snd gep: {:?}", name, e)))?
};
self.builder()
.build_store(field2_gep, snd)
.map_err(|e| CodegenError::Internal(format!("{}: snd store: {:?}", name, e)))?;
Ok(Some(tuple_ptr.into()))
}
/// Lower `curry f x y = f (x, y)`.
fn lower_builtin_curry(
&mut self,
f_expr: &Expr,
x_expr: &Expr,
y_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("curry: no f".to_string()))?;
let x_val = self
.lower_expr(x_expr)?
.ok_or_else(|| CodegenError::Internal("curry: no x".to_string()))?;
let y_val = self
.lower_expr(y_expr)?
.ok_or_else(|| CodegenError::Internal("curry: no y".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"curry: f must be closure".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
// Allocate tuple (x, y)
let x_ptr = self.value_to_ptr(x_val)?;
let y_ptr = self.value_to_ptr(y_val)?;
let tuple_val = self
.allocate_ptr_pair_tuple(x_ptr, y_ptr, "curry")?
.ok_or_else(|| CodegenError::Internal("curry: tuple alloc void".to_string()))?;
let tuple_ptr = tuple_val.into_pointer_value();
// Call f(tuple)
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[f_ptr.into(), tuple_ptr.into()],
"curry_result",
)
.map_err(|e| CodegenError::Internal(format!("curry: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("curry: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `uncurry f (x, y) = f x y`.
fn lower_builtin_uncurry(
&mut self,
f_expr: &Expr,
pair_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("uncurry: no f".to_string()))?;
let pair_val = self
.lower_expr(pair_expr)?
.ok_or_else(|| CodegenError::Internal("uncurry: no pair".to_string()))?;
let f_ptr = match f_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"uncurry: f must be closure".to_string(),
))
}
};
let pair_ptr = match pair_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"uncurry: pair must be tuple".to_string(),
))
}
};
// Extract fst and snd from pair
let fst_val = self.extract_adt_field(pair_ptr, 2, 0)?;
let snd_val = self.extract_adt_field(pair_ptr, 2, 1)?;
// Call f(fst, snd) — flat 3-arg call: (closure_env, arg1, arg2)
// BHC compiles 2-arg functions as flat fn(env, x, y) -> result
let ptr_type = self.type_mapper().ptr_type();
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[f_ptr.into(), fst_val.into(), snd_val.into()],
"uncurry_result",
)
.map_err(|e| CodegenError::Internal(format!("uncurry: call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("uncurry: result void".to_string()))?;
Ok(Some(result))
}
// ========================================================================
// Phase 2: Advanced List Operation Handlers (stubs and implementations)
// ========================================================================
/// Lower `any`.
fn lower_builtin_any(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower predicate closure and list
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("any: pred has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"any: pred must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("any: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("any expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "any_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "any_body");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "any_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "any_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "any_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("any: branch: {:?}", e)))?;
// Loop header: check if list is empty
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "any_list")
.map_err(|e| CodegenError::Internal(format!("any: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_nil")
.map_err(|e| CodegenError::Internal(format!("any: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, return_false, loop_body)
.map_err(|e| CodegenError::Internal(format!("any: branch: {:?}", e)))?;
// Loop body: extract head, call predicate, check result
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call pred(head)
let fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[pred_ptr.into(), head.into()], "any_call")
.map_err(|e| CodegenError::Internal(format!("any: call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("any: pred returned void".to_string()))?;
// Check if result is True (ADT tag != 0)
let result_int = match result {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::TypeError(
"any: pred should return Bool".to_string(),
))
}
};
let is_true = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, result_int, i64_zero, "pred_true")
.map_err(|e| CodegenError::Internal(format!("any: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, return_true, loop_header)
.map_err(|e| CodegenError::Internal(format!("any: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
let true_tag = self.type_mapper().i64_type().const_int(1, false);
let false_tag = self.type_mapper().i64_type().const_zero();
// Return True
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "any_true")?
.ok_or_else(|| CodegenError::Internal("any: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("any: branch: {:?}", e)))?;
// Return False
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "any_false")?
.ok_or_else(|| CodegenError::Internal("any: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("any: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "any_result")
.map_err(|e| CodegenError::Internal(format!("any: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `all`.
fn lower_builtin_all(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower predicate closure and list
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("all: pred has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"all: pred must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("all: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("all expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "all_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "all_body");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "all_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "all_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "all_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("all: branch: {:?}", e)))?;
// Loop header: check if list is empty
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "all_list")
.map_err(|e| CodegenError::Internal(format!("all: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_nil")
.map_err(|e| CodegenError::Internal(format!("all: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, return_true, loop_body)
.map_err(|e| CodegenError::Internal(format!("all: branch: {:?}", e)))?;
// Loop body: extract head, call predicate, check result
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call pred(head)
let fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[pred_ptr.into(), head.into()], "all_call")
.map_err(|e| CodegenError::Internal(format!("all: call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("all: pred returned void".to_string()))?;
// Check if result is False (ADT tag == 0) → short-circuit
let result_int = match result {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::TypeError(
"all: pred should return Bool".to_string(),
))
}
};
let is_false = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
result_int,
i64_zero,
"pred_false",
)
.map_err(|e| CodegenError::Internal(format!("all: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_false, return_false, loop_header)
.map_err(|e| CodegenError::Internal(format!("all: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
let true_tag = self.type_mapper().i64_type().const_int(1, false);
let false_tag = self.type_mapper().i64_type().const_zero();
// Return True (all elements passed)
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "all_true")?
.ok_or_else(|| CodegenError::Internal("all: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("all: branch: {:?}", e)))?;
// Return False (short-circuited)
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "all_false")?
.ok_or_else(|| CodegenError::Internal("all: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("all: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "all_result")
.map_err(|e| CodegenError::Internal(format!("all: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `and` - conjunction of a list of Bools. Short-circuits on False.
fn lower_builtin_and(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// and [] = True; and (x:xs) = if x then and xs else False
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("and: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("and expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let i64_zero = i64_type.const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "and_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "and_body");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "and_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "and_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "and_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("and: branch: {:?}", e)))?;
// Loop header: check if list is empty
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "and_list")
.map_err(|e| CodegenError::Internal(format!("and: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_nil")
.map_err(|e| CodegenError::Internal(format!("and: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, return_true, loop_body)
.map_err(|e| CodegenError::Internal(format!("and: branch: {:?}", e)))?;
// Loop body: extract head Bool, check its tag
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// head is a Bool ADT - extract its tag (0=False, 1=True)
let head_tag = self.extract_adt_tag(head)?;
let is_false = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head_tag, i64_zero, "is_false")
.map_err(|e| CodegenError::Internal(format!("and: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_false, return_false, loop_header)
.map_err(|e| CodegenError::Internal(format!("and: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
let true_tag = i64_type.const_int(1, false);
let false_tag = i64_type.const_zero();
// Return True (all elements were True)
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "and_true")?
.ok_or_else(|| CodegenError::Internal("and: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("and: branch: {:?}", e)))?;
// Return False (found a False element)
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "and_false")?
.ok_or_else(|| CodegenError::Internal("and: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("and: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "and_result")
.map_err(|e| CodegenError::Internal(format!("and: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `or` - disjunction of a list of Bools. Short-circuits on True.
fn lower_builtin_or(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// or [] = False; or (x:xs) = if x then True else or xs
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("or: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("or expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let i64_zero = i64_type.const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "or_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "or_body");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "or_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "or_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "or_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("or: branch: {:?}", e)))?;
// Loop header: check if list is empty
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "or_list")
.map_err(|e| CodegenError::Internal(format!("or: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_nil")
.map_err(|e| CodegenError::Internal(format!("or: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, return_false, loop_body)
.map_err(|e| CodegenError::Internal(format!("or: branch: {:?}", e)))?;
// Loop body: extract head Bool, check its tag
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// head is a Bool ADT - extract its tag (0=False, 1=True)
let head_tag = self.extract_adt_tag(head)?;
let is_true = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, head_tag, i64_zero, "is_true")
.map_err(|e| CodegenError::Internal(format!("or: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, return_true, loop_header)
.map_err(|e| CodegenError::Internal(format!("or: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
let true_tag = i64_type.const_int(1, false);
let false_tag = i64_type.const_zero();
// Return True (found a True element)
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "or_true")?
.ok_or_else(|| CodegenError::Internal("or: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("or: branch: {:?}", e)))?;
// Return False (all elements were False)
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "or_false")?
.ok_or_else(|| CodegenError::Internal("or: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("or: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "or_result")
.map_err(|e| CodegenError::Internal(format!("or: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `maximum` - find the maximum element in a non-empty list.
fn lower_builtin_maximum(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_max_min_impl(list_expr, inkwell::IntPredicate::SGT, "maximum")
}
/// Lower `minimum` - find the minimum element in a non-empty list.
fn lower_builtin_minimum(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_max_min_impl(list_expr, inkwell::IntPredicate::SLT, "minimum")
}
/// Shared implementation for `maximum` and `minimum`.
fn lower_builtin_max_min_impl(
&mut self,
list_expr: &Expr,
cmp_pred: inkwell::IntPredicate,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: list has no value", label)))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(format!("{} expects a list", label))),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let _entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Blocks: extract head as initial best, then loop over tail
let check_nonempty = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_check", label));
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_header", label));
let loop_body = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_body", label));
let loop_exit = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_exit", label));
self.builder()
.build_unconditional_branch(check_nonempty)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Check non-empty: extract head as initial best, tail as rest
self.builder().position_at_end(check_nonempty);
let head = self.extract_adt_field(list_ptr, 2, 0)?;
let tail = self.extract_adt_field(list_ptr, 2, 1)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Loop header: phi for best and current list
self.builder().position_at_end(loop_header);
let best_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_best", label))
.map_err(|e| CodegenError::Internal(format!("{}: phi: {:?}", label, e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_list", label))
.map_err(|e| CodegenError::Internal(format!("{}: phi: {:?}", label, e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("{}: cmp: {:?}", label, e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Loop body: extract head, compare with best
self.builder().position_at_end(loop_body);
let cur_head =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let cur_tail =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head_int = self
.builder()
.build_ptr_to_int(cur_head, i64_type, "head_int")
.map_err(|e| CodegenError::Internal(format!("{}: p2i: {:?}", label, e)))?;
let best_int = self
.builder()
.build_ptr_to_int(
best_phi.as_basic_value().into_pointer_value(),
i64_type,
"best_int",
)
.map_err(|e| CodegenError::Internal(format!("{}: p2i: {:?}", label, e)))?;
let is_better = self
.builder()
.build_int_compare(cmp_pred, head_int, best_int, "is_better")
.map_err(|e| CodegenError::Internal(format!("{}: cmp: {:?}", label, e)))?;
// Select new best
let new_best = self
.builder()
.build_select(
is_better,
cur_head,
best_phi.as_basic_value().into_pointer_value(),
"new_best",
)
.map_err(|e| CodegenError::Internal(format!("{}: select: {:?}", label, e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Phi incoming
best_phi.add_incoming(&[
(&head, check_nonempty),
(&new_best.into_pointer_value(), loop_body),
]);
list_phi.add_incoming(&[(&tail, check_nonempty), (&cur_tail, loop_body)]);
// Exit: return best
self.builder().position_at_end(loop_exit);
Ok(Some(best_phi.as_basic_value()))
}
/// Lower `elemIndex val xs` - return Just index if found, Nothing otherwise.
fn lower_builtin_elem_index(
&mut self,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val_val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("elemIndex: value has no value".to_string()))?;
let val_ptr = self.value_to_ptr(val_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("elemIndex: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"elemIndex expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "ei_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "ei_body");
let found_block = self.llvm_ctx.append_basic_block(current_fn, "ei_found");
let not_found_block = self.llvm_ctx.append_basic_block(current_fn, "ei_not_found");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "ei_merge");
let val_int = self
.builder()
.build_ptr_to_int(val_ptr, i64_type, "val_int")
.map_err(|e| CodegenError::Internal(format!("elemIndex: p2i: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("elemIndex: branch: {:?}", e)))?;
// Loop header: phi for list and counter
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "ei_list")
.map_err(|e| CodegenError::Internal(format!("elemIndex: phi: {:?}", e)))?;
let counter_phi = self
.builder()
.build_phi(i64_type, "ei_counter")
.map_err(|e| CodegenError::Internal(format!("elemIndex: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("elemIndex: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("elemIndex: branch: {:?}", e)))?;
// Loop body: compare head with val
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head_int = self
.builder()
.build_ptr_to_int(head, i64_type, "head_int")
.map_err(|e| CodegenError::Internal(format!("elemIndex: p2i: {:?}", e)))?;
let is_equal = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head_int, val_int, "is_eq")
.map_err(|e| CodegenError::Internal(format!("elemIndex: cmp: {:?}", e)))?;
let next_counter = self
.builder()
.build_int_add(
counter_phi.as_basic_value().into_int_value(),
i64_type.const_int(1, false),
"next_idx",
)
.map_err(|e| CodegenError::Internal(format!("elemIndex: add: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_equal, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("elemIndex: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
counter_phi.add_incoming(&[
(&i64_type.const_zero(), entry_block),
(&next_counter, loop_body),
]);
// Found: return Just counter (box counter as pointer, wrap in Just ADT)
self.builder().position_at_end(found_block);
let idx_ptr = self.int_to_ptr(counter_phi.as_basic_value().into_int_value())?;
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, idx_ptr.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("elemIndex: branch: {:?}", e)))?;
// Not found: return Nothing
self.builder().position_at_end(not_found_block);
let nothing = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("elemIndex: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "ei_result")
.map_err(|e| CodegenError::Internal(format!("elemIndex: phi: {:?}", e)))?;
result_phi.add_incoming(&[(&just, found_block), (¬hing, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `findIndex pred xs` - return Just index where pred is true, Nothing otherwise.
fn lower_builtin_find_index(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("findIndex: pred has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"findIndex: pred must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("findIndex: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"findIndex expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "fi_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "fi_body");
let found_block = self.llvm_ctx.append_basic_block(current_fn, "fi_found");
let not_found_block = self.llvm_ctx.append_basic_block(current_fn, "fi_not_found");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "fi_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("findIndex: branch: {:?}", e)))?;
// Loop header: phi for list and counter
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "fi_list")
.map_err(|e| CodegenError::Internal(format!("findIndex: phi: {:?}", e)))?;
let counter_phi = self
.builder()
.build_phi(i64_type, "fi_counter")
.map_err(|e| CodegenError::Internal(format!("findIndex: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("findIndex: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("findIndex: branch: {:?}", e)))?;
// Loop body: call predicate on head
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &[pred_ptr.into(), head.into()], "fi_call")
.map_err(|e| CodegenError::Internal(format!("findIndex: call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("findIndex: pred returned void".to_string()))?;
// Check Bool result tag
let result_tag = match pred_result {
BasicValueEnum::PointerValue(p) => self.extract_adt_tag(p)?,
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::TypeError(
"findIndex: pred should return Bool".to_string(),
))
}
};
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
result_tag,
i64_type.const_zero(),
"pred_true",
)
.map_err(|e| CodegenError::Internal(format!("findIndex: cmp: {:?}", e)))?;
let next_counter = self
.builder()
.build_int_add(
counter_phi.as_basic_value().into_int_value(),
i64_type.const_int(1, false),
"next_idx",
)
.map_err(|e| CodegenError::Internal(format!("findIndex: add: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("findIndex: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
counter_phi.add_incoming(&[
(&i64_type.const_zero(), entry_block),
(&next_counter, loop_body),
]);
// Found: return Just counter
self.builder().position_at_end(found_block);
let idx_ptr = self.int_to_ptr(counter_phi.as_basic_value().into_int_value())?;
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, idx_ptr.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("findIndex: branch: {:?}", e)))?;
// Not found: return Nothing
self.builder().position_at_end(not_found_block);
let nothing = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("findIndex: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "fi_result")
.map_err(|e| CodegenError::Internal(format!("findIndex: phi: {:?}", e)))?;
result_phi.add_incoming(&[(&just, found_block), (¬hing, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `isPrefixOf xs ys` - check if xs is a prefix of ys.
fn lower_builtin_is_prefix_of(
&mut self,
prefix_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let prefix_val = self
.lower_expr(prefix_expr)?
.ok_or_else(|| CodegenError::Internal("isPrefixOf: prefix has no value".to_string()))?;
let prefix_ptr = match prefix_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isPrefixOf expects a list".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("isPrefixOf: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isPrefixOf expects a list".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let i64_zero = i64_type.const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "ipf_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "ipf_body");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "ipf_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "ipf_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "ipf_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
// Loop header: phi for both lists
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "ipf_prefix")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "ipf_list")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: phi: {:?}", e)))?;
// If prefix is exhausted, return True
let prefix_tag = self.extract_adt_tag(prefix_phi.as_basic_value().into_pointer_value())?;
let prefix_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
prefix_tag,
i64_zero,
"prefix_nil",
)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(prefix_empty, return_true, loop_body)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
// Loop body: check if list is exhausted, then compare heads
self.builder().position_at_end(loop_body);
let list_tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let list_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, list_tag, i64_zero, "list_nil")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: cmp: {:?}", e)))?;
let compare_block = self.llvm_ctx.append_basic_block(current_fn, "ipf_compare");
self.builder()
.build_conditional_branch(list_empty, return_false, compare_block)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
// Compare heads
self.builder().position_at_end(compare_block);
let p_head =
self.extract_adt_field(prefix_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let p_tail =
self.extract_adt_field(prefix_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let l_head =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let l_tail =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let p_int = self
.builder()
.build_ptr_to_int(p_head, i64_type, "p_int")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: p2i: {:?}", e)))?;
let l_int = self
.builder()
.build_ptr_to_int(l_head, i64_type, "l_int")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: p2i: {:?}", e)))?;
let heads_eq = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, p_int, l_int, "heads_eq")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(heads_eq, loop_header, return_false)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
prefix_phi.add_incoming(&[(&prefix_ptr, entry_block), (&p_tail, compare_block)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&l_tail, compare_block)]);
let true_tag = i64_type.const_int(1, false);
let false_tag = i64_type.const_zero();
// Return True
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "ipf_true")?
.ok_or_else(|| CodegenError::Internal("isPrefixOf: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
// Return False
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "ipf_false")?
.ok_or_else(|| CodegenError::Internal("isPrefixOf: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "ipf_result")
.map_err(|e| CodegenError::Internal(format!("isPrefixOf: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `isSuffixOf xs ys` - reverse both lists, then check isPrefixOf.
fn lower_builtin_is_suffix_of(
&mut self,
suffix_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let suffix_val = self
.lower_expr(suffix_expr)?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: suffix has no value".to_string()))?;
let suffix_ptr = match suffix_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isSuffixOf expects a list".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isSuffixOf expects a list".to_string(),
))
}
};
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Reverse both lists
let rev_suffix = self
.build_inline_reverse(suffix_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: reverse suffix failed".to_string()))?
.into_pointer_value();
let rev_list = self
.build_inline_reverse(list_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: reverse list failed".to_string()))?
.into_pointer_value();
// Now do isPrefixOf logic inline on reversed lists
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let i64_zero = i64_type.const_zero();
let pre_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "isf_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "isf_body");
let compare_block = self.llvm_ctx.append_basic_block(current_fn, "isf_compare");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "isf_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "isf_false");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "isf_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "isf_prefix")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "isf_list")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: phi: {:?}", e)))?;
let prefix_tag = self.extract_adt_tag(prefix_phi.as_basic_value().into_pointer_value())?;
let prefix_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
prefix_tag,
i64_zero,
"prefix_nil",
)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(prefix_empty, return_true, loop_body)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let list_tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let list_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, list_tag, i64_zero, "list_nil")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(list_empty, return_false, compare_block)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
self.builder().position_at_end(compare_block);
let p_head =
self.extract_adt_field(prefix_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let p_tail =
self.extract_adt_field(prefix_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let l_head =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let l_tail =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let p_int = self
.builder()
.build_ptr_to_int(p_head, i64_type, "p_int")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: p2i: {:?}", e)))?;
let l_int = self
.builder()
.build_ptr_to_int(l_head, i64_type, "l_int")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: p2i: {:?}", e)))?;
let heads_eq = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, p_int, l_int, "heads_eq")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(heads_eq, loop_header, return_false)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
prefix_phi.add_incoming(&[(&rev_suffix, pre_block), (&p_tail, compare_block)]);
list_phi.add_incoming(&[(&rev_list, pre_block), (&l_tail, compare_block)]);
let true_tag = i64_type.const_int(1, false);
let false_tag = i64_type.const_zero();
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "isf_true")?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
self.builder().position_at_end(return_false);
let false_bool = self
.allocate_bool_adt(false_tag, "isf_false")?
.ok_or_else(|| CodegenError::Internal("isSuffixOf: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: branch: {:?}", e)))?;
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "isf_result")
.map_err(|e| CodegenError::Internal(format!("isSuffixOf: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `isInfixOf needle haystack` - check if needle appears anywhere in haystack.
fn lower_builtin_is_infix_of(
&mut self,
needle_expr: &Expr,
haystack_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let needle_val = self
.lower_expr(needle_expr)?
.ok_or_else(|| CodegenError::Internal("isInfixOf: needle has no value".to_string()))?;
let needle_ptr = match needle_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isInfixOf expects a list".to_string(),
))
}
};
let haystack_val = self.lower_expr(haystack_expr)?.ok_or_else(|| {
CodegenError::Internal("isInfixOf: haystack has no value".to_string())
})?;
let haystack_ptr = match haystack_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"isInfixOf expects a list".to_string(),
))
}
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let i64_zero = i64_type.const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let _entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// First check: if needle is empty, return True immediately
let needle_tag = self.extract_adt_tag(needle_ptr)?;
let needle_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
needle_tag,
i64_zero,
"needle_nil",
)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: cmp: {:?}", e)))?;
let needle_ok = self
.llvm_ctx
.append_basic_block(current_fn, "iif_needle_ok");
let return_true_early = self
.llvm_ctx
.append_basic_block(current_fn, "iif_true_early");
let outer_header = self.llvm_ctx.append_basic_block(current_fn, "iif_outer");
let inner_header = self.llvm_ctx.append_basic_block(current_fn, "iif_inner");
let inner_body = self
.llvm_ctx
.append_basic_block(current_fn, "iif_inner_body");
let inner_compare = self
.llvm_ctx
.append_basic_block(current_fn, "iif_inner_cmp");
let return_true = self.llvm_ctx.append_basic_block(current_fn, "iif_true");
let return_false = self.llvm_ctx.append_basic_block(current_fn, "iif_false");
let next_outer = self
.llvm_ctx
.append_basic_block(current_fn, "iif_next_outer");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "iif_merge");
self.builder()
.build_conditional_branch(needle_empty, return_true_early, needle_ok)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Return True early for empty needle
self.builder().position_at_end(return_true_early);
let true_tag = i64_type.const_int(1, false);
let true_early = self
.allocate_bool_adt(true_tag, "iif_true_early")?
.ok_or_else(|| CodegenError::Internal("isInfixOf: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Non-empty needle: outer loop over haystack
self.builder().position_at_end(needle_ok);
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
self.builder().position_at_end(outer_header);
let hay_phi = self
.builder()
.build_phi(ptr_type, "iif_hay")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: phi: {:?}", e)))?;
// Check if haystack exhausted
let hay_tag = self.extract_adt_tag(hay_phi.as_basic_value().into_pointer_value())?;
let hay_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, hay_tag, i64_zero, "hay_nil")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(hay_empty, return_false, inner_header)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Inner loop: isPrefixOf at current haystack position
self.builder().position_at_end(inner_header);
let n_phi = self
.builder()
.build_phi(ptr_type, "iif_n")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: phi: {:?}", e)))?;
let h_phi = self
.builder()
.build_phi(ptr_type, "iif_h")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: phi: {:?}", e)))?;
// If needle exhausted at this position, we found a match
let n_tag = self.extract_adt_tag(n_phi.as_basic_value().into_pointer_value())?;
let n_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, n_tag, i64_zero, "n_nil")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(n_empty, return_true, inner_body)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Inner body: check if haystack portion exhausted
self.builder().position_at_end(inner_body);
let h_tag = self.extract_adt_tag(h_phi.as_basic_value().into_pointer_value())?;
let h_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, h_tag, i64_zero, "h_nil")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(h_empty, next_outer, inner_compare)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Inner compare: compare heads
self.builder().position_at_end(inner_compare);
let n_head = self.extract_adt_field(n_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let n_tail = self.extract_adt_field(n_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let h_head = self.extract_adt_field(h_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let h_tail = self.extract_adt_field(h_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let n_int = self
.builder()
.build_ptr_to_int(n_head, i64_type, "n_int")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: p2i: {:?}", e)))?;
let h_int = self
.builder()
.build_ptr_to_int(h_head, i64_type, "h_int")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: p2i: {:?}", e)))?;
let heads_eq = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, n_int, h_int, "heads_eq")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(heads_eq, inner_header, next_outer)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
n_phi.add_incoming(&[(&needle_ptr, outer_header), (&n_tail, inner_compare)]);
h_phi.add_incoming(&[
(&hay_phi.as_basic_value().into_pointer_value(), outer_header),
(&h_tail, inner_compare),
]);
// Next outer: advance haystack by one
self.builder().position_at_end(next_outer);
let hay_tail =
self.extract_adt_field(hay_phi.as_basic_value().into_pointer_value(), 2, 1)?;
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
hay_phi.add_incoming(&[(&haystack_ptr, needle_ok), (&hay_tail, next_outer)]);
// Return True
self.builder().position_at_end(return_true);
let true_bool = self
.allocate_bool_adt(true_tag, "iif_true")?
.ok_or_else(|| CodegenError::Internal("isInfixOf: alloc true failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Return False
self.builder().position_at_end(return_false);
let false_tag = i64_type.const_zero();
let false_bool = self
.allocate_bool_adt(false_tag, "iif_false")?
.ok_or_else(|| CodegenError::Internal("isInfixOf: alloc false failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("isInfixOf: branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "iif_result")
.map_err(|e| CodegenError::Internal(format!("isInfixOf: phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&true_early.into_pointer_value(), return_true_early),
(&true_bool.into_pointer_value(), return_true),
(&false_bool.into_pointer_value(), return_false),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `tails xs` - return all suffixes of a list, including the empty list.
fn lower_builtin_tails(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("tails: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("tails expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Build result list in reverse: cons each suffix onto accumulator, then reverse
let nil = self.build_nil()?;
// First cons the empty list at the very end: we accumulate [xs, tail xs, ..., [], current_suffixes_reversed]
// Actually: walk list, at each position cons the current pointer onto acc. Then cons nil. Then reverse.
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "tails_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "tails_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "tails_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("tails: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "tails_list")
.map_err(|e| CodegenError::Internal(format!("tails: phi: {:?}", e)))?;
let acc_phi = self
.builder()
.build_phi(ptr_type, "tails_acc")
.map_err(|e| CodegenError::Internal(format!("tails: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("tails: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("tails: branch: {:?}", e)))?;
// Loop body: cons current list pointer onto acc, advance to tail
self.builder().position_at_end(loop_body);
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_cons = self.build_cons(
list_phi.as_basic_value().into_pointer_value().into(),
acc_phi.as_basic_value(),
)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("tails: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body)]);
acc_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
// Exit: cons the empty list (nil) onto acc, then reverse
self.builder().position_at_end(loop_exit);
let final_cons = self.build_cons(nil.into(), acc_phi.as_basic_value())?;
// Reverse the accumulated list
self.build_inline_reverse(final_cons, current_fn)
}
/// Lower `inits xs` - return all prefixes of a list, including the empty list.
fn lower_builtin_inits(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("inits: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("inits expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Strategy: walk list with a counter. For each position i (0..=length),
// build the prefix of length i by taking i elements from the original list.
// Accumulate results in reverse, then reverse at the end.
//
// Simpler approach: accumulate elements seen into a reversed list.
// At each step, reverse the reversed-elements to get the prefix, and cons onto result acc.
// This is O(n^2) but correct and simple.
let nil = self.build_nil()?;
// First, cons the empty list (nil) as the first init
let init_acc_start = self.build_cons(nil.into(), nil.into())?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "inits_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "inits_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "inits_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("inits: branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "inits_list")
.map_err(|e| CodegenError::Internal(format!("inits: phi: {:?}", e)))?;
let elems_phi = self
.builder()
.build_phi(ptr_type, "inits_elems")
.map_err(|e| CodegenError::Internal(format!("inits: phi: {:?}", e)))?;
let acc_phi = self
.builder()
.build_phi(ptr_type, "inits_acc")
.map_err(|e| CodegenError::Internal(format!("inits: phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("inits: cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("inits: branch: {:?}", e)))?;
// Loop body: add current head to reversed elements, reverse to get prefix, cons prefix onto acc
self.builder().position_at_end(loop_body);
let head = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail = self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// cons head onto reversed-elements (building reversed prefix)
let new_elems = self.build_cons(head.into(), elems_phi.as_basic_value())?;
let new_elems_ptr = new_elems; // build_cons returns PointerValue directly
// Reverse the reversed-elements to get the actual prefix
let prefix = self
.build_inline_reverse(new_elems_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("inits: reverse failed".to_string()))?;
// Cons prefix onto accumulator
let new_acc = self.build_cons(prefix, acc_phi.as_basic_value())?;
// Get the current block before branching (build_inline_reverse changed it)
let loop_body_end = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("inits: branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail, loop_body_end)]);
elems_phi.add_incoming(&[(&nil, entry_block), (&new_elems_ptr, loop_body_end)]);
acc_phi.add_incoming(&[(&init_acc_start, entry_block), (&new_acc, loop_body_end)]);
// Exit: reverse the accumulated result
self.builder().position_at_end(loop_exit);
self.build_inline_reverse(acc_phi.as_basic_value().into_pointer_value(), current_fn)
}
/// Lower `maximumBy cmp xs` - find maximum element using comparison function.
fn lower_builtin_maximum_by(
&mut self,
cmp_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_extremum_by(cmp_expr, list_expr, 2, "maximumBy")
}
/// Lower `minimumBy cmp xs` - find minimum element using comparison function.
fn lower_builtin_minimum_by(
&mut self,
cmp_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_extremum_by(cmp_expr, list_expr, 0, "minimumBy")
}
/// Shared implementation for `maximumBy` and `minimumBy`.
/// `update_tag` is 2 for GT (maximumBy) or 0 for LT (minimumBy).
fn lower_builtin_extremum_by(
&mut self,
cmp_expr: &Expr,
list_expr: &Expr,
update_tag: u64,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let cmp_val = self
.lower_expr(cmp_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: cmp has no value", label)))?;
let cmp_ptr = match cmp_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(format!(
"{}: cmp must be a closure",
label
)))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: list has no value", label)))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(format!("{} expects a list", label))),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Extract head as initial best
let check_block = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_check", label));
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_header", label));
let loop_body = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_body", label));
let loop_exit = self
.llvm_ctx
.append_basic_block(current_fn, &format!("{}_exit", label));
self.builder()
.build_unconditional_branch(check_block)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
self.builder().position_at_end(check_block);
let head = self.extract_adt_field(list_ptr, 2, 0)?;
let tail = self.extract_adt_field(list_ptr, 2, 1)?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let best_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_best", label))
.map_err(|e| CodegenError::Internal(format!("{}: phi: {:?}", label, e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, &format!("{}_list", label))
.map_err(|e| CodegenError::Internal(format!("{}: phi: {:?}", label, e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("{}: cmp: {:?}", label, e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
// Loop body: extract head, call cmp(head, best), check ordering
self.builder().position_at_end(loop_body);
let cur_head =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let cur_tail =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call cmp(cur_head, best) - cmp is a flat 2-arg closure: (env, arg1, arg2) -> Ordering
let cmp_fn_ptr = self.extract_closure_fn_ptr(cmp_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let ordering_result = self
.builder()
.build_indirect_call(
fn_type,
cmp_fn_ptr,
&[
cmp_ptr.into(),
cur_head.into(),
best_phi.as_basic_value().into_pointer_value().into(),
],
&format!("{}_ord", label),
)
.map_err(|e| CodegenError::Internal(format!("{}: call: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: cmp returned void", label)))?;
// Check Ordering ADT tag: 0=LT, 1=EQ, 2=GT
let ord_tag = self.extract_adt_tag(ordering_result.into_pointer_value())?;
let should_update = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
ord_tag,
i64_type.const_int(update_tag, false),
"should_update",
)
.map_err(|e| CodegenError::Internal(format!("{}: cmp: {:?}", label, e)))?;
let new_best = self
.builder()
.build_select(
should_update,
cur_head,
best_phi.as_basic_value().into_pointer_value(),
"new_best",
)
.map_err(|e| CodegenError::Internal(format!("{}: select: {:?}", label, e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("{}: branch: {:?}", label, e)))?;
best_phi.add_incoming(&[
(&head, check_block),
(&new_best.into_pointer_value(), loop_body),
]);
list_phi.add_incoming(&[(&tail, check_block), (&cur_tail, loop_body)]);
// Exit: return best
self.builder().position_at_end(loop_exit);
Ok(Some(best_phi.as_basic_value()))
}
/// Lower `elem`.
fn lower_builtin_elem(
&mut self,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the value to search for
let val_val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("elem: value has no value".to_string()))?;
let val_ptr = self.value_to_ptr(val_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("elem: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("elem expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "elem_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "elem_body");
let found_block = self.llvm_ctx.append_basic_block(current_fn, "elem_found");
let not_found_block = self
.llvm_ctx
.append_basic_block(current_fn, "elem_not_found");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "elem_merge");
// Convert search value to i64 for comparison
let val_int = self
.builder()
.build_ptr_to_int(val_ptr, tm.i64_type(), "val_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "elem_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract head, compare via ptr_to_int
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head_int = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "head_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let is_equal = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head_int, val_int, "is_equal")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_equal, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Found: return True = int_to_ptr(1)
self.builder().position_at_end(found_block);
let true_val = self.int_to_ptr(tm.i64_type().const_int(1, false))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found: return False = int_to_ptr(0)
self.builder().position_at_end(not_found_block);
let false_val = self.int_to_ptr(tm.i64_type().const_zero())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "elem_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
result_phi.add_incoming(&[(&true_val, found_block), (&false_val, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `notElem :: Eq a => a -> [a] -> Bool`.
/// Inverse of elem: returns True if element is NOT in the list.
fn lower_builtin_not_elem(
&mut self,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the value to search for
let val_val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("notElem: value has no value".to_string()))?;
let val_ptr = self.value_to_ptr(val_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("notElem: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"notElem expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "notelem_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "notelem_body");
let found_block = self
.llvm_ctx
.append_basic_block(current_fn, "notelem_found");
let not_found_block = self
.llvm_ctx
.append_basic_block(current_fn, "notelem_not_found");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "notelem_merge");
// Convert search value to i64 for comparison
let val_int = self
.builder()
.build_ptr_to_int(val_ptr, tm.i64_type(), "val_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "notelem_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract head, compare via ptr_to_int
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head_int = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "head_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let is_equal = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head_int, val_int, "is_equal")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_equal, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Found: element IS in list, so notElem returns False = int_to_ptr(0)
self.builder().position_at_end(found_block);
let false_val = self.int_to_ptr(tm.i64_type().const_zero())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found: element is NOT in list, so notElem returns True = int_to_ptr(1)
self.builder().position_at_end(not_found_block);
let true_val = self.int_to_ptr(tm.i64_type().const_int(1, false))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "notelem_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
result_phi.add_incoming(&[(&false_val, found_block), (&true_val, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `iterate` (stub: single-element list).
fn lower_builtin_iterate(
&mut self,
_func_expr: &Expr,
seed_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let _ = self.lower_expr(_func_expr)?;
let seed_val = self
.lower_expr(seed_expr)?
.ok_or_else(|| CodegenError::Internal("iterate: no seed".to_string()))?;
let seed_ptr = self.value_to_ptr(seed_val)?;
let nil = self.alloc_adt(0, 0)?;
let cons = self.alloc_adt(1, 2)?;
self.store_adt_field(cons, 2, 0, seed_ptr.into())?;
self.store_adt_field(cons, 2, 1, nil.into())?;
Ok(Some(cons.into()))
}
/// Lower `repeat` (stub).
fn lower_builtin_repeat(
&mut self,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_iterate(val_expr, val_expr)
}
/// Lower `cycle` (stub).
fn lower_builtin_cycle(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_expr(list_expr)
}
/// Lower `takeWhile` — take longest prefix where predicate holds.
fn lower_builtin_take_while(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("takeWhile: no predicate".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"takeWhile: predicate must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("takeWhile: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"takeWhile expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "tw_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "tw_body");
let tw_keep = self.llvm_ctx.append_basic_block(current_fn, "tw_keep");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "tw_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "tw_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "tw_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_tag = self.extract_bool_tag(pred_result.into_pointer_value())?;
// Capture actual block after extract_bool_tag (may have created new blocks)
let pred_check_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("takeWhile: no block after bool check".to_string())
})?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
// If pred is true, keep; if false, stop
self.builder()
.build_conditional_branch(is_true, tw_keep, loop_exit)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(tw_keep);
let new_cons = self.build_cons(head_ptr.into(), acc_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_cons, tw_keep)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, tw_keep)]);
// Exit: reverse accumulated prefix
self.builder().position_at_end(loop_exit);
let acc_at_exit = self
.builder()
.build_phi(ptr_type, "tw_acc_exit")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
acc_at_exit.add_incoming(&[
(&acc_phi.as_basic_value(), loop_header),
(&acc_phi.as_basic_value(), pred_check_block),
]);
// Inline reverse
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "tw_rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "tw_rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "tw_rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&acc_at_exit.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `dropWhile` — drop longest prefix where predicate holds.
fn lower_builtin_drop_while(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("dropWhile: no predicate".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"dropWhile: predicate must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("dropWhile: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"dropWhile expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "dw_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "dw_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "dw_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "dw_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_tag = self.extract_bool_tag(pred_result.into_pointer_value())?;
// Capture actual block after extract_bool_tag (may have created new blocks)
let pred_check_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("dropWhile: no block after bool check".to_string())
})?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
// If pred true, continue dropping (advance to tail); if false, stop
self.builder()
.build_conditional_branch(is_true, loop_header, loop_exit)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Note: when branching back from body, we use tail_ptr
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, pred_check_block)]);
// Exit: return the remaining list
self.builder().position_at_end(loop_exit);
let result_phi = self
.builder()
.build_phi(ptr_type, "dw_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
result_phi.add_incoming(&[
(&list_phi.as_basic_value(), loop_header), // empty list case
(&list_phi.as_basic_value(), pred_check_block), // pred became false case
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `span :: (a -> Bool) -> [a] -> ([a], [a])`.
/// Takes the longest prefix of elements satisfying the predicate.
/// Returns (prefix, rest) as a pair ADT (tag=0, arity=2).
fn lower_builtin_span(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower predicate closure
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("span: predicate has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"span: predicate must be a closure".to_string(),
))
}
};
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("span: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("span expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "span_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "span_body");
let span_keep = self.llvm_ctx.append_basic_block(current_fn, "span_keep");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "span_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header: phi for reversed prefix and current list
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "span_prefix")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "span_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: test predicate on head
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call predicate
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_tag = self.extract_bool_tag(pred_result.into_pointer_value())?;
// Capture actual block after extract_bool_tag (may have created new blocks)
let pred_check_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("span: no block after bool check".to_string()))?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, span_keep, loop_exit)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Keep block: cons head onto prefix, continue with tail
self.builder().position_at_end(span_keep);
let new_cons = self.build_cons(head_ptr.into(), prefix_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Add phi incoming values
prefix_phi.add_incoming(&[(&nil, entry_block), (&new_cons, span_keep)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, span_keep)]);
// Exit: we have reversed prefix and rest = current list_phi
self.builder().position_at_end(loop_exit);
// Phi nodes for values arriving at exit from header (empty list) or pred_check_block (pred false)
let rest_phi = self
.builder()
.build_phi(ptr_type, "span_rest")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
rest_phi.add_incoming(&[
(&list_phi.as_basic_value(), loop_header),
(&list_phi.as_basic_value(), pred_check_block),
]);
let prefix_at_exit = self
.builder()
.build_phi(ptr_type, "span_prefix_exit")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
prefix_at_exit.add_incoming(&[
(&prefix_phi.as_basic_value(), loop_header),
(&prefix_phi.as_basic_value(), pred_check_block),
]);
// Inline reverse of prefix
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "span_rev_header");
let rev_body = self
.llvm_ctx
.append_basic_block(current_fn, "span_rev_body");
let rev_exit = self
.llvm_ctx
.append_basic_block(current_fn, "span_rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&prefix_at_exit.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
// Build pair (reversed_prefix, rest) as ADT (tag=0, arity=2)
self.builder().position_at_end(rev_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, rev_acc.as_basic_value())?;
self.store_adt_field(pair, 2, 1, rest_phi.as_basic_value())?;
Ok(Some(pair.into()))
}
/// Lower `break` — split list at first element where predicate is True.
/// break p xs = span (not . p) xs
/// Takes prefix where pred is False, rest starts where pred becomes True.
fn lower_builtin_break(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("break: no predicate".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"break: predicate must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("break: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("break expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "break_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "break_body");
let break_keep = self.llvm_ctx.append_basic_block(current_fn, "break_keep");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "break_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "break_prefix")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "break_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: test predicate
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_tag = self.extract_bool_tag(pred_result.into_pointer_value())?;
// Capture actual block after extract_bool_tag (may have created new blocks)
let pred_check_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("break: no block after bool check".to_string())
})?;
// break stops when pred is TRUE (opposite of span)
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
// If pred is true, exit (break found). If false, keep going.
self.builder()
.build_conditional_branch(is_true, loop_exit, break_keep)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Keep block: pred is false, cons head onto prefix
self.builder().position_at_end(break_keep);
let new_cons = self.build_cons(head_ptr.into(), prefix_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
prefix_phi.add_incoming(&[(&nil, entry_block), (&new_cons, break_keep)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, break_keep)]);
// Exit
self.builder().position_at_end(loop_exit);
let rest_phi = self
.builder()
.build_phi(ptr_type, "break_rest")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
rest_phi.add_incoming(&[
(&list_phi.as_basic_value(), loop_header),
(&list_phi.as_basic_value(), pred_check_block),
]);
let prefix_at_exit = self
.builder()
.build_phi(ptr_type, "break_prefix_exit")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
prefix_at_exit.add_incoming(&[
(&prefix_phi.as_basic_value(), loop_header),
(&prefix_phi.as_basic_value(), pred_check_block),
]);
// Inline reverse prefix
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "brk_rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "brk_rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "brk_rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&prefix_at_exit.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, rev_acc.as_basic_value())?;
self.store_adt_field(pair, 2, 1, rest_phi.as_basic_value())?;
Ok(Some(pair.into()))
}
/// Lower `splitAt` — split list at position n.
/// splitAt n xs = (take n xs, drop n xs)
fn lower_builtin_split_at(
&mut self,
n_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n_val = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("splitAt: no n".to_string()))?;
let n_int = self.coerce_to_int(n_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("splitAt: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"splitAt expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "splitat_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "splitat_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "splitat_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "splitat_prefix")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "splitat_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let counter_phi = self
.builder()
.build_phi(i64_type, "splitat_counter")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if counter reached n or list is empty
let counter_done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SGE,
counter_phi.as_basic_value().into_int_value(),
n_int,
"counter_done",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_type.const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let should_stop = self
.builder()
.build_or(counter_done, is_empty, "should_stop")
.map_err(|e| CodegenError::Internal(format!("or: {:?}", e)))?;
self.builder()
.build_conditional_branch(should_stop, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: take one element
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let new_cons = self.build_cons(head_ptr.into(), prefix_phi.as_basic_value())?;
let next_counter = self
.builder()
.build_int_add(
counter_phi.as_basic_value().into_int_value(),
i64_type.const_int(1, false),
"next_counter",
)
.map_err(|e| CodegenError::Internal(format!("add: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
prefix_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
counter_phi.add_incoming(&[
(&i64_type.const_zero(), entry_block),
(&next_counter, loop_body),
]);
// Exit: reverse prefix, rest = current list
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "sa_rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "sa_rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "sa_rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
i64_type.const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&prefix_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, rev_acc.as_basic_value())?;
self.store_adt_field(pair, 2, 1, list_phi.as_basic_value())?;
Ok(Some(pair.into()))
}
/// Lower `find :: (a -> Bool) -> [a] -> Maybe a`.
/// Traverses the list, applying predicate to each element.
/// Returns Just element for the first match, or Nothing if none found.
fn lower_builtin_find(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower predicate closure
let pred_val = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("find: predicate has no value".to_string()))?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"find: predicate must be a closure".to_string(),
))
}
};
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("find: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("find expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "find_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "find_body");
let found_block = self.llvm_ctx.append_basic_block(current_fn, "find_found");
let not_found_block = self
.llvm_ctx
.append_basic_block(current_fn, "find_not_found");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "find_merge");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header: phi for current list pointer
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "find_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if list is empty (tag == 0 means nil)
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract head, apply predicate
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call predicate: pred(pred_ptr, head)
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
// Check boolean result
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
let pred_bool = self.extract_bool_tag(pred_result.into_pointer_value())?;
// Capture actual block after extract_bool_tag (may have created new blocks)
let pred_check_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("find: no block after bool check".to_string()))?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_bool,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Add phi incoming for list_phi
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, pred_check_block)]);
// Found block: return Just element (tag=1, arity=1)
self.builder().position_at_end(found_block);
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, head_ptr.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found block: return Nothing (tag=0, arity=0)
self.builder().position_at_end(not_found_block);
let nothing = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Merge block: phi to select Just or Nothing
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "find_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
result_phi.add_incoming(&[(&just, found_block), (¬hing, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `lookup :: Eq k => k -> [(k,v)] -> Maybe v`.
/// Traverses an association list (list of pairs). Each head element is a pair
/// ADT (tag=0, arity=2) with field 0 = key, field 1 = value.
/// Compares key via ptr_to_int equality.
fn lower_builtin_lookup(
&mut self,
key_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the key
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("lookup: key has no value".to_string()))?;
let key_ptr = self.value_to_ptr(key_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("lookup: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("lookup expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "lookup_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "lookup_body");
let found_block = self.llvm_ctx.append_basic_block(current_fn, "lookup_found");
let not_found_block = self
.llvm_ctx
.append_basic_block(current_fn, "lookup_not_found");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "lookup_merge");
// Convert search key to i64 for comparison
let key_int = self
.builder()
.build_ptr_to_int(key_ptr, tm.i64_type(), "key_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let list_phi = self
.builder()
.build_phi(ptr_type, "lookup_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, not_found_block, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract head (a pair), then extract key and value from it
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// head_ptr is a pair (tag=0, arity=2): extract field 0 (key) and field 1 (value)
let pair_key = self.extract_adt_field(head_ptr, 2, 0)?;
let pair_val = self.extract_adt_field(head_ptr, 2, 1)?;
// Compare pair key with search key
let pair_key_int = self
.builder()
.build_ptr_to_int(pair_key, tm.i64_type(), "pair_key_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let is_match = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, pair_key_int, key_int, "is_match")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_match, found_block, loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Add phi incoming
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Found: return Just value (tag=1, arity=1)
self.builder().position_at_end(found_block);
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, pair_val.into())?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found: return Nothing (tag=0, arity=0)
self.builder().position_at_end(not_found_block);
let nothing = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "lookup_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
result_phi.add_incoming(&[(&just, found_block), (¬hing, not_found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `partition :: (a -> Bool) -> [a] -> ([a], [a])`.
/// Splits the list into (elements satisfying pred, elements not satisfying).
/// Both sublists built in reverse during traversal, then reversed.
fn lower_builtin_partition(
&mut self,
pred_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower predicate closure
let pred_val = self.lower_expr(pred_expr)?.ok_or_else(|| {
CodegenError::Internal("partition: predicate has no value".to_string())
})?;
let pred_ptr = match pred_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"partition: predicate must be a closure".to_string(),
))
}
};
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("partition: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"partition expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "part_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "part_body");
let part_yes = self.llvm_ctx.append_basic_block(current_fn, "part_yes");
let part_no = self.llvm_ctx.append_basic_block(current_fn, "part_no");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "part_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header: phi for yes_list (reversed), no_list (reversed), current list
self.builder().position_at_end(loop_header);
let yes_phi = self
.builder()
.build_phi(ptr_type, "part_yes_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let no_phi = self
.builder()
.build_phi(ptr_type, "part_no_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "part_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: test predicate
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call predicate
let pred_fn_ptr = self.extract_closure_fn_ptr(pred_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pred_result = self
.builder()
.build_indirect_call(
fn_type,
pred_fn_ptr,
&[pred_ptr.into(), head_ptr.into()],
"pred_result",
)
.map_err(|e| CodegenError::Internal(format!("call predicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("predicate returned void".to_string()))?;
let pred_bool = self
// Handle both tagged-int-as-pointer (0/1) and Bool ADT (heap struct with tag)
.extract_bool_tag(pred_result.into_pointer_value())?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_bool,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, part_yes, part_no)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Yes block: cons head onto yes_list
self.builder().position_at_end(part_yes);
let yes_new_cons = self.build_cons(head_ptr.into(), yes_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// No block: cons head onto no_list
self.builder().position_at_end(part_no);
let no_new_cons = self.build_cons(head_ptr.into(), no_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Add phi incoming values
yes_phi.add_incoming(&[
(&nil, entry_block),
(&yes_new_cons, part_yes),
(&yes_phi.as_basic_value(), part_no),
]);
no_phi.add_incoming(&[
(&nil, entry_block),
(&no_phi.as_basic_value(), part_yes),
(&no_new_cons, part_no),
]);
list_phi.add_incoming(&[
(&list_ptr, entry_block),
(&tail_ptr, part_yes),
(&tail_ptr, part_no),
]);
// Exit: reverse both lists
self.builder().position_at_end(loop_exit);
// --- Reverse yes_list ---
let rev1_header = self.llvm_ctx.append_basic_block(current_fn, "rev1_header");
let rev1_body = self.llvm_ctx.append_basic_block(current_fn, "rev1_body");
let rev1_exit = self.llvm_ctx.append_basic_block(current_fn, "rev1_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev1_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev1_header);
let rev1_acc = self
.builder()
.build_phi(ptr_type, "rev1_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev1_list = self
.builder()
.build_phi(ptr_type, "rev1_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev1_tag = self.extract_adt_tag(rev1_list.as_basic_value().into_pointer_value())?;
let rev1_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev1_tag,
tm.i64_type().const_zero(),
"rev1_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev1_is_empty, rev1_exit, rev1_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev1_body);
let rev1_head =
self.extract_adt_field(rev1_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev1_tail =
self.extract_adt_field(rev1_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev1_new_cons = self.build_cons(rev1_head.into(), rev1_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev1_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev1_acc.add_incoming(&[(&nil2, loop_exit), (&rev1_new_cons, rev1_body)]);
rev1_list.add_incoming(&[
(&yes_phi.as_basic_value(), loop_exit),
(&rev1_tail, rev1_body),
]);
// --- Reverse no_list ---
self.builder().position_at_end(rev1_exit);
let rev2_header = self.llvm_ctx.append_basic_block(current_fn, "rev2_header");
let rev2_body = self.llvm_ctx.append_basic_block(current_fn, "rev2_body");
let rev2_exit = self.llvm_ctx.append_basic_block(current_fn, "rev2_exit");
let nil3 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev2_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev2_header);
let rev2_acc = self
.builder()
.build_phi(ptr_type, "rev2_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev2_list = self
.builder()
.build_phi(ptr_type, "rev2_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev2_tag = self.extract_adt_tag(rev2_list.as_basic_value().into_pointer_value())?;
let rev2_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev2_tag,
tm.i64_type().const_zero(),
"rev2_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev2_is_empty, rev2_exit, rev2_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev2_body);
let rev2_head =
self.extract_adt_field(rev2_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev2_tail =
self.extract_adt_field(rev2_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev2_new_cons = self.build_cons(rev2_head.into(), rev2_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev2_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev2_acc.add_incoming(&[(&nil3, rev1_exit), (&rev2_new_cons, rev2_body)]);
rev2_list.add_incoming(&[
(&no_phi.as_basic_value(), rev1_exit),
(&rev2_tail, rev2_body),
]);
// Build pair (reversed_yes, reversed_no) as ADT (tag=0, arity=2)
self.builder().position_at_end(rev2_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, rev1_acc.as_basic_value())?;
self.store_adt_field(pair, 2, 1, rev2_acc.as_basic_value())?;
Ok(Some(pair.into()))
}
/// Lower `intersperse :: a -> [a] -> [a]`.
/// Inserts separator between each element. Empty/singleton lists unchanged.
fn lower_builtin_intersperse(
&mut self,
sep_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the separator
let sep_val = self.lower_expr(sep_expr)?.ok_or_else(|| {
CodegenError::Internal("intersperse: separator has no value".to_string())
})?;
let sep_ptr = self.value_to_ptr(sep_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("intersperse: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"intersperse expects a list".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Check if list is empty first
let empty_block = self.llvm_ctx.append_basic_block(current_fn, "inter_empty");
let first_block = self.llvm_ctx.append_basic_block(current_fn, "inter_first");
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "inter_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "inter_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "inter_exit");
let final_merge = self.llvm_ctx.append_basic_block(current_fn, "inter_merge");
let tag = self.extract_adt_tag(list_ptr)?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, empty_block, first_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Empty list: return nil
self.builder().position_at_end(empty_block);
let empty_nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(final_merge)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// First element: extract head, start reversed result with just head
self.builder().position_at_end(first_block);
let first_head = self.extract_adt_field(list_ptr, 2, 0)?;
let first_tail = self.extract_adt_field(list_ptr, 2, 1)?;
let nil = self.build_nil()?;
let first_cons = self.build_cons(first_head.into(), nil.into())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header: process remaining elements, prepending sep then element
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "inter_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "inter_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let loop_tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let loop_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
loop_tag,
tm.i64_type().const_zero(),
"loop_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(loop_is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: cons element, then cons sep (building reversed)
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// In reverse: cons element first, then sep on top
let with_elem = self.build_cons(head_ptr.into(), result_phi.as_basic_value())?;
let with_sep = self.build_cons(sep_ptr.into(), with_elem.into())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
result_phi.add_incoming(&[(&first_cons, first_block), (&with_sep, loop_body)]);
list_phi.add_incoming(&[(&first_tail, first_block), (&tail_ptr, loop_body)]);
// Loop exit: reverse the result
self.builder().position_at_end(loop_exit);
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
// After reverse, branch to final merge
self.builder().position_at_end(rev_exit);
self.builder()
.build_unconditional_branch(final_merge)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Final merge: either empty nil or reversed result
self.builder().position_at_end(final_merge);
let final_phi = self
.builder()
.build_phi(ptr_type, "inter_final")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
final_phi.add_incoming(&[
(&empty_nil, empty_block),
(&rev_acc.as_basic_value(), rev_exit),
]);
Ok(Some(final_phi.as_basic_value()))
}
/// Lower `intercalate` — call RTS bhc_list_intercalate.
fn lower_builtin_intercalate(
&mut self,
sep_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let sep_val = self
.lower_expr(sep_expr)?
.ok_or_else(|| CodegenError::Internal("intercalate: no sep".to_string()))?;
let sep_ptr = self.value_to_ptr(sep_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("intercalate: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self.functions.get(&VarId::new(1000174)).ok_or_else(|| {
CodegenError::Internal("bhc_list_intercalate not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[sep_ptr.into(), list_ptr.into()], "intercalate")
.map_err(|e| CodegenError::Internal(format!("intercalate call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("intercalate: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `transpose` — call RTS bhc_list_transpose.
fn lower_builtin_transpose(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("transpose: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000175))
.ok_or_else(|| CodegenError::Internal("bhc_list_transpose not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[list_ptr.into()], "transpose")
.map_err(|e| CodegenError::Internal(format!("transpose call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("transpose: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `nub` — call RTS bhc_list_nub.
fn lower_builtin_nub(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("nub: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000172))
.ok_or_else(|| CodegenError::Internal("bhc_list_nub not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[list_ptr.into()], "nub")
.map_err(|e| CodegenError::Internal(format!("nub call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("nub: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `sort` — call RTS bhc_list_sort.
fn lower_builtin_sort(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("sort: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000170))
.ok_or_else(|| CodegenError::Internal("bhc_list_sort not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[list_ptr.into()], "sort")
.map_err(|e| CodegenError::Internal(format!("sort call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sort: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `sortBy` — call RTS bhc_list_sort_by.
fn lower_builtin_sort_by(
&mut self,
cmp_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let cmp_val = self
.lower_expr(cmp_expr)?
.ok_or_else(|| CodegenError::Internal("sortBy: no cmp".to_string()))?;
let cmp_ptr = self.value_to_ptr(cmp_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("sortBy: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000171))
.ok_or_else(|| CodegenError::Internal("bhc_list_sort_by not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[cmp_ptr.into(), list_ptr.into()], "sort_by")
.map_err(|e| CodegenError::Internal(format!("sort_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sort_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `group` — call RTS bhc_list_group.
fn lower_builtin_group(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("group: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000173))
.ok_or_else(|| CodegenError::Internal("bhc_list_group not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[list_ptr.into()], "group")
.map_err(|e| CodegenError::Internal(format!("group call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("group: returned void".to_string()))?;
Ok(Some(result))
}
// ---- E.26: *By variants, sortOn, stripPrefix, insert, mapAccumL/R ----
/// Lower `sortOn` — call RTS bhc_list_sort_on.
fn lower_builtin_sort_on(
&mut self,
key_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("sortOn: no key fn".to_string()))?;
let key_ptr = self.value_to_ptr(key_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("sortOn: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000550))
.ok_or_else(|| CodegenError::Internal("bhc_list_sort_on not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[key_ptr.into(), list_ptr.into()], "sort_on")
.map_err(|e| CodegenError::Internal(format!("sort_on call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sort_on: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `nubBy` — call RTS bhc_list_nub_by.
fn lower_builtin_nub_by(
&mut self,
eq_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let eq_val = self
.lower_expr(eq_expr)?
.ok_or_else(|| CodegenError::Internal("nubBy: no eq fn".to_string()))?;
let eq_ptr = self.value_to_ptr(eq_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("nubBy: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000551))
.ok_or_else(|| CodegenError::Internal("bhc_list_nub_by not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[eq_ptr.into(), list_ptr.into()], "nub_by")
.map_err(|e| CodegenError::Internal(format!("nub_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("nub_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `groupBy` — call RTS bhc_list_group_by.
fn lower_builtin_group_by(
&mut self,
eq_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let eq_val = self
.lower_expr(eq_expr)?
.ok_or_else(|| CodegenError::Internal("groupBy: no eq fn".to_string()))?;
let eq_ptr = self.value_to_ptr(eq_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("groupBy: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000552))
.ok_or_else(|| CodegenError::Internal("bhc_list_group_by not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[eq_ptr.into(), list_ptr.into()], "group_by")
.map_err(|e| CodegenError::Internal(format!("group_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("group_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `deleteBy` — call RTS bhc_list_delete_by.
fn lower_builtin_delete_by(
&mut self,
eq_expr: &Expr,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let eq_val = self
.lower_expr(eq_expr)?
.ok_or_else(|| CodegenError::Internal("deleteBy: no eq fn".to_string()))?;
let eq_ptr = self.value_to_ptr(eq_val)?;
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("deleteBy: no val".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("deleteBy: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000553))
.ok_or_else(|| CodegenError::Internal("bhc_list_delete_by not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[eq_ptr.into(), val_ptr.into(), list_ptr.into()],
"delete_by",
)
.map_err(|e| CodegenError::Internal(format!("delete_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("delete_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `unionBy` — call RTS bhc_list_union_by.
fn lower_builtin_union_by(
&mut self,
eq_expr: &Expr,
xs_expr: &Expr,
ys_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let eq_val = self
.lower_expr(eq_expr)?
.ok_or_else(|| CodegenError::Internal("unionBy: no eq fn".to_string()))?;
let eq_ptr = self.value_to_ptr(eq_val)?;
let xs_val = self
.lower_expr(xs_expr)?
.ok_or_else(|| CodegenError::Internal("unionBy: no xs".to_string()))?;
let xs_ptr = self.value_to_ptr(xs_val)?;
let ys_val = self
.lower_expr(ys_expr)?
.ok_or_else(|| CodegenError::Internal("unionBy: no ys".to_string()))?;
let ys_ptr = self.value_to_ptr(ys_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000554))
.ok_or_else(|| CodegenError::Internal("bhc_list_union_by not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[eq_ptr.into(), xs_ptr.into(), ys_ptr.into()],
"union_by",
)
.map_err(|e| CodegenError::Internal(format!("union_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("union_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `intersectBy` — call RTS bhc_list_intersect_by.
fn lower_builtin_intersect_by(
&mut self,
eq_expr: &Expr,
xs_expr: &Expr,
ys_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let eq_val = self
.lower_expr(eq_expr)?
.ok_or_else(|| CodegenError::Internal("intersectBy: no eq fn".to_string()))?;
let eq_ptr = self.value_to_ptr(eq_val)?;
let xs_val = self
.lower_expr(xs_expr)?
.ok_or_else(|| CodegenError::Internal("intersectBy: no xs".to_string()))?;
let xs_ptr = self.value_to_ptr(xs_val)?;
let ys_val = self
.lower_expr(ys_expr)?
.ok_or_else(|| CodegenError::Internal("intersectBy: no ys".to_string()))?;
let ys_ptr = self.value_to_ptr(ys_val)?;
let rts_fn = self.functions.get(&VarId::new(1000555)).ok_or_else(|| {
CodegenError::Internal("bhc_list_intersect_by not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[eq_ptr.into(), xs_ptr.into(), ys_ptr.into()],
"intersect_by",
)
.map_err(|e| CodegenError::Internal(format!("intersect_by call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("intersect_by: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `stripPrefix` — call RTS bhc_list_strip_prefix.
fn lower_builtin_strip_prefix(
&mut self,
prefix_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let prefix_val = self
.lower_expr(prefix_expr)?
.ok_or_else(|| CodegenError::Internal("stripPrefix: no prefix".to_string()))?;
let prefix_ptr = self.value_to_ptr(prefix_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("stripPrefix: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self.functions.get(&VarId::new(1000556)).ok_or_else(|| {
CodegenError::Internal("bhc_list_strip_prefix not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[prefix_ptr.into(), list_ptr.into()],
"strip_prefix",
)
.map_err(|e| CodegenError::Internal(format!("strip_prefix call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("strip_prefix: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `insert` — call RTS bhc_list_insert.
fn lower_builtin_insert(
&mut self,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("insert: no val".to_string()))?;
let val_ptr = self.value_to_ptr(val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("insert: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000557))
.ok_or_else(|| CodegenError::Internal("bhc_list_insert not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_ptr.into(), list_ptr.into()], "insert")
.map_err(|e| CodegenError::Internal(format!("insert call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("insert: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `mapAccumL` — call RTS bhc_list_map_accum_l.
fn lower_builtin_map_accum_l(
&mut self,
f_expr: &Expr,
acc_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumL: no fn".to_string()))?;
let f_ptr = self.value_to_ptr(f_val)?;
let acc_val = self
.lower_expr(acc_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumL: no acc".to_string()))?;
let acc_ptr = self.value_to_ptr(acc_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumL: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self.functions.get(&VarId::new(1000558)).ok_or_else(|| {
CodegenError::Internal("bhc_list_map_accum_l not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[f_ptr.into(), acc_ptr.into(), list_ptr.into()],
"map_accum_l",
)
.map_err(|e| CodegenError::Internal(format!("mapAccumL call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mapAccumL: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `mapAccumR` — call RTS bhc_list_map_accum_r.
fn lower_builtin_map_accum_r(
&mut self,
f_expr: &Expr,
acc_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let f_val = self
.lower_expr(f_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumR: no fn".to_string()))?;
let f_ptr = self.value_to_ptr(f_val)?;
let acc_val = self
.lower_expr(acc_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumR: no acc".to_string()))?;
let acc_ptr = self.value_to_ptr(acc_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("mapAccumR: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let rts_fn = self.functions.get(&VarId::new(1000559)).ok_or_else(|| {
CodegenError::Internal("bhc_list_map_accum_r not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[f_ptr.into(), acc_ptr.into(), list_ptr.into()],
"map_accum_r",
)
.map_err(|e| CodegenError::Internal(format!("mapAccumR call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mapAccumR: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `delete` — remove first occurrence of value from list.
/// delete x [] = []
/// delete x (y:ys) = if x == y then ys else y : delete x ys
fn lower_builtin_delete(
&mut self,
val_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("delete: no value".to_string()))?;
let val_int = self.coerce_to_int(val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("delete: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("delete expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Phase 1: scan for element, collecting prefix in reverse
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "del_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "del_body");
let del_found = self.llvm_ctx.append_basic_block(current_fn, "del_found");
let del_notfound = self.llvm_ctx.append_basic_block(current_fn, "del_notfound");
let del_merge = self.llvm_ctx.append_basic_block(current_fn, "del_merge");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let prefix_phi = self
.builder()
.build_phi(ptr_type, "del_prefix")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "del_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
// Empty list → element not in list, go to merge with nil suffix
self.builder()
.build_conditional_branch(is_empty, del_merge, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Body: compare element
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let head_int = self
.builder()
.build_ptr_to_int(head_ptr, tm.i64_type(), "head_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let is_match = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head_int, val_int, "is_match")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_match, del_found, del_notfound)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Found: result = reverse(prefix) ++ tail (skip matched element)
self.builder().position_at_end(del_found);
self.builder()
.build_unconditional_branch(del_merge)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found in this element: cons head onto prefix, continue with tail
self.builder().position_at_end(del_notfound);
let new_cons = self.build_cons(head_ptr.into(), prefix_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
prefix_phi.add_incoming(&[(&nil, entry_block), (&new_cons, del_notfound)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, del_notfound)]);
// Merge: reverse prefix onto suffix
// Reached from: loop_header (empty, suffix=nil) or del_found (suffix=tail)
self.builder().position_at_end(del_merge);
let suffix_phi = self
.builder()
.build_phi(ptr_type, "del_suffix")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
suffix_phi.add_incoming(&[
(&nil, loop_header), // empty list: no suffix
(&tail_ptr, del_found), // found: skip matched, suffix = tail
]);
let prefix_at_merge = self
.builder()
.build_phi(ptr_type, "del_prefix_merge")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
prefix_at_merge.add_incoming(&[
(&prefix_phi.as_basic_value(), loop_header),
(&prefix_phi.as_basic_value(), del_found),
]);
// Reverse prefix onto suffix
let rev_header = self
.llvm_ctx
.append_basic_block(current_fn, "del_rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "del_rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "del_rev_exit");
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let r_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let r_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let r_cons = self.build_cons(r_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Note: initial acc is the suffix (we reverse-prepend prefix onto it)
rev_acc.add_incoming(&[
(&suffix_phi.as_basic_value(), del_merge),
(&r_cons, rev_body),
]);
rev_list.add_incoming(&[
(&prefix_at_merge.as_basic_value(), del_merge),
(&r_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `union` (stub: append).
fn lower_builtin_union(
&mut self,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
self.lower_builtin_append(list1_expr, list2_expr)
}
/// Lower `intersect :: Eq a => [a] -> [a] -> [a]`.
/// For each element in xs, scan ys for a match (pointer equality on Int tags).
fn lower_builtin_intersect(
&mut self,
list1_expr: &Expr,
list2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("intersect: list1 has no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"intersect expects lists".to_string(),
))
}
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("intersect: list2 has no value".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"intersect expects lists".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
// Outer loop: iterate through xs
let outer_header = self
.llvm_ctx
.append_basic_block(current_fn, "isect_outer_hdr");
let outer_body = self
.llvm_ctx
.append_basic_block(current_fn, "isect_outer_body");
let outer_exit = self
.llvm_ctx
.append_basic_block(current_fn, "isect_outer_exit");
// Inner loop: scan ys for match
let inner_header = self
.llvm_ctx
.append_basic_block(current_fn, "isect_inner_hdr");
let inner_body = self
.llvm_ctx
.append_basic_block(current_fn, "isect_inner_body");
let inner_found = self.llvm_ctx.append_basic_block(current_fn, "isect_found");
let inner_next = self
.llvm_ctx
.append_basic_block(current_fn, "isect_not_found");
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Outer header
self.builder().position_at_end(outer_header);
let xs_phi = self
.builder()
.build_phi(ptr_type, "isect_xs")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "isect_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let xs_tag = self.extract_adt_tag(xs_phi.as_basic_value().into_pointer_value())?;
let xs_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
xs_tag,
tm.i64_type().const_zero(),
"xs_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(xs_empty, outer_exit, outer_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Outer body: get head of xs
self.builder().position_at_end(outer_body);
let x_head = self.extract_adt_field(xs_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let x_tail = self.extract_adt_field(xs_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Convert x_head to int for comparison (works for Int values)
let x_int = self
.builder()
.build_ptr_to_int(x_head, tm.i64_type(), "x_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
self.builder()
.build_unconditional_branch(inner_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Inner header: scan ys
self.builder().position_at_end(inner_header);
let ys_phi = self
.builder()
.build_phi(ptr_type, "isect_ys")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let ys_tag = self.extract_adt_tag(ys_phi.as_basic_value().into_pointer_value())?;
let ys_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
ys_tag,
tm.i64_type().const_zero(),
"ys_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(ys_empty, inner_next, inner_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Inner body: compare x_head with y_head
self.builder().position_at_end(inner_body);
let y_head = self.extract_adt_field(ys_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let y_tail = self.extract_adt_field(ys_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let y_int = self
.builder()
.build_ptr_to_int(y_head, tm.i64_type(), "y_int")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let eq = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, x_int, y_int, "eq")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(eq, inner_found, inner_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
ys_phi.add_incoming(&[(&list2_ptr, outer_body), (&y_tail, inner_body)]);
// Found: cons x_head onto result, continue outer
self.builder().position_at_end(inner_found);
let found_cons = self.build_cons(x_head.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Not found: skip, continue outer
self.builder().position_at_end(inner_next);
self.builder()
.build_unconditional_branch(outer_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
xs_phi.add_incoming(&[
(&list1_ptr, entry_block),
(&x_tail, inner_found),
(&x_tail, inner_next),
]);
result_phi.add_incoming(&[
(&nil, entry_block),
(&found_cons, inner_found),
(&result_phi.as_basic_value(), inner_next),
]);
// Reverse result
self.builder().position_at_end(outer_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `scanl :: (b -> a -> b) -> b -> [a] -> [b]`.
/// Produces a list starting with init, then applying f to accumulator and each element.
/// Output always starts with init even if input is empty.
/// Builds result in reverse, then reverses at end.
fn lower_builtin_scanl(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the function closure
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("scanl: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"scanl: function must be a closure".to_string(),
))
}
};
// Lower the initial value
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("scanl: init has no value".to_string()))?;
let init_ptr = self.value_to_ptr(init_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("scanl: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("scanl expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "scanl_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "scanl_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "scanl_exit");
// Build the initial reversed result: cons init onto nil
// (scanl always starts with init, even for empty input)
let nil = self.build_nil()?;
let init_cons = self.build_cons(init_ptr.into(), nil.into())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop header: phi for acc, list, and reversed result
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "scanl_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "scanl_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "scanl_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if list is empty
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: new_acc = f(acc, head); cons new_acc onto result
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f(func_ptr, acc, head) - 2-arg closure call
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
acc_phi.as_basic_value().into(),
head_ptr.into(),
],
"scanl_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("call function: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("scanl: function returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
// Cons new_acc onto the reversed result
let new_cons = self.build_cons(new_acc_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Add phi incoming values
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc_ptr, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
result_phi.add_incoming(&[(&init_cons, entry_block), (&new_cons, loop_body)]);
// Exit: reverse the accumulated result
self.builder().position_at_end(loop_exit);
// Inline reverse
let rev_header = self.llvm_ctx.append_basic_block(current_fn, "rev_header");
let rev_body = self.llvm_ctx.append_basic_block(current_fn, "rev_body");
let rev_exit = self.llvm_ctx.append_basic_block(current_fn, "rev_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_header);
let rev_acc = self
.builder()
.build_phi(ptr_type, "rev_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_list = self
.builder()
.build_phi(ptr_type, "rev_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev_tag = self.extract_adt_tag(rev_list.as_basic_value().into_pointer_value())?;
let rev_is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev_tag,
tm.i64_type().const_zero(),
"rev_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev_is_empty, rev_exit, rev_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev_body);
let rev_head =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 0)?;
let rev_tail =
self.extract_adt_field(rev_list.as_basic_value().into_pointer_value(), 2, 1)?;
let rev_new_cons = self.build_cons(rev_head.into(), rev_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev_acc.add_incoming(&[(&nil2, loop_exit), (&rev_new_cons, rev_body)]);
rev_list.add_incoming(&[
(&result_phi.as_basic_value(), loop_exit),
(&rev_tail, rev_body),
]);
self.builder().position_at_end(rev_exit);
Ok(Some(rev_acc.as_basic_value()))
}
/// Lower `scanr :: (a -> b -> b) -> b -> [a] -> [b]`.
/// scanr f z [x1,x2,...,xn] = [f x1 (f x2 (...(f xn z)...)), ..., f xn z, z]
/// Strategy: reverse input, then left-accumulate with f(elem, acc), result comes out in order.
fn lower_builtin_scanr(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Lower the function closure
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("scanr: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"scanr: function must be a closure".to_string(),
))
}
};
// Lower the initial value
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("scanr: init has no value".to_string()))?;
let init_ptr = self.value_to_ptr(init_val)?;
// Lower the list
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("scanr: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("scanr expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Step 1: Reverse the input list
let reversed = self
.build_inline_reverse(list_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("scanr: reverse failed".to_string()))?;
let reversed_ptr = reversed.into_pointer_value();
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
// Step 2: Left-accumulate over reversed list, building result in forward order.
// Start with result = [z], acc = z
let nil = self.build_nil()?;
let init_cons = self.build_cons(init_ptr.into(), nil.into())?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "scanr_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "scanr_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "scanr_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "scanr_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "scanr_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "scanr_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_empty")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: new_acc = f(head, acc), cons new_acc onto result
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// scanr applies f(elem, acc) — note the arg order
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[
func_ptr.into(),
head_ptr.into(),
acc_phi.as_basic_value().into(),
],
"scanr_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("scanr: function returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
let new_cons = self.build_cons(new_acc_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc_ptr, loop_body)]);
list_phi.add_incoming(&[(&reversed_ptr, entry_block), (&tail_ptr, loop_body)]);
result_phi.add_incoming(&[(&init_cons, entry_block), (&new_cons, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `scanl1 :: (a -> a -> a) -> [a] -> [a]`.
/// scanl1 f [x1,x2,...] = scanl f x1 [x2,...]
/// Extract head as init, then run scanl loop on tail.
fn lower_builtin_scanl1(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("scanl1: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"scanl1: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("scanl1: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("scanl1 expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Check if list is empty — if so, return empty list
let tag = self.extract_adt_tag(list_ptr)?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_empty")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let nonempty_block = self
.llvm_ctx
.append_basic_block(current_fn, "scanl1_nonempty");
let empty_block = self.llvm_ctx.append_basic_block(current_fn, "scanl1_empty");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "scanl1_merge");
self.builder()
.build_conditional_branch(is_empty, empty_block, nonempty_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Empty case: return nil
self.builder().position_at_end(empty_block);
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Non-empty case: extract head as init, scanl on tail
self.builder().position_at_end(nonempty_block);
let head_ptr = self.extract_adt_field(list_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(list_ptr, 2, 1)?;
// Build reversed result starting with head (the init value)
let nil2 = self.build_nil()?;
let init_cons = self.build_cons(head_ptr.into(), nil2.into())?;
let ne_entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "scanl1_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "scanl1_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "scanl1_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "scanl1_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l_phi = self
.builder()
.build_phi(ptr_type, "scanl1_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "scanl1_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l_tag = self.extract_adt_tag(l_phi.as_basic_value().into_pointer_value())?;
let l_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, l_tag, i64_zero, "l_empty")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(l_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: new_acc = f(acc, head); cons new_acc onto result
self.builder().position_at_end(loop_body);
let lh = self.extract_adt_field(l_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let lt = self.extract_adt_field(l_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), acc_phi.as_basic_value().into(), lh.into()],
"scanl1_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("scanl1: function returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
let new_cons = self.build_cons(new_acc_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&head_ptr, ne_entry_block), (&new_acc_ptr, loop_body)]);
l_phi.add_incoming(&[(&tail_ptr, ne_entry_block), (<, loop_body)]);
result_phi.add_incoming(&[(&init_cons, ne_entry_block), (&new_cons, loop_body)]);
// Reverse the accumulated result
self.builder().position_at_end(loop_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?
.ok_or_else(|| CodegenError::Internal("scanl1: reverse failed".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
let rev_exit_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no block after reverse".to_string()))?;
// Merge
self.builder().position_at_end(merge_block);
let final_phi = self
.builder()
.build_phi(ptr_type, "scanl1_final")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
final_phi.add_incoming(&[(&nil, empty_block), (&reversed, rev_exit_block)]);
Ok(Some(final_phi.as_basic_value()))
}
/// Lower `scanr1 :: (a -> a -> a) -> [a] -> [a]`.
/// scanr1 f [x1,...,xn] = scanr f xn [x1,...,x(n-1)]
/// Strategy: reverse input, extract head of reversed (= original last) as init,
/// then left-accumulate with f(elem, acc) on rest. Result is already in order.
fn lower_builtin_scanr1(
&mut self,
func_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("scanr1: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"scanr1: function must be a closure".to_string(),
))
}
};
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("scanr1: list has no value".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("scanr1 expects a list".to_string())),
};
let ptr_type = self.type_mapper().ptr_type();
let i64_zero = self.type_mapper().i64_type().const_zero();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Check if empty
let tag = self.extract_adt_tag(list_ptr)?;
let is_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag, i64_zero, "is_empty")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let nonempty_block = self
.llvm_ctx
.append_basic_block(current_fn, "scanr1_nonempty");
let empty_block = self.llvm_ctx.append_basic_block(current_fn, "scanr1_empty");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "scanr1_merge");
self.builder()
.build_conditional_branch(is_empty, empty_block, nonempty_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Empty case
self.builder().position_at_end(empty_block);
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Non-empty: reverse input, extract head of reversed as init
self.builder().position_at_end(nonempty_block);
let reversed = self
.build_inline_reverse(list_ptr, current_fn)?
.ok_or_else(|| CodegenError::Internal("scanr1: reverse failed".to_string()))?;
let reversed_ptr = reversed.into_pointer_value();
let rev_head = self.extract_adt_field(reversed_ptr, 2, 0)?;
let rev_tail = self.extract_adt_field(reversed_ptr, 2, 1)?;
// Build initial result: cons init_val onto nil
let nil2 = self.build_nil()?;
let init_cons = self.build_cons(rev_head.into(), nil2.into())?;
let ne_entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no block".to_string()))?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "scanr1_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "scanr1_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "scanr1_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "scanr1_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l_phi = self
.builder()
.build_phi(ptr_type, "scanr1_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "scanr1_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l_tag = self.extract_adt_tag(l_phi.as_basic_value().into_pointer_value())?;
let l_empty = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, l_tag, i64_zero, "l_empty")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(l_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: new_acc = f(head, acc), cons onto result
self.builder().position_at_end(loop_body);
let lh = self.extract_adt_field(l_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let lt = self.extract_adt_field(l_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let new_acc = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), lh.into(), acc_phi.as_basic_value().into()],
"scanr1_new_acc",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("scanr1: function returned void".to_string()))?;
let new_acc_ptr = self.value_to_ptr(new_acc)?;
let new_cons = self.build_cons(new_acc_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
acc_phi.add_incoming(&[(&rev_head, ne_entry_block), (&new_acc_ptr, loop_body)]);
l_phi.add_incoming(&[(&rev_tail, ne_entry_block), (<, loop_body)]);
result_phi.add_incoming(&[(&init_cons, ne_entry_block), (&new_cons, loop_body)]);
// Result is already in the correct order (built forward from rightmost element)
self.builder().position_at_end(loop_exit);
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(merge_block);
let final_phi = self
.builder()
.build_phi(ptr_type, "scanr1_final")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
final_phi.add_incoming(&[
(&nil, empty_block),
(&result_phi.as_basic_value(), loop_exit),
]);
Ok(Some(final_phi.as_basic_value()))
}
/// Lower `unfoldr :: (b -> Maybe (a, b)) -> b -> [a]`.
/// Loop: call f(seed), if Nothing stop, if Just (val, newSeed) cons val and continue.
fn lower_builtin_unfoldr(
&mut self,
func_expr: &Expr,
seed_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("unfoldr: function has no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"unfoldr: function must be a closure".to_string(),
))
}
};
let seed_val = self
.lower_expr(seed_expr)?
.ok_or_else(|| CodegenError::Internal("unfoldr: seed has no value".to_string()))?;
let seed_ptr = self.value_to_ptr(seed_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let loop_header = self
.llvm_ctx
.append_basic_block(current_fn, "unfoldr_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "unfoldr_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "unfoldr_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let seed_phi = self
.builder()
.build_phi(ptr_type, "unfoldr_seed")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let result_phi = self
.builder()
.build_phi(ptr_type, "unfoldr_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Call f(seed) -> Maybe (a, b)
let fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let maybe_val = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_ptr.into(), seed_phi.as_basic_value().into()],
"unfoldr_maybe",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("unfoldr: function returned void".to_string()))?;
let maybe_ptr = self.value_to_ptr(maybe_val)?;
// Check Maybe tag: 0 = Nothing, 1 = Just
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_nothing = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
maybe_tag,
tm.i64_type().const_zero(),
"is_nothing",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nothing, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract (val, newSeed) from Just payload
self.builder().position_at_end(loop_body);
// Just has 1 field: the payload, which is a tuple (a, b)
let tuple_ptr = self.extract_adt_field(maybe_ptr, 1, 0)?;
// Extract fst (value) and snd (new seed) from the tuple
let val_ptr = self.extract_adt_field(tuple_ptr, 2, 0)?;
let new_seed_ptr = self.extract_adt_field(tuple_ptr, 2, 1)?;
let new_cons = self.build_cons(val_ptr.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
seed_phi.add_incoming(&[(&seed_ptr, entry_block), (&new_seed_ptr, loop_body)]);
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
// Reverse the accumulated result
self.builder().position_at_end(loop_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `lines` — call RTS bhc_string_lines.
fn lower_builtin_lines(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("lines: no string".to_string()))?;
// Convert [Char] input to C-string for RTS
let str_ptr = self.value_to_ptr(str_val)?;
let str_cstr = self.char_list_to_cstring(str_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000176))
.ok_or_else(|| CodegenError::Internal("bhc_string_lines not declared".to_string()))?;
let cstr_list = self
.builder()
.build_call(*rts_fn, &[str_cstr.into()], "lines")
.map_err(|e| CodegenError::Internal(format!("lines call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lines: returned void".to_string()))?;
// Convert list of C-strings to list of [Char] lists
let result = self.cstring_list_to_char_list_list(cstr_list.into_pointer_value())?;
Ok(Some(result.into()))
}
/// Lower `unlines` — call RTS bhc_string_unlines.
fn lower_builtin_unlines(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("unlines: no list".to_string()))?;
// Convert list of [Char] lists to list of C-strings for RTS
let list_ptr = self.value_to_ptr(list_val)?;
let cstr_list = self.char_list_list_to_cstring_list(list_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000177))
.ok_or_else(|| CodegenError::Internal("bhc_string_unlines not declared".to_string()))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[cstr_list.into()], "unlines")
.map_err(|e| CodegenError::Internal(format!("unlines call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("unlines: returned void".to_string()))?;
// Convert C-string result to [Char] linked list
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
}
/// Lower `words` — call RTS bhc_string_words.
fn lower_builtin_words(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("words: no string".to_string()))?;
// Convert [Char] input to C-string for RTS
let str_ptr = self.value_to_ptr(str_val)?;
let str_cstr = self.char_list_to_cstring(str_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000178))
.ok_or_else(|| CodegenError::Internal("bhc_string_words not declared".to_string()))?;
let cstr_list = self
.builder()
.build_call(*rts_fn, &[str_cstr.into()], "words")
.map_err(|e| CodegenError::Internal(format!("words call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("words: returned void".to_string()))?;
// Convert list of C-strings to list of [Char] lists
let result = self.cstring_list_to_char_list_list(cstr_list.into_pointer_value())?;
Ok(Some(result.into()))
}
/// Lower `unwords` — call RTS bhc_string_unwords.
fn lower_builtin_unwords(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("unwords: no list".to_string()))?;
// Convert list of [Char] lists to list of C-strings for RTS
let list_ptr = self.value_to_ptr(list_val)?;
let cstr_list = self.char_list_list_to_cstring_list(list_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000179))
.ok_or_else(|| CodegenError::Internal("bhc_string_unwords not declared".to_string()))?;
let cstr_result = self
.builder()
.build_call(*rts_fn, &[cstr_list.into()], "unwords")
.map_err(|e| CodegenError::Internal(format!("unwords call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("unwords: returned void".to_string()))?;
// Convert C-string result to [Char] linked list
let char_list = self.cstring_to_char_list(cstr_result.into_pointer_value())?;
Ok(Some(char_list.into()))
}
/// Lower `read :: String -> a`.
///
/// For user-defined ADTs with derived Read, generates inline char-list
/// comparison against constructor names. For Int, falls back to RTS.
fn lower_builtin_read(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check for derived Read instance on the result type
if let Some(type_name) = self.infer_read_target_type(str_expr) {
if self.derived_read_fns.contains_key(&type_name) {
// Collect constructor names and tags for this type
let constructors: Vec<(String, u32)> = self
.constructor_metadata
.iter()
.filter_map(|(name, meta)| {
if meta.type_name.as_deref() == Some(&type_name) {
Some((name.clone(), meta.tag))
} else {
None
}
})
.collect();
if !constructors.is_empty() {
return self.lower_read_adt_inline(str_expr, &constructors);
}
}
}
// Default: read as Int via RTS
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("read: no string".to_string()))?;
let str_ptr = self.value_to_ptr(str_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000540))
.ok_or_else(|| CodegenError::Internal("bhc_read_int not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[str_ptr.into()], "read_int")
.map_err(|e| CodegenError::Internal(format!("read call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("read: returned void".to_string()))?;
// Convert i64 to int-as-pointer
let i64_val = result.into_int_value();
let ptr_val = self
.builder()
.build_int_to_ptr(i64_val, self.type_mapper().ptr_type(), "read_as_ptr")
.map_err(|e| CodegenError::Internal(format!("read int_to_ptr: {:?}", e)))?;
Ok(Some(ptr_val.into()))
}
/// Generate inline ADT read: compare char list against constructor names
/// character by character, return matching tag-as-pointer.
fn lower_read_adt_inline(
&mut self,
str_expr: &Expr,
constructors: &[(String, u32)],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("read: no string".to_string()))?;
let list_ptr = self.value_to_ptr(str_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Create merge block where the result will be collected
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "read_merge");
// Build phi in merge block to collect results
let mut incoming: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
for (con_name, tag) in constructors {
// For each constructor, generate a comparison chain:
// Walk the char list and compare each character against the constructor name.
// If all chars match and list ends, this is a match.
let check_block = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_check_{}", con_name));
let match_block = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_match_{}", con_name));
let next_block = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_next_{}", con_name));
// Branch to check block
self.builder()
.build_unconditional_branch(check_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(check_block);
// Walk the list, comparing each char
let mut current_list = list_ptr;
let _all_matched = true;
let chars: Vec<char> = con_name.chars().collect();
for (i, ch) in chars.iter().enumerate() {
let char_check = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_{}_ch{}", con_name, i));
let char_mismatch = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_{}_miss{}", con_name, i));
// Check if list is non-empty (tag != 0)
let tag_val = self.extract_adt_tag(current_list)?;
let is_cons = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
tag_val,
i64_type.const_zero(),
"is_cons",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_cons, char_check, char_mismatch)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// char_mismatch: list too short, go to next constructor
self.builder().position_at_end(char_mismatch);
self.builder()
.build_unconditional_branch(next_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// char_check: extract head char and compare
self.builder().position_at_end(char_check);
let head_ptr = self.extract_adt_field(current_list, 2, 0)?;
let char_val = self
.builder()
.build_ptr_to_int(head_ptr, i64_type, "char_val")
.map_err(|e| CodegenError::Internal(format!("ptr_to_int: {:?}", e)))?;
let expected = i64_type.const_int(*ch as u64, false);
let chars_eq = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, char_val, expected, "char_eq")
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let next_char = self
.llvm_context()
.append_basic_block(current_fn, &format!("read_{}_next{}", con_name, i));
self.builder()
.build_conditional_branch(chars_eq, next_char, next_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// next_char: advance to tail
self.builder().position_at_end(next_char);
let tail = self.extract_adt_field(current_list, 2, 1)?;
current_list = tail;
}
// After all chars matched, check that list is empty (tag == 0)
let end_tag = self.extract_adt_tag(current_list)?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
end_tag,
i64_type.const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, match_block, next_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// match_block: construct the ADT value (heap-allocated with tag)
self.builder().position_at_end(match_block);
let result_ptr = self.alloc_adt(*tag, 0)?;
incoming.push((result_ptr.into(), match_block));
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// next_block: continue to next constructor
self.builder().position_at_end(next_block);
}
// After all constructors checked without match: return null (error)
let null_ptr = ptr_type.const_null();
let final_block = self.builder().get_insert_block().unwrap();
incoming.push((null_ptr.into(), final_block));
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Merge block: phi to select the result
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(ptr_type, "read_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
for (val, block) in &incoming {
phi.add_incoming(&[(&val.into_pointer_value(), *block)]);
}
Ok(Some(phi.as_basic_value()))
}
/// Lower `readMaybe :: String -> Maybe Int` (monomorphic).
///
/// Passes the char list to the RTS `bhc_try_read_int` which returns a
/// Maybe ADT pointer (Nothing on parse failure, Just n on success).
fn lower_builtin_read_maybe(
&mut self,
str_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let str_val = self
.lower_expr(str_expr)?
.ok_or_else(|| CodegenError::Internal("readMaybe: no string".to_string()))?;
let str_ptr = self.value_to_ptr(str_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000541))
.ok_or_else(|| CodegenError::Internal("bhc_try_read_int not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[str_ptr.into()], "try_read_int")
.map_err(|e| CodegenError::Internal(format!("readMaybe call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("readMaybe: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `zip3 :: [a] -> [b] -> [c] -> [(a,b,c)]`.
/// 3-list parallel iteration, build 3-tuples.
fn lower_builtin_zip3(
&mut self,
list1_expr: &Expr,
list2_expr: &Expr,
list3_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zip3: list1 no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zip3 expects lists".to_string())),
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zip3: list2 no value".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zip3 expects lists".to_string())),
};
let list3_val = self
.lower_expr(list3_expr)?
.ok_or_else(|| CodegenError::Internal("zip3: list3 no value".to_string()))?;
let list3_ptr = match list3_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("zip3 expects lists".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "zip3_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "zip3_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "zip3_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "zip3_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l1_phi = self
.builder()
.build_phi(ptr_type, "zip3_l1")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l2_phi = self
.builder()
.build_phi(ptr_type, "zip3_l2")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l3_phi = self
.builder()
.build_phi(ptr_type, "zip3_l3")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
// Check if any list is empty
let tag1 = self.extract_adt_tag(l1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(l2_phi.as_basic_value().into_pointer_value())?;
let tag3 = self.extract_adt_tag(l3_phi.as_basic_value().into_pointer_value())?;
let empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
tm.i64_type().const_zero(),
"e1",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
tm.i64_type().const_zero(),
"e2",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let empty3 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag3,
tm.i64_type().const_zero(),
"e3",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let any_empty = self
.builder()
.build_or(empty1, empty2, "e12")
.map_err(|e| CodegenError::Internal(format!("or: {:?}", e)))?;
let any_empty = self
.builder()
.build_or(any_empty, empty3, "e123")
.map_err(|e| CodegenError::Internal(format!("or: {:?}", e)))?;
self.builder()
.build_conditional_branch(any_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract heads, build triple, cons
self.builder().position_at_end(loop_body);
let h1 = self.extract_adt_field(l1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t1 = self.extract_adt_field(l1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let h2 = self.extract_adt_field(l2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t2 = self.extract_adt_field(l2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let h3 = self.extract_adt_field(l3_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t3 = self.extract_adt_field(l3_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let triple = self.build_triple(h1.into(), h2.into(), h3.into())?;
let new_cons = self.build_cons(triple.into(), result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
l1_phi.add_incoming(&[(&list1_ptr, entry_block), (&t1, loop_body)]);
l2_phi.add_incoming(&[(&list2_ptr, entry_block), (&t2, loop_body)]);
l3_phi.add_incoming(&[(&list3_ptr, entry_block), (&t3, loop_body)]);
// Reverse result
self.builder().position_at_end(loop_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]`.
/// 3-list parallel iteration, call 3-arg function.
fn lower_builtin_zipwith3(
&mut self,
func_expr: &Expr,
list1_expr: &Expr,
list2_expr: &Expr,
list3_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func_val = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith3: func no value".to_string()))?;
let func_ptr = match func_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWith3: function must be a closure".to_string(),
))
}
};
let list1_val = self
.lower_expr(list1_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith3: list1 no value".to_string()))?;
let list1_ptr = match list1_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWith3 expects lists".to_string(),
))
}
};
let list2_val = self
.lower_expr(list2_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith3: list2 no value".to_string()))?;
let list2_ptr = match list2_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWith3 expects lists".to_string(),
))
}
};
let list3_val = self
.lower_expr(list3_expr)?
.ok_or_else(|| CodegenError::Internal("zipWith3: list3 no value".to_string()))?;
let list3_ptr = match list3_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::TypeError(
"zipWith3 expects lists".to_string(),
))
}
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let nil = self.build_nil()?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "zw3_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "zw3_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "zw3_exit");
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let result_phi = self
.builder()
.build_phi(ptr_type, "zw3_result")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l1_phi = self
.builder()
.build_phi(ptr_type, "zw3_l1")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l2_phi = self
.builder()
.build_phi(ptr_type, "zw3_l2")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let l3_phi = self
.builder()
.build_phi(ptr_type, "zw3_l3")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag1 = self.extract_adt_tag(l1_phi.as_basic_value().into_pointer_value())?;
let tag2 = self.extract_adt_tag(l2_phi.as_basic_value().into_pointer_value())?;
let tag3 = self.extract_adt_tag(l3_phi.as_basic_value().into_pointer_value())?;
let empty1 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag1,
tm.i64_type().const_zero(),
"e1",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let empty2 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag2,
tm.i64_type().const_zero(),
"e2",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let empty3 = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag3,
tm.i64_type().const_zero(),
"e3",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
let any_empty = self
.builder()
.build_or(empty1, empty2, "e12")
.map_err(|e| CodegenError::Internal(format!("or: {:?}", e)))?;
let any_empty = self
.builder()
.build_or(any_empty, empty3, "e123")
.map_err(|e| CodegenError::Internal(format!("or: {:?}", e)))?;
self.builder()
.build_conditional_branch(any_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract heads, call f(h1, h2, h3), cons result
self.builder().position_at_end(loop_body);
let h1 = self.extract_adt_field(l1_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t1 = self.extract_adt_field(l1_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let h2 = self.extract_adt_field(l2_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t2 = self.extract_adt_field(l2_phi.as_basic_value().into_pointer_value(), 2, 1)?;
let h3 = self.extract_adt_field(l3_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let t3 = self.extract_adt_field(l3_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// Call f as 4-arg closure: fn(closure_ptr, h1, h2, h3) -> result
let closure_fn_ptr = self.extract_closure_fn_ptr(func_ptr)?;
let fn_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let mapped_val = self
.builder()
.build_indirect_call(
fn_type,
closure_fn_ptr,
&[func_ptr.into(), h1.into(), h2.into(), h3.into()],
"zw3_val",
)
.map_err(|e| CodegenError::Internal(format!("call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("zipWith3: function returned void".to_string())
})?;
let new_cons = self.build_cons(mapped_val, result_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
result_phi.add_incoming(&[(&nil, entry_block), (&new_cons, loop_body)]);
l1_phi.add_incoming(&[(&list1_ptr, entry_block), (&t1, loop_body)]);
l2_phi.add_incoming(&[(&list2_ptr, entry_block), (&t2, loop_body)]);
l3_phi.add_incoming(&[(&list3_ptr, entry_block), (&t3, loop_body)]);
// Reverse result
self.builder().position_at_end(loop_exit);
let reversed = self
.build_inline_reverse(result_phi.as_basic_value().into_pointer_value(), current_fn)?;
Ok(reversed)
}
/// Lower `unzip` — split list of pairs into pair of lists.
/// unzip [(a1,b1), (a2,b2), ...] = ([a1,a2,...], [b1,b2,...])
fn lower_builtin_unzip(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("unzip: no list".to_string()))?;
let list_ptr = match list_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError("unzip expects a list".to_string())),
};
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_header = self.llvm_ctx.append_basic_block(current_fn, "unzip_header");
let loop_body = self.llvm_ctx.append_basic_block(current_fn, "unzip_body");
let loop_exit = self.llvm_ctx.append_basic_block(current_fn, "unzip_exit");
let nil = self.build_nil()?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let fsts_phi = self
.builder()
.build_phi(ptr_type, "unzip_fsts")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let snds_phi = self
.builder()
.build_phi(ptr_type, "unzip_snds")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let list_phi = self
.builder()
.build_phi(ptr_type, "unzip_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let tag = self.extract_adt_tag(list_phi.as_basic_value().into_pointer_value())?;
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_empty, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
// Loop body: extract pair from head, split into fst and snd
self.builder().position_at_end(loop_body);
let head_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 0)?;
let tail_ptr =
self.extract_adt_field(list_phi.as_basic_value().into_pointer_value(), 2, 1)?;
// head is a pair ADT (tag=0, arity=2)
let fst_val = self.extract_adt_field(head_ptr, 2, 0)?;
let snd_val = self.extract_adt_field(head_ptr, 2, 1)?;
let fst_cons = self.build_cons(fst_val.into(), fsts_phi.as_basic_value())?;
let snd_cons = self.build_cons(snd_val.into(), snds_phi.as_basic_value())?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
fsts_phi.add_incoming(&[(&nil, entry_block), (&fst_cons, loop_body)]);
snds_phi.add_incoming(&[(&nil, entry_block), (&snd_cons, loop_body)]);
list_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
// Exit: reverse both lists, then build pair
self.builder().position_at_end(loop_exit);
// Reverse fsts
let rev1_header = self.llvm_ctx.append_basic_block(current_fn, "rev1_header");
let rev1_body = self.llvm_ctx.append_basic_block(current_fn, "rev1_body");
let rev1_exit = self.llvm_ctx.append_basic_block(current_fn, "rev1_exit");
let nil2 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev1_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev1_header);
let rev1_acc = self
.builder()
.build_phi(ptr_type, "rev1_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev1_list = self
.builder()
.build_phi(ptr_type, "rev1_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev1_tag = self.extract_adt_tag(rev1_list.as_basic_value().into_pointer_value())?;
let rev1_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev1_tag,
tm.i64_type().const_zero(),
"rev1_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev1_empty, rev1_exit, rev1_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev1_body);
let r1_head =
self.extract_adt_field(rev1_list.as_basic_value().into_pointer_value(), 2, 0)?;
let r1_tail =
self.extract_adt_field(rev1_list.as_basic_value().into_pointer_value(), 2, 1)?;
let r1_cons = self.build_cons(r1_head.into(), rev1_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev1_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev1_acc.add_incoming(&[(&nil2, loop_exit), (&r1_cons, rev1_body)]);
rev1_list.add_incoming(&[
(&fsts_phi.as_basic_value(), loop_exit),
(&r1_tail, rev1_body),
]);
// Reverse snds
self.builder().position_at_end(rev1_exit);
let rev2_header = self.llvm_ctx.append_basic_block(current_fn, "rev2_header");
let rev2_body = self.llvm_ctx.append_basic_block(current_fn, "rev2_body");
let rev2_exit = self.llvm_ctx.append_basic_block(current_fn, "rev2_exit");
let nil3 = self.build_nil()?;
self.builder()
.build_unconditional_branch(rev2_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev2_header);
let rev2_acc = self
.builder()
.build_phi(ptr_type, "rev2_acc")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev2_list = self
.builder()
.build_phi(ptr_type, "rev2_list")
.map_err(|e| CodegenError::Internal(format!("phi: {:?}", e)))?;
let rev2_tag = self.extract_adt_tag(rev2_list.as_basic_value().into_pointer_value())?;
let rev2_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rev2_tag,
tm.i64_type().const_zero(),
"rev2_empty",
)
.map_err(|e| CodegenError::Internal(format!("cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(rev2_empty, rev2_exit, rev2_body)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
self.builder().position_at_end(rev2_body);
let r2_head =
self.extract_adt_field(rev2_list.as_basic_value().into_pointer_value(), 2, 0)?;
let r2_tail =
self.extract_adt_field(rev2_list.as_basic_value().into_pointer_value(), 2, 1)?;
let r2_cons = self.build_cons(r2_head.into(), rev2_acc.as_basic_value())?;
self.builder()
.build_unconditional_branch(rev2_header)
.map_err(|e| CodegenError::Internal(format!("branch: {:?}", e)))?;
rev2_acc.add_incoming(&[(&nil3, rev1_exit), (&r2_cons, rev2_body)]);
rev2_list.add_incoming(&[
(&snds_phi.as_basic_value(), rev1_exit),
(&r2_tail, rev2_body),
]);
// Build result pair
self.builder().position_at_end(rev2_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, rev1_acc.as_basic_value())?;
self.store_adt_field(pair, 2, 1, rev2_acc.as_basic_value())?;
Ok(Some(pair.into()))
}
// ========================================================================
// Phase 3: Container Operation Handlers (Data.Map, Data.Set, Data.IntMap, Data.IntSet)
// ========================================================================
// --- Data.Map handlers ---
/// Lower `Data.Map.empty` - create empty map.
fn lower_builtin_map_empty(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "map_empty")
.map_err(|e| CodegenError::Internal(format!("map_empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_empty: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.singleton` - create singleton map.
fn lower_builtin_map_singleton(
&mut self,
key_expr: &Expr,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_singleton: no key".to_string()))?;
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("map_singleton: no value".to_string()))?;
let key_int = self.coerce_to_int(key)?;
let val_ptr = self.value_to_ptr(val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000101))
.ok_or_else(|| CodegenError::Internal("bhc_map_singleton not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[key_int.into(), val_ptr.into()], "map_singleton")
.map_err(|e| CodegenError::Internal(format!("map_singleton call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_singleton: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.null` - check if map is empty.
fn lower_builtin_map_null(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_null: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000102))
.ok_or_else(|| CodegenError::Internal("bhc_map_null not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[map_ptr.into()], "map_null")
.map_err(|e| CodegenError::Internal(format!("map_null call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_null: returned void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "null_bool")
}
/// Lower `Data.Map.size` - get map size.
fn lower_builtin_map_size(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_size: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000103))
.ok_or_else(|| CodegenError::Internal("bhc_map_size not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[map_ptr.into()], "map_size")
.map_err(|e| CodegenError::Internal(format!("map_size call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_size: returned void".to_string()))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `Data.Map.member` - check membership.
fn lower_builtin_map_member(
&mut self,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_member: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_member: no map".to_string()))?;
let key_int = self.coerce_to_int(key)?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000104))
.ok_or_else(|| CodegenError::Internal("bhc_map_member not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[key_int.into(), map_ptr.into()], "map_member")
.map_err(|e| CodegenError::Internal(format!("map_member call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_member: returned void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "member_bool")
}
/// Lower `Data.Map.lookup` - lookup key, returns Maybe.
fn lower_builtin_map_lookup(
&mut self,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_lookup: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_lookup: no map".to_string()))?;
let key_int = self.coerce_to_int(key)?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[key_int.into(), map_ptr.into()], "map_lookup")
.map_err(|e| CodegenError::Internal(format!("map_lookup call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_lookup: returned void".to_string()))?;
// Result is a pointer: null = Nothing, non-null = the value
// Wrap in Maybe: check for null, construct Just/Nothing ADT
let result_ptr = result.into_pointer_value();
let is_null = self
.builder()
.build_is_null(result_ptr, "is_null")
.map_err(|e| CodegenError::Internal(format!("map_lookup is_null: {:?}", e)))?;
let nothing = self.alloc_adt(0, 0)?; // Nothing = tag 0, 0 fields
let just = self.alloc_adt(1, 1)?; // Just = tag 1, 1 field
self.store_adt_field(just, 1, 0, result_ptr.into())?;
let maybe = self
.builder()
.build_select(is_null, nothing, just, "maybe")
.map_err(|e| CodegenError::Internal(format!("map_lookup select: {:?}", e)))?;
Ok(Some(maybe))
}
/// Lower `Data.Map.findWithDefault` - lookup with default.
fn lower_builtin_map_find_with_default(
&mut self,
default_expr: &Expr,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let default = self
.lower_expr(default_expr)?
.ok_or_else(|| CodegenError::Internal("map_fwd: no default".to_string()))?;
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_fwd: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_fwd: no map".to_string()))?;
let default_ptr = self.value_to_ptr(default)?;
let key_int = self.coerce_to_int(key)?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self.functions.get(&VarId::new(1000106)).ok_or_else(|| {
CodegenError::Internal("bhc_map_find_with_default not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[default_ptr.into(), key_int.into(), map_ptr.into()],
"map_fwd",
)
.map_err(|e| CodegenError::Internal(format!("map_fwd call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_fwd: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.insert` - insert key-value pair.
fn lower_builtin_map_insert(
&mut self,
key_expr: &Expr,
val_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_insert: no key".to_string()))?;
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("map_insert: no value".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_insert: no map".to_string()))?;
let key_int = self.coerce_to_int(key)?;
let val_ptr = self.value_to_ptr(val)?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[key_int.into(), val_ptr.into(), map_ptr.into()],
"map_insert",
)
.map_err(|e| CodegenError::Internal(format!("map_insert call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_insert: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.delete` - delete key from map.
fn lower_builtin_map_delete(
&mut self,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let key = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_delete: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_delete: no map".to_string()))?;
let key_int = self.coerce_to_int(key)?;
let map_ptr = self.value_to_ptr(map_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000108))
.ok_or_else(|| CodegenError::Internal("bhc_map_delete not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[key_int.into(), map_ptr.into()], "map_delete")
.map_err(|e| CodegenError::Internal(format!("map_delete call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_delete: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.union` - union of two maps.
fn lower_builtin_map_union(
&mut self,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1 = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_union: no map1".to_string()))?;
let m2 = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_union: no map2".to_string()))?;
let m1_ptr = self.value_to_ptr(m1)?;
let m2_ptr = self.value_to_ptr(m2)?;
let rts_fn = self
.functions
.get(&VarId::new(1000109))
.ok_or_else(|| CodegenError::Internal("bhc_map_union not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[m1_ptr.into(), m2_ptr.into()], "map_union")
.map_err(|e| CodegenError::Internal(format!("map_union call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_union: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.intersection` - intersection of two maps.
fn lower_builtin_map_intersection(
&mut self,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1 = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_intersection: no map1".to_string()))?;
let m2 = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_intersection: no map2".to_string()))?;
let m1_ptr = self.value_to_ptr(m1)?;
let m2_ptr = self.value_to_ptr(m2)?;
let rts_fn = self.functions.get(&VarId::new(1000110)).ok_or_else(|| {
CodegenError::Internal("bhc_map_intersection not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[m1_ptr.into(), m2_ptr.into()], "map_intersection")
.map_err(|e| CodegenError::Internal(format!("map_intersection call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_intersection: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.difference` - difference of two maps.
fn lower_builtin_map_difference(
&mut self,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1 = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_difference: no map1".to_string()))?;
let m2 = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_difference: no map2".to_string()))?;
let m1_ptr = self.value_to_ptr(m1)?;
let m2_ptr = self.value_to_ptr(m2)?;
let rts_fn = self
.functions
.get(&VarId::new(1000111))
.ok_or_else(|| CodegenError::Internal("bhc_map_difference not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[m1_ptr.into(), m2_ptr.into()], "map_difference")
.map_err(|e| CodegenError::Internal(format!("map_difference call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_difference: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Map.isSubmapOf` - check submap.
fn lower_builtin_map_is_submap_of(
&mut self,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let m1 = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_isSubmapOf: no map1".to_string()))?;
let m2 = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_isSubmapOf: no map2".to_string()))?;
let m1_ptr = self.value_to_ptr(m1)?;
let m2_ptr = self.value_to_ptr(m2)?;
let rts_fn = self.functions.get(&VarId::new(1000112)).ok_or_else(|| {
CodegenError::Internal("bhc_map_is_submap_of not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[m1_ptr.into(), m2_ptr.into()], "map_is_submap_of")
.map_err(|e| CodegenError::Internal(format!("map_is_submap_of call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_isSubmapOf: returned void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "is_submap_bool")
}
// ========================================================================
// Container Higher-Order Operations
// ========================================================================
//
// These iterate over container elements using RTS indexed access,
// calling closures via indirect LLVM calls.
/// Lower `Data.Map.map` — applies closure to each value, building new map.
fn lower_builtin_map_map(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_map: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_map: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Get element count
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
// Start with empty map
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Extract function pointer from closure
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mm_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mm_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key and value, apply fn to value, insert (key, new_val)
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Call closure: fn_ptr(closure, val)
let mapped_val = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val.into()],
"mapped",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let mapped_ptr = self.value_to_ptr(mapped_val)?;
let acc_ptr = acc_phi.as_basic_value().into_pointer_value();
let new_map = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), mapped_ptr.into(), acc_ptr.into()],
"new_map",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&empty_map, entry_block), (&new_map, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.filter` — applies predicate to each value, keeping matches.
fn lower_builtin_map_filter(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_filter: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_filter: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mf_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mf_body");
let loop_insert = self
.llvm_context()
.append_basic_block(current_fn, "mf_insert");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "mf_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mf_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Call predicate: fn_ptr(closure, val) -> Bool (tag 0=False, 1=True)
let pred_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val.into()],
"pred",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check if predicate returned True (non-zero tag)
let pred_ptr = self.value_to_ptr(pred_result)?;
let pred_tag = self.extract_adt_tag(pred_ptr)?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_insert, loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Insert branch: add (key, val) to accumulator map
self.builder().position_at_end(loop_insert);
let val_ptr = self.value_to_ptr(val)?;
let acc_ptr_ins = acc_phi.as_basic_value().into_pointer_value();
let inserted = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), val_ptr.into(), acc_ptr_ins.into()],
"inserted",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Skip/merge block
self.builder().position_at_end(loop_skip);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&inserted, loop_insert),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), loop_skip),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_skip),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.foldr` — right fold over map values.
fn lower_builtin_map_foldr(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldr: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldr: no init".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldr: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
// foldr fn takes 2 args: value, accumulator
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
// Iterate backwards for right fold
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mfr_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mfr_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mfr_exit");
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Call closure: fn_ptr(closure, val, acc)
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val.into(), acc_val.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.foldl'` — strict left fold over map values.
fn lower_builtin_map_foldl(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldl: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldl: no init".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldl: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
// foldl fn takes 2 args: accumulator, value
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mfl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mfl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mfl_exit");
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Call closure: fn_ptr(closure, acc, val)
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), acc_val.into(), val.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Set.map` — applies closure to each element, building new set.
fn lower_builtin_set_map(
&mut self,
fn_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("set_map: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_map: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let empty_set = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sm_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sm_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
// Call closure: fn_ptr(closure, elem)
let mapped_val = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), elem_ptr.into()],
"mapped",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let mapped_int = self.coerce_to_int(mapped_val)?;
let acc_ptr = acc_phi.as_basic_value().into_pointer_value();
let new_set = self
.builder()
.build_call(insert_fn, &[mapped_int.into(), acc_ptr.into()], "new_set")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&empty_set, entry_block), (&new_set, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Set.filter` — applies predicate, keeping matching elements.
fn lower_builtin_set_filter(
&mut self,
fn_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("set_filter: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_filter: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let empty_set = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sf_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sf_body");
let loop_insert = self
.llvm_context()
.append_basic_block(current_fn, "sf_insert");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "sf_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sf_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
// Call predicate: fn_ptr(closure, elem)
let pred_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), elem_ptr.into()],
"pred",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let pred_ptr = self.value_to_ptr(pred_result)?;
let pred_tag = self.extract_adt_tag(pred_ptr)?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_insert, loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_insert);
let acc_ptr_ins = acc_phi.as_basic_value().into_pointer_value();
let inserted = self
.builder()
.build_call(
insert_fn,
&[elem_i64.into(), acc_ptr_ins.into()],
"inserted",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_skip);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&inserted, loop_insert),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_set, entry_block),
(&merge_phi.as_basic_value(), loop_skip),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_skip),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Set.foldr` — right fold over set elements.
fn lower_builtin_set_foldr(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldr: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldr: no init".to_string()))?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldr: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sfr_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sfr_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sfr_exit");
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), elem_ptr.into(), acc_val.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Set.foldl'` — strict left fold over set elements.
fn lower_builtin_set_foldl(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldl: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldl: no init".to_string()))?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_foldl: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sfl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sfl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sfl_exit");
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), acc_val.into(), elem_ptr.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.mapWithKey` -- applies closure(key, val) to each entry, building new map.
/// mapWithKey :: (k -> v -> v') -> Map k v -> Map k v'
fn lower_builtin_map_map_with_key(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_mapWithKey: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_mapWithKey: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Get element count
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
// Start with empty map
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Extract function pointer from closure; 2 args: key, val
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key and value, apply fn to (key, value), insert (key, new_val)
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Convert key i64 to ptr for closure call
let key_ptr = self.int_to_ptr(key_i64)?;
// Call closure: fn_ptr(closure, key_ptr, val)
let mapped_val = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), key_ptr.into(), val.into()],
"mapped",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let mapped_ptr = self.value_to_ptr(mapped_val)?;
let acc_ptr = acc_phi.as_basic_value().into_pointer_value();
let new_map = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), mapped_ptr.into(), acc_ptr.into()],
"new_map",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&empty_map, entry_block), (&new_map, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.filterWithKey` -- applies predicate(key, val), keeping matches.
/// filterWithKey :: (k -> v -> Bool) -> Map k v -> Map k v
fn lower_builtin_map_filter_with_key(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_filterWithKey: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_filterWithKey: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// 2-arg closure: (key, val)
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mfwk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mfwk_body");
let loop_insert = self
.llvm_context()
.append_basic_block(current_fn, "mfwk_insert");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "mfwk_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mfwk_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let key_ptr = self.int_to_ptr(key_i64)?;
// Call predicate: fn_ptr(closure, key_ptr, val) -> Bool (tag 0=False, 1=True)
let pred_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), key_ptr.into(), val.into()],
"pred",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check if predicate returned True (non-zero tag)
let pred_ptr = self.value_to_ptr(pred_result)?;
let pred_tag = self.extract_adt_tag(pred_ptr)?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_insert, loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Insert branch: add (key, val) to accumulator map
self.builder().position_at_end(loop_insert);
let val_ptr = self.value_to_ptr(val)?;
let acc_ptr_ins = acc_phi.as_basic_value().into_pointer_value();
let inserted = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), val_ptr.into(), acc_ptr_ins.into()],
"inserted",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Skip/merge block
self.builder().position_at_end(loop_skip);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&inserted, loop_insert),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), loop_skip),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_skip),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.mapMaybe` — apply function returning Maybe, keep Just values.
/// mapMaybe :: (a -> Maybe b) -> Map k a -> Map k b
fn lower_builtin_data_map_maybe(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybe: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybe: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// 1-arg closure: fn(closure_env, val) -> Maybe b
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mm_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mm_body");
let loop_insert = self
.llvm_context()
.append_basic_block(current_fn, "mm_insert");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "mm_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mm_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Call function: fn_ptr(closure, val) -> Maybe b
let maybe_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val.into()],
"maybe_res",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check Maybe tag: 0=Nothing, 1=Just
let maybe_ptr = self.value_to_ptr(maybe_result)?;
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, loop_insert, loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Insert branch: extract Just value and add (key, inner_val) to accumulator map
self.builder().position_at_end(loop_insert);
let inner_val = self.extract_adt_field(maybe_ptr, 1, 0)?;
let acc_ptr_ins = acc_phi.as_basic_value().into_pointer_value();
let inserted = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), inner_val.into(), acc_ptr_ins.into()],
"inserted",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Skip/merge block
self.builder().position_at_end(loop_skip);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&inserted, loop_insert),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), loop_skip),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_skip),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.mapMaybeWithKey` — apply function with key returning Maybe, keep Just values.
/// mapMaybeWithKey :: (k -> a -> Maybe b) -> Map k a -> Map k b
fn lower_builtin_data_map_maybe_with_key(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybeWithKey: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("mapMaybeWithKey: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// 2-arg closure: fn(closure_env, key, val) -> Maybe b
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_body");
let loop_insert = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_insert");
let loop_skip = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mmwk_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let key_ptr = self.int_to_ptr(key_i64)?;
// Call function: fn_ptr(closure, key_ptr, val) -> Maybe b
let maybe_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), key_ptr.into(), val.into()],
"maybe_res",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check Maybe tag: 0=Nothing, 1=Just
let maybe_ptr = self.value_to_ptr(maybe_result)?;
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, loop_insert, loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Insert branch: extract Just value and add (key, inner_val) to accumulator map
self.builder().position_at_end(loop_insert);
let inner_val = self.extract_adt_field(maybe_ptr, 1, 0)?;
let acc_ptr_ins = acc_phi.as_basic_value().into_pointer_value();
let inserted = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), inner_val.into(), acc_ptr_ins.into()],
"inserted",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_skip)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Skip/merge block
self.builder().position_at_end(loop_skip);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&inserted, loop_insert),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), loop_skip),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_skip),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.foldlWithKey'` -- strict left fold with key.
/// foldlWithKey' :: (a -> k -> v -> a) -> a -> Map k v -> a
fn lower_builtin_map_foldl_with_key(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldlWithKey: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldlWithKey: no init".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldlWithKey: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
// foldlWithKey takes 3 args: acc, key, val
let call_fn_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mflwk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mflwk_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mflwk_exit");
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let key_ptr = self.int_to_ptr(key_i64)?;
// Call closure: fn_ptr(closure, acc, key_ptr, val)
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), acc_val.into(), key_ptr.into(), val.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.foldrWithKey` -- right fold with key.
/// foldrWithKey :: (k -> v -> a -> a) -> a -> Map k v -> a
fn lower_builtin_map_foldr_with_key(
&mut self,
fn_expr: &Expr,
init_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldrWithKey: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let init_val = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldrWithKey: no init".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_foldrWithKey: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
// foldrWithKey takes 3 args: key, val, acc
let call_fn_type = ptr_type.fn_type(
&[
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
ptr_type.into(),
],
false,
);
// Iterate backwards for right fold
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mfrwk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mfrwk_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mfrwk_exit");
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let init_ptr = self.value_to_ptr(init_val)?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let key_ptr = self.int_to_ptr(key_i64)?;
// Call closure: fn_ptr(closure, key_ptr, val, acc)
let acc_val = acc_phi.as_basic_value();
let new_acc = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), key_ptr.into(), val.into(), acc_val.into()],
"new_acc",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&init_ptr, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.unionWith` -- union of two maps with combining function.
/// unionWith :: (v -> v -> v) -> Map k v -> Map k v -> Map k v
fn lower_builtin_map_union_with(
&mut self,
fn_expr: &Expr,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_unionWith: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map1_val = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_unionWith: no map1".to_string()))?;
let map1_ptr = self.value_to_ptr(map1_val)?;
let map2_val = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_unionWith: no map2".to_string()))?;
let map2_ptr = self.value_to_ptr(map2_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = *self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
// Start with result = map1 (copy all of map1 first by iterating it)
// Actually, we can start by copying map1 entries into a fresh map,
// then iterate map2. But simpler: just start with map1 as the accumulator
// and iterate map2, since insert overwrites.
// However, unionWith prefers left values, so we need to check.
// Get count of map2
let count2 = self
.builder()
.build_call(count_fn, &[map2_ptr.into()], "count2")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
// 2-arg closure: (v1, v2)
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "muw_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "muw_body");
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "muw_found");
let not_found_block = self
.llvm_context()
.append_basic_block(current_fn, "muw_notfound");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "muw_merge");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "muw_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header: iterate over map2
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count2, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key/val from map2, lookup in result (which starts as map1)
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map2_ptr.into(), idx.into()], "key2")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val2 = self
.builder()
.build_call(val_at_fn, &[map2_ptr.into(), idx.into()], "val2")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Lookup key in accumulator (result map, initially map1)
let acc_ptr = acc_phi.as_basic_value().into_pointer_value();
let found_val = self
.builder()
.build_call(lookup_fn, &[key_i64.into(), acc_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, not_found_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found: call closure(found_val, val2), insert result
self.builder().position_at_end(found_block);
let val2_ptr = self.value_to_ptr(val2)?;
let combined = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), found_val.into(), val2_ptr.into()],
"combined",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let combined_ptr = self.value_to_ptr(combined)?;
let new_map_found = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), combined_ptr.into(), acc_ptr.into()],
"ins_found",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Not found: just insert (key, val2)
self.builder().position_at_end(not_found_block);
let val2_ptr_nf = self.value_to_ptr(val2)?;
let new_map_nf = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), val2_ptr_nf.into(), acc_ptr.into()],
"ins_nf",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge block
self.builder().position_at_end(merge_block);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_map")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&new_map_found, found_block),
(&new_map_nf, not_found_block),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&map1_ptr, entry_block),
(&merge_phi.as_basic_value(), merge_block),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, merge_block),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.intersectionWith` -- intersection with combining function.
/// intersectionWith :: (a -> b -> c) -> Map k a -> Map k b -> Map k c
fn lower_builtin_map_intersection_with(
&mut self,
fn_expr: &Expr,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self.lower_expr(fn_expr)?.ok_or_else(|| {
CodegenError::Internal("map_intersectionWith: no function".to_string())
})?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map1_val = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_intersectionWith: no map1".to_string()))?;
let map1_ptr = self.value_to_ptr(map1_val)?;
let map2_val = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_intersectionWith: no map2".to_string()))?;
let map2_ptr = self.value_to_ptr(map2_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = *self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
// Start with empty result
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Get count of map1
let count1 = self
.builder()
.build_call(count_fn, &[map1_ptr.into()], "count1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "miw_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "miw_body");
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "miw_found");
let skip_block = self
.llvm_context()
.append_basic_block(current_fn, "miw_skip");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "miw_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header: iterate over map1
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count1, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key/val from map1, lookup in map2
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map1_ptr.into(), idx.into()], "key1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val1 = self
.builder()
.build_call(val_at_fn, &[map1_ptr.into(), idx.into()], "val1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Lookup key in map2
let found_val = self
.builder()
.build_call(lookup_fn, &[key_i64.into(), map2_ptr.into()], "lookup2")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, skip_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found: call closure(val1, found_val), insert into result
self.builder().position_at_end(found_block);
let val1_ptr = self.value_to_ptr(val1)?;
let combined = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val1_ptr.into(), found_val.into()],
"combined",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let combined_ptr = self.value_to_ptr(combined)?;
let acc_ptr_found = acc_phi.as_basic_value().into_pointer_value();
let new_map_found = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), combined_ptr.into(), acc_ptr_found.into()],
"ins_found",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(skip_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Skip/merge block
self.builder().position_at_end(skip_block);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&new_map_found, found_block),
(&acc_phi.as_basic_value(), loop_body),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), skip_block),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, skip_block),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.differenceWith` -- difference with combining function.
/// differenceWith :: (a -> b -> Maybe a) -> Map k a -> Map k b -> Map k a
fn lower_builtin_map_difference_with(
&mut self,
fn_expr: &Expr,
map1_expr: &Expr,
map2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_differenceWith: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map1_val = self
.lower_expr(map1_expr)?
.ok_or_else(|| CodegenError::Internal("map_differenceWith: no map1".to_string()))?;
let map1_ptr = self.value_to_ptr(map1_val)?;
let map2_val = self
.lower_expr(map2_expr)?
.ok_or_else(|| CodegenError::Internal("map_differenceWith: no map2".to_string()))?;
let map2_ptr = self.value_to_ptr(map2_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = *self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
// Start with empty result
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Get count of map1
let count1 = self
.builder()
.build_call(count_fn, &[map1_ptr.into()], "count1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mdw_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mdw_body");
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "mdw_found");
let just_block = self
.llvm_context()
.append_basic_block(current_fn, "mdw_just");
let not_found_block = self
.llvm_context()
.append_basic_block(current_fn, "mdw_notfound");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "mdw_merge");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mdw_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header: iterate over map1
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count1, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key/val from map1, lookup in map2
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map1_ptr.into(), idx.into()], "key1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val1 = self
.builder()
.build_call(val_at_fn, &[map1_ptr.into(), idx.into()], "val1")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Lookup key in map2
let found_val = self
.builder()
.build_call(lookup_fn, &[key_i64.into(), map2_ptr.into()], "lookup2")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, not_found_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found in map2: call closure(val1, val2) -> Maybe a
self.builder().position_at_end(found_block);
let val1_ptr = self.value_to_ptr(val1)?;
let maybe_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val1_ptr.into(), found_val.into()],
"maybe_result",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check Maybe tag: 0 = Nothing, 1 = Just
let maybe_ptr = self.value_to_ptr(maybe_result)?;
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, just_block, merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Just: extract value from Just, insert into result
self.builder().position_at_end(just_block);
let just_val = self.extract_adt_field(maybe_ptr, 1, 0)?;
let acc_ptr_just = acc_phi.as_basic_value().into_pointer_value();
let new_map_just = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), just_val.into(), acc_ptr_just.into()],
"ins_just",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Not found in map2: insert (key, val1) directly
self.builder().position_at_end(not_found_block);
let val1_ptr_nf = self.value_to_ptr(val1)?;
let acc_ptr_nf = acc_phi.as_basic_value().into_pointer_value();
let new_map_nf = self
.builder()
.build_call(
insert_fn,
&[key_i64.into(), val1_ptr_nf.into(), acc_ptr_nf.into()],
"ins_nf",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge block: phi for the three incoming edges (just, nothing/found, not_found)
self.builder().position_at_end(merge_block);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&new_map_just, just_block),
(&acc_phi.as_basic_value(), found_block), // Nothing case: no change
(&new_map_nf, not_found_block),
]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), merge_block),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, merge_block),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.insertWith` -- insert with combining function.
/// insertWith :: (v -> v -> v) -> k -> v -> Map k v -> Map k v
fn lower_builtin_map_insert_with(
&mut self,
fn_expr: &Expr,
key_expr: &Expr,
val_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_insertWith: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_insertWith: no key".to_string()))?;
let new_val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("map_insertWith: no value".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_insertWith: no map".to_string()))?;
let key_int = self.coerce_to_int(key_val)?;
let new_ptr = self.value_to_ptr(new_val)?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
// Lookup key in map
let found_val = self
.builder()
.build_call(lookup_fn, &[key_int.into(), map_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "iw_found");
let not_found_block = self
.llvm_context()
.append_basic_block(current_fn, "iw_notfound");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "iw_merge");
self.builder()
.build_conditional_branch(is_null, not_found_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found: call closure(new_val, old_val), insert result
self.builder().position_at_end(found_block);
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let combined = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), new_ptr.into(), found_val.into()],
"combined",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let combined_ptr = self.value_to_ptr(combined)?;
let new_map_found = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), combined_ptr.into(), map_ptr.into()],
"ins_found",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Not found: insert (key, new_val) directly
self.builder().position_at_end(not_found_block);
let new_map_nf = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), new_ptr.into(), map_ptr.into()],
"ins_nf",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "result")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
result_phi.add_incoming(&[
(&new_map_found, found_block),
(&new_map_nf, not_found_block),
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `Data.Map.adjust` -- adjust value at key with function.
/// adjust :: (v -> v) -> k -> Map k v -> Map k v
fn lower_builtin_map_adjust(
&mut self,
fn_expr: &Expr,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_adjust: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_adjust: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_adjust: no map".to_string()))?;
let key_int = self.coerce_to_int(key_val)?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let delete_fn = *self
.functions
.get(&VarId::new(1000108))
.ok_or_else(|| CodegenError::Internal("bhc_map_delete not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
// Lookup key in map
let found_val = self
.builder()
.build_call(lookup_fn, &[key_int.into(), map_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "adj_found");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "adj_merge");
self.builder()
.build_conditional_branch(is_null, merge_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
// Found: call closure(old_val), delete key, insert (key, result)
self.builder().position_at_end(found_block);
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let adjusted = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), found_val.into()],
"adjusted",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let adjusted_ptr = self.value_to_ptr(adjusted)?;
// Delete old key, then insert with new value
let deleted_map = self
.builder()
.build_call(delete_fn, &[key_int.into(), map_ptr.into()], "deleted")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let deleted_ptr = self.value_to_ptr(deleted_map)?;
let new_map = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), adjusted_ptr.into(), deleted_ptr.into()],
"ins_adj",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge: if not found, return original map; if found, return adjusted map
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "result")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
result_phi.add_incoming(&[(&map_ptr, entry_block), (&new_map, found_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `Data.Map.mapKeys` -- apply function to all keys, building new map.
/// mapKeys :: (k -> k') -> Map k v -> Map k' v
fn lower_builtin_map_map_keys(
&mut self,
fn_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_mapKeys: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_mapKeys: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// 1-arg closure: (key) -> new_key
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mmk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mmk_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mmk_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
// Convert key i64 to ptr, call closure(key_ptr) -> new_key_ptr
let key_ptr = self.int_to_ptr(key_i64)?;
let new_key = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), key_ptr.into()],
"new_key",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Convert new key back to i64 for map insert
let new_key_int = self.coerce_to_int(new_key)?;
let val_ptr = self.value_to_ptr(val)?;
let acc_ptr = acc_phi.as_basic_value().into_pointer_value();
let new_map = self
.builder()
.build_call(
insert_fn,
&[new_key_int.into(), val_ptr.into(), acc_ptr.into()],
"new_map",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&empty_map, entry_block), (&new_map, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.fromListWith` -- build map from list with combining function.
/// fromListWith :: (v -> v -> v) -> [(k,v)] -> Map k v
fn lower_builtin_map_from_list_with(
&mut self,
fn_expr: &Expr,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_fromListWith: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("map_fromListWith: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Start with empty map
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty_map")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mflw_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mflw_body");
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "mflw_found");
let not_found_block = self
.llvm_context()
.append_basic_block(current_fn, "mflw_notfound");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "mflw_merge");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mflw_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header: traverse the list
self.builder().position_at_end(loop_header);
let map_phi = self
.builder()
.build_phi(ptr_type, "map_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(ptr_type, "cur_list")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self
.builder()
.build_load(tm.i64_type(), cur_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("load tag failed: {:?}", e)))?
.into_int_value();
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: extract (key, value) pair from head
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
// Pair is an ADT with (key, value)
let key_ptr = self.extract_adt_field(head_ptr, 2, 0)?;
let val_ptr = self.extract_adt_field(head_ptr, 2, 1)?;
let key_int = self.coerce_to_int(key_ptr.into())?;
// Lookup key in current map
let map_acc_ptr = map_phi.as_basic_value().into_pointer_value();
let found_val = self
.builder()
.build_call(lookup_fn, &[key_int.into(), map_acc_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_null, not_found_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found: call closure(new_val, old_val), insert combined
self.builder().position_at_end(found_block);
let combined = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), val_ptr.into(), found_val.into()],
"combined",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let combined_ptr = self.value_to_ptr(combined)?;
let new_map_found = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), combined_ptr.into(), map_acc_ptr.into()],
"ins_found",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Not found: insert (key, val) directly
self.builder().position_at_end(not_found_block);
let new_map_nf = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), val_ptr.into(), map_acc_ptr.into()],
"ins_nf",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let merge_phi = self
.builder()
.build_phi(ptr_type, "merge_map")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
merge_phi.add_incoming(&[
(&new_map_found, found_block),
(&new_map_nf, not_found_block),
]);
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
map_phi.add_incoming(&[
(&empty_map, entry_block),
(&merge_phi.as_basic_value(), merge_block),
]);
cur_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, merge_block)]);
self.builder().position_at_end(loop_exit);
Ok(Some(map_phi.as_basic_value()))
}
/// Lower container operations that need higher-order functions (stub).
/// Covers: update, alter, unions (remaining unimplemented operations).
// Stub for not-yet-dispatched container operations.
#[allow(dead_code)]
fn lower_builtin_container_ho_stub(
&mut self,
args: &[&Expr],
_name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Evaluate all arguments (for side effects / forcing)
for arg in args {
let _ = self.lower_expr(arg)?;
}
// For operations that return containers, return the last container arg
// For operations that return values, return null
if let Some(last) = args.last() {
self.lower_expr(last)
} else {
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
}
/// Lower `Data.Map.unions` — fold `union` over a list of maps.
/// unions :: [Map k a] -> Map k a
fn lower_builtin_map_unions(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("map_unions: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Start with empty map
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty_map")
.map_err(|e| CodegenError::Internal(format!("empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("empty: void".to_string()))?;
let union_fn = *self
.functions
.get(&VarId::new(1000109))
.ok_or_else(|| CodegenError::Internal("bhc_map_union not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mu_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mu_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mu_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let map_phi = self
.builder()
.build_phi(tm.ptr_type(), "map_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(tm.ptr_type(), "cur_list")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self
.builder()
.build_load(tm.i64_type(), cur_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("load tag failed: {:?}", e)))?
.into_int_value();
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: extract head (a map) and tail, union with accumulator
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
let map_ptr_val = map_phi.as_basic_value().into_pointer_value();
let new_map = self
.builder()
.build_call(
union_fn,
&[map_ptr_val.into(), head_ptr.into()],
"union_map",
)
.map_err(|e| CodegenError::Internal(format!("union call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("union: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
map_phi.add_incoming(&[(&empty_map, entry_block), (&new_map, loop_body)]);
cur_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(map_phi.as_basic_value()))
}
/// Lower `Data.Map.keysSet` — build a Set from map keys.
/// keysSet :: Map k a -> Set k
fn lower_builtin_map_keys_set(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_keysSet: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = *self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let set_empty_fn = *self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let set_insert_fn = *self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
// Get count of map keys
let count = self
.builder()
.build_call(count_fn, &[map_ptr.into()], "map_count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
// Start with empty set
let empty_set = self
.builder()
.build_call(set_empty_fn, &[], "empty_set")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mks_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mks_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mks_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header: iterate 0..count
self.builder().position_at_end(loop_header);
let set_phi = self
.builder()
.build_phi(tm.ptr_type(), "set_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key at index, insert into set
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let set_ptr = set_phi.as_basic_value().into_pointer_value();
let new_set = self
.builder()
.build_call(set_insert_fn, &[key_i64.into(), set_ptr.into()], "new_set")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
set_phi.add_incoming(&[(&empty_set, entry_block), (&new_set, loop_body)]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, loop_body),
]);
self.builder().position_at_end(loop_exit);
Ok(Some(set_phi.as_basic_value()))
}
/// Lower `Data.Set.unions` — fold `union` over a list of sets.
/// unions :: [Set a] -> Set a
fn lower_builtin_set_unions(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("set_unions: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Start with empty set
let empty_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let empty_set = self
.builder()
.build_call(*empty_fn, &[], "empty_set")
.map_err(|e| CodegenError::Internal(format!("empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("empty: void".to_string()))?;
let union_fn = *self
.functions
.get(&VarId::new(1000127))
.ok_or_else(|| CodegenError::Internal("bhc_set_union not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "su_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "su_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "su_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let set_phi = self
.builder()
.build_phi(tm.ptr_type(), "set_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(tm.ptr_type(), "cur_list")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self
.builder()
.build_load(tm.i64_type(), cur_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("load tag failed: {:?}", e)))?
.into_int_value();
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: extract head (a set) and tail, union with accumulator
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
let set_ptr_val = set_phi.as_basic_value().into_pointer_value();
let new_set = self
.builder()
.build_call(
union_fn,
&[set_ptr_val.into(), head_ptr.into()],
"union_set",
)
.map_err(|e| CodegenError::Internal(format!("union call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("union: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
set_phi.add_incoming(&[(&empty_set, entry_block), (&new_set, loop_body)]);
cur_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(set_phi.as_basic_value()))
}
/// Lower `Data.Set.partition` — split set into (matching, non-matching).
/// partition :: (a -> Bool) -> Set a -> (Set a, Set a)
fn lower_builtin_set_partition(
&mut self,
fn_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("set_partition: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_partition: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
let empty_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let empty_yes = self
.builder()
.build_call(*empty_fn, &[], "empty_yes")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let empty_no = self
.builder()
.build_call(*empty_fn, &[], "empty_no")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sp_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sp_body");
let sp_yes = self.llvm_context().append_basic_block(current_fn, "sp_yes");
let sp_no = self.llvm_context().append_basic_block(current_fn, "sp_no");
let sp_merge = self
.llvm_context()
.append_basic_block(current_fn, "sp_merge");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sp_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let yes_phi = self
.builder()
.build_phi(ptr_type, "yes_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let no_phi = self
.builder()
.build_phi(ptr_type, "no_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "done")
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get element, call predicate
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
let pred_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), elem_ptr.into()],
"pred",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
let pred_ptr = self.value_to_ptr(pred_result)?;
let pred_tag = self.extract_adt_tag(pred_ptr)?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
pred_tag,
tm.i64_type().const_zero(),
"is_true",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, sp_yes, sp_no)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Yes branch: insert into yes_acc
self.builder().position_at_end(sp_yes);
let yes_ptr = yes_phi.as_basic_value().into_pointer_value();
let yes_inserted = self
.builder()
.build_call(insert_fn, &[elem_i64.into(), yes_ptr.into()], "yes_ins")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(sp_merge)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// No branch: insert into no_acc
self.builder().position_at_end(sp_no);
let no_ptr = no_phi.as_basic_value().into_pointer_value();
let no_inserted = self
.builder()
.build_call(insert_fn, &[elem_i64.into(), no_ptr.into()], "no_ins")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(sp_merge)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge: phi for yes_acc and no_acc
self.builder().position_at_end(sp_merge);
let yes_merge = self
.builder()
.build_phi(ptr_type, "yes_merge")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
yes_merge.add_incoming(&[(&yes_inserted, sp_yes), (&yes_phi.as_basic_value(), sp_no)]);
let no_merge = self
.builder()
.build_phi(ptr_type, "no_merge")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
no_merge.add_incoming(&[(&no_phi.as_basic_value(), sp_yes), (&no_inserted, sp_no)]);
let new_idx = self
.builder()
.build_int_add(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("add failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
yes_phi.add_incoming(&[
(&empty_yes, entry_block),
(&yes_merge.as_basic_value(), sp_merge),
]);
no_phi.add_incoming(&[
(&empty_no, entry_block),
(&no_merge.as_basic_value(), sp_merge),
]);
idx_phi.add_incoming(&[
(&tm.i64_type().const_zero(), entry_block),
(&new_idx, sp_merge),
]);
// Exit: build tuple (yes_set, no_set)
self.builder().position_at_end(loop_exit);
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, yes_phi.as_basic_value())?;
self.store_adt_field(pair, 2, 1, no_phi.as_basic_value())?;
Ok(Some(pair.into()))
}
/// Lower `Data.Map.update` — update a value at a key, possibly deleting it.
/// update :: (a -> Maybe a) -> k -> Map k a -> Map k a
fn lower_builtin_map_update(
&mut self,
fn_expr: &Expr,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_update: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_update: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_update: no map".to_string()))?;
let key_int = self.coerce_to_int(key_val)?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let delete_fn = *self
.functions
.get(&VarId::new(1000108))
.ok_or_else(|| CodegenError::Internal("bhc_map_delete not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
// Lookup key in map
let found_val = self
.builder()
.build_call(lookup_fn, &[key_int.into(), map_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
let found_block = self
.llvm_context()
.append_basic_block(current_fn, "upd_found");
let just_block = self
.llvm_context()
.append_basic_block(current_fn, "upd_just");
let nothing_block = self
.llvm_context()
.append_basic_block(current_fn, "upd_nothing");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "upd_merge");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_conditional_branch(is_null, merge_block, found_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Found: call closure(old_val) -> Maybe a
self.builder().position_at_end(found_block);
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let maybe_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), found_val.into()],
"maybe_result",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check Maybe tag: 0 = Nothing, 1 = Just
let maybe_ptr = self.value_to_ptr(maybe_result)?;
let maybe_tag = self.extract_adt_tag(maybe_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
maybe_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_just, just_block, nothing_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Just: extract value, delete+insert
self.builder().position_at_end(just_block);
let just_val = self.extract_adt_field(maybe_ptr, 1, 0)?;
let deleted_map_j = self
.builder()
.build_call(delete_fn, &[key_int.into(), map_ptr.into()], "deleted_j")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let deleted_ptr_j = self.value_to_ptr(deleted_map_j)?;
let new_map_j = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), just_val.into(), deleted_ptr_j.into()],
"ins_just",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Nothing: delete the key
self.builder().position_at_end(nothing_block);
let deleted_map_n = self
.builder()
.build_call(delete_fn, &[key_int.into(), map_ptr.into()], "deleted_n")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge: not found -> original map, Just -> inserted, Nothing -> deleted
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "result")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
result_phi.add_incoming(&[
(&map_ptr, entry_block), // key not found: return original
(&new_map_j, just_block), // Just: return updated map
(&deleted_map_n, nothing_block), // Nothing: return map with key deleted
]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `Data.Map.alter` — alter a value at a key.
/// alter :: (Maybe a -> Maybe a) -> k -> Map k a -> Map k a
fn lower_builtin_map_alter(
&mut self,
fn_expr: &Expr,
key_expr: &Expr,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let fn_val = self
.lower_expr(fn_expr)?
.ok_or_else(|| CodegenError::Internal("map_alter: no function".to_string()))?;
let fn_ptr = self.value_to_ptr(fn_val)?;
let key_val = self
.lower_expr(key_expr)?
.ok_or_else(|| CodegenError::Internal("map_alter: no key".to_string()))?;
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_alter: no map".to_string()))?;
let key_int = self.coerce_to_int(key_val)?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let delete_fn = *self
.functions
.get(&VarId::new(1000108))
.ok_or_else(|| CodegenError::Internal("bhc_map_delete not declared".to_string()))?;
let lookup_fn = *self
.functions
.get(&VarId::new(1000105))
.ok_or_else(|| CodegenError::Internal("bhc_map_lookup not declared".to_string()))?;
// Lookup key in map
let found_val = self
.builder()
.build_call(lookup_fn, &[key_int.into(), map_ptr.into()], "lookup")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_pointer_value();
let is_null = self
.builder()
.build_is_null(found_val, "is_null")
.map_err(|e| CodegenError::Internal(format!("is_null failed: {:?}", e)))?;
// Build input Maybe: Nothing if not found, Just(val) if found
let nothing = self.alloc_adt(0, 0)?; // Nothing = tag 0, 0 fields
let just = self.alloc_adt(1, 1)?; // Just = tag 1, 1 field
self.store_adt_field(just, 1, 0, found_val.into())?;
let input_maybe = self
.builder()
.build_select(is_null, nothing, just, "input_maybe")
.map_err(|e| CodegenError::Internal(format!("select failed: {:?}", e)))?;
// Call closure(input_maybe) -> Maybe a
let closure_fn_ptr = self.extract_closure_fn_ptr(fn_ptr)?;
let call_fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let input_ptr = self.value_to_ptr(input_maybe)?;
let maybe_result = self
.builder()
.build_indirect_call(
call_fn_type,
closure_fn_ptr,
&[fn_ptr.into(), input_ptr.into()],
"maybe_result",
)
.map_err(|e| CodegenError::Internal(format!("indirect call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("closure returned void".to_string()))?;
// Check result Maybe tag: 0 = Nothing, 1 = Just
let result_ptr = self.value_to_ptr(maybe_result)?;
let result_tag = self.extract_adt_tag(result_ptr)?;
let is_just = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
result_tag,
tm.i64_type().const_zero(),
"is_just",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
let just_block = self
.llvm_context()
.append_basic_block(current_fn, "alt_just");
let nothing_block = self
.llvm_context()
.append_basic_block(current_fn, "alt_nothing");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "alt_merge");
self.builder()
.build_conditional_branch(is_just, just_block, nothing_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Just: extract value, delete old key (if any), insert new value
self.builder().position_at_end(just_block);
let just_val = self.extract_adt_field(result_ptr, 1, 0)?;
let deleted_map_j = self
.builder()
.build_call(delete_fn, &[key_int.into(), map_ptr.into()], "deleted_j")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let deleted_ptr_j = self.value_to_ptr(deleted_map_j)?;
let new_map_j = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), just_val.into(), deleted_ptr_j.into()],
"ins_just",
)
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Nothing: delete the key (safe even if key doesn't exist)
self.builder().position_at_end(nothing_block);
let deleted_map_n = self
.builder()
.build_call(delete_fn, &[key_int.into(), map_ptr.into()], "deleted_n")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_block);
let result_phi = self
.builder()
.build_phi(ptr_type, "result")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
result_phi.add_incoming(&[(&new_map_j, just_block), (&deleted_map_n, nothing_block)]);
Ok(Some(result_phi.as_basic_value()))
}
/// Lower `Data.Map.toList` / `toAscList` / `toDescList` / `assocs` / `keys` / `elems`.
/// Builds a list from the container using iteration RTS functions.
fn lower_builtin_map_to_list(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_toList: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Get element count
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "map_count")
.map_err(|e| CodegenError::Internal(format!("map_count call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("map_count: returned void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
// Build list backwards: iterate from (count-1) down to 0, consing (key,value) tuples
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mtl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mtl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mtl_exit");
let nil = self.build_nil()?;
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header with PHI nodes
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get key and value at index, build (key, value) tuple, cons onto acc
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("key_at call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("key_at: void".to_string()))?
.into_int_value();
let val_ptr = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("val_at call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("val_at: void".to_string()))?;
// Build a (key, value) pair as a 2-tuple ADT (tag=0, arity=2)
let key_ptr = self.int_to_ptr(key_i64)?;
let pair = self.alloc_adt(0, 2)?;
self.store_adt_field(pair, 2, 0, key_ptr.into())?;
self.store_adt_field(pair, 2, 1, val_ptr)?;
// Cons pair onto accumulator
let new_acc = self.build_cons(pair.into(), acc_phi.as_basic_value())?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.fromList` — iterates the list, inserting each (k,v) pair.
fn lower_builtin_map_from_list(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("map_fromList: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Start with empty map
let empty_fn = self
.functions
.get(&VarId::new(1000100))
.ok_or_else(|| CodegenError::Internal("bhc_map_empty not declared".to_string()))?;
let empty_map = self
.builder()
.build_call(*empty_fn, &[], "empty_map")
.map_err(|e| CodegenError::Internal(format!("empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("empty: void".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000107))
.ok_or_else(|| CodegenError::Internal("bhc_map_insert not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mfl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mfl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mfl_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let map_phi = self
.builder()
.build_phi(tm.ptr_type(), "map_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(tm.ptr_type(), "cur_list")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self
.builder()
.build_load(tm.i64_type(), cur_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("load tag failed: {:?}", e)))?
.into_int_value();
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: extract (key, value) pair from head, insert into map
self.builder().position_at_end(loop_body);
// head = field 0 of cons cell (a pair)
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
// tail = field 1 of cons cell
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
// pair is an ADT with (key, value) — extract key (field 0) and value (field 1)
let key_ptr = self.extract_adt_field(head_ptr, 2, 0)?;
let val_ptr = self.extract_adt_field(head_ptr, 2, 1)?;
let key_int = self.coerce_to_int(key_ptr.into())?;
let map_ptr = map_phi.as_basic_value().into_pointer_value();
let new_map = self
.builder()
.build_call(
insert_fn,
&[key_int.into(), val_ptr.into(), map_ptr.into()],
"new_map",
)
.map_err(|e| CodegenError::Internal(format!("insert call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("insert: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
map_phi.add_incoming(&[(&empty_map, entry_block), (&new_map, loop_body)]);
cur_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(map_phi.as_basic_value()))
}
/// Lower `Data.Map.keys` — returns list of keys only.
fn lower_builtin_map_keys(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_keys: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "map_count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_at_fn = *self
.functions
.get(&VarId::new(1000161))
.ok_or_else(|| CodegenError::Internal("bhc_map_key_at not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "mk_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "mk_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "mk_exit");
let nil = self.build_nil()?;
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let key_i64 = self
.builder()
.build_call(key_at_fn, &[map_ptr.into(), idx.into()], "key")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let key_ptr = self.int_to_ptr(key_i64)?;
let new_acc = self.build_cons(key_ptr.into(), acc_phi.as_basic_value())?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Map.elems` — returns list of values only.
fn lower_builtin_map_elems(
&mut self,
map_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let map_val = self
.lower_expr(map_expr)?
.ok_or_else(|| CodegenError::Internal("map_elems: no map".to_string()))?;
let map_ptr = self.value_to_ptr(map_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
let count_fn = self
.functions
.get(&VarId::new(1000160))
.ok_or_else(|| CodegenError::Internal("bhc_map_keys_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[map_ptr.into()], "map_count")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?
.into_int_value();
let val_at_fn = *self
.functions
.get(&VarId::new(1000162))
.ok_or_else(|| CodegenError::Internal("bhc_map_value_at not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "me_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "me_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "me_exit");
let nil = self.build_nil()?;
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_body);
let val = self
.builder()
.build_call(val_at_fn, &[map_ptr.into(), idx.into()], "val")
.map_err(|e| CodegenError::Internal(format!("call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("void".to_string()))?;
let new_acc = self.build_cons(val, acc_phi.as_basic_value())?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
// --- Data.Set handlers ---
/// Lower `Data.Set.empty`.
fn lower_builtin_set_empty(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "set_empty")
.map_err(|e| CodegenError::Internal(format!("set_empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_empty: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Set.singleton`.
fn lower_builtin_set_singleton(
&mut self,
val_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("set_singleton: no value".to_string()))?;
let val_int = self.coerce_to_int(val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000121))
.ok_or_else(|| CodegenError::Internal("bhc_set_singleton not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_int.into()], "set_singleton")
.map_err(|e| CodegenError::Internal(format!("set_singleton call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_singleton: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Set.null`.
fn lower_builtin_set_null(
&mut self,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_null: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000122))
.ok_or_else(|| CodegenError::Internal("bhc_set_null not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[set_ptr.into()], "set_null")
.map_err(|e| CodegenError::Internal(format!("set_null call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_null: returned void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "set_null_bool")
}
/// Lower `Data.Set.size`.
fn lower_builtin_set_size(
&mut self,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_size: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000123))
.ok_or_else(|| CodegenError::Internal("bhc_set_size not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[set_ptr.into()], "set_size")
.map_err(|e| CodegenError::Internal(format!("set_size call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_size: returned void".to_string()))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `Data.Set.member`.
fn lower_builtin_set_member(
&mut self,
val_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("set_member: no value".to_string()))?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_member: no set".to_string()))?;
let val_int = self.coerce_to_int(val)?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000124))
.ok_or_else(|| CodegenError::Internal("bhc_set_member not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_int.into(), set_ptr.into()], "set_member")
.map_err(|e| CodegenError::Internal(format!("set_member call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_member: returned void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "set_member_bool")
}
/// Lower `Data.Set.insert`.
fn lower_builtin_set_insert(
&mut self,
val_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("set_insert: no value".to_string()))?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_insert: no set".to_string()))?;
let val_int = self.coerce_to_int(val)?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_int.into(), set_ptr.into()], "set_insert")
.map_err(|e| CodegenError::Internal(format!("set_insert call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_insert: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower `Data.Set.delete`.
fn lower_builtin_set_delete(
&mut self,
val_expr: &Expr,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(val_expr)?
.ok_or_else(|| CodegenError::Internal("set_delete: no value".to_string()))?;
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_delete: no set".to_string()))?;
let val_int = self.coerce_to_int(val)?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000126))
.ok_or_else(|| CodegenError::Internal("bhc_set_delete not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[val_int.into(), set_ptr.into()], "set_delete")
.map_err(|e| CodegenError::Internal(format!("set_delete call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_delete: returned void".to_string()))?;
Ok(Some(result))
}
/// Lower Data.Set binary operations (union, intersection, difference).
fn lower_builtin_set_binary(
&mut self,
set1_expr: &Expr,
set2_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let s1 = self
.lower_expr(set1_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set1", name)))?;
let s2 = self
.lower_expr(set2_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set2", name)))?;
let s1_ptr = self.value_to_ptr(s1)?;
let s2_ptr = self.value_to_ptr(s2)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[s1_ptr.into(), s2_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(result))
}
/// Lower Data.Set predicate operations (isSubsetOf, isProperSubsetOf).
fn lower_builtin_set_predicate(
&mut self,
set1_expr: &Expr,
set2_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let s1 = self
.lower_expr(set1_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set1", name)))?;
let s2 = self
.lower_expr(set2_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set2", name)))?;
let s1_ptr = self.value_to_ptr(s1)?;
let s2_ptr = self.value_to_ptr(s2)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[s1_ptr.into(), s2_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `Data.Set.findMin` / `findMax`.
fn lower_builtin_set_find_extremum(
&mut self,
set_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set", name)))?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[set_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Lower `Data.Set.deleteMin` / `deleteMax`.
fn lower_builtin_set_delete_extremum(
&mut self,
set_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set", name)))?;
let set_ptr = self.value_to_ptr(set_val)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[set_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
Ok(Some(result))
}
/// Lower `Data.Set.toList` / `toAscList` / `toDescList` / `elems` (stub).
fn lower_builtin_set_to_list(
&mut self,
set_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal("set_toList: no set".to_string()))?;
let set_ptr = self.value_to_ptr(set_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Get element count
let count_fn = self
.functions
.get(&VarId::new(1000163))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[set_ptr.into()], "set_count")
.map_err(|e| CodegenError::Internal(format!("set_count call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("set_count: void".to_string()))?
.into_int_value();
let elem_at_fn = *self
.functions
.get(&VarId::new(1000164))
.ok_or_else(|| CodegenError::Internal("bhc_set_elem_at not declared".to_string()))?;
// Build list backwards: iterate from (count-1) down to 0
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "stl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "stl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "stl_exit");
let nil = self.build_nil()?;
let start_idx = self
.builder()
.build_int_sub(count, tm.i64_type().const_int(1, false), "start_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "idx")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"done",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: get element at index, cons onto accumulator
self.builder().position_at_end(loop_body);
let elem_i64 = self
.builder()
.build_call(elem_at_fn, &[set_ptr.into(), idx.into()], "elem")
.map_err(|e| CodegenError::Internal(format!("elem_at call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("elem_at: void".to_string()))?
.into_int_value();
let elem_ptr = self.int_to_ptr(elem_i64)?;
let new_acc = self.build_cons(elem_ptr.into(), acc_phi.as_basic_value())?;
let new_idx = self
.builder()
.build_int_sub(idx, tm.i64_type().const_int(1, false), "new_idx")
.map_err(|e| CodegenError::Internal(format!("sub failed: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Lower `Data.Set.fromList` — iterates list, inserting each element.
fn lower_builtin_set_from_list(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list_val = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("set_fromList: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list_val)?;
let tm = self.type_mapper();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Start with empty set
let empty_fn = self
.functions
.get(&VarId::new(1000120))
.ok_or_else(|| CodegenError::Internal("bhc_set_empty not declared".to_string()))?;
let empty_set = self
.builder()
.build_call(*empty_fn, &[], "empty_set")
.map_err(|e| CodegenError::Internal(format!("empty call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("empty: void".to_string()))?;
let insert_fn = *self
.functions
.get(&VarId::new(1000125))
.ok_or_else(|| CodegenError::Internal("bhc_set_insert not declared".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sfl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sfl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sfl_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
self.builder().position_at_end(loop_header);
let set_phi = self
.builder()
.build_phi(tm.ptr_type(), "set_acc")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(tm.ptr_type(), "cur_list")
.map_err(|e| CodegenError::Internal(format!("phi failed: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self
.builder()
.build_load(tm.i64_type(), cur_ptr, "tag")
.map_err(|e| CodegenError::Internal(format!("load tag failed: {:?}", e)))?
.into_int_value();
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"is_nil",
)
.map_err(|e| CodegenError::Internal(format!("cmp failed: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
// Loop body: extract head element, insert into set
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
let elem_int = self.coerce_to_int(head_ptr.into())?;
let set_ptr = set_phi.as_basic_value().into_pointer_value();
let new_set = self
.builder()
.build_call(insert_fn, &[elem_int.into(), set_ptr.into()], "new_set")
.map_err(|e| CodegenError::Internal(format!("insert call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("insert: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("branch failed: {:?}", e)))?;
set_phi.add_incoming(&[(&empty_set, entry_block), (&new_set, loop_body)]);
cur_phi.add_incoming(&[(&list_ptr, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(set_phi.as_basic_value()))
}
/// Lower `Data.Set.lookupMin` / `lookupMax` - returns Maybe.
fn lower_builtin_set_lookup_extremum(
&mut self,
set_expr: &Expr,
rts_id: usize,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let set_val = self
.lower_expr(set_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no set", name)))?;
let set_ptr = self.value_to_ptr(set_val)?;
// Check if set is empty
let null_fn = self
.functions
.get(&VarId::new(1000122))
.ok_or_else(|| CodegenError::Internal("bhc_set_null not declared".to_string()))?;
let is_empty = self
.builder()
.build_call(*null_fn, &[set_ptr.into()], "set_is_empty")
.map_err(|e| CodegenError::Internal(format!("{}: null check failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: null returned void", name)))?;
let is_empty_bool = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
is_empty.into_int_value(),
self.type_mapper().i64_type().const_zero(),
"is_empty_bool",
)
.map_err(|e| CodegenError::Internal(format!("{}: compare failed: {:?}", name, e)))?;
let nothing = self.alloc_adt(0, 0)?;
let rts_fn = self
.functions
.get(&VarId::new(rts_id))
.ok_or_else(|| CodegenError::Internal(format!("{} RTS function not declared", name)))?;
let val = self
.builder()
.build_call(*rts_fn, &[set_ptr.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let val_ptr = self.int_to_ptr(val.into_int_value())?;
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, val_ptr.into())?;
let result = self
.builder()
.build_select(is_empty_bool, nothing, just, "maybe_extremum")
.map_err(|e| CodegenError::Internal(format!("{}: select failed: {:?}", name, e)))?;
Ok(Some(result))
}
// ========================================================================
// Text / ByteArray handler methods
// ========================================================================
/// Text nullary: () -> ptr (e.g. text_empty)
fn lower_builtin_text_nullary(
&mut self,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// Text unary: (ptr) -> ptr (e.g. text_tail, text_reverse)
fn lower_builtin_text_unary_ptr_to_ptr(
&mut self,
expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", label)))?;
let ptr = self.value_to_ptr(val)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// Text unary: (ptr) -> i64 (e.g. text_null, text_length, text_head)
fn lower_builtin_text_unary_ptr_to_int(
&mut self,
expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", label)))?;
let ptr = self.value_to_ptr(val)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Text unary: (i64) -> ptr (e.g. text_singleton)
fn lower_builtin_text_unary_int_to_ptr(
&mut self,
expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let val = self
.lower_expr(expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no value", label)))?;
let int_val = self.coerce_to_int(val)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[int_val.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// Text binary: (ptr, ptr) -> ptr (e.g. text_append)
fn lower_builtin_text_binary_ptr_to_ptr(
&mut self,
expr_a: &Expr,
expr_b: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a = self
.lower_expr(expr_a)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no a", label)))?;
let b = self
.lower_expr(expr_b)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no b", label)))?;
let a_ptr = self.value_to_ptr(a)?;
let b_ptr = self.value_to_ptr(b)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[a_ptr.into(), b_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// Text binary: (ptr, ptr) -> i64 (e.g. text_eq, text_compare)
fn lower_builtin_text_binary_ptr_to_int(
&mut self,
expr_a: &Expr,
expr_b: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a = self
.lower_expr(expr_a)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no a", label)))?;
let b = self
.lower_expr(expr_b)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no b", label)))?;
let a_ptr = self.value_to_ptr(a)?;
let b_ptr = self.value_to_ptr(b)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[a_ptr.into(), b_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Text: (i64, ptr) -> ptr (e.g. text_take, text_drop)
fn lower_builtin_text_int_ptr_to_ptr(
&mut self,
int_expr: &Expr,
ptr_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n = self
.lower_expr(int_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no int", label)))?;
let t = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no ptr", label)))?;
let n_int = self.coerce_to_int(n)?;
let t_ptr = self.value_to_ptr(t)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[n_int.into(), t_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// ByteArray: (ptr, i64) -> i64 (e.g. ba_index)
fn lower_builtin_text_ptr_int_to_int(
&mut self,
ptr_expr: &Expr,
int_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let p = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no ptr", label)))?;
let n = self
.lower_expr(int_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no int", label)))?;
let p_ptr = self.value_to_ptr(p)?;
let n_int = self.coerce_to_int(n)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[p_ptr.into(), n_int.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// ByteArray: (ptr, i64) -> ptr (e.g. ba_ptr_plus)
fn lower_builtin_text_ptr_int_to_ptr(
&mut self,
ptr_expr: &Expr,
int_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let p = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no ptr", label)))?;
let n = self
.lower_expr(int_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no int", label)))?;
let p_ptr = self.value_to_ptr(p)?;
let n_int = self.coerce_to_int(n)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[p_ptr.into(), n_int.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// ByteArray copy: (ptr, ptr, i64, i64) -> void
fn lower_builtin_ba_copy(
&mut self,
dst_expr: &Expr,
src_expr: &Expr,
offset_expr: &Expr,
len_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let dst = self
.lower_expr(dst_expr)?
.ok_or_else(|| CodegenError::Internal("ba_copy: no dst".to_string()))?;
let src = self
.lower_expr(src_expr)?
.ok_or_else(|| CodegenError::Internal("ba_copy: no src".to_string()))?;
let off = self
.lower_expr(offset_expr)?
.ok_or_else(|| CodegenError::Internal("ba_copy: no offset".to_string()))?;
let len = self
.lower_expr(len_expr)?
.ok_or_else(|| CodegenError::Internal("ba_copy: no len".to_string()))?;
let dst_ptr = self.value_to_ptr(dst)?;
let src_ptr = self.value_to_ptr(src)?;
let off_int = self.coerce_to_int(off)?;
let len_int = self.coerce_to_int(len)?;
let rts_fn = self
.functions
.get(&VarId::new(1000253))
.ok_or_else(|| CodegenError::Internal("bhc_bytearray_copy not declared".to_string()))?;
self.builder()
.build_call(
*rts_fn,
&[
dst_ptr.into(),
src_ptr.into(),
off_int.into(),
len_int.into(),
],
"ba_copy",
)
.map_err(|e| CodegenError::Internal(format!("ba_copy call failed: {:?}", e)))?;
// void return — return unit (null ptr)
let unit = self.type_mapper().ptr_type().const_null();
Ok(Some(unit.into()))
}
/// ByteArray poke: (ptr, i64, i64) -> void
fn lower_builtin_ba_poke(
&mut self,
ptr_expr: &Expr,
offset_expr: &Expr,
value_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let p = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal("ba_poke: no ptr".to_string()))?;
let off = self
.lower_expr(offset_expr)?
.ok_or_else(|| CodegenError::Internal("ba_poke: no offset".to_string()))?;
let val = self
.lower_expr(value_expr)?
.ok_or_else(|| CodegenError::Internal("ba_poke: no value".to_string()))?;
let p_ptr = self.value_to_ptr(p)?;
let off_int = self.coerce_to_int(off)?;
let val_int = self.coerce_to_int(val)?;
let rts_fn = self
.functions
.get(&VarId::new(1000255))
.ok_or_else(|| CodegenError::Internal("bhc_poke_byte not declared".to_string()))?;
self.builder()
.build_call(
*rts_fn,
&[p_ptr.into(), off_int.into(), val_int.into()],
"ba_poke",
)
.map_err(|e| CodegenError::Internal(format!("ba_poke call failed: {:?}", e)))?;
let unit = self.type_mapper().ptr_type().const_null();
Ok(Some(unit.into()))
}
/// Text unpack: builds a BHC cons-list from a Text using char_count + char_at iteration.
///
/// This uses the same pattern as `map_keys` codegen:
/// 1. Call bhc_text_char_count to get N
/// 2. Loop from N-1 down to 0, calling bhc_text_char_at(text, i)
/// 3. Build cons-list in reverse (so we end up with correct order)
fn lower_builtin_text_unpack(
&mut self,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let text_val = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_unpack: no text".to_string()))?;
let text_ptr = self.value_to_ptr(text_val)?;
// Get char count
let count_fn = self.functions.get(&VarId::new(1000224)).ok_or_else(|| {
CodegenError::Internal("bhc_text_char_count not declared".to_string())
})?;
let count_val = self
.builder()
.build_call(*count_fn, &[text_ptr.into()], "char_count")
.map_err(|e| CodegenError::Internal(format!("char_count call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("char_count: returned void".to_string()))?
.into_int_value();
let char_at_fn = self
.functions
.get(&VarId::new(1000225))
.ok_or_else(|| CodegenError::Internal("bhc_text_char_at not declared".to_string()))?;
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let ptr_type = tm.ptr_type();
// Build Nil (empty list) as starting accumulator
let nil = self.alloc_adt(0, 0)?;
// Create loop blocks
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("text_unpack: no current function".to_string())
})?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "unpack_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "unpack_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "unpack_exit");
// Initial: i = count - 1, acc = nil
let one = i64_type.const_int(1, false);
let init_i = self
.builder()
.build_int_sub(count_val, one, "init_i")
.map_err(|e| CodegenError::Internal(format!("text_unpack sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("text_unpack br: {:?}", e)))?;
// Loop header: phi for i and acc
self.builder().position_at_end(loop_header);
let phi_i = self
.builder()
.build_phi(i64_type, "i")
.map_err(|e| CodegenError::Internal(format!("text_unpack phi_i: {:?}", e)))?;
let phi_acc = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("text_unpack phi_acc: {:?}", e)))?;
let zero = i64_type.const_int(0, false);
let cond = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SGE,
phi_i.as_basic_value().into_int_value(),
zero,
"cond",
)
.map_err(|e| CodegenError::Internal(format!("text_unpack cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(cond, loop_body, loop_exit)
.map_err(|e| CodegenError::Internal(format!("text_unpack cbr: {:?}", e)))?;
// Loop body: char = char_at(text, i); cons = Cons(char, acc); i = i - 1
self.builder().position_at_end(loop_body);
let i_val = phi_i.as_basic_value().into_int_value();
let char_val = self
.builder()
.build_call(*char_at_fn, &[text_ptr.into(), i_val.into()], "char_at")
.map_err(|e| CodegenError::Internal(format!("char_at call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("char_at: returned void".to_string()))?;
// Alloc Cons node: tag=1, arity=2
let cons = self.alloc_adt(1, 2)?;
let char_ptr = self.int_to_ptr(char_val.into_int_value())?;
self.store_adt_field(cons, 2, 0, char_ptr.into())?;
let acc_val = phi_acc.as_basic_value().into_pointer_value();
self.store_adt_field(cons, 2, 1, acc_val.into())?;
let next_i = self
.builder()
.build_int_sub(i_val, one, "next_i")
.map_err(|e| CodegenError::Internal(format!("text_unpack dec: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("text_unpack loop_br: {:?}", e)))?;
// Wire phi nodes
let pre_header = loop_header.get_previous_basic_block().ok_or_else(|| {
CodegenError::Internal("text_unpack: no pre-header block".to_string())
})?;
phi_i.add_incoming(&[(&init_i, pre_header), (&next_i, loop_body)]);
phi_acc.add_incoming(&[(&nil, pre_header), (&cons, loop_body)]);
// Exit: return accumulated list
self.builder().position_at_end(loop_exit);
Ok(Some(phi_acc.as_basic_value()))
}
/// Text map: (Char -> Char) -> Text -> Text
///
/// The closure is dispatched via the RTS `bhc_text_map(fn_ptr, env_ptr, text_ptr)`.
/// The Haskell closure is represented as an ADT with fn_ptr at field 0 and env at field 1.
fn lower_builtin_text_map(
&mut self,
func_expr: &Expr,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("text_map: no func".to_string()))?;
let text = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_map: no text".to_string()))?;
let func_ptr = self.value_to_ptr(func)?;
let text_ptr = self.value_to_ptr(text)?;
// For closure dispatch, pass the closure pointer as both fn_ptr and env_ptr.
// The RTS function will interpret the closure accordingly.
let rts_fn = self
.functions
.get(&VarId::new(1000226))
.ok_or_else(|| CodegenError::Internal("bhc_text_map not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[func_ptr.into(), func_ptr.into(), text_ptr.into()],
"text_map",
)
.map_err(|e| CodegenError::Internal(format!("text_map call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_map: returned void".to_string()))?;
Ok(Some(result))
}
/// Text filter: (Char -> Bool) -> Text -> Text
///
/// Dispatches via `bhc_text_filter(fn_ptr, env_ptr, text_ptr)`.
fn lower_builtin_text_filter(
&mut self,
func_expr: &Expr,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("text_filter: no func".to_string()))?;
let text = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_filter: no text".to_string()))?;
let func_ptr = self.value_to_ptr(func)?;
let text_ptr = self.value_to_ptr(text)?;
let rts_fn = self
.functions
.get(&VarId::new(1000227))
.ok_or_else(|| CodegenError::Internal("bhc_text_filter not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[func_ptr.into(), func_ptr.into(), text_ptr.into()],
"text_filter",
)
.map_err(|e| CodegenError::Internal(format!("text_filter call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_filter: returned void".to_string()))?;
Ok(Some(result))
}
/// Text foldl': (a -> Char -> a) -> a -> Text -> a
///
/// Dispatches via `bhc_text_foldl(fn_ptr, env_ptr, init_val, text_ptr)`.
fn lower_builtin_text_foldl(
&mut self,
func_expr: &Expr,
init_expr: &Expr,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("text_foldl: no func".to_string()))?;
let init = self
.lower_expr(init_expr)?
.ok_or_else(|| CodegenError::Internal("text_foldl: no init".to_string()))?;
let text = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_foldl: no text".to_string()))?;
let func_ptr = self.value_to_ptr(func)?;
let init_int = self.coerce_to_int(init)?;
let text_ptr = self.value_to_ptr(text)?;
let rts_fn = self
.functions
.get(&VarId::new(1000228))
.ok_or_else(|| CodegenError::Internal("bhc_text_foldl not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[
func_ptr.into(),
func_ptr.into(),
init_int.into(),
text_ptr.into(),
],
"text_foldl",
)
.map_err(|e| CodegenError::Internal(format!("text_foldl call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_foldl: returned void".to_string()))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// Text ternary: (ptr, ptr, ptr) -> ptr (e.g. text_replace)
fn lower_builtin_text_ternary_ptr(
&mut self,
expr_a: &Expr,
expr_b: &Expr,
expr_c: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let a = self
.lower_expr(expr_a)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no a", label)))?;
let b = self
.lower_expr(expr_b)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no b", label)))?;
let c = self
.lower_expr(expr_c)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no c", label)))?;
let a_ptr = self.value_to_ptr(a)?;
let b_ptr = self.value_to_ptr(b)?;
let c_ptr = self.value_to_ptr(c)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[a_ptr.into(), b_ptr.into(), c_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
// ================================================================
// Data.Text.IO codegen helpers
// ================================================================
/// Data.Text.IO.readFile: convert [Char] path to C-string, call RTS, return Text ptr.
fn lower_builtin_text_io_read_file(
&mut self,
path_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("text_io_read_file: no path".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let rts_fn = self
.functions
.get(&VarId::new(1000240))
.ok_or_else(|| CodegenError::Internal("bhc_text_read_file not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[path_cstr.into()], "text_read_file")
.map_err(|e| CodegenError::Internal(format!("text_read_file call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_read_file: returned void".to_string()))?;
Ok(Some(result))
}
/// Data.Text.IO.writeFile: convert [Char] path to C-string, lower Text arg, call RTS void.
fn lower_builtin_text_io_write_file(
&mut self,
path_expr: &Expr,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("text_io_write_file: no path".to_string()))?;
let text_val = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_io_write_file: no text".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let text_ptr = self.value_to_ptr(text_val)?;
let rts_fn = self.functions.get(&VarId::new(1000241)).ok_or_else(|| {
CodegenError::Internal("bhc_text_write_file not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into(), text_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("text_write_file call failed: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Data.Text.IO.appendFile: convert [Char] path to C-string, lower Text arg, call RTS void.
fn lower_builtin_text_io_append_file(
&mut self,
path_expr: &Expr,
text_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path_val = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("text_io_append_file: no path".to_string()))?;
let text_val = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal("text_io_append_file: no text".to_string()))?;
let path_ptr = self.value_to_ptr(path_val)?;
let path_cstr = self.char_list_to_cstring(path_ptr)?;
let text_ptr = self.value_to_ptr(text_val)?;
let rts_fn = self.functions.get(&VarId::new(1000242)).ok_or_else(|| {
CodegenError::Internal("bhc_text_append_file not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[path_cstr.into(), text_ptr.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("text_append_file call failed: {:?}", e))
})?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Data.Text.IO handle -> Text: hGetContents, hGetLine.
fn lower_builtin_text_io_handle_to_text(
&mut self,
handle_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no handle", label)))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(format!("{} expects handle", label))),
};
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[handle_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// Data.Text.IO (handle, Text) -> void: hPutStr, hPutStrLn.
fn lower_builtin_text_io_handle_text_void(
&mut self,
handle_expr: &Expr,
text_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let handle_val = self
.lower_expr(handle_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no handle", label)))?;
let text_val = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no text", label)))?;
let handle_ptr = match handle_val {
BasicValueEnum::PointerValue(p) => p,
_ => return Err(CodegenError::TypeError(format!("{} expects handle", label))),
};
let text_ptr = self.value_to_ptr(text_val)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
self.builder()
.build_call(*rts_fn, &[handle_ptr.into(), text_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Data.Text.IO putStr/putStrLn: get stdout, call hPutStr/hPutStrLn.
fn lower_builtin_text_io_stdout_text(
&mut self,
text_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Get stdout handle
let stdout_fn = self
.functions
.get(&VarId::new(1000061))
.ok_or_else(|| CodegenError::Internal("bhc_stdout not declared".to_string()))?;
let stdout = self
.builder()
.build_call(*stdout_fn, &[], "stdout")
.map_err(|e| CodegenError::Internal(format!("{}: stdout call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: stdout returned void", label)))?;
let stdout_ptr = stdout.into_pointer_value();
// Lower text argument
let text_val = self
.lower_expr(text_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no text", label)))?;
let text_ptr = self.value_to_ptr(text_val)?;
// Call RTS function
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
self.builder()
.build_call(*rts_fn, &[stdout_ptr.into(), text_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
/// Data.Text.IO getLine/getContents: get stdin, call hGetLine/hGetContents.
fn lower_builtin_text_io_stdin_to_text(
&mut self,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Get stdin handle
let stdin_fn = self
.functions
.get(&VarId::new(1000060))
.ok_or_else(|| CodegenError::Internal("bhc_stdin not declared".to_string()))?;
let stdin = self
.builder()
.build_call(*stdin_fn, &[], "stdin")
.map_err(|e| CodegenError::Internal(format!("{}: stdin call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: stdin returned void", label)))?;
let stdin_ptr = stdin.into_pointer_value();
// Call RTS function
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[stdin_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// ByteString snoc: (ptr, i64) -> ptr (e.g. bs_snoc)
fn lower_builtin_text_snoc(
&mut self,
ptr_expr: &Expr,
int_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let p = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no ptr", label)))?;
let n = self
.lower_expr(int_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no int", label)))?;
let p_ptr = self.value_to_ptr(p)?;
let n_int = self.coerce_to_int(n)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[p_ptr.into(), n_int.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(result))
}
/// ByteString: (i64, ptr) -> i64 (e.g. bs_elem)
fn lower_builtin_text_int_ptr_to_int(
&mut self,
int_expr: &Expr,
ptr_expr: &Expr,
rts_id: usize,
label: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n = self
.lower_expr(int_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no int", label)))?;
let p = self
.lower_expr(ptr_expr)?
.ok_or_else(|| CodegenError::Internal(format!("{}: no ptr", label)))?;
let n_int = self.coerce_to_int(n)?;
let p_ptr = self.value_to_ptr(p)?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("{}: RTS function {} not declared", label, rts_id))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[n_int.into(), p_ptr.into()], label)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", label, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", label)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
/// ByteString unpack: ByteString -> [Int]
///
/// Similar to text_unpack but returns list of Word8 (as Int).
fn lower_builtin_bs_unpack(
&mut self,
bs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let bs_val = self
.lower_expr(bs_expr)?
.ok_or_else(|| CodegenError::Internal("bs_unpack: no bs".to_string()))?;
let bs_ptr = self.value_to_ptr(bs_val)?;
// Get byte count
let length_fn = self
.functions
.get(&VarId::new(1000403))
.ok_or_else(|| CodegenError::Internal("bhc_bs_length not declared".to_string()))?;
let count_val = self
.builder()
.build_call(*length_fn, &[bs_ptr.into()], "bs_len")
.map_err(|e| CodegenError::Internal(format!("bs_length call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bs_length: returned void".to_string()))?
.into_int_value();
let unpack_fn = self
.functions
.get(&VarId::new(1000421))
.ok_or_else(|| CodegenError::Internal("bhc_bs_unpack not declared".to_string()))?;
let tm = self.type_mapper();
let i64_type = tm.i64_type();
let ptr_type = tm.ptr_type();
// Build Nil as starting accumulator
let nil = self.alloc_adt(0, 0)?;
// Create loop blocks
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("bs_unpack: no current function".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "bs_unpack_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "bs_unpack_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "bs_unpack_exit");
// Initial: i = count - 1, acc = nil
let one = i64_type.const_int(1, false);
let init_i = self
.builder()
.build_int_sub(count_val, one, "init_i")
.map_err(|e| CodegenError::Internal(format!("bs_unpack sub: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("bs_unpack br: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let phi_i = self
.builder()
.build_phi(i64_type, "i")
.map_err(|e| CodegenError::Internal(format!("bs_unpack phi_i: {:?}", e)))?;
let phi_acc = self
.builder()
.build_phi(ptr_type, "acc")
.map_err(|e| CodegenError::Internal(format!("bs_unpack phi_acc: {:?}", e)))?;
let zero = i64_type.const_int(0, false);
let cond = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SGE,
phi_i.as_basic_value().into_int_value(),
zero,
"cond",
)
.map_err(|e| CodegenError::Internal(format!("bs_unpack cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(cond, loop_body, loop_exit)
.map_err(|e| CodegenError::Internal(format!("bs_unpack cbr: {:?}", e)))?;
// Loop body: byte = unpack(bs, i); cons = Cons(byte, acc); i = i - 1
self.builder().position_at_end(loop_body);
let i_val = phi_i.as_basic_value().into_int_value();
let byte_val = self
.builder()
.build_call(*unpack_fn, &[bs_ptr.into(), i_val.into()], "bs_byte_at")
.map_err(|e| CodegenError::Internal(format!("bs_unpack call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bs_unpack: returned void".to_string()))?;
// Alloc Cons node: tag=1, arity=2
let cons = self.alloc_adt(1, 2)?;
let byte_ptr = self.int_to_ptr(byte_val.into_int_value())?;
self.store_adt_field(cons, 2, 0, byte_ptr.into())?;
let acc_val = phi_acc.as_basic_value().into_pointer_value();
self.store_adt_field(cons, 2, 1, acc_val.into())?;
let next_i = self
.builder()
.build_int_sub(i_val, one, "next_i")
.map_err(|e| CodegenError::Internal(format!("bs_unpack dec: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("bs_unpack loop_br: {:?}", e)))?;
// Wire phi nodes
let pre_header = loop_header
.get_previous_basic_block()
.ok_or_else(|| CodegenError::Internal("bs_unpack: no pre-header block".to_string()))?;
phi_i.add_incoming(&[(&init_i, pre_header), (&next_i, loop_body)]);
phi_acc.add_incoming(&[(&nil, pre_header), (&cons, loop_body)]);
// Exit
self.builder().position_at_end(loop_exit);
Ok(Some(phi_acc.as_basic_value()))
}
/// ByteString writeFile: String -> ByteString -> IO ()
fn lower_builtin_bs_write_file(
&mut self,
path_expr: &Expr,
bs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("bs_write_file: no path".to_string()))?;
let bs = self
.lower_expr(bs_expr)?
.ok_or_else(|| CodegenError::Internal("bs_write_file: no bs".to_string()))?;
let path_ptr = self.value_to_ptr(path)?;
let bs_ptr = self.value_to_ptr(bs)?;
let rts_fn = self
.functions
.get(&VarId::new(1000423))
.ok_or_else(|| CodegenError::Internal("bhc_bs_write_file not declared".to_string()))?;
self.builder()
.build_call(*rts_fn, &[path_ptr.into(), bs_ptr.into()], "bs_write_file")
.map_err(|e| CodegenError::Internal(format!("bs_write_file call failed: {:?}", e)))?;
// void return — return unit (null ptr)
let unit = self.type_mapper().ptr_type().const_null();
Ok(Some(unit.into()))
}
/// Lazy ByteString filter: (Word8 -> Bool) -> LazyByteString -> LazyByteString
fn lower_builtin_lazy_bs_filter(
&mut self,
func_expr: &Expr,
bs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_filter: no func".to_string()))?;
let bs = self
.lower_expr(bs_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_filter: no bs".to_string()))?;
let func_ptr = self.value_to_ptr(func)?;
let bs_ptr = self.value_to_ptr(bs)?;
let rts_fn = self
.functions
.get(&VarId::new(1000453))
.ok_or_else(|| CodegenError::Internal("bhc_lazy_bs_filter not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[func_ptr.into(), func_ptr.into(), bs_ptr.into()],
"lazy_bs_filter",
)
.map_err(|e| CodegenError::Internal(format!("lazy_bs_filter call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lazy_bs_filter: returned void".to_string()))?;
Ok(Some(result))
}
/// Lazy ByteString Char8 dropWhile: (Char -> Bool) -> LazyByteString -> LazyByteString
fn lower_builtin_lazy_bs_drop_while(
&mut self,
func_expr: &Expr,
bs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let func = self
.lower_expr(func_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_drop_while: no func".to_string()))?;
let bs = self
.lower_expr(bs_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_drop_while: no bs".to_string()))?;
let func_ptr = self.value_to_ptr(func)?;
let bs_ptr = self.value_to_ptr(bs)?;
let rts_fn = self.functions.get(&VarId::new(1000474)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_char8_drop_while not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[func_ptr.into(), func_ptr.into(), bs_ptr.into()],
"lazy_bs_drop_while",
)
.map_err(|e| {
CodegenError::Internal(format!("lazy_bs_drop_while call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("lazy_bs_drop_while: returned void".to_string())
})?;
Ok(Some(result))
}
/// Lazy ByteString writeFile: FilePath -> LazyByteString -> IO ()
fn lower_builtin_lazy_bs_write_file(
&mut self,
path_expr: &Expr,
bs_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let path = self
.lower_expr(path_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_write_file: no path".to_string()))?;
let bs = self
.lower_expr(bs_expr)?
.ok_or_else(|| CodegenError::Internal("lazy_bs_write_file: no bs".to_string()))?;
let path_ptr = self.value_to_ptr(path)?;
let bs_ptr = self.value_to_ptr(bs)?;
let rts_fn = self.functions.get(&VarId::new(1000456)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_write_file not declared".to_string())
})?;
self.builder()
.build_call(
*rts_fn,
&[path_ptr.into(), bs_ptr.into()],
"lazy_bs_write_file",
)
.map_err(|e| {
CodegenError::Internal(format!("lazy_bs_write_file call failed: {:?}", e))
})?;
let unit = self.type_mapper().ptr_type().const_null();
Ok(Some(unit.into()))
}
// ============================================================
// ByteString.Builder composed helpers
// ============================================================
/// floatBE: fptrunc f64->f32, bitcast f32->i32, zext i32->i64, word32BE
fn lower_builtin_bsb_float_be(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let word = self.lower_bsb_float_to_word(arg_expr)?;
let enc_fn = self
.functions
.get(&VarId::new(1000487))
.ok_or_else(|| CodegenError::Internal("bhc_bsb_word32_be not declared".to_string()))?;
let result = self
.builder()
.build_call(*enc_fn, &[word.into()], "bsb_float_be")
.map_err(|e| CodegenError::Internal(format!("bsb_float_be failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_float_be: no return".to_string()))?;
Ok(Some(result))
}
/// doubleBE: bitcast f64->i64, word64BE
fn lower_builtin_bsb_double_be(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let word = self.lower_bsb_double_to_word(arg_expr)?;
let enc_fn = self
.functions
.get(&VarId::new(1000488))
.ok_or_else(|| CodegenError::Internal("bhc_bsb_word64_be not declared".to_string()))?;
let result = self
.builder()
.build_call(*enc_fn, &[word.into()], "bsb_double_be")
.map_err(|e| CodegenError::Internal(format!("bsb_double_be failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_double_be: no return".to_string()))?;
Ok(Some(result))
}
/// floatLE / floatHost: fptrunc f64->f32, bitcast f32->i32, zext i32->i64, word32LE
fn lower_builtin_bsb_float_le(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let word = self.lower_bsb_float_to_word(arg_expr)?;
let enc_fn = self
.functions
.get(&VarId::new(1000490))
.ok_or_else(|| CodegenError::Internal("bhc_bsb_word32_le not declared".to_string()))?;
let result = self
.builder()
.build_call(*enc_fn, &[word.into()], "bsb_float_le")
.map_err(|e| CodegenError::Internal(format!("bsb_float_le failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_float_le: no return".to_string()))?;
Ok(Some(result))
}
/// doubleLE / doubleHost: bitcast f64->i64, word64LE
fn lower_builtin_bsb_double_le(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let word = self.lower_bsb_double_to_word(arg_expr)?;
let enc_fn = self
.functions
.get(&VarId::new(1000491))
.ok_or_else(|| CodegenError::Internal("bhc_bsb_word64_le not declared".to_string()))?;
let result = self
.builder()
.build_call(*enc_fn, &[word.into()], "bsb_double_le")
.map_err(|e| CodegenError::Internal(format!("bsb_double_le failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_double_le: no return".to_string()))?;
Ok(Some(result))
}
/// Helper: convert float expr to i64 word representation (fptrunc->bitcast->zext)
fn lower_bsb_float_to_word(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<inkwell::values::IntValue<'ctx>> {
let arg = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("bsb_float: no arg".to_string()))?;
let f64_val = self.coerce_to_f64(arg)?;
let f32_type = self.llvm_ctx.f32_type();
let i32_type = self.llvm_ctx.i32_type();
let i64_type = self.llvm_ctx.i64_type();
let f32_val = self
.builder()
.build_float_trunc(f64_val, f32_type, "ftrunc")
.map_err(|e| CodegenError::Internal(format!("ftrunc: {:?}", e)))?;
let i32_val = self
.builder()
.build_bit_cast(f32_val, i32_type, "fbitcast")
.map_err(|e| CodegenError::Internal(format!("fbitcast: {:?}", e)))?
.into_int_value();
let word = self
.builder()
.build_int_z_extend(i32_val, i64_type, "fzext")
.map_err(|e| CodegenError::Internal(format!("fzext: {:?}", e)))?;
Ok(word)
}
/// Helper: convert double expr to i64 word representation (bitcast)
fn lower_bsb_double_to_word(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<inkwell::values::IntValue<'ctx>> {
let arg = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("bsb_double: no arg".to_string()))?;
let f64_val = self.coerce_to_f64(arg)?;
let i64_type = self.llvm_ctx.i64_type();
let word = self
.builder()
.build_bit_cast(f64_val, i64_type, "dbitcast")
.map_err(|e| CodegenError::Internal(format!("dbitcast: {:?}", e)))?
.into_int_value();
Ok(word)
}
/// word16HexFixed: append(word8HexFixed(hi), word8HexFixed(lo))
fn lower_builtin_bsb_word16_hex_fixed(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let arg = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("bsb_word16_hex_fixed: no arg".to_string()))?;
let i64_val = self.coerce_to_int(arg)?;
let i64_type = self.llvm_ctx.i64_type();
// hi = (value >> 8) & 0xff
let hi = self
.builder()
.build_right_shift(i64_val, i64_type.const_int(8, false), false, "hi")
.map_err(|e| CodegenError::Internal(format!("hi shift: {:?}", e)))?;
let hi_masked = self
.builder()
.build_and(hi, i64_type.const_int(0xff, false), "hi_masked")
.map_err(|e| CodegenError::Internal(format!("hi mask: {:?}", e)))?;
// lo = value & 0xff
let lo = self
.builder()
.build_and(i64_val, i64_type.const_int(0xff, false), "lo")
.map_err(|e| CodegenError::Internal(format!("lo mask: {:?}", e)))?;
let hex_fn = self.functions.get(&VarId::new(1000493)).ok_or_else(|| {
CodegenError::Internal("bhc_bsb_word8_hex_fixed not declared".to_string())
})?;
let hi_bld = self
.builder()
.build_call(*hex_fn, &[hi_masked.into()], "hi_hex")
.map_err(|e| CodegenError::Internal(format!("hi_hex failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hi_hex: no return".to_string()))?;
let lo_bld = self
.builder()
.build_call(*hex_fn, &[lo.into()], "lo_hex")
.map_err(|e| CodegenError::Internal(format!("lo_hex failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lo_hex: no return".to_string()))?;
let append_fn = self
.functions
.get(&VarId::new(1000448))
.ok_or_else(|| CodegenError::Internal("bhc_lazy_bs_append not declared".to_string()))?;
let hi_ptr = self.value_to_ptr(hi_bld)?;
let lo_ptr = self.value_to_ptr(lo_bld)?;
let result = self
.builder()
.build_call(*append_fn, &[hi_ptr.into(), lo_ptr.into()], "hex16_fixed")
.map_err(|e| CodegenError::Internal(format!("hex16_fixed failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hex16_fixed: no return".to_string()))?;
Ok(Some(result))
}
/// word32HexFixed: append 4 word8HexFixed calls
fn lower_builtin_bsb_word32_hex_fixed(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let arg = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("bsb_word32_hex_fixed: no arg".to_string()))?;
let i64_val = self.coerce_to_int(arg)?;
let i64_type = self.llvm_ctx.i64_type();
let hex_fn = *self.functions.get(&VarId::new(1000493)).ok_or_else(|| {
CodegenError::Internal("bhc_bsb_word8_hex_fixed not declared".to_string())
})?;
let append_fn = *self
.functions
.get(&VarId::new(1000448))
.ok_or_else(|| CodegenError::Internal("bhc_lazy_bs_append not declared".to_string()))?;
// Extract 4 bytes: b3 b2 b1 b0
let mut byte_builders = Vec::new();
for shift in [24u64, 16, 8, 0] {
let shifted = self
.builder()
.build_right_shift(
i64_val,
i64_type.const_int(shift, false),
false,
"byte_shift",
)
.map_err(|e| CodegenError::Internal(format!("shift: {:?}", e)))?;
let masked = self
.builder()
.build_and(shifted, i64_type.const_int(0xff, false), "byte_mask")
.map_err(|e| CodegenError::Internal(format!("mask: {:?}", e)))?;
let bld = self
.builder()
.build_call(hex_fn, &[masked.into()], "byte_hex")
.map_err(|e| CodegenError::Internal(format!("byte_hex failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("byte_hex: no return".to_string()))?;
byte_builders.push(bld);
}
// Chain appends: append(append(append(b3, b2), b1), b0)
let mut result = byte_builders[0];
for bld in &byte_builders[1..] {
let r_ptr = self.value_to_ptr(result)?;
let b_ptr = self.value_to_ptr(*bld)?;
result = self
.builder()
.build_call(append_fn, &[r_ptr.into(), b_ptr.into()], "hex32_append")
.map_err(|e| CodegenError::Internal(format!("hex32 append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hex32 append: no return".to_string()))?;
}
Ok(Some(result))
}
/// word64HexFixed: append 8 word8HexFixed calls
fn lower_builtin_bsb_word64_hex_fixed(
&mut self,
arg_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let arg = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("bsb_word64_hex_fixed: no arg".to_string()))?;
let i64_val = self.coerce_to_int(arg)?;
let i64_type = self.llvm_ctx.i64_type();
let hex_fn = *self.functions.get(&VarId::new(1000493)).ok_or_else(|| {
CodegenError::Internal("bhc_bsb_word8_hex_fixed not declared".to_string())
})?;
let append_fn = *self
.functions
.get(&VarId::new(1000448))
.ok_or_else(|| CodegenError::Internal("bhc_lazy_bs_append not declared".to_string()))?;
// Extract 8 bytes: b7 b6 b5 b4 b3 b2 b1 b0
let mut byte_builders = Vec::new();
for shift in [56u64, 48, 40, 32, 24, 16, 8, 0] {
let shifted = self
.builder()
.build_right_shift(
i64_val,
i64_type.const_int(shift, false),
false,
"byte_shift",
)
.map_err(|e| CodegenError::Internal(format!("shift: {:?}", e)))?;
let masked = self
.builder()
.build_and(shifted, i64_type.const_int(0xff, false), "byte_mask")
.map_err(|e| CodegenError::Internal(format!("mask: {:?}", e)))?;
let bld = self
.builder()
.build_call(hex_fn, &[masked.into()], "byte_hex")
.map_err(|e| CodegenError::Internal(format!("byte_hex failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("byte_hex: no return".to_string()))?;
byte_builders.push(bld);
}
// Chain appends
let mut result = byte_builders[0];
for bld in &byte_builders[1..] {
let r_ptr = self.value_to_ptr(result)?;
let b_ptr = self.value_to_ptr(*bld)?;
result = self
.builder()
.build_call(append_fn, &[r_ptr.into(), b_ptr.into()], "hex64_append")
.map_err(|e| CodegenError::Internal(format!("hex64 append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("hex64 append: no return".to_string()))?;
}
Ok(Some(result))
}
/// Check if an expression is structurally a list (Cons applications or Nil).
/// This is used when type information isn't available.
fn is_list_expr(expr: &Expr) -> bool {
match expr {
// Empty list []
Expr::Var(v, _) if v.name.as_str() == "[]" => true,
// Cons application: (:) head tail
Expr::App(func, _, _) => {
// Check if this is a (:) application
let mut current = func.as_ref();
// Unwrap one more App for curried (:) x y
if let Expr::App(inner_func, _, _) = current {
current = inner_func.as_ref();
}
// Check if the head is the (:) constructor
if let Expr::Var(v, _) = current {
v.name.as_str() == ":"
} else {
false
}
}
_ => false,
}
}
/// E.45: Check if an expression is an Integer (arbitrary precision) value.
fn is_integer_expr(&self, expr: &Expr) -> bool {
match expr {
Expr::Lit(Literal::Integer(_), _, _) => true,
Expr::Lit(_, ty, _) => matches!(ty, Ty::Con(tc) if tc.name.as_str() == "Integer"),
Expr::Var(var, _) => {
self.integer_vars.contains(&var.id) || self.is_integer_type(&var.ty)
}
// Type application wrapping
Expr::TyApp(inner, _, _) => self.is_integer_expr(inner),
// Application of known Integer RHS functions
Expr::App(f, arg, _) => {
if let Expr::Var(var, _) = f.as_ref() {
// negate on Integer
if var.name.as_str() == "negate" && self.is_integer_expr(arg) {
return true;
}
// Check function's return type from its type signature
if let Ty::Fun(_, result) = &var.ty {
if self.is_integer_type(result) {
return true;
}
}
// E.45: Known function with Integer parameter → result is Integer
// e.g., factorial :: Integer -> Integer, when applied to an arg
if self.integer_vars.contains(&var.id) {
// If the function itself is tracked as returning Integer
return true;
}
}
// Also check for curried apps: f x y where f :: a -> b -> Integer
if let Expr::App(f2, inner_arg, _) = f.as_ref() {
if let Expr::Var(var, _) = f2.as_ref() {
// Check if the return type is concretely Integer
if let Ty::Fun(_, inner) = &var.ty {
if let Ty::Fun(_, result) = inner.as_ref() {
if self.is_integer_type(result) {
return true;
}
}
}
// E.45: For polymorphic arithmetic operators (+ - * div mod etc.),
// if either operand is Integer, the result is Integer too.
let name = var.name.as_str();
if matches!(
name,
"+" | "-"
| "*"
| "div"
| "mod"
| "rem"
| "quot"
| "abs"
| "signum"
| "gcd"
| "lcm"
) && (self.is_integer_expr(inner_arg) || self.is_integer_expr(arg))
{
return true;
}
}
}
// Check type annotation
let ty = expr.ty();
matches!(ty, Ty::Con(tc) if tc.name.as_str() == "Integer")
}
_ => {
// Check type annotation
let ty = expr.ty();
matches!(ty, Ty::Con(tc) if tc.name.as_str() == "Integer")
}
}
}
/// E.45: Lower a PrimOp on Integer (arbitrary precision) operands.
/// Dispatches to RTS functions (bhc_integer_add, bhc_integer_sub, etc.)
fn lower_integer_primop(
&mut self,
op: PrimOp,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Map PrimOp to RTS VarId
let (rts_var_id, rts_name): (usize, &str) = match op {
PrimOp::Add => (1000603, "bhc_integer_add"),
PrimOp::Sub => (1000604, "bhc_integer_sub"),
PrimOp::Mul => (1000605, "bhc_integer_mul"),
PrimOp::Div => (1000606, "bhc_integer_div"),
PrimOp::Mod => (1000607, "bhc_integer_mod"),
PrimOp::Rem => (1000607, "bhc_integer_mod"), // rem == mod for Integer
PrimOp::Quot => (1000606, "bhc_integer_div"), // quot == div for Integer
PrimOp::Negate => (1000608, "bhc_integer_negate"),
PrimOp::Eq => (1000612, "bhc_integer_eq"),
PrimOp::Ne => (1000612, "bhc_integer_eq"), // negate result
PrimOp::Lt => (1000613, "bhc_integer_lt"),
PrimOp::Le => (1000614, "bhc_integer_le"),
PrimOp::Gt => (1000615, "bhc_integer_gt"),
PrimOp::Ge => (1000616, "bhc_integer_ge"),
_ => {
return Err(CodegenError::Internal(format!(
"unsupported Integer PrimOp: {:?}",
op
)))
}
};
if matches!(op, PrimOp::Negate) {
// Unary: negate(x)
let arg = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("integer negate: no arg".to_string()))?;
let rts_fn = *self
.functions
.get(&VarId::new(rts_var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", rts_name)))?;
let result = self
.builder()
.build_call(rts_fn, &[arg.into()], "integer_negate")
.map_err(|e| CodegenError::Internal(format!("integer negate call: {:?}", e)))?;
let val = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("integer negate returned void".to_string())
})?;
return Ok(Some(val));
}
// Binary operations
let lhs_is_integer = self.is_integer_expr(args[0]);
let rhs_is_integer = self.is_integer_expr(args[1]);
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("integer primop: no lhs".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("integer primop: no rhs".to_string()))?;
// Convert non-Integer operands to Integer via bhc_integer_from_i64
let lhs_ptr = if lhs_is_integer {
self.value_to_ptr(lhs)?
} else {
// Int value — convert to Integer
let from_i64_fn = *self.functions.get(&VarId::new(1000600)).ok_or_else(|| {
CodegenError::Internal("bhc_integer_from_i64 not declared".to_string())
})?;
let int_val = if lhs.is_int_value() {
lhs.into_int_value()
} else {
self.ptr_to_int(lhs.into_pointer_value())?
};
let result = self
.builder()
.build_call(from_i64_fn, &[int_val.into()], "int_to_integer_lhs")
.map_err(|e| CodegenError::Internal(format!("int_to_integer: {:?}", e)))?;
let ptr = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("int_to_integer returned void".to_string())
})?;
ptr.into_pointer_value()
};
let rhs_ptr = if rhs_is_integer {
self.value_to_ptr(rhs)?
} else {
let from_i64_fn = *self.functions.get(&VarId::new(1000600)).ok_or_else(|| {
CodegenError::Internal("bhc_integer_from_i64 not declared".to_string())
})?;
let int_val = if rhs.is_int_value() {
rhs.into_int_value()
} else {
self.ptr_to_int(rhs.into_pointer_value())?
};
let result = self
.builder()
.build_call(from_i64_fn, &[int_val.into()], "int_to_integer_rhs")
.map_err(|e| CodegenError::Internal(format!("int_to_integer: {:?}", e)))?;
let ptr = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("int_to_integer returned void".to_string())
})?;
ptr.into_pointer_value()
};
let rts_fn = *self
.functions
.get(&VarId::new(rts_var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", rts_name)))?;
let result = self
.builder()
.build_call(
rts_fn,
&[lhs_ptr.into(), rhs_ptr.into()],
&format!("integer_{:?}", op),
)
.map_err(|e| CodegenError::Internal(format!("integer {:?} call: {:?}", op, e)))?;
let val = result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("integer {:?} returned void", op)))?;
// For comparison operations, the RTS returns i64 (0 or 1)
// We need to handle Ne specially (negate eq result)
if matches!(
op,
PrimOp::Eq | PrimOp::Ne | PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge
) {
// RTS returns i64 (0 or 1) — extract the int value
let int_result = if val.is_int_value() {
val.into_int_value()
} else {
// If declared as returning ptr, convert ptr→int
self.ptr_to_int(val.into_pointer_value())?
};
if matches!(op, PrimOp::Ne) {
// Negate: 0→1, 1→0
let one = self.type_mapper().i64_type().const_int(1, false);
let inverted = self
.builder()
.build_int_sub(one, int_result, "integer_ne")
.map_err(|e| CodegenError::Internal(format!("integer ne: {:?}", e)))?;
return Ok(Some(inverted.into()));
}
return Ok(Some(int_result.into()));
}
// For arithmetic operations, result is an Integer pointer — track it
Ok(Some(val))
}
/// E.45: Promote an Int value to Integer via bhc_integer_from_i64.
/// If the value is already a pointer (e.g., already Integer), returns it unchanged.
fn promote_to_integer(
&mut self,
value: BasicValueEnum<'ctx>,
) -> CodegenResult<BasicValueEnum<'ctx>> {
if value.is_pointer_value() {
// Already a pointer — assume it's an Integer value
return Ok(value);
}
// Int value → convert to Integer via bhc_integer_from_i64
let int_val = if value.is_int_value() {
value.into_int_value()
} else {
return Ok(value); // Unexpected type, don't convert
};
let from_i64_fn = *self.functions.get(&VarId::new(1000600)).ok_or_else(|| {
CodegenError::Internal("bhc_integer_from_i64 not declared".to_string())
})?;
let result = self
.builder()
.build_call(from_i64_fn, &[int_val.into()], "promote_to_integer")
.map_err(|e| CodegenError::Internal(format!("promote_to_integer: {:?}", e)))?;
let ptr = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal("promote_to_integer returned void".to_string())
})?;
Ok(ptr)
}
/// Check if an expression is of Rational type.
fn is_rational_expr(&self, expr: &Expr) -> bool {
match expr {
Expr::Var(var, _) => self.is_rational_type(&var.ty),
Expr::TyApp(inner, _, _) => self.is_rational_expr(inner),
Expr::App(f, arg, _) => {
// Check if function returns Rational
match f.as_ref() {
Expr::Var(var, _) => {
let name = var.name.as_str();
// % always returns Rational
if name == "%" {
return true;
}
// fromInteger on Rational returns Rational
if name == "fromInteger" && self.is_rational_type(&var.ty) {
return true;
}
// Unary ops that preserve Rational type
if matches!(name, "negate" | "abs" | "signum" | "recip")
&& self.is_rational_expr(arg)
{
return true;
}
// Check return type
if let Ty::Fun(_, result) = &var.ty {
if self.is_rational_type(result) {
return true;
}
}
false
}
// Binary ops: App(App(op, x), y) — f is App(op, x)
Expr::App(ff, inner_arg, _) => {
if let Expr::Var(var, _) = ff.as_ref() {
let name = var.name.as_str();
if name == "%" {
return true;
}
if matches!(name, "+" | "-" | "*" | "/" | "negate" | "abs" | "signum")
&& (self.is_rational_expr(inner_arg) || self.is_rational_expr(arg))
{
return true;
}
}
false
}
_ => false,
}
}
_ => {
let ty = expr.ty();
self.is_rational_type(&ty)
}
}
}
/// Lower a PrimOp on Rational operands.
/// Dispatches to RTS functions (bhc_rational_add, bhc_rational_sub, etc.)
fn lower_rational_primop(
&mut self,
op: PrimOp,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Map PrimOp to RTS VarId
let (rts_var_id, rts_name): (usize, &str) = match op {
PrimOp::Add => (1000903, "bhc_rational_add"),
PrimOp::Sub => (1000904, "bhc_rational_sub"),
PrimOp::Mul => (1000905, "bhc_rational_mul"),
PrimOp::Div => (1000906, "bhc_rational_div"),
PrimOp::Negate => (1000907, "bhc_rational_negate"),
PrimOp::Abs => (1000908, "bhc_rational_abs"),
PrimOp::Signum => (1000909, "bhc_rational_signum"),
PrimOp::Eq => (1000910, "bhc_rational_eq"),
PrimOp::Ne => (1000910, "bhc_rational_eq"), // negate result
PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge => (1000911, "bhc_rational_compare"),
_ => {
return Err(CodegenError::Internal(format!(
"unsupported Rational PrimOp: {:?}",
op
)))
}
};
if matches!(op, PrimOp::Negate | PrimOp::Abs | PrimOp::Signum) {
// Unary operations
let arg = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("rational unary: no arg".to_string()))?;
let arg_ptr = self.value_to_ptr(arg)?;
let rts_fn = *self
.functions
.get(&VarId::new(rts_var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", rts_name)))?;
let result = self
.builder()
.build_call(rts_fn, &[arg_ptr.into()], &format!("rational_{:?}", op))
.map_err(|e| CodegenError::Internal(format!("rational {:?} call: {:?}", op, e)))?;
let val = result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal(format!("rational {:?} returned void", op))
})?;
return Ok(Some(val));
}
// Binary operations
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("rational primop: no lhs".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("rational primop: no rhs".to_string()))?;
// Convert non-Rational operands (Int) to Rational via bhc_rational_from_int
let lhs_ptr = if self.is_rational_expr(args[0]) {
self.value_to_ptr(lhs)?
} else {
// Int value -> convert to Rational
let from_int_fn = *self.functions.get(&VarId::new(1000912)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_from_int not declared".to_string())
})?;
let int_val = self.coerce_to_int(lhs)?;
let result = self
.builder()
.build_call(from_int_fn, &[int_val.into()], "int_to_rational_lhs")
.map_err(|e| CodegenError::Internal(format!("int_to_rational: {:?}", e)))?;
result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("int_to_rational returned void".to_string()))?
.into_pointer_value()
};
let rhs_ptr = if self.is_rational_expr(args[1]) {
self.value_to_ptr(rhs)?
} else {
let from_int_fn = *self.functions.get(&VarId::new(1000912)).ok_or_else(|| {
CodegenError::Internal("bhc_rational_from_int not declared".to_string())
})?;
let int_val = self.coerce_to_int(rhs)?;
let result = self
.builder()
.build_call(from_int_fn, &[int_val.into()], "int_to_rational_rhs")
.map_err(|e| CodegenError::Internal(format!("int_to_rational: {:?}", e)))?;
result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("int_to_rational returned void".to_string()))?
.into_pointer_value()
};
let rts_fn = *self
.functions
.get(&VarId::new(rts_var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", rts_name)))?;
let result = self
.builder()
.build_call(
rts_fn,
&[lhs_ptr.into(), rhs_ptr.into()],
&format!("rational_{:?}", op),
)
.map_err(|e| CodegenError::Internal(format!("rational {:?} call: {:?}", op, e)))?;
let val = result
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("rational {:?} returned void", op)))?;
// For comparison operations
if matches!(
op,
PrimOp::Eq | PrimOp::Ne | PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge
) {
if matches!(op, PrimOp::Eq | PrimOp::Ne) {
// bhc_rational_eq returns i64 (0 or 1)
let int_result = if val.is_int_value() {
val.into_int_value()
} else {
self.ptr_to_int(val.into_pointer_value())?
};
if matches!(op, PrimOp::Ne) {
let one = self.type_mapper().i64_type().const_int(1, false);
let inverted = self
.builder()
.build_int_sub(one, int_result, "rational_ne")
.map_err(|e| CodegenError::Internal(format!("rational ne: {:?}", e)))?;
return Ok(Some(inverted.into()));
}
return Ok(Some(int_result.into()));
}
// bhc_rational_compare returns i32 (-1, 0, 1)
let cmp_result = if val.is_int_value() {
val.into_int_value()
} else {
self.ptr_to_int(val.into_pointer_value())?
};
let i64_ty = self.type_mapper().i64_type();
let zero = i64_ty.const_zero();
let bool_result = match op {
PrimOp::Lt => self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, cmp_result, zero, "rat_lt")
.map_err(|e| CodegenError::Internal(format!("rat lt: {:?}", e)))?,
PrimOp::Le => self
.builder()
.build_int_compare(inkwell::IntPredicate::SLE, cmp_result, zero, "rat_le")
.map_err(|e| CodegenError::Internal(format!("rat le: {:?}", e)))?,
PrimOp::Gt => self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, cmp_result, zero, "rat_gt")
.map_err(|e| CodegenError::Internal(format!("rat gt: {:?}", e)))?,
PrimOp::Ge => self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, cmp_result, zero, "rat_ge")
.map_err(|e| CodegenError::Internal(format!("rat ge: {:?}", e)))?,
_ => unreachable!(),
};
let result_i64 = self
.builder()
.build_int_z_extend(bool_result, i64_ty, "rat_cmp_ext")
.map_err(|e| CodegenError::Internal(format!("rat cmp ext: {:?}", e)))?;
return Ok(Some(result_i64.into()));
}
Ok(Some(val))
}
/// Check if a name is a data constructor and return (tag, arity).
///
/// This function checks:
/// 1. Builtin types (Bool, Maybe, Either, List, Tuple, Ordering)
/// 2. User-defined types registered via `register_constructor`
fn constructor_info(&self, name: &str) -> Option<(u32, u32)> {
// First check builtin constructors
match name {
// Bool constructors
"False" => return Some((0, 0)), // tag=0, arity=0
"True" => return Some((1, 0)), // tag=1, arity=0
// Maybe constructors
"Nothing" => return Some((0, 0)), // tag=0, arity=0
"Just" => return Some((1, 1)), // tag=1, arity=1
// Either constructors
"Left" => return Some((0, 1)), // tag=0, arity=1
"Right" => return Some((1, 1)), // tag=1, arity=1
// List constructors
"[]" => return Some((0, 0)), // tag=0, arity=0 (Nil)
":" => return Some((1, 2)), // tag=1, arity=2 (Cons head tail)
// Unit constructor
"()" => return Some((0, 0)), // tag=0, arity=0
// Ordering constructors
"LT" => return Some((0, 0)), // tag=0, arity=0
"EQ" => return Some((1, 0)), // tag=1, arity=0
"GT" => return Some((2, 0)), // tag=2, arity=0
// GHC.Generics representation constructors
"U1" => return Some((0, 0)), // tag=0, arity=0 (unit)
"K1" => return Some((0, 1)), // tag=0, arity=1 (field wrapper)
"M1" => return Some((0, 1)), // tag=0, arity=1 (metadata wrapper)
"L1" => return Some((0, 1)), // tag=0, arity=1 (left sum)
"R1" => return Some((1, 1)), // tag=1, arity=1 (right sum)
":*:" => return Some((0, 2)), // tag=0, arity=2 (product)
// IOMode constructors
"ReadMode" => return Some((0, 0)), // tag=0, arity=0
"WriteMode" => return Some((1, 0)), // tag=1, arity=0
"AppendMode" => return Some((2, 0)), // tag=2, arity=0
"ReadWriteMode" => return Some((3, 0)), // tag=3, arity=0
// Tuple constructors - used for type class dictionaries
"(,)" => return Some((0, 2)), // 2-tuple
"(,,)" => return Some((0, 3)), // 3-tuple
"(,,,)" => return Some((0, 4)), // 4-tuple
"(,,,,)" => return Some((0, 5)), // 5-tuple
"(,,,,,)" => return Some((0, 6)), // 6-tuple
"(,,,,,,)" => return Some((0, 7)), // 7-tuple
"(,,,,,,,)" => return Some((0, 8)), // 8-tuple
_ => {}
}
// Check user-defined constructors registered from case alternatives
if let Some(meta) = self.constructor_metadata.get(name) {
return Some((meta.tag, meta.arity));
}
// If it looks like a constructor (starts with uppercase), return None
// to indicate we don't have metadata yet (might be registered later).
// Non-constructor names also have no metadata, so return None either way.
None
}
// ========================================================================
// Closure Support
// ========================================================================
//
// Closures are represented as heap-allocated structs:
//
// struct Closure {
// ptr fn_ptr; // Pointer to the lifted function
// i64 env_size; // Number of captured variables
// ptr env[]; // Array of captured variable pointers
// }
//
// When a lambda captures free variables, we:
// 1. Compute the free variables
// 2. Create a lifted function that takes (env_ptr, params...) as arguments
// 3. Allocate a closure struct with the function pointer and captured values
//
// When calling a closure:
// 1. Load the function pointer from the closure
// 2. Call with the closure (as env) and the arguments
// ========================================================================
/// Compute the free variables of an expression.
///
/// Returns the set of variable IDs that are referenced but not bound
/// within the expression.
fn free_vars(&self, expr: &Expr) -> FxHashSet<VarId> {
let mut free = FxHashSet::default();
let mut bound = FxHashSet::default();
self.collect_free_vars(expr, &mut free, &mut bound);
free
}
/// Helper to collect free variables, tracking bound variables.
fn collect_free_vars(
&self,
expr: &Expr,
free: &mut FxHashSet<VarId>,
bound: &mut FxHashSet<VarId>,
) {
match expr {
Expr::Lit(_, _, _) => {}
Expr::Var(var, _) => {
// A variable is free if it's not bound and not a top-level function/constructor
if !bound.contains(&var.id)
&& !self.functions.contains_key(&var.id)
&& self.constructor_info(var.name.as_str()).is_none()
&& self.primitive_op_info(var.name.as_str()).is_none()
&& self.rts_function_id(var.name.as_str()).is_none()
{
free.insert(var.id);
}
}
Expr::App(func, arg, _) => {
self.collect_free_vars(func, free, bound);
self.collect_free_vars(arg, free, bound);
}
Expr::Lam(param, body, _) => {
bound.insert(param.id);
self.collect_free_vars(body, free, bound);
bound.remove(¶m.id);
}
Expr::Let(bind, body, _) => {
match &**bind {
Bind::NonRec(var, rhs) => {
self.collect_free_vars(rhs, free, bound);
bound.insert(var.id);
self.collect_free_vars(body, free, bound);
bound.remove(&var.id);
}
Bind::Rec(bindings) => {
// For recursive bindings, all vars are bound in both RHS and body
for (var, _) in bindings {
bound.insert(var.id);
}
for (_, rhs) in bindings {
self.collect_free_vars(rhs, free, bound);
}
self.collect_free_vars(body, free, bound);
for (var, _) in bindings {
bound.remove(&var.id);
}
}
}
}
Expr::Case(scrut, alts, _, _) => {
self.collect_free_vars(scrut, free, bound);
for alt in alts {
for binder in &alt.binders {
bound.insert(binder.id);
}
self.collect_free_vars(&alt.rhs, free, bound);
for binder in &alt.binders {
bound.remove(&binder.id);
}
}
}
Expr::TyApp(inner, _, _) => {
self.collect_free_vars(inner, free, bound);
}
Expr::TyLam(_, body, _) => {
self.collect_free_vars(body, free, bound);
}
Expr::Lazy(inner, _) => {
self.collect_free_vars(inner, free, bound);
}
Expr::Cast(inner, _, _) => {
self.collect_free_vars(inner, free, bound);
}
Expr::Tick(_, inner, _) => {
self.collect_free_vars(inner, free, bound);
}
Expr::Type(_, _) | Expr::Coercion(_, _) => {}
}
}
/// Get the LLVM struct type for a closure with the given environment size.
///
/// Closure layout: { ptr fn_ptr, i64 env_size, [env_size x ptr] env }
fn closure_type(&self, env_size: u32) -> inkwell::types::StructType<'ctx> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
// Create array type for environment
let env_type = ptr_type.array_type(env_size);
// Struct: { ptr fn_ptr, i64 env_size, [env_size x ptr] env }
self.llvm_ctx
.struct_type(&[ptr_type.into(), i64_type.into(), env_type.into()], false)
}
/// Allocate a closure with the given function pointer and captured variables.
fn alloc_closure(
&self,
fn_ptr: PointerValue<'ctx>,
captured_vars: &[(VarId, BasicValueEnum<'ctx>)],
) -> CodegenResult<PointerValue<'ctx>> {
let tm = self.type_mapper();
let env_size = captured_vars.len() as u32;
let closure_ty = self.closure_type(env_size);
// Calculate size: sizeof(ptr) + sizeof(i64) + env_size * sizeof(ptr)
let size = 8 + 8 + (env_size as u64) * 8;
// Call bhc_alloc
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size_val = tm.i64_type().const_int(size, false);
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], "closure_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call bhc_alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bhc_alloc returned void".to_string()))?;
let closure_ptr = raw_ptr.into_pointer_value();
// Store function pointer at offset 0
let fn_ptr_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 0, "fn_ptr_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get fn_ptr slot: {:?}", e)))?;
self.builder()
.build_store(fn_ptr_slot, fn_ptr)
.map_err(|e| CodegenError::Internal(format!("failed to store fn_ptr: {:?}", e)))?;
// Store env_size at offset 1
let env_size_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 1, "env_size_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get env_size slot: {:?}", e)))?;
let env_size_val = tm.i64_type().const_int(env_size as u64, false);
self.builder()
.build_store(env_size_slot, env_size_val)
.map_err(|e| CodegenError::Internal(format!("failed to store env_size: {:?}", e)))?;
// Store captured variables in environment array
if env_size > 0 {
let env_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 2, "env_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get env slot: {:?}", e)))?;
for (i, (_var_id, val)) in captured_vars.iter().enumerate() {
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
tm.ptr_type().array_type(env_size),
env_slot,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(i as u64, false),
],
&format!("env_{}", i),
)
.map_err(|e| {
CodegenError::Internal(format!("failed to get env elem: {:?}", e))
})?
};
// Convert value to pointer if needed
let ptr_val = self.value_to_ptr(*val)?;
self.builder().build_store(elem_ptr, ptr_val).map_err(|e| {
CodegenError::Internal(format!("failed to store env elem: {:?}", e))
})?;
}
}
Ok(closure_ptr)
}
/// Extract the function pointer from a closure.
fn extract_closure_fn_ptr(
&self,
closure_ptr: PointerValue<'ctx>,
) -> CodegenResult<PointerValue<'ctx>> {
let closure_ty = self.closure_type(0); // Use 0-size for reading fn_ptr (same offset)
let fn_ptr_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 0, "fn_ptr_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get fn_ptr slot: {:?}", e)))?;
let fn_ptr = self
.builder()
.build_load(self.type_mapper().ptr_type(), fn_ptr_slot, "fn_ptr")
.map_err(|e| CodegenError::Internal(format!("failed to load fn_ptr: {:?}", e)))?;
Ok(fn_ptr.into_pointer_value())
}
/// Extract an element from a closure's environment.
fn extract_closure_env_elem(
&self,
closure_ptr: PointerValue<'ctx>,
env_size: u32,
index: u32,
) -> CodegenResult<PointerValue<'ctx>> {
let tm = self.type_mapper();
let closure_ty = self.closure_type(env_size);
let env_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 2, "env_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get env slot: {:?}", e)))?;
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
tm.ptr_type().array_type(env_size),
env_slot,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(index as u64, false),
],
&format!("env_elem_ptr_{}", index),
)
.map_err(|e| {
CodegenError::Internal(format!("failed to get env elem ptr: {:?}", e))
})?
};
let elem_val = self
.builder()
.build_load(tm.ptr_type(), elem_ptr, &format!("env_elem_{}", index))
.map_err(|e| CodegenError::Internal(format!("failed to load env elem: {:?}", e)))?;
Ok(elem_val.into_pointer_value())
}
// ========================================================================
// Thunk Support
// ========================================================================
//
// Thunks are suspended computations for lazy evaluation.
//
// Memory Layout:
// struct Thunk {
// i64 tag; // -1 = unevaluated, -2 = blackhole, >= 0 = evaluated
// ptr eval_fn; // Function to evaluate (when unevaluated)
// i64 env_size; // Number of captured variables
// ptr[] env; // Captured environment
// }
//
// The eval_fn has signature: extern "C" fn(env: *mut u8) -> *mut u8
// It takes the environment pointer and returns the evaluated value.
// ========================================================================
/// Tag constant for unevaluated thunks.
const THUNK_TAG: i64 = -1;
/// Tag constant for indirections (evaluated thunks with cached result).
#[allow(dead_code)]
const INDIRECTION_TAG: i64 = -3;
/// Check if an expression should be thunked in a let-binding.
///
/// WHNF (Weak Head Normal Form) expressions don't need thunking:
/// - Literals: already values
/// - Variables: already evaluated or thunked themselves
/// - Lambdas: already values (closures)
/// - Constructors: already values
///
/// Non-WHNF expressions need thunking to achieve lazy evaluation:
/// - Applications: may trigger computation
/// - Case expressions: pattern matching
/// - Let expressions: nested bindings
/// - PrimOp: primitive operations
///
/// Check if an expression should be thunked in a let-binding.
///
/// WHNF (Weak Head Normal Form) expressions don't need thunking:
/// E.58: Check if an expression is trivial (doesn't need thunking).
/// Trivial expressions are cheap to evaluate and have no side effects,
/// so they can be evaluated eagerly without waste.
/// Everything else should be thunked for lazy evaluation.
fn is_trivial_expr(expr: &Expr) -> bool {
match expr {
// Literals are trivial
Expr::Lit(_, _, _) => true,
// Variable references are trivial (just a lookup)
Expr::Var(_, _) => true,
// Lambdas are trivial (just a closure allocation, no computation)
Expr::Lam(_, _, _) => true,
// Type-level wrappers are trivial if their inner expression is
Expr::TyLam(_, inner, _) => Self::is_trivial_expr(inner),
Expr::TyApp(inner, _, _) => Self::is_trivial_expr(inner),
Expr::Cast(inner, _, _) => Self::is_trivial_expr(inner),
Expr::Tick(_, inner, _) => Self::is_trivial_expr(inner),
Expr::Type(_, _) => true,
Expr::Coercion(_, _) => true,
// Everything else is non-trivial (App, Case, Let, Lazy)
_ => false,
}
}
/// Get the LLVM struct type for a thunk with the given environment size.
///
/// Thunk layout: { i64 tag, ptr eval_fn, i64 env_size, [env_size x ptr] env }
fn thunk_type(&self, env_size: u32) -> inkwell::types::StructType<'ctx> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
// Create array type for environment
let env_type = ptr_type.array_type(env_size);
// Struct: { i64 tag, ptr eval_fn, i64 env_size, [env_size x ptr] env }
self.llvm_ctx.struct_type(
&[
i64_type.into(),
ptr_type.into(),
i64_type.into(),
env_type.into(),
],
false,
)
}
/// Allocate a thunk with the given evaluation function and captured variables.
fn alloc_thunk(
&self,
eval_fn: PointerValue<'ctx>,
captured_vars: &[(VarId, BasicValueEnum<'ctx>)],
) -> CodegenResult<PointerValue<'ctx>> {
let tm = self.type_mapper();
let env_size = captured_vars.len() as u32;
let thunk_ty = self.thunk_type(env_size);
// Calculate size: sizeof(i64) + sizeof(ptr) + sizeof(i64) + env_size * sizeof(ptr)
let size = 8 + 8 + 8 + (env_size as u64) * 8;
// Call bhc_alloc
let alloc_fn = self
.functions
.get(&VarId::new(1000005))
.ok_or_else(|| CodegenError::Internal("bhc_alloc not declared".to_string()))?;
let size_val = tm.i64_type().const_int(size, false);
let raw_ptr = self
.builder()
.build_call(*alloc_fn, &[size_val.into()], "thunk_alloc")
.map_err(|e| CodegenError::Internal(format!("failed to call bhc_alloc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bhc_alloc returned void".to_string()))?;
let thunk_ptr = raw_ptr.into_pointer_value();
// Store tag = -1 (THUNK_TAG) at offset 0
let tag_slot = self
.builder()
.build_struct_gep(thunk_ty, thunk_ptr, 0, "thunk_tag_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get tag slot: {:?}", e)))?;
let tag_val = tm.i64_type().const_int(Self::THUNK_TAG as u64, true);
self.builder()
.build_store(tag_slot, tag_val)
.map_err(|e| CodegenError::Internal(format!("failed to store thunk tag: {:?}", e)))?;
// Store eval function pointer at offset 1
let eval_fn_slot = self
.builder()
.build_struct_gep(thunk_ty, thunk_ptr, 1, "thunk_eval_fn_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get eval_fn slot: {:?}", e)))?;
self.builder()
.build_store(eval_fn_slot, eval_fn)
.map_err(|e| CodegenError::Internal(format!("failed to store eval_fn: {:?}", e)))?;
// Store env_size at offset 2
let env_size_slot = self
.builder()
.build_struct_gep(thunk_ty, thunk_ptr, 2, "thunk_env_size_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get env_size slot: {:?}", e)))?;
let env_size_val = tm.i64_type().const_int(env_size as u64, false);
self.builder()
.build_store(env_size_slot, env_size_val)
.map_err(|e| CodegenError::Internal(format!("failed to store env_size: {:?}", e)))?;
// Store captured variables in environment array
if env_size > 0 {
let env_slot = self
.builder()
.build_struct_gep(thunk_ty, thunk_ptr, 3, "thunk_env_slot")
.map_err(|e| CodegenError::Internal(format!("failed to get env slot: {:?}", e)))?;
for (i, (_var_id, val)) in captured_vars.iter().enumerate() {
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
tm.ptr_type().array_type(env_size),
env_slot,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(i as u64, false),
],
&format!("thunk_env_{}", i),
)
.map_err(|e| {
CodegenError::Internal(format!("failed to get env elem: {:?}", e))
})?
};
// Convert value to pointer if needed
let ptr_val = self.value_to_ptr(*val)?;
self.builder().build_store(elem_ptr, ptr_val).map_err(|e| {
CodegenError::Internal(format!("failed to store thunk env elem: {:?}", e))
})?;
}
}
Ok(thunk_ptr)
}
/// Generate a call to bhc_force to evaluate a thunk to WHNF.
fn build_force(&self, val: BasicValueEnum<'ctx>) -> CodegenResult<BasicValueEnum<'ctx>> {
let ptr = match val {
BasicValueEnum::PointerValue(p) => p,
// Non-pointers don't need forcing (primitives)
_ => return Ok(val),
};
let force_fn = self
.functions
.get(&VarId::new(1000011))
.ok_or_else(|| CodegenError::Internal("bhc_force not declared".to_string()))?;
let result = self
.builder()
.build_call(*force_fn, &[ptr.into()], "forced")
.map_err(|e| CodegenError::Internal(format!("failed to call bhc_force: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bhc_force returned void".to_string()))?;
Ok(result)
}
/// Generate a unique name for a thunk evaluation function.
/// Includes module name to avoid cross-module duplicate symbols.
fn next_thunk_name(&mut self) -> String {
let counter = self.closure_counter;
self.closure_counter += 1;
if let Some(ref mod_name) = self.module_name {
format!("__thunk_eval_{}.{}", mod_name, counter)
} else {
format!("__thunk_eval_{}", counter)
}
}
/// Generate a unique name for a closure function.
/// Includes module name to avoid cross-module duplicate symbols.
fn next_closure_name(&mut self) -> String {
let counter = self.closure_counter;
self.closure_counter += 1;
if let Some(ref mod_name) = self.module_name {
format!("__closure_{}.{}", mod_name, counter)
} else {
format!("__closure_{}", counter)
}
}
/// Check if an expression is a saturated constructor application.
///
/// Returns Some((tag, collected_args)) if the expression is a fully-applied constructor.
fn is_saturated_constructor<'a>(
&self,
expr: &'a Expr,
) -> Option<(u32, u32, Vec<&'a Expr>, String)> {
// Collect arguments while unwrapping applications
let mut args = Vec::new();
let mut current = expr;
while let Expr::App(func, arg, _) = current {
args.push(arg.as_ref());
current = func.as_ref();
}
// Skip through type applications (erased at runtime)
while let Expr::TyApp(inner, _, _) = current {
current = inner.as_ref();
}
// Check if the head is a constructor
if let Expr::Var(var, _) = current {
if let Some((tag, arity)) = self.constructor_info(var.name.as_str()) {
args.reverse();
if args.len() == arity as usize {
return Some((tag, arity, args, var.name.as_str().to_string()));
}
}
}
None
}
/// Lower a Core module to LLVM IR.
pub fn lower_module(&mut self, core_module: &CoreModule) -> CodegenResult<()> {
// Propagate language extensions from the Core module
self.overloaded_strings = core_module.overloaded_strings;
// Register constructors from CoreModule metadata.
// This ensures all constructors defined in the module are known to codegen,
// even if they are only used as values (not in case alternatives).
for con in &core_module.constructors {
self.register_constructor(&con.name, con.tag, con.arity);
if let Some(meta) = self.constructor_metadata.get_mut(&con.name) {
if let Some(ref type_name) = con.type_name {
if meta.type_name.is_none() {
meta.type_name = Some(type_name.clone());
}
}
if con.is_newtype {
meta.is_newtype = true;
}
}
}
// Pre-pass: collect all constructor metadata from case alternatives
// This ensures constructors are known before we try to lower applications
for bind in &core_module.bindings {
self.collect_constructors_from_binding(bind);
}
// Pre-pass: detect which functions use StateT/ReaderT operations.
// This must happen before lowering so that callers of these functions
// can detect the correct transformer layer.
self.detect_transformer_functions(&core_module.bindings);
// Pre-pass: detect derived instance methods (e.g., $derived_show_Color)
// and populate dispatch tables + constructor-to-type mappings.
self.detect_derived_instance_methods(&core_module.bindings);
// Declare foreign imports (creates C declarations and BHC wrappers)
self.declare_foreign_imports(&core_module.foreign_imports)?;
// First pass: declare all top-level functions
for bind in &core_module.bindings {
self.declare_binding(bind)?;
}
// Second pass: define all functions
for bind in &core_module.bindings {
self.lower_binding(bind)?;
}
Ok(())
}
/// Pre-pass: scan all bindings to detect which functions use StateT/ReaderT/ExceptT/WriterT.
/// This populates the function sets so that `detect_operation_layer` can recognize
/// calls to user-defined monadic functions.
fn detect_transformer_functions(&mut self, bindings: &[Bind]) {
// First pass: detect direct users of get/put/ask/throwE/tell/etc.
for bind in bindings {
match bind {
Bind::NonRec(var, expr) => {
if let Some(layer) = self.detect_transformer_for_body(expr) {
let name = var.name.as_str().to_string();
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(name);
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(name);
}
TransformerLayer::ExceptT => {
self.except_t_functions.insert(name);
}
TransformerLayer::WriterT => {
self.writer_t_functions.insert(name);
}
_ => {}
}
}
}
Bind::Rec(pairs) => {
for (var, expr) in pairs {
if let Some(layer) = self.detect_transformer_for_body(expr) {
let name = var.name.as_str().to_string();
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(name);
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(name);
}
TransformerLayer::ExceptT => {
self.except_t_functions.insert(name);
}
TransformerLayer::WriterT => {
self.writer_t_functions.insert(name);
}
_ => {}
}
}
}
}
}
}
// Second pass: propagate — functions that call transformer functions
// are themselves transformer functions.
let mut changed = true;
while changed {
changed = false;
for bind in bindings {
match bind {
Bind::NonRec(var, expr) => {
let name = var.name.as_str().to_string();
if !self.state_t_functions.contains(&name)
&& !self.reader_t_functions.contains(&name)
&& !self.except_t_functions.contains(&name)
&& !self.writer_t_functions.contains(&name)
{
if let Some(layer) = self.detect_transformer_for_calls(expr) {
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(name);
changed = true;
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(name);
changed = true;
}
TransformerLayer::ExceptT => {
self.except_t_functions.insert(name);
changed = true;
}
TransformerLayer::WriterT => {
self.writer_t_functions.insert(name);
changed = true;
}
_ => {}
}
}
}
}
Bind::Rec(pairs) => {
for (var, expr) in pairs {
let name = var.name.as_str().to_string();
if !self.state_t_functions.contains(&name)
&& !self.reader_t_functions.contains(&name)
&& !self.except_t_functions.contains(&name)
&& !self.writer_t_functions.contains(&name)
{
if let Some(layer) = self.detect_transformer_for_calls(expr) {
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(name);
changed = true;
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(name);
changed = true;
}
TransformerLayer::ExceptT => {
self.except_t_functions.insert(name);
changed = true;
}
TransformerLayer::WriterT => {
self.writer_t_functions.insert(name);
changed = true;
}
_ => {}
}
}
}
}
}
}
}
}
}
/// Detect if an expression calls any known transformer function.
fn detect_transformer_for_calls(&self, expr: &Expr) -> Option<TransformerLayer> {
match expr {
Expr::Var(v, _) => {
let name = v.name.as_str();
if self.state_t_functions.contains(name) {
Some(TransformerLayer::StateT)
} else if self.reader_t_functions.contains(name) {
Some(TransformerLayer::ReaderT)
} else if self.except_t_functions.contains(name) {
Some(TransformerLayer::ExceptT)
} else if self.writer_t_functions.contains(name) {
Some(TransformerLayer::WriterT)
} else {
None
}
}
Expr::App(func, arg, _) => self
.detect_transformer_for_calls(func)
.or_else(|| self.detect_transformer_for_calls(arg)),
Expr::Lam(_, body, _) => self.detect_transformer_for_calls(body),
Expr::Let(bind, body, _) => {
let in_bind = match bind.as_ref() {
Bind::NonRec(_, e) => self.detect_transformer_for_calls(e),
Bind::Rec(bindings) => bindings
.iter()
.find_map(|(_, e)| self.detect_transformer_for_calls(e)),
};
in_bind.or_else(|| self.detect_transformer_for_calls(body))
}
Expr::Case(scrut, alts, _, _) => {
self.detect_transformer_for_calls(scrut).or_else(|| {
alts.iter()
.find_map(|alt| self.detect_transformer_for_calls(&alt.rhs))
})
}
Expr::TyApp(e, _, _) => self.detect_transformer_for_calls(e),
_ => None,
}
}
/// Detect derived instance methods by naming convention ($derived_show_*, $derived_eq_*, $derived_compare_*).
/// Populates the derived_show_fns/derived_eq_fns/derived_compare_fns dispatch tables and
/// constructor_metadata.type_name from case alternatives in derived bindings.
fn detect_derived_instance_methods(&mut self, bindings: &[Bind]) {
for bind in bindings {
if let Bind::NonRec(var, expr) = bind {
let name = var.name.as_str();
// Detect auto-derived instance methods ($derived_show_TypeName_COUNTER)
if let Some(remainder) = name.strip_prefix("$derived_show_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_show_fns.insert(type_name.clone(), var.id);
// Tag constructors found in this binding with their type_name
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_read_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_read_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_eq_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_eq_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_compare_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_compare_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Functor instance methods ($derived_fmap_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_fmap_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_functor_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Foldable instance methods ($derived_foldr_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_foldr_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_foldable_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Traversable instance methods ($derived_traverse_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_traverse_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_traversable_fns
.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Enum instance methods ($derived_fromEnum_TypeName_COUNTER, $derived_toEnum_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_fromEnum_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_from_enum_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_toEnum_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_to_enum_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Bounded instance methods ($derived_minBound_TypeName_COUNTER, $derived_maxBound_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_minBound_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_min_bound_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_maxBound_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_max_bound_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect auto-derived Generic instance methods ($derived_from_TypeName_COUNTER, $derived_to_TypeName_COUNTER)
else if let Some(remainder) = name.strip_prefix("$derived_from_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_from_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
} else if let Some(remainder) = name.strip_prefix("$derived_to_") {
let type_name = Self::strip_deriving_counter_suffix(remainder);
self.derived_to_fns.insert(type_name.clone(), var.id);
self.tag_constructors_with_type(expr, &type_name);
}
// Detect manual instance methods ($instance_show_TypeName)
else if let Some(type_name) = name.strip_prefix("$instance_show_") {
self.derived_show_fns.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_==_") {
self.derived_eq_fns.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_compare_") {
self.derived_compare_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_fmap_") {
self.derived_functor_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_foldr_") {
self.derived_foldable_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_traverse_") {
self.derived_traversable_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_fromEnum_") {
self.derived_from_enum_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_toEnum_") {
self.derived_to_enum_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_minBound_") {
self.derived_min_bound_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
} else if let Some(type_name) = name.strip_prefix("$instance_maxBound_") {
self.derived_max_bound_fns
.insert(type_name.to_string(), var.id);
self.tag_constructors_with_type(expr, type_name);
}
}
}
}
/// Strip the counter suffix from a derived binding name.
/// e.g., "Color_50000" → "Color", "My_Type_50002" → "My_Type"
fn strip_deriving_counter_suffix(name: &str) -> String {
if let Some((prefix, suffix)) = name.rsplit_once('_') {
if suffix.chars().all(|c| c.is_ascii_digit()) {
return prefix.to_string();
}
}
name.to_string()
}
/// Walk an expression and tag any constructors in case alternatives with the given type name.
fn tag_constructors_with_type(&mut self, expr: &Expr, type_name: &str) {
match expr {
Expr::Lam(_, body, _) => self.tag_constructors_with_type(body, type_name),
Expr::Case(_, alts, _, _) => {
for alt in alts {
if let AltCon::DataCon(con) = &alt.con {
let con_name = con.name.as_str().to_string();
if let Some(meta) = self.constructor_metadata.get_mut(&con_name) {
meta.type_name = Some(type_name.to_string());
}
}
self.tag_constructors_with_type(&alt.rhs, type_name);
}
}
Expr::Let(bind, body, _) => {
if let Bind::NonRec(_, e) = bind.as_ref() {
self.tag_constructors_with_type(e, type_name);
}
self.tag_constructors_with_type(body, type_name);
}
Expr::App(f, arg, _) => {
self.tag_constructors_with_type(f, type_name);
self.tag_constructors_with_type(arg, type_name);
}
_ => {}
}
}
/// Infer the user ADT type name from an expression, for derived method dispatch.
fn infer_adt_type_from_expr(&self, expr: &Expr) -> Option<String> {
match expr {
// A bare constructor: e.g., `Red`
Expr::Var(var, _) => {
let name = var.name.as_str();
if let Some(meta) = self.constructor_metadata.get(name) {
return meta.type_name.clone();
}
// E.54: minBound/maxBound — use single-enum heuristic
if (name == "minBound" || name == "maxBound")
&& self.derived_min_bound_fns.len() == 1
{
return Some(self.derived_min_bound_fns.keys().next().unwrap().clone());
}
None
}
// A constructor application: e.g., `Circle 5`
Expr::App(f, arg, _) => {
// E.54: succ/pred preserve ADT type from their argument
// E.63: force/id are identity — preserve ADT type from argument
if let Expr::Var(fv, _) = f.as_ref() {
let fname = fv.name.as_str();
if fname == "succ"
|| fname == "pred"
|| fname == "force"
|| fname == "id"
|| fname == "to"
|| fname == "from"
{
return self.infer_adt_type_from_expr(arg);
}
// E.54: toEnum — use single-enum heuristic
if fname == "toEnum" && self.derived_to_enum_fns.len() == 1 {
return Some(self.derived_to_enum_fns.keys().next().unwrap().clone());
}
// read — use single-derived-read heuristic
if fname == "read" && self.derived_read_fns.len() == 1 {
return Some(self.derived_read_fns.keys().next().unwrap().clone());
}
}
self.infer_adt_type_from_expr(f)
}
// Let binding: check body
Expr::Let(_, body, _) => self.infer_adt_type_from_expr(body),
// Type application: check inner
Expr::TyApp(inner, _, _) => self.infer_adt_type_from_expr(inner),
// Case expression: check any alternative's DataCon for type info
Expr::Case(_, alts, _, _) => {
for alt in alts {
if let AltCon::DataCon(con) = &alt.con {
let con_name = con.name.as_str();
if let Some(meta) = self.constructor_metadata.get(con_name) {
if let Some(ref tn) = meta.type_name {
return Some(tn.clone());
}
}
}
}
None
}
_ => None,
}
}
/// Infer the target ADT type for a `read` call.
///
/// When `read` is called on a string expression, we need to determine which
/// derived Read instance to use. We use a heuristic: if there is exactly one
/// derived Read instance, use it. This covers the common case where a module
/// has a single ADT with `deriving Read`.
fn infer_read_target_type(&self, _str_expr: &Expr) -> Option<String> {
if self.derived_read_fns.len() == 1 {
return Some(self.derived_read_fns.keys().next().unwrap().clone());
}
None
}
/// Check if an expression is a user ADT value (for Eq dispatch).
fn is_user_adt_expr(
expr: &Expr,
constructor_metadata: &FxHashMap<String, ConstructorMeta>,
) -> Option<String> {
match expr {
Expr::Var(var, _) => {
let name = var.name.as_str();
if let Some(meta) = constructor_metadata.get(name) {
return meta.type_name.clone();
}
None
}
Expr::App(f, _, _) => Self::is_user_adt_expr(f, constructor_metadata),
Expr::Let(_, body, _) => Self::is_user_adt_expr(body, constructor_metadata),
_ => None,
}
}
/// Collect constructor metadata from a binding's expression.
fn collect_constructors_from_binding(&mut self, bind: &Bind) {
match bind {
Bind::NonRec(_, expr) => {
self.collect_constructors_from_expr(expr);
}
Bind::Rec(bindings) => {
for (_, expr) in bindings {
self.collect_constructors_from_expr(expr);
}
}
}
}
/// Recursively collect constructor metadata from an expression.
fn collect_constructors_from_expr(&mut self, expr: &Expr) {
match expr {
Expr::Case(scrut, alts, _, _) => {
self.collect_constructors_from_expr(scrut);
for alt in alts {
if let AltCon::DataCon(con) = &alt.con {
self.register_constructor(con.name.as_str(), con.tag, con.arity);
}
self.collect_constructors_from_expr(&alt.rhs);
}
}
Expr::App(func, arg, _) => {
self.collect_constructors_from_expr(func);
self.collect_constructors_from_expr(arg);
}
Expr::TyApp(func, _, _) => {
self.collect_constructors_from_expr(func);
}
Expr::Lam(_, body, _) => {
self.collect_constructors_from_expr(body);
}
Expr::TyLam(_, body, _) => {
self.collect_constructors_from_expr(body);
}
Expr::Let(bind, body, _) => {
self.collect_constructors_from_binding(bind);
self.collect_constructors_from_expr(body);
}
Expr::Lazy(inner, _) => {
self.collect_constructors_from_expr(inner);
}
Expr::Cast(inner, _, _) => {
self.collect_constructors_from_expr(inner);
}
Expr::Tick(_, inner, _) => {
self.collect_constructors_from_expr(inner);
}
// Leaf nodes that don't contain constructors
Expr::Var(_, _) | Expr::Lit(_, _, _) | Expr::Type(_, _) | Expr::Coercion(_, _) => {}
}
}
/// Declare a binding (creates function signature without body).
fn declare_binding(&mut self, bind: &Bind) -> CodegenResult<()> {
match bind {
Bind::NonRec(var, expr) => {
let fn_val = self.declare_function_from_expr(var, expr)?;
self.functions.insert(var.id, fn_val);
}
Bind::Rec(bindings) => {
for (var, expr) in bindings {
let fn_val = self.declare_function_from_expr(var, expr)?;
self.functions.insert(var.id, fn_val);
}
}
}
Ok(())
}
/// Count the number of lambda parameters in an expression.
fn count_lambda_params(&self, expr: &Expr) -> usize {
let mut count = 0;
let mut current = expr;
while let Expr::Lam(_, body, _) = current {
count += 1;
current = body.as_ref();
}
count
}
/// Check if a type contains unresolved type variables or errors.
// Type-inference predicate retained for monomorphization paths.
#[allow(dead_code)]
fn type_needs_inference(&self, ty: &Ty) -> bool {
match ty {
Ty::Error => true,
Ty::Var(_) => true,
Ty::Fun(arg, ret) => self.type_needs_inference(arg) || self.type_needs_inference(ret),
Ty::Forall(_, body) => self.type_needs_inference(body),
Ty::App(f, arg) => self.type_needs_inference(f) || self.type_needs_inference(arg),
_ => false,
}
}
/// Declare a function from a Core variable and expression.
/// Uses the expression to infer the function type when var.ty contains unresolved types.
fn declare_function_from_expr(
&self,
var: &Var,
expr: &Expr,
) -> CodegenResult<FunctionValue<'ctx>> {
let param_count = self.count_lambda_params(expr);
// Count type-level parameters for comparison
let type_param_count = {
let mut count = 0;
let mut current = &var.ty;
while let Ty::Fun(_, ret) = current {
count += 1;
current = ret;
}
count
};
// Use expression-based (pointer) typing when:
// 1. Error types (derived/generated bindings)
// 2. Expression has more lambdas than the type suggests (dictionary params
// from typeclass constraints are not reflected in the Haskell type)
let fn_type = if param_count > 0
&& (matches!(&var.ty, Ty::Error) || param_count > type_param_count)
{
let tm = self.type_mapper();
// All functions take (env_ptr, args...) for uniform closure calling convention
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(tm.ptr_type().into()); // env/closure pointer
for _ in 0..param_count {
param_types.push(tm.ptr_type().into());
}
// Default return type is pointer
tm.ptr_type().fn_type(¶m_types, false)
} else {
self.lower_function_type(&var.ty)?
};
let name = self.mangle_name(var.name.as_str());
Ok(self.module.add_function(&name, fn_type))
}
/// Declare a function from a Core variable.
// Alternate function-declaration path retained for incremental lowering.
#[allow(dead_code)]
fn declare_function(&self, var: &Var) -> CodegenResult<FunctionValue<'ctx>> {
let fn_type = self.lower_function_type(&var.ty)?;
let name = self.mangle_name(var.name.as_str());
Ok(self.module.add_function(&name, fn_type))
}
/// Lower a binding to LLVM IR.
fn lower_binding(&mut self, bind: &Bind) -> CodegenResult<()> {
match bind {
Bind::NonRec(var, expr) => {
self.lower_function_def(var, expr)?;
}
Bind::Rec(bindings) => {
for (var, expr) in bindings {
self.lower_function_def(var, expr)?;
}
}
}
Ok(())
}
/// Lower a function definition.
fn lower_function_def(&mut self, var: &Var, expr: &Expr) -> CodegenResult<()> {
let fn_val = self.functions.get(&var.id).copied().ok_or_else(|| {
CodegenError::Internal(format!("function not declared: {}", var.name.as_str()))
})?;
// Create entry block
let entry = self.llvm_context().append_basic_block(fn_val, "entry");
self.builder().position_at_end(entry);
// Detect if this function uses StateT/ReaderT context.
// We check TWO sources:
// 1. The function's type signature (for functions like `comp = return 42` where the
// body doesn't have transformer-specific operations but the type is a transformer)
// 2. The function body (for functions that use get/put/ask/etc.)
//
// For nested transformers like StateT s (ReaderT r IO), we need to push the FULL
// transformer stack so that operations like `ask` can detect they're in a nested context.
let return_type = self.get_return_type_from_function_type(&var.ty);
let mut transformer_stack_from_type = self.extract_transformer_stack_from_type(return_type);
// Remove IO from the end since it's already the base of our stack
// The stack is [StateT, ReaderT, IO], we want to push [StateT, ReaderT]
if transformer_stack_from_type.last() == Some(&TransformerLayer::IO) {
transformer_stack_from_type.pop();
}
// We need to push in REVERSE order since push() inserts at position 0
// For [StateT, ReaderT], we push ReaderT first, then StateT, resulting in [StateT, ReaderT, IO]
let layers_to_push: Vec<_> = transformer_stack_from_type.iter().rev().cloned().collect();
let pushed_count = layers_to_push.len();
// Push the full transformer stack (innermost first since push inserts at position 0)
for layer in &layers_to_push {
let fn_name = var.name.as_str().to_string();
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(fn_name.clone());
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(fn_name.clone());
}
_ => {}
}
self.push_transformer_layer(*layer);
}
// If type didn't indicate transformers, fall back to body detection
let extra_layer = if pushed_count == 0 {
self.detect_transformer_for_body(expr)
} else {
None
};
if let Some(layer) = extra_layer {
let fn_name = var.name.as_str().to_string();
match layer {
TransformerLayer::StateT => {
self.state_t_functions.insert(fn_name);
}
TransformerLayer::ReaderT => {
self.reader_t_functions.insert(fn_name);
}
_ => {}
}
self.push_transformer_layer(layer);
}
// E.45: Extract Integer parameter positions from the function's type signature.
// This is needed because Core IR lambda params have Ty::Error (type info lost).
let integer_param_positions = self.extract_integer_param_positions(&var.ty);
// Lower the function body, handling lambda parameters
let result = self.lower_function_body(fn_val, expr, &integer_param_positions)?;
// Pop all the pushed layers
if extra_layer.is_some() {
self.pop_transformer_layer();
}
for _ in 0..pushed_count {
self.pop_transformer_layer();
}
// Check if the current block already has a terminator (e.g., from `error` or `unreachable`)
// If so, don't add another terminator
let current_block = self.builder().get_insert_block();
let has_terminator = current_block
.map(|bb| bb.get_terminator().is_some())
.unwrap_or(false);
if !has_terminator {
// Build return based on function's declared return type, not the computed result
// This handles cases like IO () which produces a value but should return void
let ret_type = fn_val.get_type().get_return_type();
if ret_type.is_none() {
// Void return type - don't return a value
self.builder().build_return(None).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
} else if let Some(val) = result {
// Convert to pointer if return type is pointer (uniform calling convention)
let ret_val: BasicValueEnum<'ctx> =
if ret_type == Some(self.type_mapper().ptr_type().into()) {
self.value_to_ptr(val)?.into()
} else {
val
};
self.builder().build_return(Some(&ret_val)).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
} else {
// Function expects a return value but body produced none
// This shouldn't happen with correct type checking
self.builder().build_return(None).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
}
}
Ok(())
}
/// Lower a Core expression to LLVM IR.
fn lower_expr(&mut self, expr: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
match expr {
Expr::Lit(lit, _ty, _span) => self.lower_literal(lit).map(Some),
Expr::Var(var, _span) => {
let name = var.name.as_str();
// First, check if this is a nullary constructor (like [], True, False, Nothing)
if let Some((tag, arity)) = self.constructor_info(name) {
if arity == 0 {
// Nullary constructor - allocate ADT value with just a tag
return self.lower_constructor_application(tag, 0, &[]);
}
// Non-nullary constructor referenced without args - return as closure/function
// This case is handled below when the constructor is used in an application
}
// Look up the variable in the environment
if let Some(val) = self.env.get(&var.id) {
// E.44: Force thunks on variable lookup.
// Only force if this variable was thunked (tracked in thunked_vars set).
if self.thunked_vars.contains(&var.id) {
let forced = self.build_force(*val)?;
Ok(Some(forced))
} else {
Ok(Some(*val))
}
} else if let Some(fn_val) = self.functions.get(&var.id) {
// It's a function reference
// Check if it's a CAF (only env pointer, no real arguments)
// All functions have at least 1 param (env pointer) due to uniform calling convention
let fn_type = fn_val.get_type();
if fn_type.count_param_types() <= 1 {
// CAF - call the function with null env to get its value
let null_env = self.type_mapper().ptr_type().const_null();
let call_result = self
.builder()
.build_call(*fn_val, &[null_env.into()], "caf_result")
.map_err(|e| {
CodegenError::Internal(format!("failed to call CAF: {:?}", e))
})?;
// Get the return value
if let Some(ret_val) = call_result.try_as_basic_value().basic() {
Ok(Some(ret_val))
} else {
// Void function - shouldn't happen for CAFs
Ok(None)
}
} else {
// Function with parameters - wrap in closure for uniform calling convention
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
} else if let Some((primop, arity)) = self.primitive_op_info(name) {
// Primop used as a value - create a wrapper closure
self.create_primop_closure(primop, arity, name)
} else if let Some(arity) = self.builtin_info(name) {
// Builtin used as a value - create a wrapper closure
self.create_builtin_closure(name, arity)
} else if let Some(&fn_val) = self.external_functions.get(&var.name) {
// Cross-module imported function
// Check if it's a CAF (only env pointer, no real arguments)
let fn_type = fn_val.get_type();
if fn_type.count_param_types() <= 1 {
// CAF - call the function with null env to get its value
let null_env = self.type_mapper().ptr_type().const_null();
let call_result = self
.builder()
.build_call(fn_val, &[null_env.into()], "ext_caf_result")
.map_err(|e| {
CodegenError::Internal(format!(
"failed to call external CAF: {:?}",
e
))
})?;
if let Some(ret_val) = call_result.try_as_basic_value().basic() {
Ok(Some(ret_val))
} else {
Ok(None)
}
} else {
// Function with parameters - wrap in closure
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
} else if let Some((_tag, arity)) = self.constructor_info(name) {
// Constructor used as a first-class value — create a closure wrapper
self.create_constructor_closure(name, arity)
} else {
// Unknown variable — generate a runtime stub.
// This handles StubValue defs from external packages that passed
// type checking but have no implementation.
let stub_fn = self.get_or_create_stub_function(name)?;
let fn_ptr = stub_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
}
Expr::App(func, arg, _span) => self.lower_application(func, arg),
Expr::Lam(param, body, _span) => {
// Create a closure for this lambda
self.lower_lambda(param, body)
}
Expr::Let(bind, body, _span) => self.lower_let(bind, body),
Expr::Case(scrut, alts, ty, _span) => self.lower_case(scrut, alts, ty),
Expr::TyApp(expr, _ty, _span) => {
// Type applications are erased at runtime
self.lower_expr(expr)
}
Expr::TyLam(_tyvar, body, _span) => {
// Type lambdas are erased at runtime
self.lower_expr(body)
}
Expr::Lazy(inner, _span) => {
// Create a thunk for lazy evaluation
self.lower_lazy(inner)
}
Expr::Cast(inner, _coercion, _span) => {
// Coercions are erased at runtime
self.lower_expr(inner)
}
Expr::Tick(_tick, inner, _span) => {
// Ticks are for profiling, skip for now
self.lower_expr(inner)
}
Expr::Type(_ty, _span) => {
// Type expressions have no runtime value
Ok(None)
}
Expr::Coercion(_coercion, _span) => {
// Coercion values have no runtime representation
Ok(None)
}
}
}
/// Lower a literal to LLVM IR.
fn lower_literal(&self, lit: &Literal) -> CodegenResult<BasicValueEnum<'ctx>> {
let tm = self.type_mapper();
match lit {
Literal::Int(n) => Ok(tm.i64_type().const_int(*n as u64, true).into()),
Literal::Integer(n) => {
// Arbitrary-precision Integer: call bhc_integer_from_i64 for values that fit in i64,
// or bhc_integer_from_str for larger values
if *n >= i64::MIN as i128 && *n <= i64::MAX as i128 {
let from_i64 = self.functions.get(&VarId::new(1000600)).ok_or_else(|| {
CodegenError::Internal("bhc_integer_from_i64 not declared".to_string())
})?;
let i64_val = tm.i64_type().const_int(*n as u64, true);
let result = self
.builder()
.build_call(*from_i64, &[i64_val.into()], "integer_from_i64")
.map_err(|e| {
CodegenError::Internal(format!("integer_from_i64 call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("integer_from_i64 returned void".to_string())
})?;
Ok(result)
} else {
// Large integer: pass as string
let s = n.to_string();
let global = self
.builder()
.build_global_string_ptr(&s, "integer_str")
.map_err(|e| {
CodegenError::Internal(format!(
"failed to create integer string: {:?}",
e
))
})?;
let from_str = self.functions.get(&VarId::new(1000601)).ok_or_else(|| {
CodegenError::Internal("bhc_integer_from_str not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*from_str,
&[global.as_pointer_value().into()],
"integer_from_str",
)
.map_err(|e| {
CodegenError::Internal(format!("integer_from_str call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("integer_from_str returned void".to_string())
})?;
Ok(result)
}
}
Literal::Float(f) => Ok(tm.f32_type().const_float(*f as f64).into()),
Literal::Double(d) => Ok(tm.f64_type().const_float(*d).into()),
Literal::Char(c) => Ok(tm.i64_type().const_int(*c as u64, false).into()),
Literal::String(sym) => {
// Build a linked list of characters (proper Haskell [Char] representation).
// This allows pattern matching on strings as character lists.
let s = sym.as_str();
let chars: Vec<char> = s.chars().collect();
let mut current = self.build_nil()?;
// Build list in reverse so that head of list = first char
for &ch in chars.iter().rev() {
let char_val = tm.i64_type().const_int(ch as u64, false);
current = self.build_cons(char_val.into(), current.into())?;
}
Ok(current.into())
}
}
}
/// Lower a lambda expression to a closure.
///
/// Creates a lifted function and allocates a closure struct containing
/// the function pointer and captured variables.
fn lower_lambda(
&mut self,
param: &Var,
body: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Collect all parameters (for multi-argument lambdas)
let mut params = vec![param.clone()];
let mut current_body = body;
while let Expr::Lam(next_param, next_body, _) = current_body {
params.push(next_param.clone());
current_body = next_body;
}
// Compute free variables in the lambda body
let full_lambda = Expr::Lam(param.clone(), Box::new(body.clone()), body.span());
let free = self.free_vars(&full_lambda);
// Collect the values of free variables from current environment
let mut captured: Vec<(VarId, BasicValueEnum<'ctx>)> = Vec::new();
for var_id in &free {
if let Some(val) = self.env.get(var_id) {
captured.push((*var_id, *val));
}
}
// Save current insertion point
let current_block = self.builder().get_insert_block();
// Generate unique name for the lifted function
let fn_name = self.next_closure_name();
// Build the function type:
// - First param is the closure/env pointer
// - Remaining params are the lambda parameters
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // env/closure pointer
for _ in ¶ms {
// For now, use ptr for all params (polymorphic)
param_types.push(ptr_type.into());
}
// Return type - use ptr for now (polymorphic)
let fn_type = ptr_type.fn_type(¶m_types, false);
// Create the lifted function
let lifted_fn = self.module.add_function(&fn_name, fn_type);
// Create entry block for the lifted function
let entry = self.llvm_context().append_basic_block(lifted_fn, "entry");
self.builder().position_at_end(entry);
// Save old environment and create new scope
let old_env = std::mem::take(&mut self.env);
// Bind captured variables from environment
// The closure pointer is the first argument
if !captured.is_empty() {
let closure_ptr = lifted_fn
.get_first_param()
.ok_or_else(|| CodegenError::Internal("missing closure param".to_string()))?
.into_pointer_value();
for (i, (var_id, _)) in captured.iter().enumerate() {
let elem_ptr =
self.extract_closure_env_elem(closure_ptr, captured.len() as u32, i as u32)?;
// Store as pointer - will be unboxed when used
self.env.insert(*var_id, elem_ptr.into());
}
}
// Bind lambda parameters to function arguments
// Skip first arg (closure pointer)
for (i, lam_param) in params.iter().enumerate() {
if let Some(arg) = lifted_fn.get_nth_param((i + 1) as u32) {
self.env.insert(lam_param.id, arg);
}
}
// Lower the body in tail position (enables tail call optimization)
let was_tail = self.in_tail_position;
self.in_tail_position = true;
let result = self.lower_expr(current_body)?;
self.in_tail_position = was_tail;
// Build return
if let Some(val) = result {
// Convert to pointer for return
let ret_ptr = self.value_to_ptr(val)?;
self.builder()
.build_return(Some(&ret_ptr))
.map_err(|e| CodegenError::Internal(format!("failed to build return: {:?}", e)))?;
} else {
// Return null pointer for unit/void
let null = ptr_type.const_null();
self.builder()
.build_return(Some(&null))
.map_err(|e| CodegenError::Internal(format!("failed to build return: {:?}", e)))?;
}
// Restore old environment
self.env = old_env;
// Restore insertion point
if let Some(block) = current_block {
self.builder().position_at_end(block);
}
// Allocate closure with function pointer and captured values
let fn_ptr = lifted_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &captured)?;
Ok(Some(closure_ptr.into()))
}
/// Lower a lazy expression to a thunk.
///
/// Creates a thunk that suspends the evaluation of the inner expression.
/// The thunk is evaluated when forced (via bhc_force).
fn lower_lazy(&mut self, inner: &Expr) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Compute free variables in the lazy expression
let free = self.free_vars(inner);
// Collect the values of free variables from current environment
let mut captured: Vec<(VarId, BasicValueEnum<'ctx>)> = Vec::new();
for var_id in &free {
if let Some(val) = self.env.get(var_id) {
captured.push((*var_id, *val));
}
}
// Save current insertion point
let current_block = self.builder().get_insert_block();
// Generate unique name for the thunk evaluation function
let fn_name = self.next_thunk_name();
// Build the function type for the eval function:
// fn(env: *mut u8) -> *mut u8
// The env pointer points to the captured variables array
// Extract types early to avoid borrow conflicts with self.env
let ptr_type = self.type_mapper().ptr_type();
let i64_type = self.type_mapper().i64_type();
let env_array_type = ptr_type.array_type(captured.len() as u32);
let fn_type = ptr_type.fn_type(&[ptr_type.into()], false);
// Create the thunk evaluation function
let eval_fn = self.module.add_function(&fn_name, fn_type);
// Create entry block for the eval function
let entry = self.llvm_context().append_basic_block(eval_fn, "entry");
self.builder().position_at_end(entry);
// Save old environment and create new scope
let old_env = std::mem::take(&mut self.env);
// Bind captured variables from environment parameter
if !captured.is_empty() {
let env_ptr = eval_fn
.get_first_param()
.ok_or_else(|| CodegenError::Internal("missing env param".to_string()))?
.into_pointer_value();
for (i, (var_id, _)) in captured.iter().enumerate() {
// Load variable from env array
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
env_array_type,
env_ptr,
&[i64_type.const_zero(), i64_type.const_int(i as u64, false)],
&format!("thunk_env_load_{}", i),
)
.map_err(|e| {
CodegenError::Internal(format!("failed to get env elem ptr: {:?}", e))
})?
};
let elem_val = self
.builder()
.build_load(ptr_type, elem_ptr, &format!("thunk_env_val_{}", i))
.map_err(|e| {
CodegenError::Internal(format!("failed to load env elem: {:?}", e))
})?;
self.env.insert(*var_id, elem_val);
}
}
// Lower the inner expression (this produces the actual computation)
let result = self.lower_expr(inner)?;
// Build return — but only if the current block doesn't already have a terminator.
// Expressions like `error "..."` generate an `unreachable` terminator.
let current_bb = self.builder().get_insert_block();
let has_terminator = current_bb
.map(|bb| bb.get_terminator().is_some())
.unwrap_or(false);
if !has_terminator {
if let Some(val) = result {
let ret_ptr = self.value_to_ptr(val)?;
self.builder().build_return(Some(&ret_ptr)).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
} else {
let null = ptr_type.const_null();
self.builder().build_return(Some(&null)).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
}
}
// Restore old environment
self.env = old_env;
// Restore insertion point
if let Some(block) = current_block {
self.builder().position_at_end(block);
}
// Allocate thunk with eval function pointer and captured values
let fn_ptr = eval_fn.as_global_value().as_pointer_value();
let thunk_ptr = self.alloc_thunk(fn_ptr, &captured)?;
Ok(Some(thunk_ptr.into()))
}
// ========================================================================
// Primitive Operations
// ========================================================================
/// Check if a name is a primitive operation and return its arity.
fn primitive_op_info(&self, name: &str) -> Option<(PrimOp, u32)> {
match name {
// Arithmetic (binary)
"+" | "GHC.Num.+" => Some((PrimOp::Add, 2)),
"-" | "GHC.Num.-" => Some((PrimOp::Sub, 2)),
"*" | "GHC.Num.*" => Some((PrimOp::Mul, 2)),
"/" | "GHC.Real./" => Some((PrimOp::Div, 2)),
"div" | "GHC.Real.div" => Some((PrimOp::Div, 2)),
"mod" | "GHC.Real.mod" => Some((PrimOp::Mod, 2)),
"rem" | "GHC.Real.rem" => Some((PrimOp::Rem, 2)),
"quot" | "GHC.Real.quot" => Some((PrimOp::Quot, 2)),
// Comparison (binary)
"==" | "GHC.Classes.==" => Some((PrimOp::Eq, 2)),
"/=" | "GHC.Classes./=" => Some((PrimOp::Ne, 2)),
"<" | "GHC.Classes.<" => Some((PrimOp::Lt, 2)),
"<=" | "GHC.Classes.<=" => Some((PrimOp::Le, 2)),
">" | "GHC.Classes.>" => Some((PrimOp::Gt, 2)),
">=" | "GHC.Classes.>=" => Some((PrimOp::Ge, 2)),
// Boolean (binary)
"&&" | "GHC.Classes.&&" => Some((PrimOp::And, 2)),
"||" | "GHC.Classes.||" => Some((PrimOp::Or, 2)),
// Unary
"negate" | "GHC.Num.negate" => Some((PrimOp::Negate, 1)),
"abs" | "GHC.Num.abs" => Some((PrimOp::Abs, 1)),
"signum" | "GHC.Num.signum" => Some((PrimOp::Signum, 1)),
"not" | "GHC.Classes.not" => Some((PrimOp::Not, 1)),
// Bitwise (binary)
".&." => Some((PrimOp::BitAnd, 2)),
".|." => Some((PrimOp::BitOr, 2)),
"xor" => Some((PrimOp::BitXor, 2)),
"shiftL" => Some((PrimOp::ShiftL, 2)),
"shiftR" => Some((PrimOp::ShiftR, 2)),
// Bitwise (unary)
"complement" => Some((PrimOp::Complement, 1)),
_ => None,
}
}
/// Create a closure wrapping a primitive operation.
/// This is used when a primop like (+) is used as a first-class value.
fn create_primop_closure(
&mut self,
primop: PrimOp,
arity: u32,
name: &str,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let _i64_type = tm.i64_type();
// Create a unique name for the wrapper function
let wrapper_name = format!(
"primop_wrapper_{}",
name.replace(|c: char| !c.is_alphanumeric(), "_")
);
// Check if wrapper already exists
let wrapper_fn = if let Some(existing) =
self.module.llvm_module().get_function(&wrapper_name)
{
existing
} else {
// Create wrapper function: (ptr env, ptr arg1, [ptr arg2]) -> ptr
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // env/closure pointer
for _ in 0..arity {
param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(¶m_types, false);
let wrapper_fn = self.module.llvm_module().add_function(
&wrapper_name,
wrapper_fn_type,
Some(inkwell::module::Linkage::Internal),
);
// Build the wrapper function body
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
let current_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
// Load arguments (skip env at index 0)
let args: Vec<BasicValueEnum<'ctx>> = (1..=arity)
.map(|i| wrapper_fn.get_nth_param(i).unwrap())
.collect();
// Perform the primop
let result = self.lower_primop_direct(primop, &args)?;
// Box the result and return
let result_ptr = self.value_to_ptr(result)?;
self.builder()
.build_return(Some(&result_ptr))
.map_err(|e| CodegenError::Internal(format!("failed to build return: {:?}", e)))?;
// Restore insertion point
if let Some(bb) = current_bb {
self.builder().position_at_end(bb);
}
wrapper_fn
};
// Create a closure wrapping the primop function
let fn_ptr = wrapper_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
/// Create a closure wrapping a builtin operation.
/// This is used when a builtin like (>>) is used as a first-class value.
/// Generate (or return cached) a stub function for an unimplemented external function.
///
/// The stub has signature `(ptr) -> ptr` and calls `bhc_error` with a diagnostic
/// message, then hits unreachable. This allows compilation to succeed for code
/// paths that reference stub functions, deferring the error to runtime.
fn get_or_create_stub_function(&mut self, name: &str) -> CodegenResult<FunctionValue<'ctx>> {
if let Some(fn_val) = self.stub_functions.get(name) {
return Ok(*fn_val);
}
let ptr_type = self.type_mapper().ptr_type();
// Sanitize name for LLVM symbol
let stub_fn_name = format!(
"bhc_stub_{}",
name.replace(|c: char| !c.is_alphanumeric() && c != '_', "_")
);
let fn_type = ptr_type.fn_type(&[ptr_type.into()], false);
let stub_fn = self.module.llvm_module().add_function(
&stub_fn_name,
fn_type,
Some(inkwell::module::Linkage::Internal),
);
// Build the function body: call bhc_error then unreachable
let entry_bb = self.llvm_ctx.append_basic_block(stub_fn, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
let error_msg = format!("stub: {} not implemented", name);
let msg_global = self.module.add_global_string(&stub_fn_name, &error_msg);
let error_fn = self
.functions
.get(&VarId::new(1000006))
.copied()
.ok_or_else(|| CodegenError::Internal("bhc_error not declared".to_string()))?;
self.builder()
.build_call(error_fn, &[msg_global.into()], "")
.map_err(|e| CodegenError::Internal(format!("stub call error: {:?}", e)))?;
self.builder()
.build_unreachable()
.map_err(|e| CodegenError::Internal(format!("stub unreachable: {:?}", e)))?;
// Restore insertion point
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
self.stub_functions.insert(name.to_string(), stub_fn);
Ok(stub_fn)
}
fn create_builtin_closure(
&mut self,
name: &str,
arity: u32,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// For arity-0 builtins, directly evaluate without wrapping in another closure.
// This is important for builtins like `ask` and `get` that return closures:
// wrapping them would create a double-closure that breaks runReaderT/runStateT.
if arity == 0 {
return self.lower_builtin_direct(name, &[]);
}
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Create a unique name for the wrapper function
let wrapper_name = format!(
"builtin_wrapper_{}",
name.replace(|c: char| !c.is_alphanumeric(), "_")
);
// Check if wrapper already exists
let wrapper_fn = if let Some(existing) =
self.module.llvm_module().get_function(&wrapper_name)
{
existing
} else {
// Create wrapper function: (ptr env, ptr arg1, ptr arg2, ...) -> ptr
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // env/closure pointer
for _ in 0..arity {
param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(¶m_types, false);
let wrapper_fn = self.module.llvm_module().add_function(
&wrapper_name,
wrapper_fn_type,
Some(inkwell::module::Linkage::Internal),
);
// Build the wrapper function body
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
let current_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
// Load arguments (skip env at index 0)
let args: Vec<BasicValueEnum<'ctx>> = (1..=arity)
.map(|i| wrapper_fn.get_nth_param(i).unwrap())
.collect();
// Perform the builtin operation
let result = self.lower_builtin_direct(name, &args)?;
// Return the result — convert non-pointer values if needed
let result_ptr = match result {
Some(v) => {
if v.is_pointer_value() {
v.into_pointer_value()
} else if v.is_int_value() {
self.int_to_ptr(v.into_int_value())?
} else {
// Float or other — box it
let ptr_val = self.value_to_ptr(v)?;
ptr_val
}
}
None => ptr_type.const_null(), // Unit/void result
};
self.builder()
.build_return(Some(&result_ptr))
.map_err(|e| CodegenError::Internal(format!("failed to build return: {:?}", e)))?;
// Restore insertion point
if let Some(bb) = current_bb {
self.builder().position_at_end(bb);
}
wrapper_fn
};
// Create a closure wrapping the builtin function
let fn_ptr = wrapper_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
/// Create a closure wrapping a data constructor used as a first-class value.
/// E.g., `(:)` passed to `foldr` becomes a closure that allocates a cons cell.
fn create_constructor_closure(
&mut self,
name: &str,
arity: u32,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
if arity == 0 {
// Zero-arity constructor: just allocate the ADT struct directly
let (tag, _) = self
.constructor_info(name)
.ok_or_else(|| CodegenError::Internal(format!("unknown constructor: {}", name)))?;
let adt_ptr = self.alloc_adt(tag, 0)?;
return Ok(Some(adt_ptr.into()));
}
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let wrapper_name = format!(
"constructor_wrapper_{}",
name.replace(|c: char| !c.is_alphanumeric(), "_")
);
let wrapper_fn =
if let Some(existing) = self.module.llvm_module().get_function(&wrapper_name) {
existing
} else {
let (tag, _) = self.constructor_info(name).ok_or_else(|| {
CodegenError::Internal(format!("unknown constructor: {}", name))
})?;
// Create wrapper function: (ptr env, ptr arg1, ..., ptr argN) -> ptr
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // env/closure pointer
for _ in 0..arity {
param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(¶m_types, false);
let wrapper_fn = self.module.llvm_module().add_function(
&wrapper_name,
wrapper_fn_type,
Some(inkwell::module::Linkage::Internal),
);
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
let current_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
// For cons (:), use build_cons for correct list layout
let result_ptr = if name == ":" && arity == 2 {
let head = wrapper_fn.get_nth_param(1).unwrap();
let tail = wrapper_fn.get_nth_param(2).unwrap();
self.build_cons(head, tail)?
} else {
// General constructor: allocate ADT with tag and fields
let adt_ptr = self.alloc_adt(tag, arity)?;
for i in 0..arity {
let arg = wrapper_fn.get_nth_param(i + 1).unwrap();
self.store_adt_field(adt_ptr, arity, i, arg)?;
}
adt_ptr
};
self.builder()
.build_return(Some(&result_ptr))
.map_err(|e| {
CodegenError::Internal(format!("constructor wrapper return: {:?}", e))
})?;
if let Some(bb) = current_bb {
self.builder().position_at_end(bb);
}
wrapper_fn
};
let fn_ptr = wrapper_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &[])?;
Ok(Some(closure_ptr.into()))
}
/// Create a partially-applied builtin closure.
/// Given a builtin with arity N and M provided args (M < N), creates a
/// closure that captures the M args and accepts the remaining (N-M) args.
fn create_partial_builtin_closure(
&mut self,
name: &str,
arity: u32,
provided_args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let n_provided = provided_args.len() as u32;
let n_remaining = arity - n_provided;
// Create a unique name for the partial wrapper
let wrapper_name = format!(
"builtin_partial_{}_{}of{}",
name.replace(|c: char| !c.is_alphanumeric(), "_"),
n_provided,
arity
);
// Check if wrapper already exists
let wrapper_fn = if let Some(existing) =
self.module.llvm_module().get_function(&wrapper_name)
{
existing
} else {
// Create wrapper: (ptr env, ptr remaining_arg1, ...) -> ptr
// The env closure has the captured args stored in its environment
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // env/closure pointer
for _ in 0..n_remaining {
param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(¶m_types, false);
let wrapper_fn = self.module.llvm_module().add_function(
&wrapper_name,
wrapper_fn_type,
Some(inkwell::module::Linkage::Internal),
);
// Build the wrapper function body
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
let current_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
// Extract captured args from closure environment
let closure_param = wrapper_fn.get_nth_param(0).unwrap().into_pointer_value();
let closure_ty = self.closure_type(n_provided);
let env_slot = self
.builder()
.build_struct_gep(closure_ty, closure_param, 2, "env_slot")
.map_err(|e| CodegenError::Internal(format!("partial env slot: {:?}", e)))?;
let mut all_args: Vec<BasicValueEnum<'ctx>> = Vec::new();
// Load captured args from environment
for i in 0..n_provided {
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
ptr_type.array_type(n_provided),
env_slot,
&[
tm.i64_type().const_zero(),
tm.i64_type().const_int(i as u64, false),
],
&format!("cap_{}", i),
)
.map_err(|e| CodegenError::Internal(format!("partial cap gep: {:?}", e)))?
};
let captured_val = self
.builder()
.build_load(ptr_type, elem_ptr, &format!("captured_{}", i))
.map_err(|e| CodegenError::Internal(format!("partial cap load: {:?}", e)))?;
all_args.push(captured_val);
}
// Add remaining args from function parameters
for i in 0..n_remaining {
all_args.push(wrapper_fn.get_nth_param(1 + i).unwrap());
}
// Call lower_builtin_direct with all args
let result = self.lower_builtin_direct(name, &all_args)?;
let result_ptr = match result {
Some(v) => v.into_pointer_value(),
None => ptr_type.const_null(),
};
self.builder()
.build_return(Some(&result_ptr))
.map_err(|e| CodegenError::Internal(format!("partial return: {:?}", e)))?;
// Restore insertion point
if let Some(bb) = current_bb {
self.builder().position_at_end(bb);
}
wrapper_fn
};
// Lower the provided args
let was_tail = self.in_tail_position;
self.in_tail_position = false;
let mut captured_vals: Vec<(VarId, BasicValueEnum<'ctx>)> = Vec::new();
for (i, arg_expr) in provided_args.iter().enumerate() {
let val = self
.lower_expr(arg_expr)?
.ok_or_else(|| CodegenError::Internal("partial arg has no value".to_string()))?;
let ptr_val = self.value_to_ptr(val)?;
captured_vals.push((VarId::new(900000 + i), ptr_val.into()));
}
self.in_tail_position = was_tail;
// Create closure with captured args
let fn_ptr = wrapper_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &captured_vals)?;
Ok(Some(closure_ptr.into()))
}
/// Execute a builtin operation directly on LLVM values (already lowered).
/// This is for when builtins are used as values and receive runtime arguments.
fn lower_builtin_direct(
&mut self,
name: &str,
args: &[BasicValueEnum<'ctx>],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let ptr_type = self.type_mapper().ptr_type();
match name {
">>" => {
// (>>) :: m a -> m b -> m b
// Execute first action (arg1), ignore result, return second (arg2)
// For our simple model, arg1 and arg2 are thunks/values
// We just evaluate both and return the second
// Since args are already evaluated values, we just return arg2
// Note: If these were actual IO thunks, we'd need to force them
Ok(Some(args[1]))
}
">>=" => {
// (>>=) :: m a -> (a -> m b) -> m b
// Execute first action, pass result to function, return result of function
let action_result = args[0]; // Result of first action
let func = args[1].into_pointer_value(); // Function closure
// Call the function with the action result
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), action_result.into()],
"bind_result",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to call bind function: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal(">>=: function returned void".to_string())
})?;
Ok(Some(result))
}
"=<<" => {
// (=<<) :: (a -> m b) -> m a -> m b (flipped >>=)
let func = args[0].into_pointer_value();
let action_result = args[1];
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), action_result.into()],
"bind_result",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to call bind function: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("=<<: function returned void".to_string())
})?;
Ok(Some(result))
}
"return" | "pure" => {
// return :: a -> m a
// For our simple IO model, just return the value
Ok(Some(args[0]))
}
"id" => {
// id :: a -> a
Ok(Some(args[0]))
}
"const" if args.len() >= 2 => {
// const :: a -> b -> a
Ok(Some(args[0]))
}
"flip" if args.len() >= 3 => {
// flip :: (a -> b -> c) -> b -> a -> c
let func = args[0].into_pointer_value();
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), args[2].into(), args[1].into()],
"flip_result",
)
.map_err(|e| CodegenError::Internal(format!("flip call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("flip: returned void".to_string()))?;
Ok(Some(result))
}
"seq" if args.len() >= 2 => {
// seq :: a -> b -> b (evaluate first, return second)
Ok(Some(args[1]))
}
"$" if args.len() >= 2 => {
// ($) :: (a -> b) -> a -> b
let func = args[0].into_pointer_value();
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), args[1].into()],
"dollar_result",
)
.map_err(|e| CodegenError::Internal(format!("$ call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("$: returned void".to_string()))?;
Ok(Some(result))
}
"." if args.len() >= 3 => {
// (.) :: (b -> c) -> (a -> b) -> a -> c
let f = args[0].into_pointer_value();
let g = args[1].into_pointer_value();
let x = args[2];
// Apply g to x first
let g_fn_ptr = self.extract_closure_fn_ptr(g)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let g_result = self
.builder()
.build_indirect_call(fn_type, g_fn_ptr, &[g.into(), x.into()], "compose_g")
.map_err(|e| CodegenError::Internal(format!("compose g call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("compose g: returned void".to_string())
})?;
// Apply f to (g x)
let f_fn_ptr = self.extract_closure_fn_ptr(f)?;
let f_result = self
.builder()
.build_indirect_call(
fn_type,
f_fn_ptr,
&[f.into(), g_result.into()],
"compose_f",
)
.map_err(|e| CodegenError::Internal(format!("compose f call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("compose f: returned void".to_string())
})?;
Ok(Some(f_result))
}
"fmap" | "<$>" => {
// fmap :: (a -> b) -> f a -> f b
// For IO: apply function to action result
let func = args[0].into_pointer_value();
let action_result = args[1];
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let action_ptr = self.value_to_ptr(action_result)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), action_ptr.into()],
"fmap_result",
)
.map_err(|e| CodegenError::Internal(format!("fmap call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("fmap: returned void".to_string()))?;
Ok(Some(result))
}
"<*>" => {
// (<*>) :: f (a -> b) -> f a -> f b
// For IO: extract function from first action, apply to second
let func_closure = args[0].into_pointer_value();
let val = args[1];
let fn_ptr = self.extract_closure_fn_ptr(func_closure)?;
let val_ptr = self.value_to_ptr(val)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func_closure.into(), val_ptr.into()],
"ap_result",
)
.map_err(|e| CodegenError::Internal(format!("<*> call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("<*>: returned void".to_string()))?;
Ok(Some(result))
}
"putStrLn" => {
// putStrLn :: String -> IO ()
let str_ptr = args[0].into_pointer_value();
let rts_fn = self.functions.get(&VarId::new(1000002)).ok_or_else(|| {
CodegenError::Internal("bhc_print_string_ln not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[str_ptr.into()], "")
.map_err(|e| {
CodegenError::Internal(format!("putStrLn call failed: {:?}", e))
})?;
Ok(Some(ptr_type.const_null().into()))
}
"putStr" => {
let str_ptr = args[0].into_pointer_value();
let rts_fn = self.functions.get(&VarId::new(1000004)).ok_or_else(|| {
CodegenError::Internal("bhc_print_string not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[str_ptr.into()], "")
.map_err(|e| CodegenError::Internal(format!("putStr call failed: {:?}", e)))?;
Ok(Some(ptr_type.const_null().into()))
}
// Identity operations (newtype = pass through)
"Identity" | "runIdentity" | "Identity.pure" => Ok(Some(args[0])),
"Identity.fmap" | "Identity.>>=" | "Identity.<*>" => {
// Apply function to value
let func = args[0].into_pointer_value();
let val = args[1];
let fn_ptr = self.extract_closure_fn_ptr(func)?;
let val_ptr = self.value_to_ptr(val)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[func.into(), val_ptr.into()],
"identity_call",
)
.map_err(|e| CodegenError::Internal(format!("Identity call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("Identity: returned void".to_string()))?;
Ok(Some(result))
}
"Identity.>>" => Ok(Some(args[1])),
// ReaderT/StateT constructors (newtype = pass through)
"ReaderT" | "StateT" => Ok(Some(args[0])),
"runReaderT" | "runStateT" => {
// Call the closure (arg0) with the environment/state (arg1)
let closure = args[0].into_pointer_value();
let env_val = args[1];
let fn_ptr = self.extract_closure_fn_ptr(closure)?;
let env_ptr = self.value_to_ptr(env_val)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[closure.into(), env_ptr.into()],
"run_t_result",
)
.map_err(|e| CodegenError::Internal(format!("runT call failed: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("runT: returned void".to_string()))?;
Ok(Some(result))
}
// ReaderT operations (zero-arity: ask returns closure \r -> r)
"ask" => self.lower_builtin_ask(),
// ReaderT operations with pre-lowered args
"asks" => {
// asks f = closure \r -> f(r)
let f_val = args[0];
let fn_name = "bhc_reader_t_asks";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let r_ptr = self.value_to_ptr(r)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
f_fn,
&[f.into(), r_ptr.into()],
"asks_result",
)
.map_err(|e| CodegenError::Internal(format!("asks call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("asks: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("asks return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"local" => {
// local f m = closure \r -> m(f(r))
let f_val = args[0];
let m_val = args[1];
let fn_name = "bhc_reader_t_local";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let f_fn = self.extract_closure_fn_ptr(f)?;
let r_ptr = self.value_to_ptr(r)?;
let r_prime = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), r_ptr.into()], "local_r")
.map_err(|e| CodegenError::Internal(format!("local f call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("local f: void".to_string()))?;
let m_fn = self.extract_closure_fn_ptr(m)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), r_prime.into()],
"local_result",
)
.map_err(|e| CodegenError::Internal(format!("local m call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("local m: void".to_string()))?;
self.builder()
.build_return(Some(&result))
.map_err(|e| CodegenError::Internal(format!("local return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
// ReaderT instance methods with pre-lowered args
"ReaderT.pure" => {
// \r -> x where x = args[0]
let x_val = args[0];
let fn_name = "bhc_reader_t_pure";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let x = self.extract_closure_env_elem(env, 1, 0)?;
self.builder().build_return(Some(&x)).map_err(|e| {
CodegenError::Internal(format!("reader_t_pure return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"ReaderT.>>=" => {
// \r -> let a = m(r) in k(a)(r) where m = args[0], k = args[1]
let m_val = args[0];
let k_val = args[1];
let fn_name = "bhc_reader_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let r_ptr = self.value_to_ptr(r)?;
let a = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r_ptr.into()], "bind_a")
.map_err(|e| CodegenError::Internal(format!("reader bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader bind m: void".to_string()))?;
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "bind_kr")
.map_err(|e| CodegenError::Internal(format!("reader bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader bind k: void".to_string()))?;
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr_ptr.into(), r_ptr.into()],
"bind_result",
)
.map_err(|e| CodegenError::Internal(format!("reader bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("reader bind kr: void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader bind return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"ReaderT.>>" => {
// \r -> m1(r); m2(r) where m1 = args[0], m2 = args[1]
let m1_val = args[0];
let m2_val = args[1];
let fn_name = "bhc_reader_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let r_ptr = self.value_to_ptr(r)?;
self.builder()
.build_indirect_call(
fn_type,
m1_fn,
&[m1.into(), r_ptr.into()],
"then_discard",
)
.map_err(|e| CodegenError::Internal(format!("reader then m1: {:?}", e)))?;
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
m2_fn,
&[m2.into(), r_ptr.into()],
"then_result",
)
.map_err(|e| CodegenError::Internal(format!("reader then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("reader then m2: void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader then return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"ReaderT.fmap" => {
// \r -> f(m(r)) where f = args[0], m = args[1]
let f_val = args[0];
let m_val = args[1];
let fn_name = "bhc_reader_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let r_ptr = self.value_to_ptr(r)?;
let a = self
.builder()
.build_indirect_call(fn_type, m_fn, &[m.into(), r_ptr.into()], "fmap_a")
.map_err(|e| CodegenError::Internal(format!("reader fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader fmap m: void".to_string()))?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let result = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "fmap_result")
.map_err(|e| CodegenError::Internal(format!("reader fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader fmap f: void".to_string()))?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader fmap return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"ReaderT.<*>" => {
// \r -> (mf(r))(mx(r)) where mf = args[0], mx = args[1]
let mf_val = args[0];
let mx_val = args[1];
let fn_name = "bhc_reader_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let r = func.get_nth_param(1).unwrap();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let mx = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let r_ptr = self.value_to_ptr(r)?;
let f = self
.builder()
.build_indirect_call(fn_type, mf_fn, &[mf.into(), r_ptr.into()], "ap_f")
.map_err(|e| CodegenError::Internal(format!("reader ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader ap mf: void".to_string()))?;
let mx_fn = self.extract_closure_fn_ptr(mx)?;
let x = self
.builder()
.build_indirect_call(fn_type, mx_fn, &[mx.into(), r_ptr.into()], "ap_x")
.map_err(|e| CodegenError::Internal(format!("reader ap mx: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("reader ap mx: void".to_string()))?;
let f_ptr_val = f.into_pointer_value();
let f_fn = self.extract_closure_fn_ptr(f_ptr_val)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
f_fn,
&[f_ptr_val.into(), x.into()],
"ap_result",
)
.map_err(|e| CodegenError::Internal(format!("reader ap f(x): {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("reader ap f(x): void".to_string())
})?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("reader ap return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let mx_ptr = self.value_to_ptr(mx_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), mx_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"ReaderT.lift" | "ReaderT.liftIO" => {
// \r -> action (const function) where action = args[0]
let action_val = args[0];
let fn_name = "bhc_reader_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let _r = func.get_nth_param(1).unwrap();
let action = self.extract_closure_env_elem(env, 1, 0)?;
self.builder().build_return(Some(&action)).map_err(|e| {
CodegenError::Internal(format!("reader lift return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let a_ptr = self.value_to_ptr(action_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), a_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
// StateT operations (zero-arity: get returns closure \s -> (s, s))
"get" => self.lower_builtin_get(),
// StateT operations with pre-lowered args
"put" => {
// put s' = closure \_ -> ((), s')
let s_val = args[0];
let fn_name = "bhc_state_t_put";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let _s = func.get_nth_param(1).unwrap();
let new_s = self.extract_closure_env_elem(env, 1, 0)?;
let unit_val = ptr_type.const_null();
let pair = self.alloc_pair(unit_val.into(), new_s.into())?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("put return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let s_ptr = self.value_to_ptr(s_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), s_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"modify" => {
// modify f = closure \s -> ((), f(s))
let f_val = args[0];
let fn_name = "bhc_state_t_modify";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let s_ptr_val = self.value_to_ptr(s)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let new_s = self
.builder()
.build_indirect_call(
fn_type,
f_fn,
&[f.into(), s_ptr_val.into()],
"modify_s",
)
.map_err(|e| CodegenError::Internal(format!("modify f call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("modify f: void".to_string()))?;
let unit_val = ptr_type.const_null();
let pair = self.alloc_pair(unit_val.into(), new_s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("modify return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"gets" => {
// gets f = closure \s -> (f(s), s)
let f_val = args[0];
let fn_name = "bhc_state_t_gets";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 1, 0)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let s_ptr_val = self.value_to_ptr(s)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
f_fn,
&[f.into(), s_ptr_val.into()],
"gets_result",
)
.map_err(|e| CodegenError::Internal(format!("gets f call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("gets f: void".to_string()))?;
let pair = self.alloc_pair(result, s)?;
self.builder()
.build_return(Some(&pair))
.map_err(|e| CodegenError::Internal(format!("gets return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), f_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"evalStateT" => {
// evalStateT m s = fst (runStateT m s)
let m_val = args[0];
let s_val = args[1];
let closure = m_val.into_pointer_value();
let fn_ptr_val = self.extract_closure_fn_ptr(closure)?;
let s_ptr = self.value_to_ptr(s_val)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pair_result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr_val,
&[closure.into(), s_ptr.into()],
"eval_state",
)
.map_err(|e| CodegenError::Internal(format!("evalStateT call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("evalStateT: void".to_string()))?;
let result = self.extract_pair_fst(pair_result.into_pointer_value())?;
Ok(Some(result.into()))
}
"execStateT" => {
// execStateT m s = snd (runStateT m s)
let m_val = args[0];
let s_val = args[1];
let closure = m_val.into_pointer_value();
let fn_ptr_val = self.extract_closure_fn_ptr(closure)?;
let s_ptr = self.value_to_ptr(s_val)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let pair_result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr_val,
&[closure.into(), s_ptr.into()],
"exec_state",
)
.map_err(|e| CodegenError::Internal(format!("execStateT call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("execStateT: void".to_string()))?;
let result = self.extract_pair_snd(pair_result.into_pointer_value())?;
Ok(Some(result.into()))
}
// StateT instance methods with pre-lowered args
"StateT.pure" => {
// \s -> (x, s) where x = args[0]
let x_val = args[0];
let fn_name = "bhc_state_t_pure";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let x = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(x.into(), s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("state_t_pure return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let x_ptr = self.value_to_ptr(x_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), x_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"StateT.>>=" => {
// \s -> let (a, s') = m(s); k(a)(s') where m = args[0], k = args[1]
let m_val = args[0];
let k_val = args[1];
let fn_name = "bhc_state_t_bind";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m = self.extract_closure_env_elem(env, 2, 0)?;
let k = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let s_ptr_val = self.value_to_ptr(s)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), s_ptr_val.into()],
"sbind_pair",
)
.map_err(|e| CodegenError::Internal(format!("state bind m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state bind m: void".to_string()))?;
let pair_ptr = pair.into_pointer_value();
let a = self.extract_pair_fst(pair_ptr)?;
let s_prime = self.extract_pair_snd(pair_ptr)?;
let k_fn = self.extract_closure_fn_ptr(k)?;
let kr = self
.builder()
.build_indirect_call(fn_type, k_fn, &[k.into(), a.into()], "sbind_kr")
.map_err(|e| CodegenError::Internal(format!("state bind k: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state bind k: void".to_string()))?;
let kr_ptr = kr.into_pointer_value();
let kr_fn = self.extract_closure_fn_ptr(kr_ptr)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
kr_fn,
&[kr_ptr.into(), s_prime.into()],
"sbind_result",
)
.map_err(|e| CodegenError::Internal(format!("state bind kr: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state bind kr: void".to_string()))?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("state bind return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m_ptr = self.value_to_ptr(m_val)?;
let k_ptr = self.value_to_ptr(k_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m_ptr.into()),
(VarId::new(900001), k_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"StateT.>>" => {
// \s -> let (_, s') = m1(s); m2(s') where m1 = args[0], m2 = args[1]
let m1_val = args[0];
let m2_val = args[1];
let fn_name = "bhc_state_t_then";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let m1 = self.extract_closure_env_elem(env, 2, 0)?;
let m2 = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m1_fn = self.extract_closure_fn_ptr(m1)?;
let s_ptr_val = self.value_to_ptr(s)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m1_fn,
&[m1.into(), s_ptr_val.into()],
"sthen_pair",
)
.map_err(|e| CodegenError::Internal(format!("state then m1: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state then m1: void".to_string()))?;
let s_prime = self.extract_pair_snd(pair.into_pointer_value())?;
let m2_fn = self.extract_closure_fn_ptr(m2)?;
let result = self
.builder()
.build_indirect_call(
fn_type,
m2_fn,
&[m2.into(), s_prime.into()],
"sthen_result",
)
.map_err(|e| CodegenError::Internal(format!("state then m2: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state then m2: void".to_string()))?;
self.builder().build_return(Some(&result)).map_err(|e| {
CodegenError::Internal(format!("state then return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let m1_ptr = self.value_to_ptr(m1_val)?;
let m2_ptr = self.value_to_ptr(m2_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), m1_ptr.into()),
(VarId::new(900001), m2_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"StateT.fmap" => {
// \s -> let (a, s') = m(s) in (f(a), s') where f = args[0], m = args[1]
let f_val = args[0];
let m_val = args[1];
let fn_name = "bhc_state_t_fmap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let f = self.extract_closure_env_elem(env, 2, 0)?;
let m = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let m_fn = self.extract_closure_fn_ptr(m)?;
let s_ptr_val = self.value_to_ptr(s)?;
let pair = self
.builder()
.build_indirect_call(
fn_type,
m_fn,
&[m.into(), s_ptr_val.into()],
"sfmap_pair",
)
.map_err(|e| CodegenError::Internal(format!("state fmap m: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state fmap m: void".to_string()))?;
let pair_ptr = pair.into_pointer_value();
let a = self.extract_pair_fst(pair_ptr)?;
let s_prime = self.extract_pair_snd(pair_ptr)?;
let f_fn = self.extract_closure_fn_ptr(f)?;
let fa = self
.builder()
.build_indirect_call(fn_type, f_fn, &[f.into(), a.into()], "sfmap_fa")
.map_err(|e| CodegenError::Internal(format!("state fmap f: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state fmap f: void".to_string()))?;
let result_pair = self.alloc_pair(fa, s_prime.into())?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| {
CodegenError::Internal(format!("state fmap return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let f_ptr = self.value_to_ptr(f_val)?;
let m_ptr = self.value_to_ptr(m_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), f_ptr.into()),
(VarId::new(900001), m_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"StateT.<*>" => {
// \s -> let (f, s') = mf(s); (x, s'') = mx(s') in (f(x), s'')
let mf_val = args[0];
let mx_val = args[1];
let fn_name = "bhc_state_t_ap";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let mf = self.extract_closure_env_elem(env, 2, 0)?;
let mx = self.extract_closure_env_elem(env, 2, 1)?;
let fn_type = ptr_type.fn_type(&[ptr_type.into(), ptr_type.into()], false);
let mf_fn = self.extract_closure_fn_ptr(mf)?;
let s_ptr_val = self.value_to_ptr(s)?;
let pair1 = self
.builder()
.build_indirect_call(
fn_type,
mf_fn,
&[mf.into(), s_ptr_val.into()],
"sap_p1",
)
.map_err(|e| CodegenError::Internal(format!("state ap mf: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state ap mf: void".to_string()))?;
let pair1_ptr = pair1.into_pointer_value();
let f = self.extract_pair_fst(pair1_ptr)?;
let s_prime = self.extract_pair_snd(pair1_ptr)?;
let mx_fn = self.extract_closure_fn_ptr(mx)?;
let pair2 = self
.builder()
.build_indirect_call(fn_type, mx_fn, &[mx.into(), s_prime.into()], "sap_p2")
.map_err(|e| CodegenError::Internal(format!("state ap mx: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state ap mx: void".to_string()))?;
let pair2_ptr = pair2.into_pointer_value();
let x = self.extract_pair_fst(pair2_ptr)?;
let s_double_prime = self.extract_pair_snd(pair2_ptr)?;
let f_fn_ptr = self.extract_closure_fn_ptr(f)?;
let fx = self
.builder()
.build_indirect_call(fn_type, f_fn_ptr, &[f.into(), x.into()], "sap_fx")
.map_err(|e| CodegenError::Internal(format!("state ap f(x): {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("state ap f(x): void".to_string()))?;
let result_pair = self.alloc_pair(fx, s_double_prime.into())?;
self.builder()
.build_return(Some(&result_pair))
.map_err(|e| CodegenError::Internal(format!("state ap return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let mf_ptr = self.value_to_ptr(mf_val)?;
let mx_ptr = self.value_to_ptr(mx_val)?;
let closure_ptr = self.alloc_closure(
fn_ptr,
&[
(VarId::new(900000), mf_ptr.into()),
(VarId::new(900001), mx_ptr.into()),
],
)?;
Ok(Some(closure_ptr.into()))
}
"StateT.lift" | "StateT.liftIO" => {
// \s -> (action, s) where action = args[0]
let action_val = args[0];
let fn_name = "bhc_state_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let s = func.get_nth_param(1).unwrap();
let action = self.extract_closure_env_elem(env, 1, 0)?;
let pair = self.alloc_pair(action.into(), s)?;
self.builder().build_return(Some(&pair)).map_err(|e| {
CodegenError::Internal(format!("state lift return: {:?}", e))
})?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let a_ptr = self.value_to_ptr(action_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), a_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
// Generic lift/liftIO (delegate to ReaderT versions as default)
"lift" => {
let action_val = args[0];
let fn_name = "bhc_reader_t_lift";
let func = self.get_or_create_transformer_fn(fn_name);
if func.count_basic_blocks() == 0 {
let entry = self.llvm_ctx.append_basic_block(func, "entry");
let saved_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry);
let env = func.get_nth_param(0).unwrap().into_pointer_value();
let _r = func.get_nth_param(1).unwrap();
let action = self.extract_closure_env_elem(env, 1, 0)?;
self.builder()
.build_return(Some(&action))
.map_err(|e| CodegenError::Internal(format!("lift return: {:?}", e)))?;
if let Some(bb) = saved_bb {
self.builder().position_at_end(bb);
}
}
let fn_ptr = func.as_global_value().as_pointer_value();
let a_ptr = self.value_to_ptr(action_val)?;
let closure_ptr =
self.alloc_closure(fn_ptr, &[(VarId::new(900000), a_ptr.into())])?;
Ok(Some(closure_ptr.into()))
}
"liftIO" => {
// For IO, liftIO = id
Ok(Some(args[0]))
}
// Data.Text operations - call RTS functions with already-lowered values
"Data.Text.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000200)).ok_or_else(|| {
CodegenError::Internal("bhc_text_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "text_empty")
.map_err(|e| CodegenError::Internal(format!("text_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.singleton" => {
let rts_fn = self.functions.get(&VarId::new(1000201)).ok_or_else(|| {
CodegenError::Internal("bhc_text_singleton not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_singleton")
.map_err(|e| CodegenError::Internal(format!("text_singleton: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_singleton: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.pack" => {
let rts_fn = self.functions.get(&VarId::new(1000223)).ok_or_else(|| {
CodegenError::Internal("bhc_text_pack not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_pack")
.map_err(|e| CodegenError::Internal(format!("text_pack: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_pack: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.unpack" => {
let rts_fn = self.functions.get(&VarId::new(1000224)).ok_or_else(|| {
CodegenError::Internal("bhc_text_unpack not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_unpack")
.map_err(|e| CodegenError::Internal(format!("text_unpack: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_unpack: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.null" => {
let rts_fn = self.functions.get(&VarId::new(1000202)).ok_or_else(|| {
CodegenError::Internal("bhc_text_null not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_null")
.map_err(|e| CodegenError::Internal(format!("text_null: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_null: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.length" => {
let rts_fn = self.functions.get(&VarId::new(1000203)).ok_or_else(|| {
CodegenError::Internal("bhc_text_length not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_length")
.map_err(|e| CodegenError::Internal(format!("text_length: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_length: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.append" | "Data.Text.<>" => {
let rts_fn = self.functions.get(&VarId::new(1000210)).ok_or_else(|| {
CodegenError::Internal("bhc_text_append not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "text_append")
.map_err(|e| CodegenError::Internal(format!("text_append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_append: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.toUpper" => {
let rts_fn = self.functions.get(&VarId::new(1000220)).ok_or_else(|| {
CodegenError::Internal("bhc_text_to_upper not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_to_upper")
.map_err(|e| CodegenError::Internal(format!("text_to_upper: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_to_upper: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.toLower" => {
let rts_fn = self.functions.get(&VarId::new(1000219)).ok_or_else(|| {
CodegenError::Internal("bhc_text_to_lower not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "text_to_lower")
.map_err(|e| CodegenError::Internal(format!("text_to_lower: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_to_lower: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.take" => {
let rts_fn = self.functions.get(&VarId::new(1000212)).ok_or_else(|| {
CodegenError::Internal("bhc_text_take not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "text_take")
.map_err(|e| CodegenError::Internal(format!("text_take: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_take: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.drop" => {
let rts_fn = self.functions.get(&VarId::new(1000214)).ok_or_else(|| {
CodegenError::Internal("bhc_text_drop not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "text_drop")
.map_err(|e| CodegenError::Internal(format!("text_drop: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("text_drop: void".to_string()))?;
Ok(Some(result))
}
// Data.Text.Lazy — direct RTS dispatch
"Data.Text.Lazy.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000270)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_text_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "lt_empty")
.map_err(|e| CodegenError::Internal(format!("lt_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lt_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.Lazy.fromStrict"
| "Data.Text.Lazy.toStrict"
| "Data.Text.Lazy.pack"
| "Data.Text.Lazy.unpack"
| "Data.Text.Lazy.fromChunks"
| "Data.Text.Lazy.toChunks"
| "Data.Text.Lazy.tail" => {
let var_id = match name {
"Data.Text.Lazy.fromStrict" => 1000271,
"Data.Text.Lazy.toStrict" => 1000272,
"Data.Text.Lazy.pack" => 1000273,
"Data.Text.Lazy.unpack" => 1000274,
"Data.Text.Lazy.fromChunks" => 1000278,
"Data.Text.Lazy.toChunks" => 1000279,
"Data.Text.Lazy.tail" => 1000281,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lt_unary")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.Text.Lazy.null" | "Data.Text.Lazy.length" | "Data.Text.Lazy.head" => {
let var_id = match name {
"Data.Text.Lazy.null" => 1000275,
"Data.Text.Lazy.length" => 1000276,
"Data.Text.Lazy.head" => 1000280,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lt_to_int")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
"Data.Text.Lazy.append" | "Data.Text.Lazy.<>" => {
let rts_fn = self.functions.get(&VarId::new(1000277)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_text_append not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lt_append")
.map_err(|e| CodegenError::Internal(format!("lt_append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lt_append: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.Lazy.take" | "Data.Text.Lazy.drop" => {
let var_id = if name == "Data.Text.Lazy.take" {
1000282
} else {
1000283
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lt_int_ptr")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
// Data.ByteString.Lazy — direct RTS dispatch
"Data.ByteString.Lazy.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000440)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "lbs_empty")
.map_err(|e| CodegenError::Internal(format!("lbs_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lbs_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.fromStrict"
| "Data.ByteString.Lazy.toStrict"
| "Data.ByteString.Lazy.fromChunks"
| "Data.ByteString.Lazy.toChunks"
| "Data.ByteString.Lazy.pack"
| "Data.ByteString.Lazy.tail"
| "Data.ByteString.Lazy.putStr" => {
let var_id = match name {
"Data.ByteString.Lazy.fromStrict" => 1000441,
"Data.ByteString.Lazy.toStrict" => 1000442,
"Data.ByteString.Lazy.fromChunks" => 1000443,
"Data.ByteString.Lazy.toChunks" => 1000444,
"Data.ByteString.Lazy.pack" => 1000447,
"Data.ByteString.Lazy.tail" => 1000450,
"Data.ByteString.Lazy.putStr" => 1000457,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lbs_unary")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.null"
| "Data.ByteString.Lazy.length"
| "Data.ByteString.Lazy.head" => {
let var_id = match name {
"Data.ByteString.Lazy.null" => 1000445,
"Data.ByteString.Lazy.length" => 1000446,
"Data.ByteString.Lazy.head" => 1000449,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lbs_to_int")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
"Data.ByteString.Lazy.append"
| "Data.ByteString.Lazy.<>"
| "Data.ByteString.Lazy.isPrefixOf"
| "Data.ByteString.Lazy.hPutStr" => {
let var_id = match name {
"Data.ByteString.Lazy.append" | "Data.ByteString.Lazy.<>" => 1000448,
"Data.ByteString.Lazy.isPrefixOf" => 1000454,
"Data.ByteString.Lazy.hPutStr" => 1000458,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lbs_binary")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.take" | "Data.ByteString.Lazy.drop" => {
let var_id = if name == "Data.ByteString.Lazy.take" {
1000451
} else {
1000452
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lbs_int_ptr")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.readFile" | "Data.ByteString.Lazy.hGetContents" => {
let var_id = if name == "Data.ByteString.Lazy.readFile" {
1000455
} else {
1000459
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lbs_io_unary")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.writeFile" => {
let rts_fn = self.functions.get(&VarId::new(1000456)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_write_file not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lbs_write_file")
.map_err(|e| CodegenError::Internal(format!("lbs_write_file: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lbs_write_file: void".to_string()))?;
Ok(Some(result))
}
// Data.ByteString.Lazy.Char8 — direct RTS dispatch
"Data.ByteString.Lazy.Char8.unpack"
| "Data.ByteString.Lazy.Char8.lines"
| "Data.ByteString.Lazy.Char8.unlines" => {
let var_id = match name {
"Data.ByteString.Lazy.Char8.unpack" => 1000470,
"Data.ByteString.Lazy.Char8.lines" => 1000471,
"Data.ByteString.Lazy.Char8.unlines" => 1000472,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lbs_c8_unary")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.Char8.take" => {
let rts_fn = self.functions.get(&VarId::new(1000473)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_char8_take not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lbs_c8_take")
.map_err(|e| CodegenError::Internal(format!("lbs_c8_take: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lbs_c8_take: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Lazy.Char8.cons" => {
let rts_fn = self.functions.get(&VarId::new(1000475)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_char8_cons not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "lbs_c8_cons")
.map_err(|e| CodegenError::Internal(format!("lbs_c8_cons: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lbs_c8_cons: void".to_string()))?;
Ok(Some(result))
}
// Data.Text.Lazy.Encoding — direct RTS dispatch
"Data.Text.Lazy.Encoding.encodeUtf8" => {
let rts_fn = self.functions.get(&VarId::new(1000476)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_text_encode_utf8 not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lt_encode_utf8")
.map_err(|e| CodegenError::Internal(format!("lt_encode_utf8: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lt_encode_utf8: void".to_string()))?;
Ok(Some(result))
}
"Data.Text.Lazy.Encoding.decodeUtf8" => {
let rts_fn = self.functions.get(&VarId::new(1000477)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_text_decode_utf8 not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "lt_decode_utf8")
.map_err(|e| CodegenError::Internal(format!("lt_decode_utf8: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("lt_decode_utf8: void".to_string()))?;
Ok(Some(result))
}
// Data.ByteString.Builder — direct RTS dispatch
"Data.ByteString.Builder.empty" => {
let rts_fn = self.functions.get(&VarId::new(1000440)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_empty not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[], "bsb_empty")
.map_err(|e| CodegenError::Internal(format!("bsb_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_empty: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Builder.singleton"
| "Data.ByteString.Builder.word8"
| "Data.ByteString.Builder.charUtf8"
| "Data.ByteString.Builder.char7"
| "Data.ByteString.Builder.char8"
| "Data.ByteString.Builder.intDec"
| "Data.ByteString.Builder.int8Dec"
| "Data.ByteString.Builder.int16Dec"
| "Data.ByteString.Builder.int32Dec"
| "Data.ByteString.Builder.int64Dec"
| "Data.ByteString.Builder.integerDec"
| "Data.ByteString.Builder.wordDec"
| "Data.ByteString.Builder.word8Dec"
| "Data.ByteString.Builder.word16Dec"
| "Data.ByteString.Builder.word32Dec"
| "Data.ByteString.Builder.word64Dec"
| "Data.ByteString.Builder.word16BE"
| "Data.ByteString.Builder.int16BE"
| "Data.ByteString.Builder.word32BE"
| "Data.ByteString.Builder.int32BE"
| "Data.ByteString.Builder.word64BE"
| "Data.ByteString.Builder.int64BE"
| "Data.ByteString.Builder.word16LE"
| "Data.ByteString.Builder.int16LE"
| "Data.ByteString.Builder.word32LE"
| "Data.ByteString.Builder.int32LE"
| "Data.ByteString.Builder.word64LE"
| "Data.ByteString.Builder.int64LE"
| "Data.ByteString.Builder.word16Host"
| "Data.ByteString.Builder.int16Host"
| "Data.ByteString.Builder.word32Host"
| "Data.ByteString.Builder.int32Host"
| "Data.ByteString.Builder.word64Host"
| "Data.ByteString.Builder.int64Host"
| "Data.ByteString.Builder.wordHex"
| "Data.ByteString.Builder.word8Hex"
| "Data.ByteString.Builder.word16Hex"
| "Data.ByteString.Builder.word32Hex"
| "Data.ByteString.Builder.word64Hex"
| "Data.ByteString.Builder.word8HexFixed" => {
let var_id = match name {
"Data.ByteString.Builder.singleton" | "Data.ByteString.Builder.word8" => {
1000478
}
"Data.ByteString.Builder.charUtf8" => 1000479,
"Data.ByteString.Builder.char7" => 1000482,
"Data.ByteString.Builder.char8" => 1000483,
"Data.ByteString.Builder.intDec"
| "Data.ByteString.Builder.int8Dec"
| "Data.ByteString.Builder.int16Dec"
| "Data.ByteString.Builder.int32Dec"
| "Data.ByteString.Builder.int64Dec"
| "Data.ByteString.Builder.integerDec"
| "Data.ByteString.Builder.wordDec"
| "Data.ByteString.Builder.word8Dec"
| "Data.ByteString.Builder.word16Dec"
| "Data.ByteString.Builder.word32Dec"
| "Data.ByteString.Builder.word64Dec" => 1000481,
"Data.ByteString.Builder.word16BE" | "Data.ByteString.Builder.int16BE" => {
1000486
}
"Data.ByteString.Builder.word32BE" | "Data.ByteString.Builder.int32BE" => {
1000487
}
"Data.ByteString.Builder.word64BE" | "Data.ByteString.Builder.int64BE" => {
1000488
}
"Data.ByteString.Builder.word16LE"
| "Data.ByteString.Builder.int16LE"
| "Data.ByteString.Builder.word16Host"
| "Data.ByteString.Builder.int16Host" => 1000489,
"Data.ByteString.Builder.word32LE"
| "Data.ByteString.Builder.int32LE"
| "Data.ByteString.Builder.word32Host"
| "Data.ByteString.Builder.int32Host" => 1000490,
"Data.ByteString.Builder.word64LE"
| "Data.ByteString.Builder.int64LE"
| "Data.ByteString.Builder.word64Host"
| "Data.ByteString.Builder.int64Host" => 1000491,
"Data.ByteString.Builder.wordHex"
| "Data.ByteString.Builder.word8Hex"
| "Data.ByteString.Builder.word16Hex"
| "Data.ByteString.Builder.word32Hex"
| "Data.ByteString.Builder.word64Hex" => 1000492,
"Data.ByteString.Builder.word8HexFixed" => 1000493,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "bsb_direct")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Builder.stringUtf8"
| "Data.ByteString.Builder.string7"
| "Data.ByteString.Builder.string8" => {
let var_id = match name {
"Data.ByteString.Builder.stringUtf8" => 1000480,
"Data.ByteString.Builder.string7" => 1000484,
"Data.ByteString.Builder.string8" => 1000485,
_ => unreachable!(),
};
let rts_fn = self
.functions
.get(&VarId::new(var_id))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "bsb_string_direct")
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
"Data.ByteString.Builder.byteString" | "Data.ByteString.Builder.shortByteString" => {
let rts_fn = self.functions.get(&VarId::new(1000441)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_from_strict not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "bsb_from_strict")
.map_err(|e| CodegenError::Internal(format!("bsb_from_strict: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_from_strict: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Builder.lazyByteString"
| "Data.ByteString.Builder.toLazyByteString" => Ok(Some(args[0])),
"Data.ByteString.Builder.toStrictByteString" => {
let rts_fn = self.functions.get(&VarId::new(1000442)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_to_strict not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "bsb_to_strict")
.map_err(|e| CodegenError::Internal(format!("bsb_to_strict: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_to_strict: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Builder.append" | "Data.ByteString.Builder.<>" => {
let rts_fn = self.functions.get(&VarId::new(1000448)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_append not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "bsb_append")
.map_err(|e| CodegenError::Internal(format!("bsb_append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("bsb_append: void".to_string()))?;
Ok(Some(result))
}
"Data.ByteString.Builder.hPutBuilder" => {
let rts_fn = self.functions.get(&VarId::new(1000458)).ok_or_else(|| {
CodegenError::Internal("bhc_lazy_bs_h_put_str not declared".to_string())
})?;
self.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "bsb_h_put")
.map_err(|e| CodegenError::Internal(format!("bsb_h_put: {:?}", e)))?;
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
"even" => {
let int_val = self.coerce_to_int(args[0])?;
let i64_type = self.type_mapper().i64_type();
let two = i64_type.const_int(2, false);
let rem = self
.builder()
.build_int_signed_rem(int_val, two, "even_rem")
.map_err(|e| CodegenError::Internal(format!("even: srem: {:?}", e)))?;
let is_even = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
rem,
i64_type.const_zero(),
"is_even",
)
.map_err(|e| CodegenError::Internal(format!("even: cmp: {:?}", e)))?;
let tag = self
.builder()
.build_int_z_extend(is_even, i64_type, "even_tag")
.map_err(|e| CodegenError::Internal(format!("even: ext: {:?}", e)))?;
self.allocate_bool_adt(tag, "even")
}
"odd" => {
let int_val = self.coerce_to_int(args[0])?;
let i64_type = self.type_mapper().i64_type();
let two = i64_type.const_int(2, false);
let rem = self
.builder()
.build_int_signed_rem(int_val, two, "odd_rem")
.map_err(|e| CodegenError::Internal(format!("odd: srem: {:?}", e)))?;
let is_odd = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
rem,
i64_type.const_zero(),
"is_odd",
)
.map_err(|e| CodegenError::Internal(format!("odd: cmp: {:?}", e)))?;
let tag = self
.builder()
.build_int_z_extend(is_odd, i64_type, "odd_tag")
.map_err(|e| CodegenError::Internal(format!("odd: ext: {:?}", e)))?;
self.allocate_bool_adt(tag, "odd")
}
"compare" => {
let a_int = self.coerce_to_int(args[0])?;
let b_int = self.coerce_to_int(args[1])?;
let i64_type = self.type_mapper().i64_type();
let is_lt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, a_int, b_int, "cmp_lt")
.map_err(|e| CodegenError::Internal(format!("compare: slt: {:?}", e)))?;
let is_gt = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, a_int, b_int, "cmp_gt")
.map_err(|e| CodegenError::Internal(format!("compare: sgt: {:?}", e)))?;
let tag_gt_or_eq = self
.builder()
.build_select(
is_gt,
i64_type.const_int(2, false),
i64_type.const_int(1, false),
"gt_or_eq",
)
.map_err(|e| CodegenError::Internal(format!("compare: select1: {:?}", e)))?
.into_int_value();
let tag = self
.builder()
.build_select(is_lt, i64_type.const_int(0, false), tag_gt_or_eq, "cmp_tag")
.map_err(|e| CodegenError::Internal(format!("compare: select2: {:?}", e)))?;
self.allocate_ordering_adt(tag.into_int_value(), "compare")
}
"fst" => {
let pair_ptr = args[0].into_pointer_value();
let fst = self.extract_adt_field(pair_ptr, 2, 0)?;
Ok(Some(fst.into()))
}
"snd" => {
let pair_ptr = args[0].into_pointer_value();
let snd = self.extract_adt_field(pair_ptr, 2, 1)?;
Ok(Some(snd.into()))
}
"succ" => {
let int_val = self.coerce_to_int(args[0])?;
let one = self.type_mapper().i64_type().const_int(1, false);
let result = self
.builder()
.build_int_add(int_val, one, "succ")
.map_err(|e| CodegenError::Internal(format!("succ failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
"pred" => {
let int_val = self.coerce_to_int(args[0])?;
let one = self.type_mapper().i64_type().const_int(1, false);
let result = self
.builder()
.build_int_sub(int_val, one, "pred")
.map_err(|e| CodegenError::Internal(format!("pred failed: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
"swap" => {
let pair_ptr = args[0].into_pointer_value();
let fst_val = self.extract_adt_field(pair_ptr, 2, 0)?;
let snd_val = self.extract_adt_field(pair_ptr, 2, 1)?;
self.allocate_ptr_pair_tuple(snd_val, fst_val, "swap")
}
"min" => {
let a_int = self.coerce_to_int(args[0])?;
let b_int = self.coerce_to_int(args[1])?;
let cmp = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, a_int, b_int, "min_cmp")
.map_err(|e| CodegenError::Internal(format!("min cmp: {:?}", e)))?;
let result = self
.builder()
.build_select(cmp, a_int, b_int, "min_sel")
.map_err(|e| CodegenError::Internal(format!("min sel: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
"max" => {
let a_int = self.coerce_to_int(args[0])?;
let b_int = self.coerce_to_int(args[1])?;
let cmp = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, a_int, b_int, "max_cmp")
.map_err(|e| CodegenError::Internal(format!("max cmp: {:?}", e)))?;
let result = self
.builder()
.build_select(cmp, a_int, b_int, "max_sel")
.map_err(|e| CodegenError::Internal(format!("max sel: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result.into_int_value())?.into()))
}
"subtract" => {
// subtract x y = y - x (args flipped)
let x_int = self.coerce_to_int(args[0])?;
let y_int = self.coerce_to_int(args[1])?;
let result = self
.builder()
.build_int_sub(y_int, x_int, "subtract")
.map_err(|e| CodegenError::Internal(format!("subtract: {:?}", e)))?;
Ok(Some(self.int_to_ptr(result)?.into()))
}
"const" => {
// const x y = x — return first argument
Ok(Some(args[0]))
}
"flip" => {
// flip f x y = f y x
// args[0] = f (closure), args[1] = x, args[2] = y
let f_ptr = args[0].into_pointer_value();
let fn_ptr = self.extract_closure_fn_ptr(f_ptr)?;
// Flat 3-arg call: fn(closure_env, y, x)
let fn_type =
ptr_type.fn_type(&[ptr_type.into(), ptr_type.into(), ptr_type.into()], false);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[f_ptr.into(), args[2].into(), args[1].into()],
"flip_direct",
)
.map_err(|e| CodegenError::Internal(format!("flip_direct: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("flip_direct: void".to_string()))?;
Ok(Some(result))
}
"otherwise" => {
// otherwise = True (tagged-int-as-pointer, 1)
Ok(Some(
self.type_mapper().i64_type().const_int(1, false).into(),
))
}
// E.63: DeepSeq stubs (no-ops in strict runtime)
"rnf" => {
// rnf: evaluate arg (already done), return unit
Ok(Some(ptr_type.const_null().into()))
}
"deepseq" => {
// deepseq: evaluate first arg (already done), return second
Ok(Some(args[1]))
}
"force" => {
// force: identity — value already in normal form
Ok(Some(args[0]))
}
// GHC.Generics from/to — identity passthrough in direct mode
"from" | "to" => Ok(Some(args[0])),
"toUpper" | "toLower" => {
let rts_id = if name == "toUpper" { 1000036 } else { 1000037 };
let int_val = self.coerce_to_int(args[0])?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| {
CodegenError::Internal(format!("{}: truncate failed: {:?}", name, e))
})?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("char conv {} not declared", name))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let u32_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(u32_val, self.type_mapper().i64_type(), "char_ext")
.map_err(|e| {
CodegenError::Internal(format!("{}: extend failed: {:?}", name, e))
})?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
// E.37: Data.Char predicates as first-class functions
"isAlpha" | "isDigit" | "isAlphaNum" | "isSpace" | "isUpper" | "isLower"
| "isPrint" | "isAscii" | "isControl" | "isHexDigit" | "isLetter" | "isNumber"
| "isPunctuation" | "isSymbol" => {
let rts_id = match name {
"isAlpha" => 1000030,
"isDigit" => 1000031,
"isAlphaNum" => 1000032,
"isSpace" => 1000033,
"isUpper" => 1000034,
"isLower" => 1000035,
"isPrint" => 1000038,
"isAscii" => 1000039,
"isControl" => 1000040,
"isHexDigit" => 1000041,
"isLetter" => 1000042,
"isNumber" => 1000043,
"isPunctuation" => 1000044,
"isSymbol" => 1000045,
_ => unreachable!(),
};
let int_val = self.coerce_to_int(args[0])?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| {
CodegenError::Internal(format!("{}: truncate failed: {:?}", name, e))
})?;
let rts_fn = self.functions.get(&VarId::new(rts_id)).ok_or_else(|| {
CodegenError::Internal(format!("char pred {} not declared", name))
})?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], name)
.map_err(|e| CodegenError::Internal(format!("{} call failed: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: returned void", name)))?;
let bool_val = result.into_int_value();
let tag = self
.builder()
.build_int_z_extend(bool_val, self.type_mapper().i64_type(), "bool_tag")
.map_err(|e| {
CodegenError::Internal(format!("{}: extend failed: {:?}", name, e))
})?;
self.allocate_bool_adt(tag, name)
}
// ord/chr are identity (both Int and Char are i64)
"ord" | "chr" => Ok(Some(args[0])),
"digitToInt" => {
let int_val = self.coerce_to_int(args[0])?;
let i32_type = self.type_mapper().i32_type();
let char_val = self
.builder()
.build_int_truncate(int_val, i32_type, "char_val")
.map_err(|e| {
CodegenError::Internal(format!("digitToInt: truncate failed: {:?}", e))
})?;
let rts_fn = self.functions.get(&VarId::new(1000046)).ok_or_else(|| {
CodegenError::Internal("bhc_char_digit_to_int not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[char_val.into()], "digit_to_int")
.map_err(|e| {
CodegenError::Internal(format!("digitToInt call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("digitToInt: returned void".to_string())
})?;
let i32_val = result.into_int_value();
let extended = self
.builder()
.build_int_s_extend(i32_val, self.type_mapper().i64_type(), "digit_ext")
.map_err(|e| {
CodegenError::Internal(format!("digitToInt: extend failed: {:?}", e))
})?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
// E.54: Enum/Bounded as first-class values
"minBound" => self.lower_builtin_min_bound(),
"maxBound" => self.lower_builtin_max_bound(),
"intToDigit" => {
let int_val = self.coerce_to_int(args[0])?;
let i32_type = self.type_mapper().i32_type();
let truncated = self
.builder()
.build_int_truncate(int_val, i32_type, "int_val")
.map_err(|e| {
CodegenError::Internal(format!("intToDigit: truncate failed: {:?}", e))
})?;
let rts_fn = self.functions.get(&VarId::new(1000047)).ok_or_else(|| {
CodegenError::Internal("bhc_char_int_to_digit not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[truncated.into()], "int_to_digit")
.map_err(|e| {
CodegenError::Internal(format!("intToDigit call failed: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("intToDigit: returned void".to_string())
})?;
let u32_val = result.into_int_value();
let extended = self
.builder()
.build_int_z_extend(u32_val, self.type_mapper().i64_type(), "digit_char_ext")
.map_err(|e| {
CodegenError::Internal(format!("intToDigit: extend failed: {:?}", e))
})?;
Ok(Some(self.int_to_ptr(extended)?.into()))
}
// Async exception masking
"getMaskingState" => self.lower_builtin_get_masking_state(),
"mask_" => {
// mask_ with pre-lowered action arg
let action_closure = args[0].into_pointer_value();
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self
.functions
.get(&VarId::new(1000083))
.ok_or_else(|| CodegenError::Internal("bhc_mask not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"mask_result",
)
.map_err(|e| CodegenError::Internal(format!("mask_ call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mask_: void".to_string()))?;
Ok(Some(result))
}
"uninterruptibleMask_" => {
let action_closure = args[0].into_pointer_value();
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self.functions.get(&VarId::new(1000086)).ok_or_else(|| {
CodegenError::Internal("bhc_uninterruptible_mask not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"uninterruptible_mask_result",
)
.map_err(|e| {
CodegenError::Internal(format!("uninterruptibleMask_ call: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("uninterruptibleMask_: void".to_string())
})?;
Ok(Some(result))
}
"mask" => {
let action_closure = args[0].into_pointer_value();
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self.functions.get(&VarId::new(1000085)).ok_or_else(|| {
CodegenError::Internal("bhc_mask_with_restore not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"mask_restore_result",
)
.map_err(|e| CodegenError::Internal(format!("mask call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("mask: void".to_string()))?;
Ok(Some(result))
}
"uninterruptibleMask" => {
let action_closure = args[0].into_pointer_value();
let action_fn = self.extract_closure_fn_ptr(action_closure)?;
let rts_fn = self.functions.get(&VarId::new(1000087)).ok_or_else(|| {
CodegenError::Internal(
"bhc_uninterruptible_mask_with_restore not declared".to_string(),
)
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[action_fn.into(), action_closure.into()],
"uninterruptible_mask_result",
)
.map_err(|e| {
CodegenError::Internal(format!("uninterruptibleMask call: {:?}", e))
})?
.try_as_basic_value()
.basic()
.ok_or_else(|| {
CodegenError::Internal("uninterruptibleMask: void".to_string())
})?;
Ok(Some(result))
}
_ => {
// Check for field selector pattern: $sel_N
if let Some(index_str) = name.strip_prefix("$sel_") {
if let Ok(field_index) = index_str.parse::<u32>() {
let tuple_ptr = args[0].into_pointer_value();
let tm = self.type_mapper();
let field_offset = 1 + field_index;
let field_ptr = unsafe {
self.builder()
.build_gep(
tm.i64_type(),
tuple_ptr,
&[tm.i64_type().const_int(field_offset as u64, false)],
&format!("sel_field_ptr_{}", field_index),
)
.map_err(|e| {
CodegenError::Internal(format!(
"$sel_{} gep failed: {:?}",
field_index, e
))
})?
};
let field_val = self
.builder()
.build_load(
tm.ptr_type(),
field_ptr,
&format!("sel_field_{}", field_index),
)
.map_err(|e| {
CodegenError::Internal(format!(
"$sel_{} load failed: {:?}",
field_index, e
))
})?;
return Ok(Some(field_val));
}
}
// Fallthrough: try container operations that are used as
// first-class values or with arity 0
if name.starts_with("Data.Sequence.")
|| name.starts_with("Data.Map.")
|| name.starts_with("Data.Set.")
|| name.starts_with("Data.IntMap.")
|| name.starts_with("Data.IntSet.")
{
self.lower_builtin_container_direct(name, args)
} else {
// For builtins that are handled in lower_builtin but not
// lower_builtin_direct, generate a stub that calls bhc_error
// at runtime rather than failing compilation.
let stub_fn = self.get_or_create_stub_function(name)?;
let null_env = self.type_mapper().ptr_type().const_null();
let call_result = self
.builder()
.build_call(stub_fn, &[null_env.into()], "stub_call")
.map_err(|e| CodegenError::Internal(format!("stub call: {:?}", e)))?;
if let Some(ret_val) = call_result.try_as_basic_value().basic() {
Ok(Some(ret_val))
} else {
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
}
}
}
}
/// Handle container builtins used as first-class values in lower_builtin_direct.
/// These have pre-evaluated BasicValueEnum args rather than &Expr args.
fn lower_builtin_container_direct(
&mut self,
name: &str,
args: &[BasicValueEnum<'ctx>],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let _ptr_type = tm.ptr_type();
match name {
// Data.Sequence
"Data.Sequence.empty" => self.lower_builtin_seq_empty(),
"Data.Sequence.singleton" => {
let rts_fn = self.functions.get(&VarId::new(1000801)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_singleton not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "seq_sing")
.map_err(|e| CodegenError::Internal(format!("seq_sing: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_sing: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.null" => {
let rts_fn = self.functions.get(&VarId::new(1000802)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_null not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "seq_null")
.map_err(|e| CodegenError::Internal(format!("seq_null: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_null: void".to_string()))?;
let int_val = result.into_int_value();
self.allocate_bool_adt(int_val, "seq_null")
}
"Data.Sequence.length" => {
let rts_fn = self.functions.get(&VarId::new(1000803)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_length not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "seq_len")
.map_err(|e| CodegenError::Internal(format!("seq_len: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_len: void".to_string()))?;
let int_val = result.into_int_value();
Ok(Some(self.int_to_ptr(int_val)?.into()))
}
"Data.Sequence.index" => {
let seq_ptr = args[0];
let idx = self.coerce_to_int(args[1])?;
let rts_fn = self.functions.get(&VarId::new(1000804)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_index not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into(), idx.into()], "seq_idx")
.map_err(|e| CodegenError::Internal(format!("seq_idx: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_idx: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.reverse" => {
let rts_fn = self.functions.get(&VarId::new(1000811)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_reverse not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into()], "seq_rev")
.map_err(|e| CodegenError::Internal(format!("seq_rev: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_rev: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.<|" => {
let rts_fn = self.functions.get(&VarId::new(1000806)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_cons not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "seq_cons")
.map_err(|e| CodegenError::Internal(format!("seq_cons: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_cons: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.|>" => {
let rts_fn = self.functions.get(&VarId::new(1000807)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_snoc not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "seq_snoc")
.map_err(|e| CodegenError::Internal(format!("seq_snoc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_snoc: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.><" => {
let rts_fn = self.functions.get(&VarId::new(1000808)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_append not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[args[0].into(), args[1].into()], "seq_app")
.map_err(|e| CodegenError::Internal(format!("seq_app: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_app: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.take" => {
let n = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000809)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_take not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[n.into(), args[1].into()], "seq_take")
.map_err(|e| CodegenError::Internal(format!("seq_take: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_take: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.drop" => {
let n = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000810)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_drop not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[n.into(), args[1].into()], "seq_drop")
.map_err(|e| CodegenError::Internal(format!("seq_drop: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_drop: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.update" => {
let idx = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000812)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_update not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[idx.into(), args[1].into(), args[2].into()],
"seq_upd",
)
.map_err(|e| CodegenError::Internal(format!("seq_upd: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_upd: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.insertAt" => {
let idx = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000813)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_insert_at not declared".to_string())
})?;
let result = self
.builder()
.build_call(
*rts_fn,
&[idx.into(), args[1].into(), args[2].into()],
"seq_ins",
)
.map_err(|e| CodegenError::Internal(format!("seq_ins: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_ins: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.deleteAt" => {
let idx = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000814)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_delete_at not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[idx.into(), args[1].into()], "seq_del")
.map_err(|e| CodegenError::Internal(format!("seq_del: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_del: void".to_string()))?;
Ok(Some(result))
}
"Data.Sequence.replicate" => {
let n = self.coerce_to_int(args[0])?;
let rts_fn = self.functions.get(&VarId::new(1000817)).ok_or_else(|| {
CodegenError::Internal("bhc_seq_replicate not declared".to_string())
})?;
let result = self
.builder()
.build_call(*rts_fn, &[n.into(), args[1].into()], "seq_rep")
.map_err(|e| CodegenError::Internal(format!("seq_rep: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_rep: void".to_string()))?;
Ok(Some(result))
}
// Data.Map, Data.Set, etc. — delegate to existing impls
"Data.Map.empty" => self.lower_builtin_map_empty(),
"Data.Set.empty" => self.lower_builtin_set_empty(),
"Data.IntMap.empty" | "Data.IntSet.empty" => {
let rts_fn = self
.functions
.get(&VarId::new(if name == "Data.IntMap.empty" {
1000140
} else {
1000145
}))
.ok_or_else(|| CodegenError::Internal(format!("{} not declared", name)))?;
let result = self
.builder()
.build_call(*rts_fn, &[], &name.replace('.', "_"))
.map_err(|e| CodegenError::Internal(format!("{}: {:?}", name, e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal(format!("{}: void", name)))?;
Ok(Some(result))
}
_ => Err(CodegenError::Internal(format!(
"lower_builtin_direct: unhandled container builtin '{}'",
name
))),
}
}
/// Execute a primitive operation directly on LLVM values.
fn lower_primop_direct(
&mut self,
op: PrimOp,
args: &[BasicValueEnum<'ctx>],
) -> CodegenResult<BasicValueEnum<'ctx>> {
let tm = self.type_mapper();
match op {
// Binary arithmetic operations
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Mod
| PrimOp::Rem
| PrimOp::Quot => {
// Unbox arguments
let lhs = self.ptr_to_int(args[0].into_pointer_value())?;
let rhs = self.ptr_to_int(args[1].into_pointer_value())?;
let result = match op {
PrimOp::Add => self.builder().build_int_add(lhs, rhs, "add"),
PrimOp::Sub => self.builder().build_int_sub(lhs, rhs, "sub"),
PrimOp::Mul => self.builder().build_int_mul(lhs, rhs, "mul"),
PrimOp::Div | PrimOp::Quot => {
self.builder().build_int_signed_div(lhs, rhs, "div")
}
PrimOp::Mod | PrimOp::Rem => {
self.builder().build_int_signed_rem(lhs, rhs, "rem")
}
_ => unreachable!(),
}
.map_err(|e| {
CodegenError::Internal(format!("failed to build arithmetic: {:?}", e))
})?;
Ok(result.into())
}
// Comparison operations
PrimOp::Eq | PrimOp::Ne | PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge => {
let lhs = self.ptr_to_int(args[0].into_pointer_value())?;
let rhs = self.ptr_to_int(args[1].into_pointer_value())?;
let predicate = match op {
PrimOp::Eq => inkwell::IntPredicate::EQ,
PrimOp::Ne => inkwell::IntPredicate::NE,
PrimOp::Lt => inkwell::IntPredicate::SLT,
PrimOp::Le => inkwell::IntPredicate::SLE,
PrimOp::Gt => inkwell::IntPredicate::SGT,
PrimOp::Ge => inkwell::IntPredicate::SGE,
_ => unreachable!(),
};
let cmp = self
.builder()
.build_int_compare(predicate, lhs, rhs, "cmp")
.map_err(|e| {
CodegenError::Internal(format!("failed to build compare: {:?}", e))
})?;
let result = self
.builder()
.build_int_z_extend(cmp, tm.i64_type(), "cmp_ext")
.map_err(|e| CodegenError::Internal(format!("failed to extend: {:?}", e)))?;
Ok(result.into())
}
// Boolean operations - Bool is an ADT where False=tag 0, True=tag 1
PrimOp::And => {
// Extract tags from Bool ADT pointers
let lhs_tag = self.extract_adt_tag(args[0].into_pointer_value())?;
let rhs_tag = self.extract_adt_tag(args[1].into_pointer_value())?;
// AND the tags (0 & anything = 0, 1 & 1 = 1)
let result = self
.builder()
.build_and(lhs_tag, rhs_tag, "and")
.map_err(|e| CodegenError::Internal(format!("failed to build and: {:?}", e)))?;
Ok(result.into())
}
PrimOp::Or => {
// Extract tags from Bool ADT pointers
let lhs_tag = self.extract_adt_tag(args[0].into_pointer_value())?;
let rhs_tag = self.extract_adt_tag(args[1].into_pointer_value())?;
// OR the tags (0 | 0 = 0, anything | 1 = 1)
let result = self
.builder()
.build_or(lhs_tag, rhs_tag, "or")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
Ok(result.into())
}
// Unary operations
PrimOp::Negate => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let result = self
.builder()
.build_int_neg(val, "neg")
.map_err(|e| CodegenError::Internal(format!("failed to build neg: {:?}", e)))?;
Ok(result.into())
}
PrimOp::Abs => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let neg = self
.builder()
.build_int_neg(val, "neg")
.map_err(|e| CodegenError::Internal(format!("failed to build neg: {:?}", e)))?;
let is_neg = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
val,
tm.i64_type().const_zero(),
"is_neg",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to build compare: {:?}", e))
})?;
let result = self
.builder()
.build_select(is_neg, neg, val, "abs")
.map_err(|e| {
CodegenError::Internal(format!("failed to build select: {:?}", e))
})?;
Ok(result)
}
PrimOp::Signum => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let zero = tm.i64_type().const_zero();
let one = tm.i64_type().const_int(1, false);
let neg_one = tm.i64_type().const_int(-1i64 as u64, true);
let is_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, val, zero, "is_neg")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
let is_pos = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, val, zero, "is_pos")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
let tmp = self
.builder()
.build_select(is_neg, neg_one, zero, "tmp")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
let result = self
.builder()
.build_select(is_pos, one, tmp.into_int_value(), "signum")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result)
}
PrimOp::Not => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let is_zero = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
val,
tm.i64_type().const_zero(),
"is_zero",
)
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
let result = self
.builder()
.build_int_z_extend(is_zero, tm.i64_type(), "not")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
// Bitwise operations
PrimOp::BitAnd => {
let lhs = self.ptr_to_int(args[0].into_pointer_value())?;
let rhs = self.ptr_to_int(args[1].into_pointer_value())?;
let result = self
.builder()
.build_and(lhs, rhs, "bitand")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
PrimOp::BitOr => {
let lhs = self.ptr_to_int(args[0].into_pointer_value())?;
let rhs = self.ptr_to_int(args[1].into_pointer_value())?;
let result = self
.builder()
.build_or(lhs, rhs, "bitor")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
PrimOp::BitXor => {
let lhs = self.ptr_to_int(args[0].into_pointer_value())?;
let rhs = self.ptr_to_int(args[1].into_pointer_value())?;
let result = self
.builder()
.build_xor(lhs, rhs, "bitxor")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
PrimOp::ShiftL => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let amt = self.ptr_to_int(args[1].into_pointer_value())?;
let result = self
.builder()
.build_left_shift(val, amt, "shl")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
PrimOp::ShiftR => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let amt = self.ptr_to_int(args[1].into_pointer_value())?;
let result = self
.builder()
.build_right_shift(val, amt, true, "shr")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
PrimOp::Complement => {
let val = self.ptr_to_int(args[0].into_pointer_value())?;
let result = self
.builder()
.build_not(val, "complement")
.map_err(|e| CodegenError::Internal(format!("failed: {:?}", e)))?;
Ok(result.into())
}
}
}
/// Coerce a value to an integer, unboxing if needed.
fn coerce_to_int(&self, value: BasicValueEnum<'ctx>) -> CodegenResult<IntValue<'ctx>> {
match value {
BasicValueEnum::IntValue(i) => Ok(i),
BasicValueEnum::PointerValue(p) => self.ptr_to_int(p),
BasicValueEnum::FloatValue(f) => {
// Cast float bits to int
let bits = self
.builder()
.build_bit_cast(f, self.type_mapper().i64_type(), "float_to_int")
.map_err(|e| CodegenError::Internal(format!("float to int failed: {:?}", e)))?;
Ok(bits.into_int_value())
}
_ => Err(CodegenError::TypeError("cannot coerce to int".to_string())),
}
}
/// Coerce a value to f64, unboxing if needed.
fn coerce_to_f64(&self, value: BasicValueEnum<'ctx>) -> CodegenResult<FloatValue<'ctx>> {
match value {
BasicValueEnum::FloatValue(f) => Ok(f),
BasicValueEnum::IntValue(i) => {
// Cast int bits to float (for boxed floats)
let f = self
.builder()
.build_bit_cast(i, self.type_mapper().f64_type(), "int_to_float")
.map_err(|e| CodegenError::Internal(format!("int to float failed: {:?}", e)))?;
Ok(f.into_float_value())
}
BasicValueEnum::PointerValue(p) => {
// Unbox: ptr -> i64 -> f64 bits
let int_val = self.ptr_to_int(p)?;
let f = self
.builder()
.build_bit_cast(int_val, self.type_mapper().f64_type(), "ptr_to_float")
.map_err(|e| CodegenError::Internal(format!("ptr to float failed: {:?}", e)))?;
Ok(f.into_float_value())
}
_ => Err(CodegenError::TypeError("cannot coerce to f64".to_string())),
}
}
/// Convert an integer to a pointer (boxing).
fn int_to_ptr(&self, int_val: IntValue<'ctx>) -> CodegenResult<PointerValue<'ctx>> {
self.builder()
.build_int_to_ptr(int_val, self.type_mapper().ptr_type(), "box_int")
.map_err(|e| CodegenError::Internal(format!("failed to box int: {:?}", e)))
}
/// Convert a float to a pointer (boxing via bit cast).
fn f64_to_ptr(&self, float_val: FloatValue<'ctx>) -> CodegenResult<PointerValue<'ctx>> {
let bits = self
.builder()
.build_bit_cast(float_val, self.type_mapper().i64_type(), "float_bits")
.map_err(|e| CodegenError::Internal(format!("failed to cast float: {:?}", e)))?
.into_int_value();
self.builder()
.build_int_to_ptr(bits, self.type_mapper().ptr_type(), "box_float")
.map_err(|e| CodegenError::Internal(format!("failed to box float: {:?}", e)))
}
/// Convert a pointer to an integer (unbox).
fn ptr_to_int(&self, ptr: PointerValue<'ctx>) -> CodegenResult<IntValue<'ctx>> {
self.builder()
.build_ptr_to_int(ptr, self.type_mapper().i64_type(), "unbox")
.map_err(|e| CodegenError::Internal(format!("failed to unbox: {:?}", e)))
}
/// Check if an expression is a saturated primitive operation.
fn is_saturated_primop<'a>(&self, expr: &'a Expr) -> Option<(PrimOp, Vec<&'a Expr>)> {
// Collect arguments while unwrapping applications
let mut args = Vec::new();
let mut current = expr;
while let Expr::App(func, arg, _) = current {
args.push(arg.as_ref());
current = func.as_ref();
}
// Skip through type applications (erased at runtime)
while let Expr::TyApp(inner, _, _) = current {
current = inner.as_ref();
}
// Check if the head is a primitive operation
if let Expr::Var(var, _) = current {
if let Some((op, arity)) = self.primitive_op_info(var.name.as_str()) {
args.reverse();
if args.len() == arity as usize {
return Some((op, args));
}
}
}
None
}
/// Lower a primitive operation to LLVM instructions.
fn lower_primop(
&mut self,
op: PrimOp,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if operands are Rational — dispatch to RTS
if !args.is_empty() && self.is_rational_expr(args[0]) {
match op {
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Negate
| PrimOp::Abs
| PrimOp::Signum
| PrimOp::Eq
| PrimOp::Ne
| PrimOp::Lt
| PrimOp::Le
| PrimOp::Gt
| PrimOp::Ge => {
return self.lower_rational_primop(op, args);
}
_ => {}
}
}
if args.len() >= 2 && self.is_rational_expr(args[1]) {
match op {
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Eq
| PrimOp::Ne
| PrimOp::Lt
| PrimOp::Le
| PrimOp::Gt
| PrimOp::Ge => {
return self.lower_rational_primop(op, args);
}
_ => {}
}
}
// E.45: Check if operands are Integer (arbitrary precision) — dispatch to RTS
if !args.is_empty() && self.is_integer_expr(args[0]) {
match op {
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Mod
| PrimOp::Rem
| PrimOp::Quot
| PrimOp::Negate
| PrimOp::Eq
| PrimOp::Ne
| PrimOp::Lt
| PrimOp::Le
| PrimOp::Gt
| PrimOp::Ge => {
return self.lower_integer_primop(op, args);
}
_ => {}
}
}
// Also check second arg for Integer (e.g., `1 + x` where x is Integer)
if args.len() >= 2 && self.is_integer_expr(args[1]) {
match op {
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Mod
| PrimOp::Rem
| PrimOp::Quot
| PrimOp::Eq
| PrimOp::Ne
| PrimOp::Lt
| PrimOp::Le
| PrimOp::Gt
| PrimOp::Ge => {
return self.lower_integer_primop(op, args);
}
_ => {}
}
}
match op {
// Binary arithmetic operations
PrimOp::Add
| PrimOp::Sub
| PrimOp::Mul
| PrimOp::Div
| PrimOp::Mod
| PrimOp::Rem
| PrimOp::Quot => {
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
// Double/Float arithmetic must operate on f64. A boxed Double
// operand (e.g. a function parameter) is a PointerValue, which
// `lower_binary_arith` would int-add on its raw bits — so detect
// the floating type and route through the unboxing float path.
let t0 = args[0].ty();
let t1 = args[1].ty();
if self.is_double_type(&t0)
|| self.is_float_type(&t0)
|| self.is_double_type(&t1)
|| self.is_float_type(&t1)
{
return self.lower_float_arith(op, lhs, rhs);
}
self.lower_binary_arith(op, lhs, rhs)
}
// Binary comparison operations
PrimOp::Eq | PrimOp::Ne | PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge => {
// Check if we're comparing lists - need special handling
// Since type info may be Error, check structurally if expr is a list
let is_list = Self::is_list_expr(args[0]);
if is_list {
// List comparison - dispatch to builtin list comparison
let lhs = self.lower_expr(args[0])?.ok_or_else(|| {
CodegenError::Internal("primop arg has no value".to_string())
})?;
let rhs = self.lower_expr(args[1])?.ok_or_else(|| {
CodegenError::Internal("primop arg has no value".to_string())
})?;
return self.lower_list_comparison(op, lhs, rhs);
}
// Check if we're comparing user ADTs with derived Eq
if matches!(op, PrimOp::Eq | PrimOp::Ne) {
let adt_type = Self::is_user_adt_expr(args[0], &self.constructor_metadata)
.or_else(|| Self::is_user_adt_expr(args[1], &self.constructor_metadata));
if let Some(type_name) = adt_type {
if let Some(eq_var_id) = self.derived_eq_fns.get(&type_name).copied() {
if let Some(eq_fn) = self.functions.get(&eq_var_id).copied() {
let lhs = self.lower_expr(args[0])?.ok_or_else(|| {
CodegenError::Internal("eq: no lhs value".to_string())
})?;
let rhs = self.lower_expr(args[1])?.ok_or_else(|| {
CodegenError::Internal("eq: no rhs value".to_string())
})?;
// Call $derived_eq_TypeName(env, lhs, rhs) → Bool ADT pointer
// All BHC functions use (env, args...) calling convention
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = eq_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), lhs.into(), rhs.into()],
"derived_eq",
)
.map_err(|e| {
CodegenError::Internal(format!("derived eq call: {:?}", e))
})?;
let bool_ptr =
result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal(
"derived eq returned void".to_string(),
)
})?;
// Derived eq returns Bool ADT (tag 0=False, 1=True).
// Extract tag to get i64 result expected by comparison operators.
let tag = self.extract_adt_tag(bool_ptr.into_pointer_value())?;
if matches!(op, PrimOp::Ne) {
// Invert: 0→1, 1→0
let one = self.type_mapper().i64_type().const_int(1, false);
let inverted = self
.builder()
.build_int_sub(one, tag, "ne_invert")
.map_err(|e| {
CodegenError::Internal(format!("ne invert: {:?}", e))
})?;
return Ok(Some(inverted.into()));
}
return Ok(Some(tag.into()));
}
}
}
}
// Check if we're comparing user ADTs with derived Ord (for <, <=, >, >=)
if matches!(op, PrimOp::Lt | PrimOp::Le | PrimOp::Gt | PrimOp::Ge) {
let adt_type = Self::is_user_adt_expr(args[0], &self.constructor_metadata)
.or_else(|| Self::is_user_adt_expr(args[1], &self.constructor_metadata));
if let Some(type_name) = adt_type {
if let Some(cmp_var_id) = self.derived_compare_fns.get(&type_name).copied()
{
if let Some(cmp_fn) = self.functions.get(&cmp_var_id).copied() {
let lhs = self.lower_expr(args[0])?.ok_or_else(|| {
CodegenError::Internal("ord: no lhs value".to_string())
})?;
let rhs = self.lower_expr(args[1])?.ok_or_else(|| {
CodegenError::Internal("ord: no rhs value".to_string())
})?;
// Call $derived_compare_TypeName(env, lhs, rhs) → Ordering ADT pointer
let null_env = self.type_mapper().ptr_type().const_null();
let fn_ptr = cmp_fn.as_global_value().as_pointer_value();
let fn_type = self.type_mapper().ptr_type().fn_type(
&[
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
self.type_mapper().ptr_type().into(),
],
false,
);
let result = self
.builder()
.build_indirect_call(
fn_type,
fn_ptr,
&[null_env.into(), lhs.into(), rhs.into()],
"derived_compare",
)
.map_err(|e| {
CodegenError::Internal(format!(
"derived compare call: {:?}",
e
))
})?;
let ordering_ptr =
result.try_as_basic_value().basic().ok_or_else(|| {
CodegenError::Internal(
"derived compare returned void".to_string(),
)
})?;
// Ordering tag: 0=LT, 1=EQ, 2=GT
let tag =
self.extract_adt_tag(ordering_ptr.into_pointer_value())?;
let i64_ty = self.type_mapper().i64_type();
let bool_result = match op {
PrimOp::Lt => self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_ty.const_int(0, false),
"lt",
)
.map_err(|e| {
CodegenError::Internal(format!("lt cmp: {:?}", e))
})?,
PrimOp::Le => self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
tag,
i64_ty.const_int(2, false),
"le",
)
.map_err(|e| {
CodegenError::Internal(format!("le cmp: {:?}", e))
})?,
PrimOp::Gt => self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
i64_ty.const_int(2, false),
"gt",
)
.map_err(|e| {
CodegenError::Internal(format!("gt cmp: {:?}", e))
})?,
PrimOp::Ge => self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
tag,
i64_ty.const_int(0, false),
"ge",
)
.map_err(|e| {
CodegenError::Internal(format!("ge cmp: {:?}", e))
})?,
_ => unreachable!(),
};
// Convert i1 to i64 (0 or 1)
let result_i64 = self
.builder()
.build_int_z_extend(bool_result, i64_ty, "cmp_ext")
.map_err(|e| {
CodegenError::Internal(format!("cmp ext: {:?}", e))
})?;
return Ok(Some(result_i64.into()));
}
}
}
}
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
self.lower_comparison(op, lhs, rhs)
}
// Binary boolean operations
PrimOp::And | PrimOp::Or => {
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
self.lower_binary_bool(op, lhs, rhs)
}
// Binary bitwise operations
PrimOp::BitAnd | PrimOp::BitOr | PrimOp::BitXor | PrimOp::ShiftL | PrimOp::ShiftR => {
let lhs = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
let rhs = self
.lower_expr(args[1])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
self.lower_binary_bitwise(op, lhs, rhs)
}
// Unary operations
PrimOp::Negate | PrimOp::Abs | PrimOp::Signum | PrimOp::Not | PrimOp::Complement => {
let arg = self
.lower_expr(args[0])?
.ok_or_else(|| CodegenError::Internal("primop arg has no value".to_string()))?;
self.lower_unary(op, arg)
}
}
}
/// Unbox a value to an integer if it's a pointer (boxed int).
fn unbox_to_int(
&self,
val: BasicValueEnum<'ctx>,
) -> CodegenResult<inkwell::values::IntValue<'ctx>> {
match val {
BasicValueEnum::IntValue(i) => Ok(i),
BasicValueEnum::PointerValue(p) => {
// Unbox: pointer stores int value directly using ptr_to_int
self.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_int")
.map_err(|e| CodegenError::Internal(format!("failed to unbox int: {:?}", e)))
}
_ => Err(CodegenError::TypeError(
"expected int or boxed int".to_string(),
)),
}
}
/// Box an integer to a pointer.
fn box_int(
&self,
val: inkwell::values::IntValue<'ctx>,
) -> CodegenResult<inkwell::values::PointerValue<'ctx>> {
self.builder()
.build_int_to_ptr(val, self.type_mapper().ptr_type(), "box_int")
.map_err(|e| CodegenError::Internal(format!("failed to box int: {:?}", e)))
}
/// Lower a binary arithmetic operation on `Double`/`Float`. Both operands
/// are coerced to `f64` first — crucially unboxing a boxed `Double` (a
/// `PointerValue`, e.g. a function parameter), which `lower_binary_arith`
/// would otherwise int-add on its raw bits. Returns an unboxed `FloatValue`.
fn lower_float_arith(
&self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let l = self.coerce_to_f64(lhs)?;
let r = self.coerce_to_f64(rhs)?;
let result = match op {
PrimOp::Add => self.builder().build_float_add(l, r, "fadd"),
PrimOp::Sub => self.builder().build_float_sub(l, r, "fsub"),
PrimOp::Mul => self.builder().build_float_mul(l, r, "fmul"),
PrimOp::Div | PrimOp::Quot => self.builder().build_float_div(l, r, "fdiv"),
PrimOp::Mod | PrimOp::Rem => self.builder().build_float_rem(l, r, "frem"),
_ => return Err(CodegenError::Internal("invalid float arith op".to_string())),
};
result
.map(|v| Some(v.into()))
.map_err(|e| CodegenError::Internal(format!("failed to build float op: {:?}", e)))
}
/// Lower a binary arithmetic operation.
fn lower_binary_arith(
&self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Check if we're dealing with integers or floats
// Handle boxed values (pointers) by unboxing first
match (lhs, rhs) {
(BasicValueEnum::IntValue(l), BasicValueEnum::IntValue(r)) => {
let result = match op {
PrimOp::Add => self.builder().build_int_add(l, r, "add"),
PrimOp::Sub => self.builder().build_int_sub(l, r, "sub"),
PrimOp::Mul => self.builder().build_int_mul(l, r, "mul"),
PrimOp::Div | PrimOp::Quot => self.builder().build_int_signed_div(l, r, "div"),
PrimOp::Mod | PrimOp::Rem => self.builder().build_int_signed_rem(l, r, "rem"),
_ => return Err(CodegenError::Internal("invalid arith op".to_string())),
};
result
.map(|v| Some(v.into()))
.map_err(|e| CodegenError::Internal(format!("failed to build int op: {:?}", e)))
}
(BasicValueEnum::FloatValue(l), BasicValueEnum::FloatValue(r)) => {
let result = match op {
PrimOp::Add => self.builder().build_float_add(l, r, "fadd"),
PrimOp::Sub => self.builder().build_float_sub(l, r, "fsub"),
PrimOp::Mul => self.builder().build_float_mul(l, r, "fmul"),
PrimOp::Div | PrimOp::Quot => self.builder().build_float_div(l, r, "fdiv"),
PrimOp::Mod | PrimOp::Rem => self.builder().build_float_rem(l, r, "frem"),
_ => return Err(CodegenError::Internal("invalid arith op".to_string())),
};
result.map(|v| Some(v.into())).map_err(|e| {
CodegenError::Internal(format!("failed to build float op: {:?}", e))
})
}
// Handle boxed integers (pointers) - unbox, compute, rebox
(BasicValueEnum::PointerValue(_), _) | (_, BasicValueEnum::PointerValue(_)) => {
let l = self.unbox_to_int(lhs)?;
let r = self.unbox_to_int(rhs)?;
let result = match op {
PrimOp::Add => self.builder().build_int_add(l, r, "add"),
PrimOp::Sub => self.builder().build_int_sub(l, r, "sub"),
PrimOp::Mul => self.builder().build_int_mul(l, r, "mul"),
PrimOp::Div | PrimOp::Quot => self.builder().build_int_signed_div(l, r, "div"),
PrimOp::Mod | PrimOp::Rem => self.builder().build_int_signed_rem(l, r, "rem"),
_ => return Err(CodegenError::Internal("invalid arith op".to_string())),
};
let int_result = result.map_err(|e| {
CodegenError::Internal(format!("failed to build int op: {:?}", e))
})?;
// Box the result back to pointer for uniform calling convention
let boxed = self.box_int(int_result)?;
Ok(Some(boxed.into()))
}
_ => Err(CodegenError::TypeError(
"arithmetic operations require matching numeric types".to_string(),
)),
}
}
/// Lower a list comparison operation (== or /= for lists).
/// This generates a loop that compares elements one by one.
fn lower_list_comparison(
&mut self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("no current function".to_string()))?;
// Create basic blocks
let entry_block = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no current block".to_string()))?;
let loop_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_loop");
let both_nil_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_both_nil");
let one_nil_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_one_nil");
let compare_heads_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_heads");
let heads_equal_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_heads_eq");
let done_block = self
.llvm_context()
.append_basic_block(current_fn, "list_cmp_done");
let lhs_ptr = lhs.into_pointer_value();
let rhs_ptr = rhs.into_pointer_value();
// Jump to loop
self.builder()
.build_unconditional_branch(loop_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Loop block: check tags
self.builder().position_at_end(loop_block);
let list1_phi = self
.builder()
.build_phi(ptr_type, "list1")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
let list2_phi = self
.builder()
.build_phi(ptr_type, "list2")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
list1_phi.add_incoming(&[(&lhs_ptr, entry_block)]);
list2_phi.add_incoming(&[(&rhs_ptr, entry_block)]);
let list1_ptr = list1_phi.as_basic_value().into_pointer_value();
let list2_ptr = list2_phi.as_basic_value().into_pointer_value();
// Get tags
let tag1 = self
.builder()
.build_load(i64_type, list1_ptr, "tag1")
.map_err(|e| CodegenError::Internal(format!("failed to load tag1: {:?}", e)))?
.into_int_value();
let tag2 = self
.builder()
.build_load(i64_type, list2_ptr, "tag2")
.map_err(|e| CodegenError::Internal(format!("failed to load tag2: {:?}", e)))?
.into_int_value();
let zero = i64_type.const_int(0, false);
let is_nil1 = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag1, zero, "is_nil1")
.map_err(|e| CodegenError::Internal(format!("failed to build cmp: {:?}", e)))?;
let is_nil2 = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, tag2, zero, "is_nil2")
.map_err(|e| CodegenError::Internal(format!("failed to build cmp: {:?}", e)))?;
// Check if both are nil
let both_nil = self
.builder()
.build_and(is_nil1, is_nil2, "both_nil")
.map_err(|e| CodegenError::Internal(format!("failed to build and: {:?}", e)))?;
self.builder()
.build_conditional_branch(both_nil, both_nil_block, one_nil_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Both nil: lists are equal
self.builder().position_at_end(both_nil_block);
let true_val = i64_type.const_int(1, false);
self.builder()
.build_unconditional_branch(done_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// One nil (but not both): check if either is nil
self.builder().position_at_end(one_nil_block);
let either_nil = self
.builder()
.build_or(is_nil1, is_nil2, "either_nil")
.map_err(|e| CodegenError::Internal(format!("failed to build or: {:?}", e)))?;
let false_val = i64_type.const_int(0, false);
// If either is nil (but not both), lists are not equal
self.builder()
.build_conditional_branch(either_nil, done_block, compare_heads_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Compare heads - use extract_adt_field which handles the ADT layout
self.builder().position_at_end(compare_heads_block);
let head1 = self.extract_adt_field(list1_ptr, 2, 0)?; // arity=2 (Cons), field=0 (head)
let head2 = self.extract_adt_field(list2_ptr, 2, 0)?;
// Compare heads as integers (works for primitive types like Int)
let head1_int = self
.builder()
.build_ptr_to_int(head1, i64_type, "head1_int")
.map_err(|e| CodegenError::Internal(format!("failed to ptr_to_int: {:?}", e)))?;
let head2_int = self
.builder()
.build_ptr_to_int(head2, i64_type, "head2_int")
.map_err(|e| CodegenError::Internal(format!("failed to ptr_to_int: {:?}", e)))?;
let heads_equal = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, head1_int, head2_int, "heads_eq")
.map_err(|e| CodegenError::Internal(format!("failed to cmp: {:?}", e)))?;
// If heads not equal, lists not equal
self.builder()
.build_conditional_branch(heads_equal, heads_equal_block, done_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Heads equal - continue with tails
self.builder().position_at_end(heads_equal_block);
let tail1 = self.extract_adt_field(list1_ptr, 2, 1)?; // arity=2 (Cons), field=1 (tail)
let tail2 = self.extract_adt_field(list2_ptr, 2, 1)?;
// Update phis and loop back
list1_phi.add_incoming(&[(&tail1, heads_equal_block)]);
list2_phi.add_incoming(&[(&tail2, heads_equal_block)]);
self.builder()
.build_unconditional_branch(loop_block)
.map_err(|e| CodegenError::Internal(format!("failed to branch: {:?}", e)))?;
// Done block: collect result
self.builder().position_at_end(done_block);
let result_phi = self
.builder()
.build_phi(i64_type, "list_cmp_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
result_phi.add_incoming(&[(&true_val, both_nil_block)]); // Both nil = equal
result_phi.add_incoming(&[(&false_val, one_nil_block)]); // One nil = not equal
result_phi.add_incoming(&[(&false_val, compare_heads_block)]); // Heads not equal
let result = result_phi.as_basic_value().into_int_value();
// For /= we need to invert the result
let final_result = if matches!(op, PrimOp::Ne) {
let one = i64_type.const_int(1, false);
self.builder()
.build_xor(result, one, "invert")
.map_err(|e| CodegenError::Internal(format!("failed to xor: {:?}", e)))?
} else {
result
};
Ok(Some(final_result.into()))
}
/// Lower a comparison operation.
fn lower_comparison(
&self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Helper to do int comparison and return boxed result
let do_int_cmp = |this: &Self,
l: inkwell::values::IntValue<'ctx>,
r: inkwell::values::IntValue<'ctx>|
-> CodegenResult<Option<BasicValueEnum<'ctx>>> {
use inkwell::IntPredicate;
let pred = match op {
PrimOp::Eq => IntPredicate::EQ,
PrimOp::Ne => IntPredicate::NE,
PrimOp::Lt => IntPredicate::SLT,
PrimOp::Le => IntPredicate::SLE,
PrimOp::Gt => IntPredicate::SGT,
PrimOp::Ge => IntPredicate::SGE,
_ => return Err(CodegenError::Internal("invalid comparison op".to_string())),
};
let cmp = this
.builder()
.build_int_compare(pred, l, r, "cmp")
.map_err(|e| CodegenError::Internal(format!("failed to build int cmp: {:?}", e)))?;
// Convert i1 to i64 (0 or 1) for consistency with our Bool representation
let result = this
.builder()
.build_int_z_extend(cmp, this.type_mapper().i64_type(), "cmp_ext")
.map_err(|e| CodegenError::Internal(format!("failed to extend cmp: {:?}", e)))?;
Ok(Some(result.into()))
};
match (lhs, rhs) {
(BasicValueEnum::IntValue(l), BasicValueEnum::IntValue(r)) => do_int_cmp(self, l, r),
(BasicValueEnum::FloatValue(l), BasicValueEnum::FloatValue(r)) => {
use inkwell::FloatPredicate;
let pred = match op {
PrimOp::Eq => FloatPredicate::OEQ,
PrimOp::Ne => FloatPredicate::ONE,
PrimOp::Lt => FloatPredicate::OLT,
PrimOp::Le => FloatPredicate::OLE,
PrimOp::Gt => FloatPredicate::OGT,
PrimOp::Ge => FloatPredicate::OGE,
_ => return Err(CodegenError::Internal("invalid comparison op".to_string())),
};
let cmp = self
.builder()
.build_float_compare(pred, l, r, "fcmp")
.map_err(|e| {
CodegenError::Internal(format!("failed to build float cmp: {:?}", e))
})?;
// Convert i1 to i64
let result = self
.builder()
.build_int_z_extend(cmp, self.type_mapper().i64_type(), "fcmp_ext")
.map_err(|e| {
CodegenError::Internal(format!("failed to extend fcmp: {:?}", e))
})?;
Ok(Some(result.into()))
}
// Handle boxed integers (pointers)
(BasicValueEnum::PointerValue(_), _) | (_, BasicValueEnum::PointerValue(_)) => {
let l = self.unbox_to_int(lhs)?;
let r = self.unbox_to_int(rhs)?;
do_int_cmp(self, l, r)
}
_ => Err(CodegenError::TypeError(
"comparison operations require matching types".to_string(),
)),
}
}
/// Lower a binary boolean operation.
/// Returns a proper Bool ADT pointer (not just an integer tag).
fn lower_binary_bool(
&self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Extract the boolean value (tag) from each operand
// Bool is an ADT with False=tag 0, True=tag 1
let l = self.extract_bool_value(lhs)?;
let r = self.extract_bool_value(rhs)?;
// Convert to i1 for boolean operations (compare to zero)
let zero = self.type_mapper().i64_type().const_zero();
let l_bool = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, l, zero, "l_bool")
.map_err(|e| CodegenError::Internal(format!("failed to convert to bool: {:?}", e)))?;
let r_bool = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, r, zero, "r_bool")
.map_err(|e| CodegenError::Internal(format!("failed to convert to bool: {:?}", e)))?;
let result = match op {
PrimOp::And => self.builder().build_and(l_bool, r_bool, "and"),
PrimOp::Or => self.builder().build_or(l_bool, r_bool, "or"),
_ => return Err(CodegenError::Internal("invalid bool op".to_string())),
};
let bool_result = result
.map_err(|e| CodegenError::Internal(format!("failed to build bool op: {:?}", e)))?;
// Extend to i64 (0 or 1)
let _tag = self
.builder()
.build_int_z_extend(bool_result, self.type_mapper().i64_type(), "bool_ext")
.map_err(|e| CodegenError::Internal(format!("failed to extend bool: {:?}", e)))?;
// Allocate and return a proper Bool ADT
// We need to select between True (tag 1) and False (tag 0) ADTs
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let true_block = self
.llvm_ctx
.append_basic_block(current_fn, "bool_true_alloc");
let false_block = self
.llvm_ctx
.append_basic_block(current_fn, "bool_false_alloc");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "bool_alloc_merge");
self.builder()
.build_conditional_branch(bool_result, true_block, false_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
// True case: allocate True ADT (tag 1, arity 0)
self.builder().position_at_end(true_block);
let true_adt = self.alloc_adt(1, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let true_end = self.builder().get_insert_block().unwrap();
// False case: allocate False ADT (tag 0, arity 0)
self.builder().position_at_end(false_block);
let false_adt = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
let false_end = self.builder().get_insert_block().unwrap();
// Merge and return the ADT pointer
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(self.type_mapper().ptr_type(), "bool_adt")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&true_adt, true_end), (&false_adt, false_end)]);
Ok(Some(phi.as_basic_value()))
}
/// Extract the boolean value (as i64 tag: 0 for False, 1 for True) from a value.
/// Handles both raw integer booleans and Bool ADT pointers.
fn extract_bool_value(&self, val: BasicValueEnum<'ctx>) -> CodegenResult<IntValue<'ctx>> {
match val {
BasicValueEnum::IntValue(i) => Ok(i),
BasicValueEnum::PointerValue(p) => {
// The pointer might be:
// 1. A valid Bool ADT pointer (from constructor allocation)
// 2. A small integer (0 or 1) cast to pointer (from primop result)
let ptr_as_int = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "ptr_as_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to convert ptr to int: {:?}", e))
})?;
// Check if it's a small integer (0 or 1)
let is_raw_bool = self
.builder()
.build_int_compare(
inkwell::IntPredicate::ULE,
ptr_as_int,
self.type_mapper().i64_type().const_int(1, false),
"is_raw_bool",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to compare ptr: {:?}", e))
})?;
// Create blocks for the two cases
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let raw_bool_block = self.llvm_ctx.append_basic_block(current_fn, "raw_bool_ext");
let adt_bool_block = self.llvm_ctx.append_basic_block(current_fn, "adt_bool_ext");
let merge_block = self
.llvm_ctx
.append_basic_block(current_fn, "bool_ext_merge");
self.builder()
.build_conditional_branch(is_raw_bool, raw_bool_block, adt_bool_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build cond branch: {:?}", e))
})?;
// Raw bool case: pointer value IS the boolean (0 or 1)
self.builder().position_at_end(raw_bool_block);
let raw_val = ptr_as_int;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
let raw_bool_end = self.builder().get_insert_block().unwrap();
// ADT bool case: load the tag from the ADT structure
self.builder().position_at_end(adt_bool_block);
let adt_val = self.extract_adt_tag(p)?;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
let adt_bool_end = self.builder().get_insert_block().unwrap();
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(self.type_mapper().i64_type(), "bool_val_ext")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
phi.add_incoming(&[(&raw_val, raw_bool_end), (&adt_val, adt_bool_end)]);
Ok(phi.as_basic_value().into_int_value())
}
_ => Err(CodegenError::TypeError(
"expected Bool value in boolean operation".to_string(),
)),
}
}
/// Lower a binary bitwise operation.
fn lower_binary_bitwise(
&self,
op: PrimOp,
lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let l = self.to_int_value(lhs)?;
let r = self.to_int_value(rhs)?;
let result = match op {
PrimOp::BitAnd => self.builder().build_and(l, r, "band"),
PrimOp::BitOr => self.builder().build_or(l, r, "bor"),
PrimOp::BitXor => self.builder().build_xor(l, r, "bxor"),
PrimOp::ShiftL => self.builder().build_left_shift(l, r, "shl"),
PrimOp::ShiftR => self.builder().build_right_shift(l, r, true, "shr"),
_ => return Err(CodegenError::Internal("invalid bitwise op".to_string())),
};
result
.map(|v| Some(v.into()))
.map_err(|e| CodegenError::Internal(format!("failed to build bitwise op: {:?}", e)))
}
/// Lower a unary operation.
fn lower_unary(
&self,
op: PrimOp,
arg: BasicValueEnum<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
match op {
PrimOp::Negate => match arg {
BasicValueEnum::IntValue(i) => {
let result = self.builder().build_int_neg(i, "neg").map_err(|e| {
CodegenError::Internal(format!("failed to negate: {:?}", e))
})?;
Ok(Some(result.into()))
}
BasicValueEnum::FloatValue(f) => {
let result = self.builder().build_float_neg(f, "fneg").map_err(|e| {
CodegenError::Internal(format!("failed to fnegate: {:?}", e))
})?;
Ok(Some(result.into()))
}
_ => Err(CodegenError::TypeError(
"negate requires numeric type".to_string(),
)),
},
PrimOp::Abs => {
match arg {
BasicValueEnum::IntValue(i) => {
// abs(x) = x < 0 ? -x : x
let zero = self.type_mapper().i64_type().const_zero();
let is_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, i, zero, "is_neg")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let neg = self.builder().build_int_neg(i, "neg").map_err(|e| {
CodegenError::Internal(format!("failed to negate: {:?}", e))
})?;
let result =
self.builder()
.build_select(is_neg, neg, i, "abs")
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
Ok(Some(result))
}
BasicValueEnum::FloatValue(f) => {
// For floats, use llvm.fabs intrinsic or manual comparison
let zero = self.type_mapper().f64_type().const_zero();
let is_neg = self
.builder()
.build_float_compare(inkwell::FloatPredicate::OLT, f, zero, "is_neg")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let neg = self.builder().build_float_neg(f, "fneg").map_err(|e| {
CodegenError::Internal(format!("failed to fnegate: {:?}", e))
})?;
let result = self
.builder()
.build_select(is_neg, neg, f, "fabs")
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
Ok(Some(result))
}
_ => Err(CodegenError::TypeError(
"abs requires numeric type".to_string(),
)),
}
}
PrimOp::Signum => {
match arg {
BasicValueEnum::IntValue(i) => {
// signum(x) = x < 0 ? -1 : (x > 0 ? 1 : 0)
let zero = self.type_mapper().i64_type().const_zero();
let one = self.type_mapper().i64_type().const_int(1, false);
let neg_one = self.type_mapper().i64_type().const_int(-1i64 as u64, true);
let is_neg = self
.builder()
.build_int_compare(inkwell::IntPredicate::SLT, i, zero, "is_neg")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let is_pos = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGT, i, zero, "is_pos")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let pos_or_zero = self
.builder()
.build_select(is_pos, one, zero, "pos_or_zero")
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
let result = self
.builder()
.build_select(is_neg, neg_one, pos_or_zero.into_int_value(), "signum")
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
Ok(Some(result))
}
BasicValueEnum::FloatValue(f) => {
let zero = f.get_type().const_zero();
let one = f.get_type().const_float(1.0);
let neg_one = f.get_type().const_float(-1.0);
let is_neg = self
.builder()
.build_float_compare(inkwell::FloatPredicate::OLT, f, zero, "is_neg")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let is_pos = self
.builder()
.build_float_compare(inkwell::FloatPredicate::OGT, f, zero, "is_pos")
.map_err(|e| {
CodegenError::Internal(format!("failed to compare: {:?}", e))
})?;
let pos_or_zero = self
.builder()
.build_select(is_pos, one, zero, "pos_or_zero")
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
let result = self
.builder()
.build_select(
is_neg,
neg_one,
pos_or_zero.into_float_value(),
"fsignum",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to select: {:?}", e))
})?;
Ok(Some(result))
}
_ => Err(CodegenError::TypeError(
"signum requires numeric type".to_string(),
)),
}
}
PrimOp::Not => {
let i = self.to_int_value(arg)?;
let zero = self.type_mapper().i64_type().const_zero();
let one = self.type_mapper().i64_type().const_int(1, false);
// not x = x == 0 ? 1 : 0
let is_zero = self
.builder()
.build_int_compare(inkwell::IntPredicate::EQ, i, zero, "is_zero")
.map_err(|e| CodegenError::Internal(format!("failed to compare: {:?}", e)))?;
let result = self
.builder()
.build_select(is_zero, one, zero, "not")
.map_err(|e| CodegenError::Internal(format!("failed to select: {:?}", e)))?;
Ok(Some(result))
}
PrimOp::Complement => {
let i = self.to_int_value(arg)?;
let result = self.builder().build_not(i, "complement").map_err(|e| {
CodegenError::Internal(format!("failed to complement: {:?}", e))
})?;
Ok(Some(result.into()))
}
_ => Err(CodegenError::Internal("invalid unary op".to_string())),
}
}
/// Convert a basic value to an int value.
fn to_int_value(&self, val: BasicValueEnum<'ctx>) -> CodegenResult<IntValue<'ctx>> {
match val {
BasicValueEnum::IntValue(i) => Ok(i),
BasicValueEnum::PointerValue(p) => {
// Pointer might be a boxed int - convert
self.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "ptr_to_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to convert ptr to int: {:?}", e))
})
}
_ => Err(CodegenError::TypeError(
"expected integer value".to_string(),
)),
}
}
/// Lower a function application.
///
/// Handles four cases:
/// 1. Builtin function (e.g., `head xs`) - generate specialized code
/// 2. Primitive operation (e.g., `1 + 2`) - generate LLVM instruction
/// 3. Constructor application (e.g., `Just 42`) - allocate ADT value
/// 4. Function call - generate call instruction
fn lower_application(
&mut self,
func: &Expr,
arg: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// First, reconstruct the full application to check for special cases
let full_app = Expr::App(Box::new(func.clone()), Box::new(arg.clone()), func.span());
// Check if this is a saturated builtin function
if let Some((name, builtin_args)) = self.is_saturated_builtin(&full_app) {
return self.lower_builtin(name, &builtin_args);
}
// Check if this is a saturated primitive operation
if let Some((op, prim_args)) = self.is_saturated_primop(&full_app) {
return self.lower_primop(op, &prim_args);
}
// Check if this is a saturated constructor application
if let Some((tag, arity, con_args, con_name)) = self.is_saturated_constructor(&full_app) {
// Newtype erasure: newtype constructor is identity at runtime
if self.is_newtype_constructor(&con_name) && arity == 1 {
return self.lower_expr(con_args[0]);
}
return self.lower_constructor_application(tag, arity, &con_args);
}
// Not a primop or constructor - proceed with function call
// Collect all arguments (for curried applications)
let mut args = vec![arg];
let mut current = func;
loop {
match current {
Expr::App(inner_func, inner_arg, _) => {
args.push(inner_arg);
current = inner_func;
}
// Skip through type applications (erased at runtime)
Expr::TyApp(inner, _, _) => {
current = inner;
}
_ => break,
}
}
args.reverse();
// Get the function being called
match current {
Expr::Var(var, _) => {
let name = var.name.as_str();
// Check if this is a nullary constructor (no args, just a value)
if let Some((tag, arity)) = self.constructor_info(name) {
if arity == 0 {
// Nullary constructor like True, False, Nothing, ()
return self.lower_constructor_application(tag, 0, &[]);
}
}
// Check if this is an RTS builtin
if let Some(rts_id) = self.rts_function_id(name) {
let fn_val = self.functions.get(&rts_id).copied().ok_or_else(|| {
CodegenError::Internal(format!("RTS function not declared: {}", name))
})?;
return self.lower_direct_call(fn_val, &args, None);
}
// Check if this is a known top-level function
if let Some(fn_val) = self.functions.get(&var.id).copied() {
// E.45: Pass callee type for Integer argument promotion
return self.lower_direct_call(fn_val, &args, Some(&var.ty));
}
// Check if this is a closure in the environment
if let Some(closure_val) = self.env.get(&var.id).copied() {
return self.lower_closure_call(closure_val, &args);
}
// Check if this is an imported (external) function from another module
if let Some(&fn_val) = self.external_functions.get(&var.name) {
return self.lower_direct_call(fn_val, &args, Some(&var.ty));
}
// Check if this is an unsaturated builtin (partial application)
// e.g. (min 5) applies 1 arg to a 2-arg builtin
if let Some(arity) = self.builtin_info(name) {
let n_provided = args.len() as u32;
if n_provided < arity {
// Partial application: create a closure that captures the provided args
// and accepts the remaining args
return self.create_partial_builtin_closure(name, arity, &args);
} else if n_provided == arity {
// Exact application through builtin dispatch
return self.lower_builtin(name, &args);
} else {
// Over-application: call the builtin with its expected args,
// then call the result (a closure/function) with remaining args.
// This happens for e.g. App(App($sel_0, dict), x) where
// $sel_0 has arity 1 but we have 2 args.
let (builtin_args, remaining_args) = args.split_at(arity as usize);
let result = self.lower_builtin(name, builtin_args)?;
if let Some(result_val) = result {
return self.lower_closure_call(result_val, remaining_args);
} else {
return Err(CodegenError::Internal(format!(
"builtin {} returned no value for over-application",
name
)));
}
}
}
{
// Unknown function in application — generate a runtime stub.
let stub_fn = self.get_or_create_stub_function(name)?;
let null_env = self.type_mapper().ptr_type().const_null();
let call_result = self
.builder()
.build_call(stub_fn, &[null_env.into()], "stub_call")
.map_err(|e| CodegenError::Internal(format!("stub call: {:?}", e)))?;
if let Some(ret_val) = call_result.try_as_basic_value().basic() {
Ok(Some(ret_val))
} else {
Ok(Some(self.type_mapper().ptr_type().const_null().into()))
}
}
}
_ => {
// Indirect call - evaluate the function expression
let func_val = self.lower_expr(current)?.ok_or_else(|| {
CodegenError::Internal("function expression has no value".to_string())
})?;
// Treat as closure call
self.lower_closure_call(func_val, &args)
}
}
}
/// Lower a direct function call (to a known function).
/// `callee_ty` is the Haskell-level type of the callee, used for E.45 Integer promotion.
fn lower_direct_call(
&mut self,
fn_val: FunctionValue<'ctx>,
args: &[&Expr],
callee_ty: Option<&Ty>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Get the function's expected parameter count (excluding env pointer)
let fn_type = fn_val.get_type();
let expected_params = fn_type.count_param_types() as usize;
let expected_args = if expected_params > 0 {
expected_params - 1
} else {
0
}; // Subtract env pointer
// Check for over-application: more args than the function expects
if args.len() > expected_args {
// Split args: first part goes to this function, rest goes to the returned closure
let (fn_args, remaining_args) = args.split_at(expected_args);
// Call the function with its expected args
let closure_result = self.lower_direct_call_inner(fn_val, fn_args, callee_ty)?;
// The result should be a closure - call it with remaining args
if let Some(closure_val) = closure_result {
return self.lower_closure_call(closure_val, remaining_args);
} else {
return Err(CodegenError::Internal(
"function returned no value for over-application".to_string(),
));
}
}
// Check for under-application: fewer args than the function expects
if args.len() < expected_args {
return self.lower_partial_application(fn_val, args, expected_args);
}
// Exact application
self.lower_direct_call_inner(fn_val, args, callee_ty)
}
/// Lower a partial application (under-application) to a PAP closure.
///
/// Creates a PAP wrapper function that takes the remaining args and calls
/// the original function with all args combined.
fn lower_partial_application(
&mut self,
fn_val: FunctionValue<'ctx>,
applied_args: &[&Expr],
expected_args: usize,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let i64_type = tm.i64_type();
let num_applied = applied_args.len();
let num_remaining = expected_args - num_applied;
// Lower the applied arguments
let was_tail = self.in_tail_position;
self.in_tail_position = false;
let mut applied_vals: Vec<(VarId, BasicValueEnum<'ctx>)> = Vec::new();
for (i, arg_expr) in applied_args.iter().enumerate() {
if let Some(val) = self.lower_expr(arg_expr)? {
// Use a dummy VarId for PAP captured args
applied_vals.push((VarId::new(10000 + i), val));
}
}
self.in_tail_position = was_tail;
// Create the PAP wrapper function
// Signature: (ptr env, ptr arg1, ptr arg2, ...) -> ptr
let fn_name = fn_val.get_name().to_str().unwrap_or("fn");
let wrapper_name = format!("pap_{}_{}", fn_name, num_applied);
// Check if wrapper already exists
let wrapper_fn = if let Some(existing) =
self.module.llvm_module().get_function(&wrapper_name)
{
existing
} else {
// Create param types: env + remaining args
let mut wrapper_param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> =
Vec::new();
wrapper_param_types.push(ptr_type.into()); // env/closure pointer
for _ in 0..num_remaining {
wrapper_param_types.push(ptr_type.into());
}
let wrapper_fn_type = ptr_type.fn_type(&wrapper_param_types, false);
let wrapper_fn =
self.module
.llvm_module()
.add_function(&wrapper_name, wrapper_fn_type, None);
// Build the wrapper function body
let entry_bb = self.llvm_ctx.append_basic_block(wrapper_fn, "entry");
let current_bb = self.builder().get_insert_block();
self.builder().position_at_end(entry_bb);
// Extract applied args from closure environment
let closure_ptr = wrapper_fn.get_first_param().unwrap().into_pointer_value();
let closure_ty = self.closure_type(num_applied as u32);
let mut call_args: Vec<inkwell::values::BasicMetadataValueEnum<'ctx>> = Vec::new();
// Original function expects null env for direct calls
call_args.push(ptr_type.const_null().into());
// Load applied args from env
for i in 0..num_applied {
let env_slot = self
.builder()
.build_struct_gep(closure_ty, closure_ptr, 2, "env_slot")
.map_err(|e| CodegenError::Internal(format!("PAP gep failed: {:?}", e)))?;
let elem_ptr = unsafe {
self.builder()
.build_in_bounds_gep(
ptr_type.array_type(num_applied as u32),
env_slot,
&[i64_type.const_zero(), i64_type.const_int(i as u64, false)],
&format!("pap_arg_{}", i),
)
.map_err(|e| {
CodegenError::Internal(format!("PAP elem gep failed: {:?}", e))
})?
};
let arg_val = self
.builder()
.build_load(ptr_type, elem_ptr, &format!("pap_load_{}", i))
.map_err(|e| CodegenError::Internal(format!("PAP load failed: {:?}", e)))?;
call_args.push(arg_val.into());
}
// Add remaining args from wrapper params
for i in 0..num_remaining {
let param = wrapper_fn.get_nth_param((i + 1) as u32).unwrap(); // +1 to skip env
call_args.push(param.into());
}
// Call the original function
let result = self
.builder()
.build_call(fn_val, &call_args, "pap_call")
.map_err(|e| CodegenError::Internal(format!("PAP call failed: {:?}", e)))?
.try_as_basic_value()
.basic();
// Return the result
if let Some(ret_val) = result {
self.builder()
.build_return(Some(&ret_val))
.map_err(|e| CodegenError::Internal(format!("PAP return failed: {:?}", e)))?;
} else {
self.builder()
.build_return(Some(&ptr_type.const_null()))
.map_err(|e| CodegenError::Internal(format!("PAP return failed: {:?}", e)))?;
}
// Restore insertion point
if let Some(bb) = current_bb {
self.builder().position_at_end(bb);
}
wrapper_fn
};
// Create closure pointing to the wrapper with applied args as env
let wrapper_ptr = wrapper_fn.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(wrapper_ptr, &applied_vals)?;
Ok(Some(closure_ptr.into()))
}
/// Inner implementation of direct call (doesn't handle over-application).
/// `callee_ty` is the Haskell-level type of the callee, used for E.45 Integer promotion.
fn lower_direct_call_inner(
&mut self,
fn_val: FunctionValue<'ctx>,
args: &[&Expr],
callee_ty: Option<&Ty>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Arguments are not in tail position
let was_tail = self.in_tail_position;
self.in_tail_position = false;
// All functions take (env_ptr, args...) for uniform calling convention
let mut llvm_args = Vec::new();
// First arg is env/closure pointer (null for direct calls)
let null_env = self.type_mapper().ptr_type().const_null();
llvm_args.push(null_env.into());
// Lower remaining arguments and convert to pointers
for (i, arg_expr) in args.iter().enumerate() {
if let Some(val) = self.lower_expr(arg_expr)? {
// E.45: If callee expects Integer at this position and we have an Int value,
// promote to Integer via bhc_integer_from_i64 instead of int_to_ptr
let ptr_val = if val.is_int_value()
&& callee_ty.is_some_and(|ty| self.is_integer_param_at(ty, i))
{
self.promote_to_integer(val)?.into_pointer_value()
} else {
self.value_to_ptr(val)?
};
llvm_args.push(ptr_val.into());
}
}
// Restore tail position flag
self.in_tail_position = was_tail;
// Build the call
let call = self
.builder()
.build_call(fn_val, &llvm_args, "call")
.map_err(|e| CodegenError::Internal(format!("failed to build call: {:?}", e)))?;
// Mark as tail call if in tail position
if self.in_tail_position {
call.set_tail_call(true);
}
Ok(call.try_as_basic_value().basic())
}
/// Lower a closure call (indirect call through closure struct).
fn lower_closure_call(
&mut self,
closure_val: BasicValueEnum<'ctx>,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Get closure pointer
let closure_ptr = match closure_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::Internal(
"closure value is not a pointer".to_string(),
))
}
};
// Extract function pointer from closure
let fn_ptr = self.extract_closure_fn_ptr(closure_ptr)?;
// Build function type for the call:
// - First param is the closure pointer (environment)
// - Remaining params are the arguments
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // closure/env pointer
for _ in args {
param_types.push(ptr_type.into());
}
let fn_type = ptr_type.fn_type(¶m_types, false);
// Arguments are not in tail position
let was_tail = self.in_tail_position;
self.in_tail_position = false;
// Lower arguments
let mut llvm_args: Vec<inkwell::values::BasicMetadataValueEnum<'ctx>> = Vec::new();
llvm_args.push(closure_ptr.into()); // Pass closure as first argument (environment)
for arg_expr in args {
if let Some(val) = self.lower_expr(arg_expr)? {
// Convert to pointer for uniform calling convention
let ptr_val = self.value_to_ptr(val)?;
llvm_args.push(ptr_val.into());
}
}
// Restore tail position flag
self.in_tail_position = was_tail;
// Build indirect call through function pointer
let call = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &llvm_args, "closure_call")
.map_err(|e| {
CodegenError::Internal(format!("failed to build closure call: {:?}", e))
})?;
// Mark as tail call if in tail position
if self.in_tail_position {
call.set_tail_call(true);
}
Ok(call.try_as_basic_value().basic())
}
/// Lower a constructor application to an ADT value.
///
/// Allocates an ADT value with the given tag and stores the arguments as fields.
fn lower_constructor_application(
&mut self,
tag: u32,
arity: u32,
args: &[&Expr],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Allocate the ADT value
let adt_ptr = self.alloc_adt(tag, arity)?;
// Store each argument as a field
for (i, arg_expr) in args.iter().enumerate() {
if let Some(arg_val) = self.lower_expr(arg_expr)? {
self.store_adt_field(adt_ptr, arity, i as u32, arg_val)?;
}
}
// Return the pointer to the ADT value
Ok(Some(adt_ptr.into()))
}
/// Lower a let binding.
fn lower_let(
&mut self,
bind: &Bind,
body: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
match bind {
Bind::NonRec(var, rhs) => {
// E.58: Lazy let-bindings.
// Thunk non-trivial expressions for lazy evaluation.
// Trivial expressions (literals, vars, lambdas) are evaluated eagerly.
let should_thunk = !Self::is_trivial_expr(rhs.as_ref());
if should_thunk {
// E.45: Track Integer variables BEFORE thunking
// (is_integer_expr inspects expression structure, not thunk wrapper)
if self.is_integer_expr(rhs.as_ref()) {
self.integer_vars.insert(var.id);
}
// Create a thunk for lazy evaluation
let thunk_result = self.lower_lazy(rhs.as_ref())?;
if let Some(val) = thunk_result {
self.env.insert(var.id, val);
self.thunked_vars.insert(var.id);
}
} else {
// Eager evaluation for trivial expressions
let was_tail = self.in_tail_position;
self.in_tail_position = false;
let rhs_result = self.lower_expr(rhs.as_ref())?;
self.in_tail_position = was_tail;
if let Some(val) = rhs_result {
self.env.insert(var.id, val);
}
// E.45: Track Integer variables for show dispatch
if self.is_integer_expr(rhs.as_ref()) {
self.integer_vars.insert(var.id);
}
}
// Lower the body (preserves tail position from parent)
let result = self.lower_expr(body)?;
// Remove bindings for proper scoping
self.env.remove(&var.id);
self.thunked_vars.remove(&var.id);
Ok(result)
}
Bind::Rec(bindings) => {
// For recursive let bindings, we lift them to top-level functions.
// Free variables from the enclosing scope are captured via the
// closure environment (param 0).
// Save the current insertion point
let current_block = self.builder().get_insert_block();
// Compute free variables for the entire rec group
let rec_var_ids: FxHashSet<VarId> = bindings.iter().map(|(v, _)| v.id).collect();
let mut all_free = FxHashSet::default();
for (_var, expr) in bindings {
let fv = self.free_vars(expr);
for v in fv {
if !rec_var_ids.contains(&v) {
all_free.insert(v);
}
}
}
// Collect captured values (free vars that exist in current env)
let captured: Vec<(VarId, BasicValueEnum<'ctx>)> = all_free
.iter()
.filter_map(|vid| self.env.get(vid).map(|val| (*vid, *val)))
.collect();
// First pass: declare all recursive functions
for (var, _expr) in bindings {
let lifted_name = format!("{}${}", var.name.as_str(), var.id.index());
let fn_type = self.lower_function_type(&var.ty)?;
let fn_val = self.module.add_function(&lifted_name, fn_type);
self.functions.insert(var.id, fn_val);
}
// Second pass: define all recursive functions, injecting captured
// free variables from the closure env into self.env
for (var, expr) in bindings {
self.lower_recursive_function_with_captures(var, expr, &captured)?;
}
// Restore insertion point
if let Some(block) = current_block {
self.builder().position_at_end(block);
}
// Restore captured variable values in env (they were overwritten
// during recursive function body lowering with values from that
// function's closure env which are invalid in this scope).
for (vid, val) in &captured {
self.env.insert(*vid, *val);
}
// Create closures for the lifted functions so they can be
// called (the closure env carries the captured free variables).
for (var, _expr) in bindings {
let fn_val = self.functions[&var.id];
let fn_ptr = fn_val.as_global_value().as_pointer_value();
let closure_ptr = self.alloc_closure(fn_ptr, &captured)?;
self.env.insert(var.id, closure_ptr.into());
}
// Lower the body (recursive functions are now available)
let result = self.lower_expr(body)?;
Ok(result)
}
}
}
/// Lower a recursive function that was lifted from a let binding.
// Retained for let-lifted recursive function lowering.
#[allow(dead_code)]
fn lower_recursive_function(&mut self, var: &Var, expr: &Expr) -> CodegenResult<()> {
let fn_val = self.functions.get(&var.id).copied().ok_or_else(|| {
CodegenError::Internal(format!(
"recursive function not declared: {}",
var.name.as_str()
))
})?;
// Create entry block
let entry = self.llvm_context().append_basic_block(fn_val, "entry");
self.builder().position_at_end(entry);
// E.45: Extract Integer parameter positions from the function's type signature
let integer_param_positions = self.extract_integer_param_positions(&var.ty);
// Handle lambda parameters
let result = self.lower_function_body(fn_val, expr, &integer_param_positions)?;
// Check if the current block already has a terminator (e.g., from `error` or `unreachable`)
// If so, don't add another terminator
let current_block = self.builder().get_insert_block();
let has_terminator = current_block
.map(|bb| bb.get_terminator().is_some())
.unwrap_or(false);
if !has_terminator {
// Build return - convert to pointer if return type is pointer (uniform calling convention)
let ret_type = fn_val.get_type().get_return_type();
if let Some(val) = result {
let ret_val: BasicValueEnum<'ctx> =
if ret_type == Some(self.type_mapper().ptr_type().into()) {
self.value_to_ptr(val)?.into()
} else {
val
};
self.builder().build_return(Some(&ret_val)).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
} else {
self.builder().build_return(None).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
}
}
Ok(())
}
/// Lower a recursive function with captured free variables from enclosing scope.
/// The captured variables are extracted from the closure environment (param 0).
fn lower_recursive_function_with_captures(
&mut self,
var: &Var,
expr: &Expr,
captures: &[(VarId, BasicValueEnum<'ctx>)],
) -> CodegenResult<()> {
let fn_val = self.functions.get(&var.id).copied().ok_or_else(|| {
CodegenError::Internal(format!(
"recursive function not declared: {}",
var.name.as_str()
))
})?;
// Create entry block
let entry = self.llvm_context().append_basic_block(fn_val, "entry");
self.builder().position_at_end(entry);
// Extract captured variables from closure env (param 0)
if !captures.is_empty() {
if let Some(env_ptr) = fn_val.get_nth_param(0) {
let env_ptr = env_ptr.into_pointer_value();
let env_size = captures.len() as u32;
for (i, (vid, _)) in captures.iter().enumerate() {
let val = self.extract_closure_env_elem(env_ptr, env_size, i as u32)?;
self.env.insert(*vid, val.into());
}
}
}
// E.45: Extract Integer parameter positions from the function's type signature
let integer_param_positions = self.extract_integer_param_positions(&var.ty);
// Handle lambda parameters
let result = self.lower_function_body(fn_val, expr, &integer_param_positions)?;
// Check if the current block already has a terminator
let current_block = self.builder().get_insert_block();
let has_terminator = current_block
.map(|bb| bb.get_terminator().is_some())
.unwrap_or(false);
if !has_terminator {
let ret_type = fn_val.get_type().get_return_type();
if let Some(val) = result {
let ret_val: BasicValueEnum<'ctx> =
if ret_type == Some(self.type_mapper().ptr_type().into()) {
self.value_to_ptr(val)?.into()
} else {
val
};
self.builder().build_return(Some(&ret_val)).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
} else {
self.builder().build_return(None).map_err(|e| {
CodegenError::Internal(format!("failed to build return: {:?}", e))
})?;
}
}
Ok(())
}
/// Lower a function body, handling lambda parameters.
fn lower_function_body(
&mut self,
fn_val: FunctionValue<'ctx>,
expr: &Expr,
integer_param_positions: &[bool],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// If the expression is a lambda, bind parameters to function arguments
// Note: param_idx starts at 1 because index 0 is the env/closure pointer
let mut current = expr;
let mut param_idx = 1;
let mut lambda_idx = 0usize; // Index into integer_param_positions
// Unwrap lambdas, type lambdas, and trivial case expressions.
// - Lambdas (Lam): bind parameter to function argument
// - Type lambdas (TyLam): erased at runtime, skip
// - Trivial Case: pattern matching on a variable we just bound, rebind and continue
loop {
match current {
Expr::Lam(param, body, _span) => {
// Term lambda - bind parameter to function argument
if let Some(arg) = fn_val.get_nth_param(param_idx) {
self.env.insert(param.id, arg);
}
// E.45: Track Integer parameters using function type signature
// (param.ty is Ty::Error in Core IR, so use declared type instead)
if self.is_integer_type(¶m.ty)
|| integer_param_positions
.get(lambda_idx)
.copied()
.unwrap_or(false)
{
self.integer_vars.insert(param.id);
}
param_idx += 1;
lambda_idx += 1;
current = body.as_ref();
}
Expr::TyLam(_tyvar, body, _span) => {
// Type lambda - skip (erased at runtime)
current = body.as_ref();
}
Expr::Case(scrut, alts, _ty, _span) => {
// Check if this is a "trivial" case that just rebinds a variable.
// This pattern comes from HIR->Core lowering for pattern matching
// on function parameters.
//
// Pattern: case x of { _ -> body } or case x of { y -> body }
// where x is a variable we already have bound AND all alternatives are Default.
//
// IMPORTANT: Only optimize when ALL alternatives are Default.
// If there are any literal or constructor patterns, we must do real pattern matching.
let all_default = alts.iter().all(|a| matches!(a.con, AltCon::Default));
if all_default {
if let Expr::Var(scrut_var, _) = scrut.as_ref() {
// Use the first Default alternative
if let Some(alt) = alts.first() {
// If the alternative has a binder, bind it to the same value as scrut
for binder in &alt.binders {
if let Some(val) = self.env.get(&scrut_var.id) {
self.env.insert(binder.id, *val);
}
}
current = &alt.rhs;
continue;
}
}
}
// Not a trivial case, stop unwrapping
break;
}
_ => break,
}
}
// Check if there are remaining LLVM parameters that weren't bound to lambdas.
// This happens for definitions like `add5 = add 5` where the body is not a lambda
// but the type implies it takes arguments. We need to eta-expand at codegen.
let total_params = fn_val.count_params();
let remaining_params: Vec<_> = (param_idx..total_params)
.filter_map(|i| fn_val.get_nth_param(i))
.collect();
// Lower the body
let was_tail = self.in_tail_position;
self.in_tail_position = remaining_params.is_empty(); // Only tail if no eta-expansion needed
let result = self.lower_expr(current)?;
self.in_tail_position = was_tail;
// If there are remaining parameters, the body should evaluate to a closure.
// Apply the remaining parameters to it (eta-expansion).
if !remaining_params.is_empty() {
if let Some(closure_val) = result {
// The result should be a closure - call it with remaining params
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
let closure_ptr = match closure_val {
BasicValueEnum::PointerValue(p) => p,
_ => {
return Err(CodegenError::Internal(
"eta-expansion: expected closure but got non-pointer".to_string(),
))
}
};
// Extract function pointer from closure
let fn_ptr = self.extract_closure_fn_ptr(closure_ptr)?;
// Build function type for the call
let mut param_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
param_types.push(ptr_type.into()); // closure/env pointer
for _ in &remaining_params {
param_types.push(ptr_type.into());
}
let fn_type = ptr_type.fn_type(¶m_types, false);
// Build args: closure ptr + remaining params
let mut llvm_args: Vec<inkwell::values::BasicMetadataValueEnum<'ctx>> = Vec::new();
llvm_args.push(closure_ptr.into());
for param in &remaining_params {
// Convert to pointer if needed
let ptr_val = self.value_to_ptr(*param)?;
llvm_args.push(ptr_val.into());
}
// Build indirect call
let call = self
.builder()
.build_indirect_call(fn_type, fn_ptr, &llvm_args, "eta_call")
.map_err(|e| {
CodegenError::Internal(format!(
"failed to build eta-expansion call: {:?}",
e
))
})?;
return Ok(call.try_as_basic_value().basic());
} else {
return Err(CodegenError::Internal(
"eta-expansion: body has no value".to_string(),
));
}
}
Ok(result)
}
/// Lower a case expression.
///
/// Handles three cases:
/// 1. Literal patterns (Int, Char) - switch on the primitive value
/// 2. Constructor patterns (DataCon) - switch on the tag, extract fields
/// 3. Default pattern - catch-all fallback
fn lower_case(
&mut self,
scrut: &Expr,
alts: &[Alt],
case_ty: &Ty,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// EmptyCase: `case x of {}` — zero alternatives means bottom/unreachable
if alts.is_empty() {
// Still evaluate the scrutinee for side effects
let was_tail = self.in_tail_position;
self.in_tail_position = false;
let _scrut_val = self.lower_expr(scrut)?;
self.in_tail_position = was_tail;
self.builder().build_unreachable().map_err(|e| {
CodegenError::Internal(format!(
"failed to build unreachable for empty case: {:?}",
e
))
})?;
return Ok(None);
}
// Scrutinee is NOT in tail position
let was_tail = self.in_tail_position;
self.in_tail_position = false;
let scrut_val = self
.lower_expr(scrut)?
.ok_or_else(|| CodegenError::Internal("scrutinee has no value".to_string()))?;
self.in_tail_position = was_tail;
// Check if all alternatives are Default (no actual pattern matching needed)
// This happens for simple variable patterns like `f n = 42`
let all_default = alts.iter().all(|alt| matches!(&alt.con, AltCon::Default));
if all_default {
// Just use the first (and likely only) default alternative
let alt = &alts[0];
// Bind any pattern variables to the scrutinee value
for binder in &alt.binders {
self.env.insert(binder.id, scrut_val);
}
// Lower the RHS (inherits tail position from parent case)
let result = self.lower_expr(&alt.rhs)?;
// Clean up bindings
for binder in &alt.binders {
self.env.remove(&binder.id);
}
return Ok(result);
}
// Determine if this is a constructor case or a literal case
let has_datacon = alts
.iter()
.any(|alt| matches!(&alt.con, AltCon::DataCon(_)));
// Check if this is a Bool case (True/False patterns) with an integer scrutinee
// This happens when the condition is a comparison result
let is_bool_case = alts.iter().any(|alt| {
if let AltCon::DataCon(con) = &alt.con {
let name = con.name.as_str();
name == "True"
|| name == "False"
|| name == "GHC.Types.True"
|| name == "GHC.Types.False"
} else {
false
}
});
if is_bool_case {
// Bool case - convert scrutinee to integer if needed
let int_scrut = match scrut_val {
BasicValueEnum::IntValue(_) => scrut_val,
BasicValueEnum::PointerValue(p) => {
// The pointer might be:
// 1. A valid Bool ADT pointer (from constructor allocation)
// 2. A small integer (0 or 1) cast to pointer (from primop result)
//
// We need to distinguish these cases. Check if pointer value <= 1,
// in which case it's a raw boolean, otherwise load from the ADT.
let ptr_as_int = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "ptr_as_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to convert ptr to int: {:?}", e))
})?;
// Check if it's a small integer (0 or 1)
let is_raw_bool = self
.builder()
.build_int_compare(
inkwell::IntPredicate::ULE,
ptr_as_int,
self.type_mapper().i64_type().const_int(1, false),
"is_raw_bool",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to compare ptr: {:?}", e))
})?;
// Create blocks for the two cases
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let raw_bool_block = self.llvm_ctx.append_basic_block(current_fn, "raw_bool");
let adt_bool_block = self.llvm_ctx.append_basic_block(current_fn, "adt_bool");
let merge_block = self.llvm_ctx.append_basic_block(current_fn, "bool_merge");
self.builder()
.build_conditional_branch(is_raw_bool, raw_bool_block, adt_bool_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build cond branch: {:?}", e))
})?;
// Raw bool case: pointer value IS the boolean (0 or 1)
self.builder().position_at_end(raw_bool_block);
let raw_val = ptr_as_int;
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
let raw_bool_end = self.builder().get_insert_block().unwrap();
// ADT bool case: load the tag from the ADT structure
self.builder().position_at_end(adt_bool_block);
let adt_ty = self.adt_type(0);
let tag_ptr = self
.builder()
.build_struct_gep(adt_ty, p, 0, "bool_tag_ptr")
.map_err(|e| {
CodegenError::Internal(format!("failed to get bool tag ptr: {:?}", e))
})?;
let adt_val = self
.builder()
.build_load(self.type_mapper().i64_type(), tag_ptr, "bool_tag")
.map_err(|e| {
CodegenError::Internal(format!("failed to load bool tag: {:?}", e))
})?
.into_int_value();
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
let adt_bool_end = self.builder().get_insert_block().unwrap();
// Merge
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(self.type_mapper().i64_type(), "bool_val")
.map_err(|e| {
CodegenError::Internal(format!("failed to build phi: {:?}", e))
})?;
phi.add_incoming(&[(&raw_val, raw_bool_end), (&adt_val, adt_bool_end)]);
phi.as_basic_value()
}
_ => scrut_val, // Fall through to datacon handling
};
if matches!(int_scrut, BasicValueEnum::IntValue(_)) {
return self.lower_case_bool_as_int(int_scrut, alts, case_ty);
}
}
// Get the scrutinee's type for determining binder types
let scrut_ty = scrut.ty();
if has_datacon {
self.lower_case_datacon(scrut_val, alts, &scrut_ty)
} else {
self.lower_case_literal(scrut_val, alts)
}
}
/// Lower a case expression with literal patterns.
fn lower_case_literal(
&mut self,
scrut_val: BasicValueEnum<'ctx>,
alts: &[Alt],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Determine the type of literals in the alternatives
let has_int_lit = alts
.iter()
.any(|alt| matches!(&alt.con, AltCon::Lit(Literal::Int(_))));
let has_char_lit = alts
.iter()
.any(|alt| matches!(&alt.con, AltCon::Lit(Literal::Char(_))));
let has_float_lit = alts
.iter()
.any(|alt| matches!(&alt.con, AltCon::Lit(Literal::Double(_))));
let has_string_lit = alts
.iter()
.any(|alt| matches!(&alt.con, AltCon::Lit(Literal::String(_))));
// Dispatch based on scrutinee type
match scrut_val {
BasicValueEnum::IntValue(i) => self.lower_case_literal_int(i, alts),
BasicValueEnum::FloatValue(f) => self.lower_case_literal_float(f, alts),
BasicValueEnum::PointerValue(p) => {
// Check if the alternatives have integer/char literals - if so, unbox the pointer
if has_int_lit || has_char_lit {
// Pointer contains a boxed integer - unbox it
let int_val = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_for_case")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox for case: {:?}", e))
})?;
self.lower_case_literal_int(int_val, alts)
} else if has_float_lit {
// Pointer contains a boxed float - unbox it
let bits = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_float_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox float: {:?}", e))
})?;
let float_val = self
.builder()
.build_bit_cast(bits, self.type_mapper().f64_type(), "to_double")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to double: {:?}", e))
})?;
if let BasicValueEnum::FloatValue(f) = float_val {
self.lower_case_literal_float(f, alts)
} else {
Err(CodegenError::Internal(
"expected float value after unboxing".to_string(),
))
}
} else if has_string_lit {
// String pattern matching
self.lower_case_literal_string(p, alts)
} else {
// Default case or only Default alternatives - assume integer
let int_val = self
.builder()
.build_ptr_to_int(p, self.type_mapper().i64_type(), "unbox_for_case")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox for case: {:?}", e))
})?;
self.lower_case_literal_int(int_val, alts)
}
}
_ => Err(CodegenError::Unsupported(format!(
"unsupported scrutinee type for literal case: {:?}",
scrut_val.get_type()
))),
}
}
/// Lower a case expression with integer literal patterns.
fn lower_case_literal_int(
&mut self,
scrut_int: IntValue<'ctx>,
alts: &[Alt],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
// Create blocks for each alternative
let mut blocks = Vec::new();
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "case_merge");
let mut default_block = None;
let mut cases = Vec::new();
for alt in alts {
let block = self
.llvm_context()
.append_basic_block(current_fn, "case_alt");
blocks.push(block);
match &alt.con {
AltCon::Lit(Literal::Int(n)) => {
cases.push((
self.type_mapper().i64_type().const_int(*n as u64, true),
block,
));
}
AltCon::Lit(Literal::Char(c)) => {
// Use the same integer type as the scrutinee to avoid type mismatches
cases.push((scrut_int.get_type().const_int(*c as u64, false), block));
}
AltCon::Default => {
// Use the FIRST default block as the switch default.
// The pattern compiler adds error fallbacks as additional Defaults,
// but we want non-matching cases to go to the first (user-defined) default.
if default_block.is_none() {
default_block = Some(block);
}
}
_ => {
return Err(CodegenError::Unsupported(format!(
"unsupported pattern in literal case: {:?}",
alt.con
)))
}
}
}
// Build switch
// If there's no explicit default, create an unreachable block
let default = if let Some(db) = default_block {
db
} else {
let unreachable_block = self
.llvm_context()
.append_basic_block(current_fn, "case_unreachable");
unreachable_block
};
let _switch = self
.builder()
.build_switch(scrut_int, default, &cases)
.map_err(|e| CodegenError::Internal(format!("failed to build switch: {:?}", e)))?;
// If we created an unreachable block, fill it in
if default_block.is_none() {
self.builder().position_at_end(default);
self.builder().build_unreachable().map_err(|e| {
CodegenError::Internal(format!("failed to build unreachable: {:?}", e))
})?;
}
// Generate code for each alternative
self.lower_case_alternatives(alts, &blocks, merge_block)
}
/// Lower a case expression with float/double literal patterns.
/// Uses chained comparisons since LLVM switch doesn't support floats.
fn lower_case_literal_float(
&mut self,
scrut_float: FloatValue<'ctx>,
alts: &[Alt],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "float_case_merge");
// Separate literal alts from default
let mut literal_alts: Vec<(f64, &Alt)> = Vec::new();
let mut default_alt: Option<&Alt> = None;
for alt in alts {
match &alt.con {
AltCon::Lit(Literal::Float(f)) => literal_alts.push((*f as f64, alt)),
AltCon::Lit(Literal::Double(d)) => literal_alts.push((*d, alt)),
AltCon::Default => default_alt = Some(alt),
_ => {
return Err(CodegenError::Unsupported(format!(
"unsupported pattern in float case: {:?}",
alt.con
)))
}
}
}
// Create all blocks upfront to avoid creating duplicate blocks
let mut phi_values: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
// Create the default/else block
let default_block = self
.llvm_context()
.append_basic_block(current_fn, "float_default");
// Create comparison blocks (one per literal except the first which uses current block)
let mut cmp_blocks: Vec<inkwell::basic_block::BasicBlock<'ctx>> = Vec::new();
for i in 1..literal_alts.len() {
cmp_blocks.push(
self.llvm_context()
.append_basic_block(current_fn, &format!("float_cmp_{}", i)),
);
}
// Create match blocks (one per literal)
let mut match_blocks: Vec<inkwell::basic_block::BasicBlock<'ctx>> = Vec::new();
for i in 0..literal_alts.len() {
match_blocks.push(
self.llvm_context()
.append_basic_block(current_fn, &format!("float_match_{}", i)),
);
}
// Generate chain of comparisons
for (i, (val, alt)) in literal_alts.iter().enumerate() {
// Position at the comparison block
if i > 0 {
self.builder().position_at_end(cmp_blocks[i - 1]);
}
// i == 0 uses the current block (already positioned there)
let match_block = match_blocks[i];
// Determine next block (next comparison or default)
let next_block = if i + 1 < literal_alts.len() {
cmp_blocks[i] // cmp_blocks[0] corresponds to float_cmp_1
} else {
default_block
};
// Build float comparison (ordered equal)
// Check if it's f32 or f64 by comparing types
let is_f32 = scrut_float.get_type() == self.type_mapper().f32_type();
let const_val = if is_f32 {
self.type_mapper().f32_type().const_float(*val)
} else {
self.type_mapper().f64_type().const_float(*val)
};
let cmp = self
.builder()
.build_float_compare(
inkwell::FloatPredicate::OEQ,
scrut_float,
const_val,
"float_eq",
)
.map_err(|e| {
CodegenError::Internal(format!("failed to build float cmp: {:?}", e))
})?;
self.builder()
.build_conditional_branch(cmp, match_block, next_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build cond branch: {:?}", e))
})?;
// Generate code for match block
self.builder().position_at_end(match_block);
if let Some(result) = self.lower_expr(&alt.rhs)? {
phi_values.push((result, self.builder().get_insert_block().unwrap()));
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
}
// Generate default block
self.builder().position_at_end(default_block);
if let Some(alt) = default_alt {
// Bind scrutinee to any pattern variables
for binder in &alt.binders {
self.env.insert(binder.id, scrut_float.into());
}
if let Some(result) = self.lower_expr(&alt.rhs)? {
phi_values.push((result, self.builder().get_insert_block().unwrap()));
}
for binder in &alt.binders {
self.env.remove(&binder.id);
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
} else {
// No default - generate unreachable
self.builder().build_unreachable().map_err(|e| {
CodegenError::Internal(format!("failed to build unreachable: {:?}", e))
})?;
}
// Build phi in merge block
self.builder().position_at_end(merge_block);
if phi_values.is_empty() {
Ok(None)
} else {
let target_type = phi_values[0].0.get_type();
// Coerce all values to the target type
let mut coerced_values: Vec<(
BasicValueEnum<'ctx>,
inkwell::basic_block::BasicBlock<'ctx>,
)> = Vec::new();
for (val, block) in &phi_values {
if val.get_type() == target_type {
coerced_values.push((*val, *block));
} else {
let terminator = block.get_terminator();
if let Some(term) = terminator {
self.builder().position_before(&term);
let coerced = self.coerce_to_type(*val, target_type)?;
coerced_values.push((coerced, *block));
} else {
coerced_values.push((*val, *block));
}
}
}
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(target_type, "float_case_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
for (val, block) in &coerced_values {
phi.add_incoming(&[(val, *block)]);
}
Ok(Some(phi.as_basic_value()))
}
}
/// Lower a case expression with string literal patterns.
/// Uses strcmp to compare strings.
fn lower_case_literal_string(
&mut self,
scrut_ptr: PointerValue<'ctx>,
alts: &[Alt],
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "str_case_merge");
// Get or declare strcmp
let strcmp_fn = self.get_or_declare_strcmp()?;
// Separate literal alts from default
let mut literal_alts: Vec<(&Symbol, &Alt)> = Vec::new();
let mut default_alt: Option<&Alt> = None;
for alt in alts {
match &alt.con {
AltCon::Lit(Literal::String(s)) => literal_alts.push((s, alt)),
AltCon::Default => default_alt = Some(alt),
_ => {
return Err(CodegenError::Unsupported(format!(
"unsupported pattern in string case: {:?}",
alt.con
)))
}
}
}
// Create all blocks upfront to avoid creating duplicate blocks
let mut phi_values: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
let default_block = self
.llvm_context()
.append_basic_block(current_fn, "str_default");
// Create comparison blocks (one per literal except the first which uses current block)
let mut cmp_blocks: Vec<inkwell::basic_block::BasicBlock<'ctx>> = Vec::new();
for i in 1..literal_alts.len() {
cmp_blocks.push(
self.llvm_context()
.append_basic_block(current_fn, &format!("str_cmp_{}", i)),
);
}
// Create match blocks (one per literal)
let mut match_blocks: Vec<inkwell::basic_block::BasicBlock<'ctx>> = Vec::new();
for i in 0..literal_alts.len() {
match_blocks.push(
self.llvm_context()
.append_basic_block(current_fn, &format!("str_match_{}", i)),
);
}
// Generate chain of strcmp comparisons
for (i, (sym, alt)) in literal_alts.iter().enumerate() {
// Position at the comparison block
if i > 0 {
self.builder().position_at_end(cmp_blocks[i - 1]);
}
// i == 0 uses the current block (already positioned there)
let match_block = match_blocks[i];
// Determine next block (next comparison or default)
let next_block = if i + 1 < literal_alts.len() {
cmp_blocks[i] // cmp_blocks[0] corresponds to str_cmp_1
} else {
default_block
};
// Create global string constant for the pattern
let str_const = self
.module
.add_global_string(&format!("str_pat_{}", i), sym.as_str());
// Call strcmp(scrut, pattern)
let cmp_result = self
.builder()
.build_call(
strcmp_fn,
&[scrut_ptr.into(), str_const.into()],
"strcmp_result",
)
.map_err(|e| CodegenError::Internal(format!("failed to call strcmp: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("strcmp returned void".to_string()))?;
// strcmp returns 0 for equal strings
let zero = self.type_mapper().i32_type().const_zero();
let is_equal = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
cmp_result.into_int_value(),
zero,
"str_eq",
)
.map_err(|e| CodegenError::Internal(format!("failed to build int cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_equal, match_block, next_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build cond branch: {:?}", e))
})?;
// Generate match block
self.builder().position_at_end(match_block);
if let Some(result) = self.lower_expr(&alt.rhs)? {
phi_values.push((result, self.builder().get_insert_block().unwrap()));
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
}
// Generate default block
self.builder().position_at_end(default_block);
if let Some(alt) = default_alt {
for binder in &alt.binders {
self.env.insert(binder.id, scrut_ptr.into());
}
if let Some(result) = self.lower_expr(&alt.rhs)? {
phi_values.push((result, self.builder().get_insert_block().unwrap()));
}
for binder in &alt.binders {
self.env.remove(&binder.id);
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
} else {
self.builder().build_unreachable().map_err(|e| {
CodegenError::Internal(format!("failed to build unreachable: {:?}", e))
})?;
}
// Build phi in merge block
self.builder().position_at_end(merge_block);
if phi_values.is_empty() {
Ok(None)
} else {
let target_type = phi_values[0].0.get_type();
// Coerce all values to the target type
let mut coerced_values: Vec<(
BasicValueEnum<'ctx>,
inkwell::basic_block::BasicBlock<'ctx>,
)> = Vec::new();
for (val, block) in &phi_values {
if val.get_type() == target_type {
coerced_values.push((*val, *block));
} else {
let terminator = block.get_terminator();
if let Some(term) = terminator {
self.builder().position_before(&term);
let coerced = self.coerce_to_type(*val, target_type)?;
coerced_values.push((coerced, *block));
} else {
coerced_values.push((*val, *block));
}
}
}
self.builder().position_at_end(merge_block);
let phi = self
.builder()
.build_phi(target_type, "str_case_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
for (val, block) in &coerced_values {
phi.add_incoming(&[(val, *block)]);
}
Ok(Some(phi.as_basic_value()))
}
}
/// Get or declare the strcmp function.
fn get_or_declare_strcmp(&self) -> CodegenResult<FunctionValue<'ctx>> {
let name = "strcmp";
if let Some(fn_val) = self.module.get_function(name) {
return Ok(fn_val);
}
// int strcmp(const char*, const char*)
let tm = self.type_mapper();
let fn_type = tm
.i32_type()
.fn_type(&[tm.ptr_type().into(), tm.ptr_type().into()], false);
Ok(self.module.add_function(name, fn_type))
}
/// Lower a case expression with constructor patterns.
fn lower_case_datacon(
&mut self,
scrut_val: BasicValueEnum<'ctx>,
alts: &[Alt],
scrut_ty: &Ty,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Newtype erasure: if the case matches a single newtype constructor,
// the scrutinee IS the inner value (no ADT struct to destructure).
// Just bind the scrutinee to the binder and evaluate the RHS.
if let Some(alt) = alts.first() {
if let AltCon::DataCon(con) = &alt.con {
if self.is_newtype_constructor(con.name.as_str()) && con.arity == 1 {
// Bind the single field to the scrutinee value directly
if let Some(binder) = alt.binders.first() {
self.env.insert(binder.id, scrut_val);
}
let result = self.lower_expr(&alt.rhs)?;
// Clean up bindings
for binder in &alt.binders {
self.env.remove(&binder.id);
}
return Ok(result);
}
}
// Also handle Default alt that binds the scrutinee for newtype cases
if let AltCon::Default = &alt.con {
if alts.len() == 1 {
// Single default alt — bind scrutinee if there's a binder
if let Some(binder) = alt.binders.first() {
self.env.insert(binder.id, scrut_val);
}
let result = self.lower_expr(&alt.rhs)?;
for binder in &alt.binders {
self.env.remove(&binder.id);
}
return Ok(result);
}
}
}
// Count existing case_alt blocks before we add more
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
let _existing_blocks: Vec<_> = current_fn.get_basic_block_iter().collect();
// For constructor patterns, scrutinee must be a pointer (ADT value)
let scrut_ptr = match scrut_val {
BasicValueEnum::PointerValue(p) => p,
BasicValueEnum::IntValue(i) => {
// If it's an int, it might be a boxed value - try to interpret as ptr
self.builder()
.build_int_to_ptr(i, self.type_mapper().ptr_type(), "scrut_ptr")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast scrutinee: {:?}", e))
})?
}
_ => {
return Err(CodegenError::Unsupported(
"case on non-pointer value with constructor patterns".to_string(),
))
}
};
// Extract the tag from the ADT value
let tag = self.extract_adt_tag(scrut_ptr)?;
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
// Create blocks for each alternative
let mut blocks = Vec::new();
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "case_merge");
let mut default_block = None;
let mut cases = Vec::new();
// Collect DataCon info for field extraction later
let mut datacon_info: Vec<Option<&DataCon>> = Vec::new();
for alt in alts {
let block = self
.llvm_context()
.append_basic_block(current_fn, "case_alt");
blocks.push(block);
match &alt.con {
AltCon::DataCon(con) => {
let tag_val = self
.type_mapper()
.i64_type()
.const_int(con.tag as u64, false);
cases.push((tag_val, block));
datacon_info.push(Some(con));
// Register this constructor for later use in constructor applications
self.register_constructor(con.name.as_str(), con.tag, con.arity);
}
AltCon::Default => {
default_block = Some(block);
datacon_info.push(None);
}
AltCon::Lit(_) => {
return Err(CodegenError::Unsupported(
"mixed literal and constructor patterns".to_string(),
))
}
}
}
// Deduplicate switch cases — if nested patterns cause the same tag to
// appear multiple times, keep only the first (the later alternatives are
// dead code or handled by nested pattern matching inside the first alt).
{
let mut seen_tags = FxHashSet::default();
cases.retain(|(tag_val, _block)| {
let tag_u64 = tag_val.get_zero_extended_constant().unwrap_or(u64::MAX);
seen_tags.insert(tag_u64)
});
}
// Build switch on tag
// If there's no explicit default, create an unreachable block to indicate exhaustive matching
let default = if let Some(db) = default_block {
db
} else {
// Create an unreachable block for the default case
// This avoids having merge_block as a direct successor of the switch
let unreachable_block = self
.llvm_context()
.append_basic_block(current_fn, "case_unreachable");
unreachable_block
};
let _switch = self
.builder()
.build_switch(tag, default, &cases)
.map_err(|e| CodegenError::Internal(format!("failed to build switch: {:?}", e)))?;
// If we created an unreachable block, fill it in
if default_block.is_none() {
self.builder().position_at_end(default);
self.builder().build_unreachable().map_err(|e| {
CodegenError::Internal(format!("failed to build unreachable: {:?}", e))
})?;
}
// Generate code for each alternative with field extraction
let result = self.lower_case_datacon_alternatives(
alts,
&blocks,
merge_block,
scrut_ptr,
&datacon_info,
scrut_ty,
)?;
Ok(result)
}
/// Lower case alternatives (shared logic for RHS generation).
///
/// Uses a two-pass approach to avoid inserting instructions after terminators:
/// 1. First pass: lower all RHS expressions and collect (value, block) pairs
/// 2. Second pass: coerce values if needed, then build branches
///
/// Each alternative's RHS is in tail position if the case expression itself is.
fn lower_case_alternatives(
&mut self,
alts: &[Alt],
blocks: &[inkwell::basic_block::BasicBlock<'ctx>],
merge_block: inkwell::basic_block::BasicBlock<'ctx>,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Pass 1: Lower all expressions and collect values WITHOUT building branches
// Each alternative's RHS is in tail position if the case expression is
let mut collected: Vec<(
Option<BasicValueEnum<'ctx>>,
inkwell::basic_block::BasicBlock<'ctx>,
)> = Vec::new();
for (i, alt) in alts.iter().enumerate() {
self.builder().position_at_end(blocks[i]);
// RHS is in tail position (inherits from parent case expression)
let result = self.lower_expr(&alt.rhs)?;
// Get the ACTUAL current block (lower_expr may have created nested blocks)
let current_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after lower_expr".to_string())
})?;
collected.push((result, current_block));
}
// Determine target type from first non-None value
let target_type = collected
.iter()
.find_map(|(val, _)| val.map(|v| v.get_type()));
// Pass 2: Coerce values and build branches
let mut phi_values: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
for (result, block) in collected {
// Position at the end of the block where the value was produced
self.builder().position_at_end(block);
// Only add to phi_values if we will branch to merge_block
// If the block already has a terminator (e.g., error call with unreachable),
// it doesn't reach merge_block and shouldn't contribute to the PHI
if block.get_terminator().is_none() {
if let Some(val) = result {
// Coerce if needed (BEFORE building the branch)
let final_val = if let Some(target) = target_type {
if val.get_type() != target {
self.coerce_to_type(val, target)?
} else {
val
}
} else {
val
};
// Get the current block
let final_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after coercion".to_string())
})?;
phi_values.push((final_val, final_block));
}
// Build the branch to merge_block
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
}
// If block already has a terminator, skip it entirely for PHI
}
// Build phi node in merge block
self.builder().position_at_end(merge_block);
if phi_values.is_empty() {
Ok(None)
} else {
let phi_type = phi_values[0].0.get_type();
let phi = self
.builder()
.build_phi(phi_type, "case_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
for (val, block) in &phi_values {
phi.add_incoming(&[(val, *block)]);
}
Ok(Some(phi.as_basic_value()))
}
}
/// Lower a Bool case expression when the scrutinee is an integer (from comparison).
/// True is represented as non-zero (typically 1), False as 0.
///
/// Uses a two-pass approach to avoid inserting instructions after terminators.
fn lower_case_bool_as_int(
&mut self,
scrut_val: BasicValueEnum<'ctx>,
alts: &[Alt],
case_ty: &Ty,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let scrut_int = match scrut_val {
BasicValueEnum::IntValue(i) => i,
_ => {
return Err(CodegenError::Internal(
"lower_case_bool_as_int called with non-integer scrutinee".to_string(),
))
}
};
let current_fn = self
.builder()
.get_insert_block()
.ok_or_else(|| CodegenError::Internal("no insert block".to_string()))?
.get_parent()
.ok_or_else(|| CodegenError::Internal("no parent function".to_string()))?;
// Find the True and False alternatives
let mut true_alt = None;
let mut false_alt = None;
let mut default_alt = None;
for alt in alts {
match &alt.con {
AltCon::DataCon(con) => {
let name = con.name.as_str();
if name == "True" || name == "GHC.Types.True" {
true_alt = Some(alt);
} else if name == "False" || name == "GHC.Types.False" {
false_alt = Some(alt);
}
}
AltCon::Default => {
default_alt = Some(alt);
}
_ => {}
}
}
// Create blocks
let true_block = self
.llvm_context()
.append_basic_block(current_fn, "bool_true");
let false_block = self
.llvm_context()
.append_basic_block(current_fn, "bool_false");
let merge_block = self
.llvm_context()
.append_basic_block(current_fn, "bool_merge");
// Compare scrutinee to zero (False = 0, True = non-zero)
let zero = self.type_mapper().i64_type().const_zero();
let is_true = self
.builder()
.build_int_compare(inkwell::IntPredicate::NE, scrut_int, zero, "is_true")
.map_err(|e| CodegenError::Internal(format!("failed to build bool cmp: {:?}", e)))?;
// Branch based on the comparison
self.builder()
.build_conditional_branch(is_true, true_block, false_block)
.map_err(|e| CodegenError::Internal(format!("failed to build cond branch: {:?}", e)))?;
// Pass 1: Lower both branches and collect values WITHOUT building terminators
// Each branch RHS inherits tail position from parent case expression
// True branch
self.builder().position_at_end(true_block);
let true_rhs = if let Some(alt) = true_alt {
self.lower_expr(&alt.rhs)?
} else if let Some(alt) = default_alt {
self.lower_expr(&alt.rhs)?
} else {
None
};
let true_end_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after lower_expr".to_string())
})?;
// False branch
self.builder().position_at_end(false_block);
let false_rhs = if let Some(alt) = false_alt {
self.lower_expr(&alt.rhs)?
} else if let Some(alt) = default_alt {
self.lower_expr(&alt.rhs)?
} else {
None
};
let false_end_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after lower_expr".to_string())
})?;
// E.45: Check if the case result type is Integer — we need special promotion
// case_ty is often Ty::Error in Core IR, so also check if any branch RHS is Integer
let is_integer_case =
self.is_integer_type(case_ty) || alts.iter().any(|alt| self.is_integer_expr(&alt.rhs));
// Determine target type: prefer pointer if one branch is pointer (for Integer cases)
let target_type = if is_integer_case {
// For Integer cases, target is pointer
Some(self.type_mapper().ptr_type().into())
} else {
true_rhs
.map(|v| v.get_type())
.or_else(|| false_rhs.map(|v| v.get_type()))
};
// Pass 2: Coerce values and build branches
let mut phi_values: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
// True branch: coerce and terminate (only if block reaches merge)
self.builder().position_at_end(true_end_block);
if true_end_block.get_terminator().is_none() {
if let Some(val) = true_rhs {
let final_val = if is_integer_case && val.is_int_value() {
// Promote Int to Integer via bhc_integer_from_i64
self.promote_to_integer(val)?
} else if let Some(target) = target_type {
if val.get_type() != target {
self.coerce_to_type(val, target)?
} else {
val
}
} else {
val
};
let final_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after coercion".to_string())
})?;
phi_values.push((final_val, final_block));
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
}
// False branch: coerce and terminate (only if block reaches merge)
self.builder().position_at_end(false_end_block);
if false_end_block.get_terminator().is_none() {
if let Some(val) = false_rhs {
let final_val = if is_integer_case && val.is_int_value() {
// Promote Int to Integer via bhc_integer_from_i64
self.promote_to_integer(val)?
} else if let Some(target) = target_type {
if val.get_type() != target {
self.coerce_to_type(val, target)?
} else {
val
}
} else {
val
};
let final_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after coercion".to_string())
})?;
phi_values.push((final_val, final_block));
}
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| CodegenError::Internal(format!("failed to build branch: {:?}", e)))?;
}
// Build phi node in merge block
self.builder().position_at_end(merge_block);
if phi_values.is_empty() {
Ok(None)
} else {
let phi_type = phi_values[0].0.get_type();
let phi = self
.builder()
.build_phi(phi_type, "bool_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
for (val, block) in &phi_values {
phi.add_incoming(&[(val, *block)]);
}
Ok(Some(phi.as_basic_value()))
}
}
/// Lower case alternatives with DataCon patterns (extracts fields and binds variables).
///
/// Uses a two-pass approach:
/// 1. First pass: lower all RHS expressions and collect (value, block) pairs WITHOUT building branches
/// 2. Second pass: determine target type, coerce values if needed, then build branches
fn lower_case_datacon_alternatives(
&mut self,
alts: &[Alt],
blocks: &[inkwell::basic_block::BasicBlock<'ctx>],
merge_block: inkwell::basic_block::BasicBlock<'ctx>,
scrut_ptr: PointerValue<'ctx>,
datacon_info: &[Option<&DataCon>],
scrut_ty: &Ty,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
// Pass 1: Lower all expressions and collect values WITHOUT building branches
// We collect (Option<value>, block) so we know which block each value came from
let mut collected: Vec<(
Option<BasicValueEnum<'ctx>>,
inkwell::basic_block::BasicBlock<'ctx>,
)> = Vec::new();
for (i, alt) in alts.iter().enumerate() {
self.builder().position_at_end(blocks[i]);
// Extract fields and bind to pattern variables
if let Some(con) = datacon_info[i] {
let arity = con.arity;
// Bind each field to its corresponding pattern variable
for (field_idx, binder) in alt.binders.iter().enumerate() {
if field_idx < arity as usize {
let field_ptr =
self.extract_adt_field(scrut_ptr, arity, field_idx as u32)?;
// Determine the field type:
// - Use binder.ty if it's not Error
// - Otherwise, infer from scrutinee type (e.g., for list elements)
let field_ty = if matches!(&binder.ty, Ty::Error) {
self.infer_field_type(scrut_ty, con, field_idx)
} else {
binder.ty.clone()
};
// Determine if we need to unbox the field
let field_val = self.ptr_to_value(field_ptr, &field_ty)?;
self.env.insert(binder.id, field_val);
}
}
}
// Lower the RHS with bound variables (inherits tail position from parent case)
let result = self.lower_expr(&alt.rhs)?;
// Remove bindings (for proper scoping)
for binder in &alt.binders {
self.env.remove(&binder.id);
}
// Get the ACTUAL current block (lower_expr may have created nested blocks)
let current_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after lower_expr".to_string())
})?;
collected.push((result, current_block));
}
// Determine target type from first non-None value
let target_type = collected
.iter()
.find_map(|(val, _)| val.map(|v| v.get_type()));
// Pass 2: Coerce values and build branches
let mut phi_values: Vec<(BasicValueEnum<'ctx>, inkwell::basic_block::BasicBlock<'ctx>)> =
Vec::new();
for (result, block) in collected {
// Position at the end of the block where the value was produced
self.builder().position_at_end(block);
// Only add to phi_values if we will branch to merge_block
// If the block already has a terminator (e.g., error call with unreachable),
// it doesn't reach merge_block and shouldn't contribute to the PHI
if block.get_terminator().is_none() {
if let Some(val) = result {
// Coerce if needed (BEFORE building the branch)
let final_val = if let Some(target) = target_type {
if val.get_type() != target {
self.coerce_to_type(val, target)?
} else {
val
}
} else {
val
};
// Get the current block (coercion doesn't change it)
let final_block = self.builder().get_insert_block().ok_or_else(|| {
CodegenError::Internal("no current block after coercion".to_string())
})?;
phi_values.push((final_val, final_block));
}
// Build the branch to merge_block
self.builder()
.build_unconditional_branch(merge_block)
.map_err(|e| {
CodegenError::Internal(format!("failed to build branch: {:?}", e))
})?;
}
// If block already has a terminator, skip it entirely for PHI
}
// Build phi node in merge block
self.builder().position_at_end(merge_block);
if phi_values.is_empty() {
Ok(None)
} else {
let phi_type = phi_values[0].0.get_type();
let phi = self
.builder()
.build_phi(phi_type, "case_result")
.map_err(|e| CodegenError::Internal(format!("failed to build phi: {:?}", e)))?;
for (val, block) in &phi_values {
phi.add_incoming(&[(val, *block)]);
}
Ok(Some(phi.as_basic_value()))
}
}
/// Infer the type of a field from a data constructor based on the scrutinee type.
///
/// This is used when pattern binders have `Ty::Error` type (which happens when
/// HIR-to-Core lowering doesn't preserve types). We can infer the field types
/// from the scrutinee type.
///
/// For lists:
/// - Cons `:` (tag 1, arity 2): field 0 is element type, field 1 is list type
/// - Nil `[]` (tag 0, arity 0): no fields
fn infer_field_type(&self, scrut_ty: &Ty, con: &DataCon, field_idx: usize) -> Ty {
match scrut_ty {
Ty::List(elem_ty) => {
// List cons `:` has tag 1, arity 2
// field 0: head element (elem_ty)
// field 1: tail (list type)
if con.tag == 1 && con.arity == 2 {
match field_idx {
0 => (**elem_ty).clone(), // head: element type
1 => scrut_ty.clone(), // tail: list type
_ => Ty::Error,
}
} else {
// Nil has no fields
Ty::Error
}
}
Ty::Tuple(elem_tys) => {
// Tuple fields correspond directly to element types
if field_idx < elem_tys.len() {
elem_tys[field_idx].clone()
} else {
Ty::Error
}
}
Ty::App(_con_ty, arg_ty) => {
// For type applications like `Maybe Int`, we need to look up
// the constructor's field types and substitute type arguments.
// For now, just propagate the argument type for single-arg constructors.
if con.arity == 1 && field_idx == 0 {
(**arg_ty).clone()
} else {
Ty::Error
}
}
_ => {
// For other types, we can't infer - return Error and let codegen handle it
Ty::Error
}
}
}
/// Convert a pointer to a basic value, unboxing if necessary based on type.
fn ptr_to_value(
&self,
ptr: PointerValue<'ctx>,
ty: &Ty,
) -> CodegenResult<BasicValueEnum<'ctx>> {
let tm = self.type_mapper();
match ty {
Ty::Con(con) => {
let name = con.name.as_str();
match name {
"Int" | "Int#" | "Int64" => {
// Unbox: ptr -> int
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox int: {:?}", e))
})?;
Ok(int_val.into())
}
"Int32" => {
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox int: {:?}", e))
})?;
let truncated = self
.builder()
.build_int_truncate(int_val, tm.i32_type(), "trunc_i32")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate: {:?}", e))
})?;
Ok(truncated.into())
}
"Bool" => {
// Bool values are always pointers (either tagged-int-as-pointer
// 0/1 or heap-allocated Bool ADT). Keep as pointer — the case
// expression handler uses extract_bool_tag() to handle both
// representations correctly.
Ok(ptr.into())
}
"Char" | "Char#" => {
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_char")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox char: {:?}", e))
})?;
let char_val = self
.builder()
.build_int_truncate(int_val, tm.i32_type(), "to_char")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate char: {:?}", e))
})?;
Ok(char_val.into())
}
"Float" | "Float#" => {
// Unbox: ptr -> bits -> float
let bits = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_float_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox float: {:?}", e))
})?;
let truncated = self
.builder()
.build_int_truncate(bits, tm.i32_type(), "float_bits_32")
.map_err(|e| {
CodegenError::Internal(format!(
"failed to truncate float bits: {:?}",
e
))
})?;
let float_val = self
.builder()
.build_bit_cast(truncated, tm.f32_type(), "to_float")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to float: {:?}", e))
})?;
Ok(float_val)
}
"Double" | "Double#" => {
let bits = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_double_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox double: {:?}", e))
})?;
let double_val = self
.builder()
.build_bit_cast(bits, tm.f64_type(), "to_double")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to double: {:?}", e))
})?;
Ok(double_val)
}
_ => {
// For other types (ADTs), keep as pointer
Ok(ptr.into())
}
}
}
Ty::Prim(prim) => {
use bhc_types::PrimTy;
match prim {
PrimTy::I64 | PrimTy::U64 => {
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_i64")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox i64: {:?}", e))
})?;
Ok(int_val.into())
}
PrimTy::I32 | PrimTy::U32 => {
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_int")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox int: {:?}", e))
})?;
let truncated = self
.builder()
.build_int_truncate(int_val, tm.i32_type(), "trunc_i32")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate: {:?}", e))
})?;
Ok(truncated.into())
}
PrimTy::F32 => {
let bits = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_float_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox float: {:?}", e))
})?;
let truncated = self
.builder()
.build_int_truncate(bits, tm.i32_type(), "float_bits_32")
.map_err(|e| {
CodegenError::Internal(format!(
"failed to truncate float bits: {:?}",
e
))
})?;
let float_val = self
.builder()
.build_bit_cast(truncated, tm.f32_type(), "to_float")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to float: {:?}", e))
})?;
Ok(float_val)
}
PrimTy::F64 => {
let bits = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_double_bits")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox double: {:?}", e))
})?;
let double_val = self
.builder()
.build_bit_cast(bits, tm.f64_type(), "to_double")
.map_err(|e| {
CodegenError::Internal(format!("failed to cast to double: {:?}", e))
})?;
Ok(double_val)
}
PrimTy::Char => {
let int_val = self
.builder()
.build_ptr_to_int(ptr, tm.i64_type(), "unbox_char")
.map_err(|e| {
CodegenError::Internal(format!("failed to unbox char: {:?}", e))
})?;
let char_val = self
.builder()
.build_int_truncate(int_val, tm.i32_type(), "to_char")
.map_err(|e| {
CodegenError::Internal(format!("failed to truncate char: {:?}", e))
})?;
Ok(char_val.into())
}
PrimTy::Addr => Ok(ptr.into()),
}
}
_ => {
// For function types, type variables, etc., keep as pointer
Ok(ptr.into())
}
}
}
/// Convert a Core type to an LLVM function type.
///
/// Uses pointer types for all parameters to enable uniform calling convention
/// for higher-order functions and closures.
/// All functions take an env_ptr as the first parameter (even if unused)
/// to enable uniform closure calling convention.
fn lower_function_type(&self, ty: &Ty) -> CodegenResult<inkwell::types::FunctionType<'ctx>> {
let tm = self.type_mapper();
let ptr_type = tm.ptr_type();
// Count the number of parameters
let mut param_count = 0;
let mut current = ty;
while let Ty::Fun(_, ret) = current {
param_count += 1;
current = ret;
}
// All functions take (env_ptr, args...) for uniform closure calling convention
let mut arg_types: Vec<inkwell::types::BasicMetadataTypeEnum<'ctx>> = Vec::new();
arg_types.push(ptr_type.into()); // env/closure pointer (may be unused)
for _ in 0..param_count {
arg_types.push(ptr_type.into());
}
// Return type is also pointer for uniformity
Ok(ptr_type.fn_type(&arg_types, false))
}
/// Convert a Core type to an LLVM basic type (for function return).
// Type-lowering helper retained for return-type handling paths.
#[allow(dead_code)]
fn lower_basic_type(&self, ty: &Ty) -> CodegenResult<Option<BasicTypeEnum<'ctx>>> {
let tm = self.type_mapper();
match ty {
Ty::Con(con) => {
let name = con.name.as_str();
match name {
"Int" | "Int#" | "Int64" => Ok(Some(tm.i64_type().into())),
"Int32" => Ok(Some(tm.i32_type().into())),
"Float" | "Float#" => Ok(Some(tm.f32_type().into())),
"Double" | "Double#" => Ok(Some(tm.f64_type().into())),
"Char" | "Char#" => Ok(Some(tm.i32_type().into())),
// Bool uses i64 for consistency with how comparisons return values
"Bool" => Ok(Some(tm.i64_type().into())),
"()" | "Unit" => Ok(None), // Unit type has no value
_ => {
// Unknown type - use a pointer for now
Ok(Some(tm.ptr_type().into()))
}
}
}
Ty::Prim(prim) => {
use bhc_types::PrimTy;
match prim {
PrimTy::I32 => Ok(Some(tm.i32_type().into())),
PrimTy::I64 => Ok(Some(tm.i64_type().into())),
PrimTy::U32 => Ok(Some(tm.i32_type().into())),
PrimTy::U64 => Ok(Some(tm.i64_type().into())),
PrimTy::F32 => Ok(Some(tm.f32_type().into())),
PrimTy::F64 => Ok(Some(tm.f64_type().into())),
PrimTy::Char => Ok(Some(tm.i32_type().into())),
PrimTy::Addr => Ok(Some(tm.ptr_type().into())),
}
}
Ty::App(f, arg) => {
// Check for IO () which should map to void
if let Ty::Con(con) = f.as_ref() {
if con.name.as_str() == "IO" {
// IO a - check if a is ()
if let Ty::Tuple(elems) = arg.as_ref() {
if elems.is_empty() {
// IO () -> void
return Ok(None);
}
}
if let Ty::Con(inner_con) = arg.as_ref() {
if inner_con.name.as_str() == "()" {
// IO () -> void
return Ok(None);
}
}
}
}
self.lower_basic_type(f)
}
Ty::Fun(_, _) => {
// Function types are pointers
Ok(Some(tm.ptr_type().into()))
}
Ty::Forall(_, body) => self.lower_basic_type(body),
Ty::Var(_) => {
// Type variables are erased - use pointer
Ok(Some(tm.ptr_type().into()))
}
Ty::Tuple(elems) => {
if elems.is_empty() {
// Unit type ()
Ok(None)
} else {
// Tuples become pointers for now
Ok(Some(tm.ptr_type().into()))
}
}
Ty::List(_) => {
// Lists become pointers
Ok(Some(tm.ptr_type().into()))
}
Ty::Nat(_) => {
// Type-level naturals are erased at runtime
Ok(None)
}
Ty::TyList(_) => {
// Type-level lists are erased at runtime
Ok(None)
}
Ty::Error => Ok(None),
}
}
/// Convert a Core type to an LLVM type (for arguments).
// Type-lowering helper retained for argument-type handling paths.
#[allow(dead_code)]
fn lower_type(&self, ty: &Ty) -> CodegenResult<BasicTypeEnum<'ctx>> {
self.lower_basic_type(ty)?.ok_or_else(|| {
CodegenError::TypeError("cannot lower void type to basic type".to_string())
})
}
// ========================================================================
// Data.Sequence lowering helpers
// ========================================================================
fn lower_builtin_seq_empty(&mut self) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let rts_fn = self
.functions
.get(&VarId::new(1000800))
.ok_or_else(|| CodegenError::Internal("bhc_seq_empty not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[], "seq_empty")
.map_err(|e| CodegenError::Internal(format!("seq_empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_empty: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_singleton(
&mut self,
elem_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_singleton: no elem".to_string()))?;
let elem_ptr = self.value_to_ptr(elem)?;
let rts_fn = self
.functions
.get(&VarId::new(1000801))
.ok_or_else(|| CodegenError::Internal("bhc_seq_singleton not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[elem_ptr.into()], "seq_singleton")
.map_err(|e| CodegenError::Internal(format!("seq_singleton: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_singleton: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_null(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_null: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000802))
.ok_or_else(|| CodegenError::Internal("bhc_seq_null not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into()], "seq_null")
.map_err(|e| CodegenError::Internal(format!("seq_null: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_null: void".to_string()))?;
self.allocate_bool_adt(result.into_int_value(), "seq_null_bool")
}
fn lower_builtin_seq_length(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_length: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000803))
.ok_or_else(|| CodegenError::Internal("bhc_seq_length not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into()], "seq_length")
.map_err(|e| CodegenError::Internal(format!("seq_length: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_length: void".to_string()))?;
// Return as int (will be converted to ptr by caller if needed)
let int_val = result.into_int_value();
Ok(Some(self.int_to_ptr(int_val)?.into()))
}
fn lower_builtin_seq_index(
&mut self,
seq_expr: &Expr,
idx_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_index: no seq".to_string()))?;
let idx = self
.lower_expr(idx_expr)?
.ok_or_else(|| CodegenError::Internal("seq_index: no idx".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let idx_int = self.coerce_to_int(idx)?;
let rts_fn = self
.functions
.get(&VarId::new(1000804))
.ok_or_else(|| CodegenError::Internal("bhc_seq_index not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into(), idx_int.into()], "seq_index")
.map_err(|e| CodegenError::Internal(format!("seq_index: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_index: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_lookup(
&mut self,
idx_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let idx = self
.lower_expr(idx_expr)?
.ok_or_else(|| CodegenError::Internal("seq_lookup: no idx".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_lookup: no seq".to_string()))?;
let idx_int = self.coerce_to_int(idx)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000805))
.ok_or_else(|| CodegenError::Internal("bhc_seq_lookup not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[idx_int.into(), seq_ptr.into()], "seq_lookup")
.map_err(|e| CodegenError::Internal(format!("seq_lookup: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_lookup: void".to_string()))?;
// Wrap in Maybe: null → Nothing, non-null → Just
let result_ptr = result.into_pointer_value();
let is_null = self
.builder()
.build_is_null(result_ptr, "seq_lookup_is_null")
.map_err(|e| CodegenError::Internal(format!("seq_lookup is_null: {:?}", e)))?;
let nothing = self.alloc_adt(0, 0)?;
let just = self.alloc_adt(1, 1)?;
self.store_adt_field(just, 1, 0, result_ptr.into())?;
let maybe = self
.builder()
.build_select(is_null, nothing, just, "seq_lookup_maybe")
.map_err(|e| CodegenError::Internal(format!("seq_lookup select: {:?}", e)))?;
Ok(Some(maybe))
}
fn lower_builtin_seq_cons(
&mut self,
elem_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_cons: no elem".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_cons: no seq".to_string()))?;
let elem_ptr = self.value_to_ptr(elem)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000806))
.ok_or_else(|| CodegenError::Internal("bhc_seq_cons not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[elem_ptr.into(), seq_ptr.into()], "seq_cons")
.map_err(|e| CodegenError::Internal(format!("seq_cons: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_cons: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_snoc(
&mut self,
seq_expr: &Expr,
elem_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_snoc: no seq".to_string()))?;
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_snoc: no elem".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let elem_ptr = self.value_to_ptr(elem)?;
let rts_fn = self
.functions
.get(&VarId::new(1000807))
.ok_or_else(|| CodegenError::Internal("bhc_seq_snoc not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into(), elem_ptr.into()], "seq_snoc")
.map_err(|e| CodegenError::Internal(format!("seq_snoc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_snoc: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_append(
&mut self,
s1_expr: &Expr,
s2_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let s1 = self
.lower_expr(s1_expr)?
.ok_or_else(|| CodegenError::Internal("seq_append: no s1".to_string()))?;
let s2 = self
.lower_expr(s2_expr)?
.ok_or_else(|| CodegenError::Internal("seq_append: no s2".to_string()))?;
let s1_ptr = self.value_to_ptr(s1)?;
let s2_ptr = self.value_to_ptr(s2)?;
let rts_fn = self
.functions
.get(&VarId::new(1000808))
.ok_or_else(|| CodegenError::Internal("bhc_seq_append not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[s1_ptr.into(), s2_ptr.into()], "seq_append")
.map_err(|e| CodegenError::Internal(format!("seq_append: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_append: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_take(
&mut self,
n_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("seq_take: no n".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_take: no seq".to_string()))?;
let n_int = self.coerce_to_int(n)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000809))
.ok_or_else(|| CodegenError::Internal("bhc_seq_take not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[n_int.into(), seq_ptr.into()], "seq_take")
.map_err(|e| CodegenError::Internal(format!("seq_take: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_take: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_drop(
&mut self,
n_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("seq_drop: no n".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_drop: no seq".to_string()))?;
let n_int = self.coerce_to_int(n)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000810))
.ok_or_else(|| CodegenError::Internal("bhc_seq_drop not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[n_int.into(), seq_ptr.into()], "seq_drop")
.map_err(|e| CodegenError::Internal(format!("seq_drop: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_drop: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_reverse(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_reverse: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000811))
.ok_or_else(|| CodegenError::Internal("bhc_seq_reverse not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[seq_ptr.into()], "seq_reverse")
.map_err(|e| CodegenError::Internal(format!("seq_reverse: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_reverse: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_update(
&mut self,
idx_expr: &Expr,
elem_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let idx = self
.lower_expr(idx_expr)?
.ok_or_else(|| CodegenError::Internal("seq_update: no idx".to_string()))?;
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_update: no elem".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_update: no seq".to_string()))?;
let idx_int = self.coerce_to_int(idx)?;
let elem_ptr = self.value_to_ptr(elem)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000812))
.ok_or_else(|| CodegenError::Internal("bhc_seq_update not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[idx_int.into(), elem_ptr.into(), seq_ptr.into()],
"seq_update",
)
.map_err(|e| CodegenError::Internal(format!("seq_update: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_update: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_insert_at(
&mut self,
idx_expr: &Expr,
elem_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let idx = self
.lower_expr(idx_expr)?
.ok_or_else(|| CodegenError::Internal("seq_insert_at: no idx".to_string()))?;
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_insert_at: no elem".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_insert_at: no seq".to_string()))?;
let idx_int = self.coerce_to_int(idx)?;
let elem_ptr = self.value_to_ptr(elem)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000813))
.ok_or_else(|| CodegenError::Internal("bhc_seq_insert_at not declared".to_string()))?;
let result = self
.builder()
.build_call(
*rts_fn,
&[idx_int.into(), elem_ptr.into(), seq_ptr.into()],
"seq_insert_at",
)
.map_err(|e| CodegenError::Internal(format!("seq_insert_at: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_insert_at: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_delete_at(
&mut self,
idx_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let idx = self
.lower_expr(idx_expr)?
.ok_or_else(|| CodegenError::Internal("seq_delete_at: no idx".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_delete_at: no seq".to_string()))?;
let idx_int = self.coerce_to_int(idx)?;
let seq_ptr = self.value_to_ptr(seq)?;
let rts_fn = self
.functions
.get(&VarId::new(1000814))
.ok_or_else(|| CodegenError::Internal("bhc_seq_delete_at not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[idx_int.into(), seq_ptr.into()], "seq_delete_at")
.map_err(|e| CodegenError::Internal(format!("seq_delete_at: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_delete_at: void".to_string()))?;
Ok(Some(result))
}
fn lower_builtin_seq_replicate(
&mut self,
n_expr: &Expr,
elem_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let n = self
.lower_expr(n_expr)?
.ok_or_else(|| CodegenError::Internal("seq_replicate: no n".to_string()))?;
let elem = self
.lower_expr(elem_expr)?
.ok_or_else(|| CodegenError::Internal("seq_replicate: no elem".to_string()))?;
let n_int = self.coerce_to_int(n)?;
let elem_ptr = self.value_to_ptr(elem)?;
let rts_fn = self
.functions
.get(&VarId::new(1000817))
.ok_or_else(|| CodegenError::Internal("bhc_seq_replicate not declared".to_string()))?;
let result = self
.builder()
.build_call(*rts_fn, &[n_int.into(), elem_ptr.into()], "seq_replicate")
.map_err(|e| CodegenError::Internal(format!("seq_replicate: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_replicate: void".to_string()))?;
Ok(Some(result))
}
/// Seq.toList: iterate backward from count-1 to 0, consing elements
fn lower_builtin_seq_to_list(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_to_list: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let tm = self.type_mapper();
// Get element count
let count_fn = self
.functions
.get(&VarId::new(1000815))
.ok_or_else(|| CodegenError::Internal("bhc_seq_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[seq_ptr.into()], "seq_count")
.map_err(|e| CodegenError::Internal(format!("seq_count: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("seq_count: void".to_string()))?
.into_int_value();
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("seq_to_list: no current function".to_string())
})?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "stl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "stl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "stl_exit");
let nil = self.build_nil()?;
let one = tm.i64_type().const_int(1, false);
let start_idx = self
.builder()
.build_int_sub(count, one, "stl_start_idx")
.map_err(|e| CodegenError::Internal(format!("stl start_idx: {:?}", e)))?;
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("stl br: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "stl_acc")
.map_err(|e| CodegenError::Internal(format!("stl acc phi: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "stl_idx")
.map_err(|e| CodegenError::Internal(format!("stl idx phi: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(
inkwell::IntPredicate::SLT,
idx,
tm.i64_type().const_zero(),
"stl_done",
)
.map_err(|e| CodegenError::Internal(format!("stl cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("stl cbr: {:?}", e)))?;
// Loop body
self.builder().position_at_end(loop_body);
let elem_fn = self
.functions
.get(&VarId::new(1000816))
.ok_or_else(|| CodegenError::Internal("bhc_seq_elem_at not declared".to_string()))?;
let elem = self
.builder()
.build_call(*elem_fn, &[seq_ptr.into(), idx.into()], "stl_elem")
.map_err(|e| CodegenError::Internal(format!("stl elem: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("stl elem: void".to_string()))?;
let new_acc =
self.build_cons(elem.into_pointer_value().into(), acc_phi.as_basic_value())?;
let new_idx = self
.builder()
.build_int_sub(idx, one, "stl_new_idx")
.map_err(|e| CodegenError::Internal(format!("stl new_idx: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("stl loop br: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[(&nil, entry_block), (&new_acc, loop_body)]);
idx_phi.add_incoming(&[(&start_idx, entry_block), (&new_idx, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
/// Seq.fromList: walk cons-list, snoc each element
fn lower_builtin_seq_from_list(
&mut self,
list_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let list = self
.lower_expr(list_expr)?
.ok_or_else(|| CodegenError::Internal("seq_from_list: no list".to_string()))?;
let list_ptr = self.value_to_ptr(list)?;
let tm = self.type_mapper();
// Start with empty seq
let empty_fn = self
.functions
.get(&VarId::new(1000800))
.ok_or_else(|| CodegenError::Internal("bhc_seq_empty not declared".to_string()))?;
let empty_seq = self
.builder()
.build_call(*empty_fn, &[], "sfl_empty")
.map_err(|e| CodegenError::Internal(format!("sfl empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sfl empty: void".to_string()))?;
let snoc_fn = self
.functions
.get(&VarId::new(1000807))
.ok_or_else(|| CodegenError::Internal("bhc_seq_snoc not declared".to_string()))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| {
CodegenError::Internal("seq_from_list: no current function".to_string())
})?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sfl_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sfl_body");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sfl_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("sfl br: {:?}", e)))?;
// Loop header
self.builder().position_at_end(loop_header);
let seq_phi = self
.builder()
.build_phi(tm.ptr_type(), "sfl_seq")
.map_err(|e| CodegenError::Internal(format!("sfl seq phi: {:?}", e)))?;
let cur_phi = self
.builder()
.build_phi(tm.ptr_type(), "sfl_cur")
.map_err(|e| CodegenError::Internal(format!("sfl cur phi: {:?}", e)))?;
// Check if current list is nil (tag == 0)
let cur_ptr = cur_phi.as_basic_value().into_pointer_value();
let tag = self.extract_adt_tag(cur_ptr)?;
let is_nil = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"sfl_is_nil",
)
.map_err(|e| CodegenError::Internal(format!("sfl cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_nil, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("sfl cbr: {:?}", e)))?;
// Loop body: extract head, snoc onto seq, advance tail
self.builder().position_at_end(loop_body);
let head_ptr = self.extract_adt_field(cur_ptr, 2, 0)?;
let tail_ptr = self.extract_adt_field(cur_ptr, 2, 1)?;
let seq_acc = seq_phi.as_basic_value().into_pointer_value();
let new_seq = self
.builder()
.build_call(*snoc_fn, &[seq_acc.into(), head_ptr.into()], "sfl_snoc")
.map_err(|e| CodegenError::Internal(format!("sfl snoc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sfl snoc: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("sfl loop br: {:?}", e)))?;
// Phi incoming
seq_phi.add_incoming(&[(&empty_seq, entry_block), (&new_seq, loop_body)]);
let list_ptr_bv: inkwell::values::BasicValueEnum = list_ptr.into();
cur_phi.add_incoming(&[(&list_ptr_bv, entry_block), (&tail_ptr, loop_body)]);
self.builder().position_at_end(loop_exit);
Ok(Some(seq_phi.as_basic_value()))
}
fn lower_builtin_seq_viewl(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_viewl: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let tm = self.type_mapper();
// Get tag (0=empty, 1=non-empty)
let tag_fn = self
.functions
.get(&VarId::new(1000818))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewl_tag not declared".to_string()))?;
let tag = self
.builder()
.build_call(*tag_fn, &[seq_ptr.into()], "viewl_tag")
.map_err(|e| CodegenError::Internal(format!("viewl_tag: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewl_tag: void".to_string()))?
.into_int_value();
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"viewl_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("viewl cmp: {:?}", e)))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("seq_viewl: no current function".to_string()))?;
let empty_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewl_empty");
let nonempty_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewl_nonempty");
let merge_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewl_merge");
self.builder()
.build_conditional_branch(is_empty, empty_bb, nonempty_bb)
.map_err(|e| CodegenError::Internal(format!("viewl cbr: {:?}", e)))?;
// EmptyL: tag 0, 0 fields
self.builder().position_at_end(empty_bb);
let empty_l = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("viewl empty br: {:?}", e)))?;
// (:<): tag 1, 2 fields (head, tail_seq)
self.builder().position_at_end(nonempty_bb);
let head_fn = self
.functions
.get(&VarId::new(1000819))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewl_head not declared".to_string()))?;
let head = self
.builder()
.build_call(*head_fn, &[seq_ptr.into()], "viewl_head")
.map_err(|e| CodegenError::Internal(format!("viewl_head: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewl_head: void".to_string()))?;
let tail_fn = self
.functions
.get(&VarId::new(1000820))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewl_tail not declared".to_string()))?;
let tail = self
.builder()
.build_call(*tail_fn, &[seq_ptr.into()], "viewl_tail")
.map_err(|e| CodegenError::Internal(format!("viewl_tail: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewl_tail: void".to_string()))?;
let cons_l = self.alloc_adt(1, 2)?;
self.store_adt_field(cons_l, 2, 0, head)?;
self.store_adt_field(cons_l, 2, 1, tail)?;
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("viewl nonempty br: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_bb);
let phi = self
.builder()
.build_phi(tm.ptr_type(), "viewl_result")
.map_err(|e| CodegenError::Internal(format!("viewl phi: {:?}", e)))?;
let empty_l_bv: inkwell::values::BasicValueEnum = empty_l.into();
let cons_l_bv: inkwell::values::BasicValueEnum = cons_l.into();
phi.add_incoming(&[(&empty_l_bv, empty_bb), (&cons_l_bv, nonempty_bb)]);
Ok(Some(phi.as_basic_value()))
}
fn lower_builtin_seq_viewr(
&mut self,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_viewr: no seq".to_string()))?;
let seq_ptr = self.value_to_ptr(seq)?;
let tm = self.type_mapper();
let tag_fn = self
.functions
.get(&VarId::new(1000821))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewr_tag not declared".to_string()))?;
let tag = self
.builder()
.build_call(*tag_fn, &[seq_ptr.into()], "viewr_tag")
.map_err(|e| CodegenError::Internal(format!("viewr_tag: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewr_tag: void".to_string()))?
.into_int_value();
let is_empty = self
.builder()
.build_int_compare(
inkwell::IntPredicate::EQ,
tag,
tm.i64_type().const_zero(),
"viewr_is_empty",
)
.map_err(|e| CodegenError::Internal(format!("viewr cmp: {:?}", e)))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("seq_viewr: no current function".to_string()))?;
let empty_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewr_empty");
let nonempty_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewr_nonempty");
let merge_bb = self
.llvm_context()
.append_basic_block(current_fn, "viewr_merge");
self.builder()
.build_conditional_branch(is_empty, empty_bb, nonempty_bb)
.map_err(|e| CodegenError::Internal(format!("viewr cbr: {:?}", e)))?;
// EmptyR: tag 0, 0 fields
self.builder().position_at_end(empty_bb);
let empty_r = self.alloc_adt(0, 0)?;
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("viewr empty br: {:?}", e)))?;
// (:>): tag 1, 2 fields (init_seq, last)
self.builder().position_at_end(nonempty_bb);
let init_fn = self
.functions
.get(&VarId::new(1000823))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewr_init not declared".to_string()))?;
let init_val = self
.builder()
.build_call(*init_fn, &[seq_ptr.into()], "viewr_init")
.map_err(|e| CodegenError::Internal(format!("viewr_init: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewr_init: void".to_string()))?;
let last_fn = self
.functions
.get(&VarId::new(1000822))
.ok_or_else(|| CodegenError::Internal("bhc_seq_viewr_last not declared".to_string()))?;
let last_val = self
.builder()
.build_call(*last_fn, &[seq_ptr.into()], "viewr_last")
.map_err(|e| CodegenError::Internal(format!("viewr_last: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("viewr_last: void".to_string()))?;
let snoc_r = self.alloc_adt(1, 2)?;
self.store_adt_field(snoc_r, 2, 0, init_val)?;
self.store_adt_field(snoc_r, 2, 1, last_val)?;
self.builder()
.build_unconditional_branch(merge_bb)
.map_err(|e| CodegenError::Internal(format!("viewr nonempty br: {:?}", e)))?;
// Merge
self.builder().position_at_end(merge_bb);
let phi = self
.builder()
.build_phi(tm.ptr_type(), "viewr_result")
.map_err(|e| CodegenError::Internal(format!("viewr phi: {:?}", e)))?;
let empty_r_bv: inkwell::values::BasicValueEnum = empty_r.into();
let snoc_r_bv: inkwell::values::BasicValueEnum = snoc_r.into();
phi.add_incoming(&[(&empty_r_bv, empty_bb), (&snoc_r_bv, nonempty_bb)]);
Ok(Some(phi.as_basic_value()))
}
/// Seq.filter: walk seq, apply predicate closure, build new seq
fn lower_builtin_seq_filter(
&mut self,
pred_expr: &Expr,
seq_expr: &Expr,
) -> CodegenResult<Option<BasicValueEnum<'ctx>>> {
let pred = self
.lower_expr(pred_expr)?
.ok_or_else(|| CodegenError::Internal("seq_filter: no pred".to_string()))?;
let seq = self
.lower_expr(seq_expr)?
.ok_or_else(|| CodegenError::Internal("seq_filter: no seq".to_string()))?;
let pred_ptr = self.value_to_ptr(pred)?;
let seq_ptr = self.value_to_ptr(seq)?;
let tm = self.type_mapper();
// Get count
let count_fn = self
.functions
.get(&VarId::new(1000815))
.ok_or_else(|| CodegenError::Internal("bhc_seq_elem_count not declared".to_string()))?;
let count = self
.builder()
.build_call(*count_fn, &[seq_ptr.into()], "sf_count")
.map_err(|e| CodegenError::Internal(format!("sf count: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sf count: void".to_string()))?
.into_int_value();
// Start with empty seq
let empty_fn = self
.functions
.get(&VarId::new(1000800))
.ok_or_else(|| CodegenError::Internal("bhc_seq_empty not declared".to_string()))?;
let empty_seq = self
.builder()
.build_call(*empty_fn, &[], "sf_empty")
.map_err(|e| CodegenError::Internal(format!("sf empty: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sf empty: void".to_string()))?;
let snoc_fn = self
.functions
.get(&VarId::new(1000807))
.ok_or_else(|| CodegenError::Internal("bhc_seq_snoc not declared".to_string()))?;
let elem_fn = self
.functions
.get(&VarId::new(1000816))
.ok_or_else(|| CodegenError::Internal("bhc_seq_elem_at not declared".to_string()))?;
let current_fn = self
.builder()
.get_insert_block()
.and_then(|b| b.get_parent())
.ok_or_else(|| CodegenError::Internal("seq_filter: no current function".to_string()))?;
let loop_header = self
.llvm_context()
.append_basic_block(current_fn, "sf_header");
let loop_body = self
.llvm_context()
.append_basic_block(current_fn, "sf_body");
let loop_keep = self
.llvm_context()
.append_basic_block(current_fn, "sf_keep");
let loop_next = self
.llvm_context()
.append_basic_block(current_fn, "sf_next");
let loop_exit = self
.llvm_context()
.append_basic_block(current_fn, "sf_exit");
let entry_block = self.builder().get_insert_block().unwrap();
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("sf br: {:?}", e)))?;
// Header
self.builder().position_at_end(loop_header);
let acc_phi = self
.builder()
.build_phi(tm.ptr_type(), "sf_acc")
.map_err(|e| CodegenError::Internal(format!("sf acc phi: {:?}", e)))?;
let idx_phi = self
.builder()
.build_phi(tm.i64_type(), "sf_idx")
.map_err(|e| CodegenError::Internal(format!("sf idx phi: {:?}", e)))?;
let idx = idx_phi.as_basic_value().into_int_value();
let done = self
.builder()
.build_int_compare(inkwell::IntPredicate::SGE, idx, count, "sf_done")
.map_err(|e| CodegenError::Internal(format!("sf cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(done, loop_exit, loop_body)
.map_err(|e| CodegenError::Internal(format!("sf cbr: {:?}", e)))?;
// Body: get element, call predicate
self.builder().position_at_end(loop_body);
let elem = self
.builder()
.build_call(*elem_fn, &[seq_ptr.into(), idx.into()], "sf_elem")
.map_err(|e| CodegenError::Internal(format!("sf elem: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sf elem: void".to_string()))?;
// Call predicate closure: fn_ptr(closure_ptr, elem) -> Bool ADT
let fn_ptr_ptr = self.extract_adt_field(pred_ptr, 2, 1)?;
let fn_ty = tm
.ptr_type()
.fn_type(&[tm.ptr_type().into(), tm.ptr_type().into()], false);
let fn_ptr = fn_ptr_ptr;
let pred_result = self
.builder()
.build_indirect_call(fn_ty, fn_ptr, &[pred_ptr.into(), elem.into()], "sf_pred")
.map_err(|e| CodegenError::Internal(format!("sf pred call: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sf pred: void".to_string()))?;
// Check Bool ADT tag
let bool_tag = self.extract_adt_tag(pred_result.into_pointer_value())?;
let is_true = self
.builder()
.build_int_compare(
inkwell::IntPredicate::NE,
bool_tag,
tm.i64_type().const_zero(),
"sf_is_true",
)
.map_err(|e| CodegenError::Internal(format!("sf bool cmp: {:?}", e)))?;
self.builder()
.build_conditional_branch(is_true, loop_keep, loop_next)
.map_err(|e| CodegenError::Internal(format!("sf pred cbr: {:?}", e)))?;
// Keep: snoc element
self.builder().position_at_end(loop_keep);
let acc_val = acc_phi.as_basic_value().into_pointer_value();
let new_acc = self
.builder()
.build_call(*snoc_fn, &[acc_val.into(), elem.into()], "sf_snoc")
.map_err(|e| CodegenError::Internal(format!("sf snoc: {:?}", e)))?
.try_as_basic_value()
.basic()
.ok_or_else(|| CodegenError::Internal("sf snoc: void".to_string()))?;
self.builder()
.build_unconditional_branch(loop_next)
.map_err(|e| CodegenError::Internal(format!("sf keep br: {:?}", e)))?;
// Next: increment index
self.builder().position_at_end(loop_next);
let acc_next = self
.builder()
.build_phi(tm.ptr_type(), "sf_acc_next")
.map_err(|e| CodegenError::Internal(format!("sf acc_next phi: {:?}", e)))?;
acc_next.add_incoming(&[
(&acc_phi.as_basic_value(), loop_body),
(&new_acc, loop_keep),
]);
let one = tm.i64_type().const_int(1, false);
let new_idx = self
.builder()
.build_int_add(idx, one, "sf_new_idx")
.map_err(|e| CodegenError::Internal(format!("sf new_idx: {:?}", e)))?;
self.builder()
.build_unconditional_branch(loop_header)
.map_err(|e| CodegenError::Internal(format!("sf next br: {:?}", e)))?;
// Phi incoming
acc_phi.add_incoming(&[
(&empty_seq, entry_block),
(&acc_next.as_basic_value(), loop_next),
]);
let zero_bv: inkwell::values::BasicValueEnum = tm.i64_type().const_zero().into();
let new_idx_bv: inkwell::values::BasicValueEnum = new_idx.into();
idx_phi.add_incoming(&[(&zero_bv, entry_block), (&new_idx_bv, loop_next)]);
self.builder().position_at_end(loop_exit);
Ok(Some(acc_phi.as_basic_value()))
}
// Helper accessors
fn llvm_context(&self) -> &'ctx Context {
self.llvm_ctx
}
fn builder(&self) -> &Builder<'ctx> {
self.module.builder()
}
fn type_mapper(&self) -> &TypeMapper<'ctx> {
self.module.type_mapper()
}
}
/// Lower a Core module to an LLVM module.
///
/// The module reference `'m` can be shorter than the context lifetime `'ctx`.
pub fn lower_core_module<'ctx>(
ctx: &'ctx LlvmContext,
module: &LlvmModule<'ctx>,
core_module: &CoreModule,
) -> CodegenResult<()> {
let mut lowering = Lowering::new(ctx, module);
lowering.lower_module(core_module)
}
/// Lower a Core module to an LLVM module with multi-module support.
///
/// This variant accepts a module name for symbol mangling and a list of imported
/// symbols from already-compiled modules. Functions are declared with
/// module-qualified names (e.g., `Helper.double`) and extern declarations are
/// added for cross-module references.
pub fn lower_core_module_multimodule<'ctx>(
ctx: &'ctx LlvmContext,
module: &LlvmModule<'ctx>,
core_module: &CoreModule,
module_name: &str,
imported_symbols: &[CompiledSymbol],
) -> CodegenResult<()> {
lower_core_module_multimodule_with_constructors(
ctx,
module,
core_module,
module_name,
imported_symbols,
&[],
)
}
/// Lower a Core module to LLVM IR in multi-module mode, with imported constructor metadata.
pub fn lower_core_module_multimodule_with_constructors<'ctx>(
ctx: &'ctx LlvmContext,
module: &LlvmModule<'ctx>,
core_module: &CoreModule,
module_name: &str,
imported_symbols: &[CompiledSymbol],
imported_constructors: &[(String, ConstructorMeta)],
) -> CodegenResult<()> {
let mut lowering = Lowering::new_multimodule(ctx, module, module_name, imported_symbols)?;
// Register imported constructor metadata so the codegen knows about
// constructors from other modules (e.g., Types.hs constructors used in Parser.hs).
for (name, meta) in imported_constructors {
lowering.register_constructor(name, meta.tag, meta.arity);
if meta.is_newtype {
if let Some(m) = lowering.constructor_metadata.get_mut(name.as_str()) {
m.is_newtype = true;
}
}
}
lowering.lower_module(core_module)
}
#[cfg(test)]
mod tests {
// Tests would go here
}