mod bindings;
mod inline_value;
mod rebuild;
mod scan;
use std::collections::BTreeMap;
use crate::hir::common::{HirAssign, HirExpr, HirLValue, HirProto, HirStmt, LocalId, TempId};
use self::bindings::{
collect_materialized_binding_counts, BindingIndex, StmtBindingSummary,
collect_stmt_binding_summary,
};
use self::scan::{
constructor_seed, install_constructor_seed, trailing_constructor_handoff,
try_rebuild_constructor_region,
};
use super::walk::{HirRewritePass, rewrite_proto};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
enum TableBinding {
Temp(TempId),
Local(LocalId),
}
type BindingId = usize;
#[derive(Debug, Clone, Copy)]
enum RegionStep {
Producer {
stmt_index: usize,
slot_index: usize,
},
ProducerGroup {
stmt_index: usize,
},
Record {
stmt_index: usize,
},
SetList {
stmt_index: usize,
},
}
#[derive(Debug, Clone)]
struct PendingProducer {
binding: TableBinding,
binding_id: BindingId,
source: PendingProducerSource,
group: Option<usize>,
}
#[derive(Debug, Clone)]
enum PendingProducerSource {
Value {
stmt_index: usize,
value_index: usize,
},
Empty,
}
#[derive(Debug, Clone, Copy)]
struct ProducerGroupMeta {
drop_without_consumption_is_safe: bool,
}
#[derive(Debug, Clone, Copy)]
enum SegmentToken {
Producer { producer_index: usize },
Record { prepared_record_index: usize },
}
#[derive(Debug, Clone)]
struct RestoredPendingIntegerField {
field_index: usize,
key: i64,
value: HirExpr,
}
#[derive(Debug, Clone, Default)]
struct RebuildScratch {
pending_producers: Vec<PendingProducer>,
producer_groups: Vec<ProducerGroupMeta>,
tokens: Vec<SegmentToken>,
prepared_records: Vec<crate::hir::common::HirRecordField>,
producer_index_by_binding: Vec<Option<usize>>,
consumed_bindings: Vec<bool>,
consumed_groups: Vec<bool>,
removed_materializations: Vec<u32>,
touched_binding_ids: Vec<BindingId>,
restored_pending_integer_fields: Vec<RestoredPendingIntegerField>,
}
pub(super) fn stabilize_table_constructors_in_proto(proto: &mut HirProto) -> bool {
let materialized_bindings = collect_materialized_binding_counts(&proto.body);
let mut pass = TableConstructorPass {
materialized_bindings,
};
rewrite_proto(proto, &mut pass)
}
struct TableConstructorPass {
materialized_bindings: BTreeMap<TableBinding, usize>,
}
impl HirRewritePass for TableConstructorPass {
fn rewrite_block(&mut self, block: &mut crate::hir::common::HirBlock) -> bool {
let mut changed = false;
let mut scratch = RebuildScratch::default();
let mut binding_index = BindingIndex::default();
let mut stmt_bindings: Vec<StmtBindingSummary> = block
.stmts
.iter()
.map(|stmt| collect_stmt_binding_summary(stmt, &mut binding_index))
.collect();
let materialized_binding_counts =
binding_index.materialized_counts(&self.materialized_bindings);
let mut index = 0;
while index < block.stmts.len() {
let Some((binding, seed_ctor)) = constructor_seed(&block.stmts[index]) else {
index += 1;
continue;
};
let (constructor, end_index, rebuilt_region, retained_stmts) =
match try_rebuild_constructor_region(
block,
index,
binding,
seed_ctor.clone(),
&binding_index,
&materialized_binding_counts,
&stmt_bindings,
&mut scratch,
) {
Some((rebuilt_ctor, end_index, retained)) => {
(rebuilt_ctor, end_index, true, retained)
}
None => (seed_ctor, index, false, Vec::new()),
};
let handoff_target =
trailing_constructor_handoff(&block.stmts[(end_index + 1)..], binding);
if !rebuilt_region && handoff_target.is_none() {
index += 1;
continue;
}
let consumed_handoff = handoff_target.is_some();
install_constructor_owner(&mut block.stmts[index], handoff_target, constructor);
let drain_end = end_index + usize::from(consumed_handoff);
if drain_end > index {
if retained_stmts.is_empty() {
block.stmts.drain(index + 1..=drain_end);
stmt_bindings.drain(index + 1..=drain_end);
} else {
for i in (index + 1..=drain_end).rev() {
if !retained_stmts.contains(&i) {
block.stmts.remove(i);
stmt_bindings.remove(i);
}
}
}
}
changed = true;
index += 1;
}
changed
}
}
fn install_constructor_owner(
stmt: &mut HirStmt,
target: Option<HirLValue>,
constructor: crate::hir::common::HirTableConstructor,
) {
if let Some(target) = target {
*stmt = HirStmt::Assign(Box::new(HirAssign {
targets: vec![target],
values: vec![HirExpr::TableConstructor(Box::new(constructor))],
}));
return;
}
install_constructor_seed(stmt, constructor);
}
#[cfg(test)]
mod tests;