Files
RustAst/src/ast/compiler/optimizer/folder.rs
T
Brummel 1f3fb40bcc Update record type computation
This commit refactors the way record types are computed within the
optimizer. Previously, the optimizer would often reuse the existing
`NodeMetrics` for records, which could lead to stale or polymorphic
types.

The changes introduce a new function, `compute_record_type`, which
explicitly recalculates the `StaticType` for a `Record` node based on
the concrete types of its child fields. This ensures that after
optimizations like inlining and folding, record types are as concrete as
possible, eliminating unresolved `TypeVars`.

Additionally, the `folder.rs` module is updated to recompute the
`RecordLayout` when creating constant record values, further improving
type accuracy. Two new tests are added to verify that inlined records,
including those with multiple fields, correctly result in concrete
types.
2026-03-29 18:59:38 +02:00

117 lines
3.7 KiB
Rust

use crate::ast::nodes::{
Address, AnalyzedNode, IdentifierBinding, Node, NodeKind, NodeMetrics,
};
use crate::ast::types::{Purity, RecordLayout, StaticType, Value};
use crate::ast::vm::GlobalStore;
use std::rc::Rc;
pub struct Folder<'a> {
pub globals: &'a Option<GlobalStore>,
}
impl<'a> Folder<'a> {
pub fn new(globals: &'a Option<GlobalStore>) -> Self {
Self { globals }
}
pub fn make_constant_node(&self, val: Value, template: &AnalyzedNode) -> AnalyzedNode {
let ty = val.static_type();
let typed_original = Rc::new(Node {
identity: template.identity.clone(),
kind: NodeKind::Constant(val.clone()),
comments: template.comments.clone(),
ty: ty.clone(),
});
Node {
identity: template.identity.clone(),
kind: NodeKind::Constant(val),
comments: template.comments.clone(),
ty: NodeMetrics {
original: typed_original,
purity: Purity::Pure,
is_recursive: false,
},
}
}
pub fn make_nop_node(&self, template: &AnalyzedNode) -> AnalyzedNode {
let typed_original = Rc::new(Node {
identity: template.identity.clone(),
kind: NodeKind::Nop,
comments: template.comments.clone(),
ty: StaticType::Void,
});
Node {
identity: template.identity.clone(),
kind: NodeKind::Nop,
comments: template.comments.clone(),
ty: NodeMetrics {
original: typed_original,
purity: Purity::Pure,
is_recursive: false,
},
}
}
pub fn try_fold_record(
&self,
layout: &std::sync::Arc<RecordLayout>,
values: &[Rc<AnalyzedNode>],
template: &AnalyzedNode,
) -> Option<AnalyzedNode> {
let mut constant_values = Vec::with_capacity(values.len());
for v_node in values {
if let NodeKind::Constant(val) = &v_node.kind {
constant_values.push(val.clone());
} else {
return None;
}
}
// Recompute layout from concrete value types to eliminate stale TypeVars.
let new_fields: Vec<_> = layout
.fields
.iter()
.zip(values.iter())
.map(|((keyword, _), v_node)| (*keyword, v_node.ty.original.ty.clone()))
.collect();
let new_layout = RecordLayout::get_or_create(new_fields);
let record_val = Value::Record(new_layout, Rc::new(constant_values));
Some(self.make_constant_node(record_val, template))
}
pub fn try_fold_pure(
&self,
callee: &AnalyzedNode,
arg_nodes: &[Rc<AnalyzedNode>],
) -> Option<AnalyzedNode> {
if callee.ty.purity < Purity::Pure {
return None;
}
let mut arg_values = Vec::with_capacity(arg_nodes.len());
for node in arg_nodes {
if let NodeKind::Constant(val) = &node.kind {
arg_values.push(val.clone());
} else {
return None;
}
}
let func_val = match &callee.kind {
NodeKind::Identifier {
binding: IdentifierBinding::Reference(Address::Global(idx)),
..
} => self.globals.as_ref()?.get(idx.0 as usize)?,
NodeKind::Constant(val) => val.clone(),
_ => return None,
};
let result = match func_val {
Value::Function(f) => (f.func)(&arg_values),
_ => return None,
};
Some(self.make_constant_node(result, callee))
}
}