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.
This commit is contained in:
@@ -2,7 +2,8 @@ use crate::ast::nodes::{
|
||||
Address, AnalyzedNode, AssignBinding, GlobalAnalyzedRegistry,
|
||||
IdentifierBinding, LambdaBinding, Node, NodeKind, NodeMetrics, UpvalueIdx, VirtualId,
|
||||
};
|
||||
use crate::ast::types::{Purity, Signature, StaticType, Value};
|
||||
use crate::ast::types::{Purity, RecordLayout, Signature, StaticType, Value};
|
||||
use std::sync::Arc;
|
||||
use crate::ast::vm::GlobalStore;
|
||||
|
||||
use std::cell::RefCell;
|
||||
@@ -39,6 +40,23 @@ fn compute_tuple_type(elements: &[Rc<AnalyzedNode>]) -> StaticType {
|
||||
}
|
||||
}
|
||||
|
||||
/// Recompute the `StaticType` for a Record node from its children's concrete types.
|
||||
/// Mirrors the type-computation logic in `TypeChecker::check_node` for `NodeKind::Record`.
|
||||
fn compute_record_type(
|
||||
layout: &Arc<RecordLayout>,
|
||||
fields: &[(Rc<AnalyzedNode>, Rc<AnalyzedNode>)],
|
||||
) -> StaticType {
|
||||
let new_fields: Vec<_> = layout
|
||||
.fields
|
||||
.iter()
|
||||
.zip(fields.iter())
|
||||
.map(|((keyword, _old_ty), (_key_node, val_node))| {
|
||||
(*keyword, val_node.ty.original.ty.clone())
|
||||
})
|
||||
.collect();
|
||||
StaticType::Record(RecordLayout::get_or_create(new_fields))
|
||||
}
|
||||
|
||||
/// Return `NodeMetrics` with an updated `StaticType` if it differs from the
|
||||
/// original, otherwise clone cheaply. Avoids unnecessary `Rc` allocations.
|
||||
fn refreshed_metrics(old: &NodeMetrics, new_ty: StaticType) -> NodeMetrics {
|
||||
@@ -745,12 +763,13 @@ impl Optimizer {
|
||||
return node_rc;
|
||||
}
|
||||
|
||||
let metrics = refreshed_metrics(&node.ty, compute_record_type(layout, &mapped_fields));
|
||||
(
|
||||
NodeKind::Record {
|
||||
fields: mapped_fields,
|
||||
layout: layout.clone(),
|
||||
},
|
||||
node.ty.clone(),
|
||||
metrics,
|
||||
)
|
||||
}
|
||||
NodeKind::Expansion {
|
||||
|
||||
@@ -69,7 +69,16 @@ impl<'a> Folder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let record_val = Value::Record(layout.clone(), Rc::new(constant_values));
|
||||
// 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))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user