use inkwell::values::PointerValue;
use crate::ast::{ExpressionNode, FunctionNode};
use crate::semantics::Type;
use crate::semantics::const_fold::{self, ConstValue};
use super::CodeGenerator;
impl<'a> CodeGenerator<'a> {
pub(super) fn emit_where_checks(
&mut self,
predicates: &[ExpressionNode],
block_prefix: &str,
) -> Result<(), String> {
for (i, predicate) in predicates.iter().enumerate() {
if const_fold::fold(predicate) == Some(ConstValue::Bool(true)) {
continue;
}
let value = self.generate_expression(predicate)?;
let cond = self.get_raw_bool_value(value)?;
let current_function = self
.builder
.get_insert_block()
.expect("Builder should have an insertion block")
.get_parent()
.ok_or("No current function")?;
let prefix = format!("{}_{}", block_prefix, i);
let fail_bb = self
.context
.append_basic_block(current_function, &format!("{}_fail", prefix));
let ok_bb = self
.context
.append_basic_block(current_function, &format!("{}_ok", prefix));
self.builder
.build_conditional_branch(cond, ok_bb, fail_bb)
.map_err(|e| e.to_string())?;
self.builder.position_at_end(fail_bb);
self.emit_runtime_fatal("where constraint violated", Some(&predicate.span), &prefix)?;
self.builder.position_at_end(ok_bb);
}
Ok(())
}
pub(super) fn emit_function_preconditions(
&mut self,
func: &FunctionNode,
) -> Result<(), String> {
if let Some(clause) = &func.where_clause {
self.emit_where_checks(&clause.predicates, "where_pre")?;
}
let Some((class_name, method_name)) = func.name.split_once('.') else {
return Ok(());
};
let inherited = self
.analyzer
.inherited_preconditions(class_name, method_name)
.to_vec();
if inherited.is_empty() {
return Ok(());
}
let snapshot = self.variables.clone();
for (set_index, precondition) in inherited.iter().enumerate() {
self.variables = snapshot.clone();
for (interface_name, param) in precondition
.interface_param_names
.iter()
.zip(func.params.iter())
{
if interface_name != ¶m.name
&& let Some(entry) = snapshot.get(¶m.name)
{
self.variables.insert(interface_name.clone(), entry.clone());
}
}
self.emit_where_checks(
&precondition.predicates,
&format!("where_iface_{}", set_index),
)?;
}
self.variables = snapshot;
Ok(())
}
pub(super) fn emit_construction_invariants(
&mut self,
class_name: &str,
struct_ptr: PointerValue<'a>,
) -> Result<(), String> {
let invariants: Vec<ExpressionNode> = self
.analyzer
.class_invariants(class_name)
.iter()
.map(|inv| inv.predicate.clone())
.collect();
if invariants.is_empty() {
return Ok(());
}
let snapshot = self.variables.clone();
self.bind_class_fields(class_name, struct_ptr)?;
self.emit_where_checks(&invariants, "where_new")?;
self.variables = snapshot;
Ok(())
}
pub(super) fn emit_field_assignment_invariants(
&mut self,
class_name: &str,
assigned_field: &str,
struct_ptr: PointerValue<'a>,
block_prefix: &str,
) -> Result<(), String> {
let invariants: Vec<ExpressionNode> = self
.analyzer
.class_invariants(class_name)
.iter()
.filter(|inv| inv.referenced_fields.iter().any(|f| f == assigned_field))
.map(|inv| inv.predicate.clone())
.collect();
if invariants.is_empty() {
return Ok(());
}
let snapshot = self.variables.clone();
self.bind_class_fields(class_name, struct_ptr)?;
self.emit_where_checks(&invariants, block_prefix)?;
self.variables = snapshot;
Ok(())
}
fn bind_class_fields(
&mut self,
class_name: &str,
struct_ptr: PointerValue<'a>,
) -> Result<(), String> {
let struct_type = *self
.type_map
.get(class_name)
.ok_or_else(|| format!("Class {} not found in type map", class_name))?;
let field_indices = self
.field_map
.get(class_name)
.ok_or_else(|| format!("Class {} not found in field map", class_name))?
.clone();
let field_llvm_types = self
.field_types_map
.get(class_name)
.ok_or_else(|| format!("Class {} not found in field types map", class_name))?
.clone();
let field_semantic_types: Vec<(String, Type)> = {
let class_symbol = self
.analyzer
.symbol_table()
.lookup(class_name)
.ok_or_else(|| format!("Class {} not found in symbol table", class_name))?;
class_symbol
.fields
.iter()
.map(|(name, (ty, _))| (name.clone(), ty.clone()))
.collect()
};
for (field_name, semantic_type) in field_semantic_types {
let Some(&index) = field_indices.get(&field_name) else {
continue;
};
let field_ptr = self
.builder
.build_struct_gep(
struct_type.into_struct_type(),
struct_ptr,
index as u32,
&format!("where_field_{}", field_name),
)
.map_err(|e| e.to_string())?;
let llvm_type = *field_llvm_types
.get(index)
.ok_or_else(|| format!("Field {} index out of range", field_name))?;
self.variables
.insert(field_name, (field_ptr, llvm_type, semantic_type));
}
Ok(())
}
}