1use std::{
12 fmt::Debug,
13 hash::Hash,
14 iter, mem,
15 ops::{Index, IndexMut},
16 sync::{Arc, Mutex},
17};
18
19use crate::core_relations::{
20 BaseValue, BaseValueId, BaseValues, ColumnId, Constraint, ContainerValue, ContainerValues,
21 CounterId, Database, DisplacedTable, ExecutionState, ExternalFunction, ExternalFunctionId,
22 MergeVal, Offset, PlanStrategy, SortedWritesTable, TableId, TaggedRowBuffer, Value,
23 WrappedTable,
24};
25use crate::numeric_id::{DenseIdMap, DenseIdMapWithReuse, NumericId, define_id};
26use egglog_concurrency::ThreadPool;
27use egglog_core_relations as core_relations;
28use egglog_numeric_id as numeric_id;
29use egglog_reports::{IterationReport, ReportLevel, RuleSetReport};
30use hashbrown::HashMap;
31use indexmap::IndexSet;
32use log::info;
33use once_cell::sync::Lazy;
34use smallvec::SmallVec;
35use web_time::{Duration, Instant};
36
37pub mod macros;
38pub(crate) mod rule;
39#[cfg(test)]
40mod tests;
41
42pub use rule::{Function, QueryEntry, RuleBuilder};
43use thiserror::Error;
44
45#[derive(Clone)]
54pub struct ActionRegistry {
55 table_actions: hashbrown::HashMap<String, TableAction>,
56 union_action: UnionAction,
57 default_panic_id: ExternalFunctionId,
58}
59
60impl ActionRegistry {
61 pub(crate) fn new(union_action: UnionAction, default_panic_id: ExternalFunctionId) -> Self {
62 Self {
63 table_actions: hashbrown::HashMap::new(),
64 union_action,
65 default_panic_id,
66 }
67 }
68
69 pub(crate) fn register_table(&mut self, name: String, action: TableAction) {
70 self.table_actions.insert(name, action);
71 }
72
73 pub fn lookup_table(&self, name: &str) -> Option<&TableAction> {
76 self.table_actions.get(name)
77 }
78
79 pub fn table_sizes(&self, state: &ExecutionState) -> Vec<(&str, usize)> {
81 self.table_actions
82 .iter()
83 .map(|(name, action)| (name.as_str(), action.row_count(state)))
84 .collect()
85 }
86
87 pub fn union_action(&self) -> &UnionAction {
89 &self.union_action
90 }
91
92 pub fn default_panic_id(&self) -> ExternalFunctionId {
95 self.default_panic_id
96 }
97}
98
99#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
100pub enum ColumnTy {
101 Id,
102 Base(BaseValueId),
103}
104
105define_id!(pub RuleId, u32, "An egglog-style rule");
106define_id!(pub FunctionId, u32, "An id representing an egglog function");
107define_id!(pub(crate) Timestamp, u32, "An abstract timestamp used to track execution of egglog rules");
108impl Timestamp {
109 fn to_value(self) -> Value {
110 Value::new(self.rep())
111 }
112}
113
114#[derive(Clone)]
116pub struct EGraph {
117 db: Database,
118 uf_table: TableId,
119 id_counter: CounterId,
120 timestamp_counter: CounterId,
121 rules: DenseIdMapWithReuse<RuleId, RuleInfo>,
122 funcs: DenseIdMap<FunctionId, FunctionInfo>,
123 panic_message: SideChannel<String>,
124 panic_funcs: HashMap<String, ExternalFunctionId>,
130 report_level: ReportLevel,
131 action_registry: Arc<std::sync::RwLock<ActionRegistry>>,
137 threads: usize,
138 thread_pool: Option<Arc<ThreadPool>>,
139}
140
141pub type Result<T> = std::result::Result<T, anyhow::Error>;
142
143fn normalize_thread_count(threads: usize) -> usize {
144 #[cfg(target_family = "wasm")]
145 {
146 if threads > 1 {
147 panic!("cannot use more than 1 thread on wasm");
148 }
149 1
150 }
151 #[cfg(not(target_family = "wasm"))]
152 {
153 if threads == 0 {
154 std::thread::available_parallelism()
155 .map(usize::from)
156 .unwrap_or(1)
157 } else {
158 threads
159 }
160 }
161}
162
163fn install_thread_pool<R>(thread_pool: Option<Arc<ThreadPool>>, f: impl FnOnce() -> R) -> R {
164 match thread_pool {
165 Some(thread_pool) => thread_pool.install(f),
166 None => egglog_concurrency::without_current_pool(f),
167 }
168}
169
170impl Default for EGraph {
171 fn default() -> Self {
172 Self::new(1)
173 }
174}
175
176pub struct FunctionConfig {
178 pub schema: Vec<ColumnTy>,
180 pub default: DefaultVal,
182 pub merge: MergeFn,
184 pub name: String,
186 pub can_subsume: bool,
188}
189
190impl EGraph {
191 pub fn new(threads: usize) -> Self {
196 let threads = normalize_thread_count(threads);
197 let thread_pool = (threads > 1).then(|| Arc::new(ThreadPool::new(threads)));
198 let (mut db, uf_table, id_counter, ts_counter) =
199 install_thread_pool(thread_pool.clone(), || {
200 let mut db = Database::new();
201 let uf_table = db.add_table_named(
202 DisplacedTable::default(),
203 "$uf".into(),
204 iter::empty(),
205 iter::empty(),
206 );
207 let id_counter = db.add_counter();
208 let ts_counter = db.add_counter();
209 db.inc_counter(ts_counter);
211 (db, uf_table, id_counter, ts_counter)
212 });
213
214 let panic_message: SideChannel<String> = Default::default();
219 let mut panic_funcs: HashMap<String, ExternalFunctionId> = Default::default();
220 let default_panic_msg = "primitive panicked".to_string();
221 let default_panic_id = db.add_external_function(Box::new(Panic(
222 default_panic_msg.clone(),
223 panic_message.clone(),
224 )));
225 panic_funcs.insert(default_panic_msg, default_panic_id);
226
227 let union_action = UnionAction {
228 table: uf_table,
229 timestamp: ts_counter,
230 };
231 let action_registry = Arc::new(std::sync::RwLock::new(ActionRegistry::new(
232 union_action,
233 default_panic_id,
234 )));
235
236 Self {
237 db,
238 uf_table,
239 id_counter,
240 timestamp_counter: ts_counter,
241 rules: Default::default(),
242 funcs: Default::default(),
243 panic_message,
244 panic_funcs,
245 report_level: Default::default(),
246 action_registry,
247 threads,
248 thread_pool,
249 }
250 }
251
252 pub fn with_num_threads(mut self, threads: usize) -> Self {
254 self.set_num_threads(threads);
255 self
256 }
257
258 pub fn set_num_threads(&mut self, threads: usize) {
262 let threads = normalize_thread_count(threads);
263 self.threads = threads;
264 self.thread_pool = (threads > 1).then(|| Arc::new(ThreadPool::new(threads)));
265 }
266
267 pub fn num_threads(&self) -> usize {
269 self.threads
270 }
271
272 fn thread_pool(&self) -> Option<Arc<ThreadPool>> {
273 self.thread_pool.clone()
274 }
275
276 fn next_ts(&self) -> Timestamp {
277 Timestamp::from_usize(self.db.read_counter(self.timestamp_counter))
278 }
279
280 fn inc_ts(&mut self) {
281 self.db.inc_counter(self.timestamp_counter);
282 }
283
284 pub fn base_values_mut(&mut self) -> &mut BaseValues {
287 self.db.base_values_mut()
288 }
289
290 pub fn container_values_mut(&mut self) -> &mut ContainerValues {
293 self.db.container_values_mut()
294 }
295
296 pub fn container_values(&self) -> &ContainerValues {
298 self.db.container_values()
299 }
300
301 pub fn get_container_value<C: ContainerValue>(&mut self, val: C) -> Value {
303 self.register_container_ty::<C>();
304 self.db
305 .with_execution_state(|state| state.clone().container_values().register_val(val, state))
306 }
307
308 pub fn register_container_ty<C: ContainerValue>(&mut self) {
313 let uf_table = self.uf_table;
314 let ts_counter = self.timestamp_counter;
315 self.db.container_values_mut().register_type::<C>(
316 self.id_counter,
317 move |state, old, new| {
318 if old != new {
319 let next_ts = Value::from_usize(state.read_counter(ts_counter));
320 state.stage_insert(uf_table, &[old, new, next_ts]);
321 std::cmp::min(old, new)
322 } else {
323 old
324 }
325 },
326 );
327 }
328
329 pub fn base_values(&self) -> &BaseValues {
331 self.db.base_values()
332 }
333
334 pub fn base_value_constant<T>(&self, x: T) -> QueryEntry
336 where
337 T: BaseValue,
338 {
339 QueryEntry::Const {
340 val: self.base_values().get(x),
341 ty: ColumnTy::Base(self.base_values().get_ty::<T>()),
342 }
343 }
344
345 pub fn register_external_func(
357 &mut self,
358 func: Box<dyn ExternalFunction + 'static>,
359 ) -> ExternalFunctionId {
360 self.db.add_external_function(func)
361 }
362
363 pub fn free_external_func(&mut self, func: ExternalFunctionId) {
364 self.db.free_external_function(func)
365 }
366
367 pub fn fresh_id(&mut self) -> Value {
369 Value::from_usize(self.db.inc_counter(self.id_counter))
370 }
371
372 fn get_canon_in_uf(&self, val: Value) -> Value {
376 let table = self.db.get_table(self.uf_table);
377 let row = table.get_row(&[val]);
378 row.map(|row| row.vals[1]).unwrap_or(val)
379 }
380
381 pub fn get_canon_repr(&self, val: Value, ty: ColumnTy) -> Value {
386 match ty {
387 ColumnTy::Id => self.get_canon_in_uf(val),
388 ColumnTy::Base(_) => val,
389 }
390 }
391
392 pub fn add_values(&mut self, values: impl IntoIterator<Item = (FunctionId, Vec<Value>)>) {
401 let mut extended_row = Vec::<Value>::new();
402 let mut bufs = DenseIdMap::default();
403 for (func, row) in values.into_iter() {
404 let table_info = &self.funcs[func];
405 let schema_math = SchemaMath {
406 subsume: table_info.can_subsume,
407 func_cols: table_info.schema.len(),
408 };
409 let table_id = table_info.table;
410 extended_row.extend_from_slice(&row);
411 schema_math.write_table_row(
412 &mut extended_row,
413 RowVals {
414 timestamp: self.next_ts().to_value(),
415 subsume: schema_math.subsume.then_some(NOT_SUBSUMED),
416 ret_val: None, },
418 );
419 let buf = bufs.get_or_insert(table_id, || self.db.new_buffer(table_id));
420 buf.stage_insert(&extended_row);
421 extended_row.clear();
422 }
423 mem::drop(bufs);
425 self.flush_updates();
426 }
427
428 pub fn add_term(&mut self, func: FunctionId, inputs: &[Value]) -> Value {
434 let info = &self.funcs[func];
435 let schema_math = SchemaMath {
436 subsume: info.can_subsume,
437 func_cols: info.schema.len(),
438 };
439 let mut extended_row = Vec::new();
440 extended_row.extend_from_slice(inputs);
441 let res = self.fresh_id();
442 schema_math.write_table_row(
443 &mut extended_row,
444 RowVals {
445 timestamp: self.next_ts().to_value(),
446 ret_val: Some(res),
447 subsume: schema_math.subsume.then_some(NOT_SUBSUMED),
448 },
449 );
450 extended_row[schema_math.ret_val_col()] = res;
451 let table_id = self.funcs[func].table;
452 self.db.new_buffer(table_id).stage_insert(&extended_row);
453 self.flush_updates();
454 self.get_canon_in_uf(res)
455 }
456
457 pub fn lookup_id(&self, func: FunctionId, key: &[Value]) -> Option<Value> {
460 let info = &self.funcs[func];
461 let schema_math = SchemaMath {
462 subsume: info.can_subsume,
463 func_cols: info.schema.len(),
464 };
465 let table_id = info.table;
466 let table = self.db.get_table(table_id);
467 let row = table.get_row(key)?;
468 Some(row.vals[schema_math.ret_val_col()])
469 }
470
471 pub fn approx_table_size(&self, table: FunctionId) -> usize {
472 self.db.estimate_size(self.funcs[table].table, None)
473 }
474
475 pub fn table_size(&self, table: FunctionId) -> usize {
476 self.db.get_table(self.funcs[table].table).len()
477 }
478
479 pub fn clear_table(&mut self, func: FunctionId) {
492 let table_id = self.funcs[func].table;
493 self.db.clear_table(table_id);
494 }
495
496 pub fn for_each(&self, table: FunctionId, mut f: impl FnMut(ScanEntry<'_>)) {
500 self.for_each_while(table, |row| {
501 f(row);
502 true
503 });
504 }
505
506 pub fn for_each_while(&self, table: FunctionId, mut f: impl FnMut(ScanEntry<'_>) -> bool) {
509 let info = &self.funcs[table];
510 let table = self.funcs[table].table;
511 let schema_math = SchemaMath {
512 subsume: info.can_subsume,
513 func_cols: info.schema.len(),
514 };
515 let imp = self.db.get_table(table);
516 let all = imp.all();
517 let mut cur = Offset::new(0);
518 let mut buf = TaggedRowBuffer::new(imp.spec().arity());
519 macro_rules! drain_buf {
524 ($buf:expr) => {
525 for (_, row) in $buf.non_stale() {
526 let subsumed =
527 schema_math.subsume && row[schema_math.subsume_col()] == SUBSUMED;
528 if !f(ScanEntry {
529 vals: &row[0..schema_math.func_cols],
530 subsumed,
531 }) {
532 return;
533 }
534 }
535 $buf.clear();
536 };
537 }
538 while let Some(next) = imp.scan_bounded(all.as_ref(), cur, 32, &mut buf) {
539 drain_buf!(buf);
540 cur = next;
541 }
542 drain_buf!(buf);
543 }
544
545 pub fn dump_debug_info(&self) {
549 info!("=== View Tables ===");
550 for (id, info) in self.funcs.iter() {
551 let table = self.db.get_table(info.table);
552 self.scan_table(table, |row| {
553 info!(
554 "View Table {name} / {id:?} / {table:?}: {row:?}",
555 name = info.name,
556 table = info.table
557 )
558 });
559 }
560 }
561
562 fn scan_table(&self, table: &WrappedTable, mut f: impl FnMut(&[Value])) {
564 const BATCH_SIZE: usize = 128;
565 let all = table.all();
566 let mut cur = Offset::new(0);
567 let mut out = TaggedRowBuffer::new(table.spec().arity());
568 while let Some(next) = table.scan_bounded(all.as_ref(), cur, BATCH_SIZE, &mut out) {
569 out.non_stale().for_each(|(_, row)| f(row));
570 out.clear();
571 cur = next;
572 }
573 out.non_stale().for_each(|(_, row)| f(row));
574 }
575
576 pub fn add_table(&mut self, config: FunctionConfig) -> FunctionId {
578 let FunctionConfig {
579 schema,
580 default,
581 merge,
582 name,
583 can_subsume,
584 } = config;
585 assert!(
586 !schema.is_empty(),
587 "must have at least one column in schema"
588 );
589 let to_rebuild: Vec<ColumnId> = schema
590 .iter()
591 .enumerate()
592 .filter(|(_, ty)| matches!(ty, ColumnTy::Id))
593 .map(|(i, _)| ColumnId::from_usize(i))
594 .collect();
595 let schema_math = SchemaMath {
596 subsume: can_subsume,
597 func_cols: schema.len(),
598 };
599 let n_args = schema_math.num_keys();
600 let n_cols = schema_math.table_columns();
601 let next_func_id = self.funcs.next_id();
602 let mut read_deps = IndexSet::<TableId>::new();
603 let mut write_deps = IndexSet::<TableId>::new();
604 merge.fill_deps(self, &mut read_deps, &mut write_deps);
605 let merge_fn = merge.to_callback(schema_math, &name, self);
606 let table = install_thread_pool(self.thread_pool(), || {
607 SortedWritesTable::new(
608 n_args,
609 n_cols,
610 Some(ColumnId::from_usize(schema.len())),
611 to_rebuild,
612 merge_fn,
613 )
614 });
615 let name: Arc<str> = name.into();
616 let table_id = self.db.add_table_named(
617 table,
618 name.clone(),
619 read_deps.iter().copied(),
620 write_deps.iter().copied(),
621 );
622
623 let res = self.funcs.push(FunctionInfo {
624 table: table_id,
625 schema: schema.clone(),
626 incremental_rebuild_rules: Default::default(),
627 nonincremental_rebuild_rule: RuleId::new(!0),
628 default_val: default,
629 can_subsume,
630 name,
631 });
632 debug_assert_eq!(res, next_func_id);
633 let incremental_rebuild_rules = self.incremental_rebuild_rules(res, &schema);
634 let nonincremental_rebuild_rule = self.nonincremental_rebuild(res, &schema);
635 let info = &mut self.funcs[res];
636 info.incremental_rebuild_rules = incremental_rebuild_rules;
637 info.nonincremental_rebuild_rule = nonincremental_rebuild_rule;
638 let action = TableAction::new(self, res);
639 let table_name = self.funcs[res].name.to_string();
640 self.action_registry
641 .write()
642 .unwrap()
643 .register_table(table_name, action);
644 res
645 }
646
647 pub fn action_registry(&self) -> &Arc<std::sync::RwLock<ActionRegistry>> {
653 &self.action_registry
654 }
655
656 pub fn run_rules(&mut self, rules: &[RuleId]) -> Result<IterationReport> {
660 let thread_pool = self.thread_pool();
661 install_thread_pool(thread_pool, || self.run_rules_inner(rules))
662 }
663
664 fn run_rules_inner(&mut self, rules: &[RuleId]) -> Result<IterationReport> {
665 let ts = self.next_ts();
666
667 let uf_size_before = self.db.get_table(self.uf_table).len();
668 let rule_set_report =
669 run_rules_impl(&mut self.db, &mut self.rules, rules, ts, self.report_level)?;
670 if let Some(message) = self.panic_message.lock().unwrap().take() {
671 return Err(PanicError(message).into());
672 }
673
674 let mut iteration_report = IterationReport {
675 rule_set_report,
676 rebuild_time: Duration::ZERO,
677 };
678 let uf_size_after = self.db.get_table(self.uf_table).len();
679 if uf_size_before == uf_size_after {
680 self.inc_ts();
685 return Ok(iteration_report);
686 }
687
688 let rebuild_timer = Instant::now();
689 self.rebuild()?;
690 iteration_report.rebuild_time = rebuild_timer.elapsed();
691
692 if let Some(message) = self.panic_message.lock().unwrap().take() {
693 return Err(PanicError(message).into());
694 }
695
696 Ok(iteration_report)
697 }
698
699 fn rebuild(&mut self) -> Result<()> {
700 let do_parallel = egglog_concurrency::current_num_threads() > 1;
701 if self.db.get_table(self.uf_table).rebuilder(&[]).is_some() {
702 let mut tables = Vec::with_capacity(self.funcs.next_id().index());
704 for (_, func) in self.funcs.iter() {
705 tables.push(func.table);
706 }
707 loop {
708 let container_rebuild = self.db.rebuild_containers(self.uf_table);
740 let next_ts = self.next_ts().to_value();
741 let table_rebuild = self.db.apply_rebuild(self.uf_table, &tables, next_ts);
742 let dirty_ids: Vec<Value> = container_rebuild.dirty_ids().iter().copied().collect();
746 let refreshed_rows = self
747 .db
748 .refresh_rows_for_values(&tables, &dirty_ids, next_ts);
749 self.inc_ts();
750 if !table_rebuild && !refreshed_rows && !container_rebuild.changed() {
751 break;
752 }
753 }
754 return Ok(());
755 }
756 if do_parallel {
757 return self.rebuild_parallel();
758 }
759 let start = Instant::now();
760
761 let mut changed = true;
763 while changed {
764 changed = false;
765 self.inc_ts();
768 let ts = self.next_ts();
769 for (_, info) in self.funcs.iter_mut() {
770 let last_rebuilt_at = self.rules[info.nonincremental_rebuild_rule].last_run_at;
771 let table_size = self.db.estimate_size(info.table, None);
772 let uf_size = self.db.estimate_size(
773 self.uf_table,
774 Some(Constraint::GeConst {
775 col: ColumnId::new(2),
776 val: last_rebuilt_at.to_value(),
777 }),
778 );
779 if incremental_rebuild(uf_size, table_size, false) {
780 marker_incremental_rebuild(|| -> Result<()> {
781 for rule in &info.incremental_rebuild_rules {
786 changed |= run_rules_impl(
787 &mut self.db,
788 &mut self.rules,
789 &[*rule],
790 ts,
791 ReportLevel::TimeOnly,
792 )?
793 .changed;
794 }
795 self.rules[info.nonincremental_rebuild_rule].last_run_at = ts;
797 Ok(())
798 })?;
799 } else {
800 marker_nonincremental_rebuild(|| -> Result<()> {
801 changed |= run_rules_impl(
802 &mut self.db,
803 &mut self.rules,
804 &[info.nonincremental_rebuild_rule],
805 ts,
806 ReportLevel::TimeOnly,
807 )?
808 .changed;
809 for rule in &info.incremental_rebuild_rules {
810 self.rules[*rule].last_run_at = ts;
811 }
812 Ok(())
813 })?;
814 }
815 }
816 }
817 log::info!("rebuild took {:?}", start.elapsed());
818 Ok(())
819 }
820
821 fn rebuild_parallel(&mut self) -> Result<()> {
826 let start = Instant::now();
827 #[derive(Default)]
828 struct RebuildState {
829 nonincremental: Vec<FunctionId>,
830 incremental: DenseIdMap<usize, SmallVec<[FunctionId; 2]>>,
831 }
832
833 impl RebuildState {
834 fn clear(&mut self) {
835 self.nonincremental.clear();
836 self.incremental.iter_mut().for_each(|(_, v)| v.clear());
837 }
838 }
839
840 let mut changed = true;
841 let mut state = RebuildState::default();
842 let mut scratch = Vec::new();
843 while changed {
844 changed = false;
845 state.clear();
846 self.inc_ts();
847 for (func, info) in self.funcs.iter_mut() {
850 let last_rebuilt_at = self.rules[info.nonincremental_rebuild_rule].last_run_at;
851 let table_size = self.db.estimate_size(info.table, None);
852 let uf_size = self.db.estimate_size(
853 self.uf_table,
854 Some(Constraint::GeConst {
855 col: ColumnId::new(2),
856 val: last_rebuilt_at.to_value(),
857 }),
858 );
859 if incremental_rebuild(uf_size, table_size, true) {
860 for (i, _) in info.incremental_rebuild_rules.iter().enumerate() {
861 state.incremental.get_or_default(i).push(func);
862 }
863 } else {
864 state.nonincremental.push(func);
865 }
866 }
867 let ts = self.next_ts();
868 for func in state.nonincremental.iter().copied() {
869 scratch.push(self.funcs[func].nonincremental_rebuild_rule);
870 for rule in &self.funcs[func].incremental_rebuild_rules {
871 self.rules[*rule].last_run_at = ts;
872 }
873 }
874 changed |= run_rules_impl(
875 &mut self.db,
876 &mut self.rules,
877 &scratch,
878 ts,
879 ReportLevel::TimeOnly,
880 )?
881 .changed;
882 scratch.clear();
883 let ts = self.next_ts();
884 for (i, funcs) in state.incremental.iter() {
885 for func in funcs.iter().copied() {
886 let info = &mut self.funcs[func];
887 scratch.push(info.incremental_rebuild_rules[i]);
888 self.rules[info.nonincremental_rebuild_rule].last_run_at = ts;
889 }
890 changed |= run_rules_impl(
891 &mut self.db,
892 &mut self.rules,
893 &scratch,
894 ts,
895 ReportLevel::TimeOnly,
896 )?
897 .changed;
898 scratch.clear();
899 }
900 }
901 log::info!("rebuild took {:?}", start.elapsed());
902 Ok(())
903 }
904
905 fn incremental_rebuild_rules(&mut self, table: FunctionId, schema: &[ColumnTy]) -> Vec<RuleId> {
906 schema
907 .iter()
908 .enumerate()
909 .filter_map(|(i, ty)| match ty {
910 ColumnTy::Id => {
911 Some(self.incremental_rebuild_rule(table, schema, ColumnId::from_usize(i)))
912 }
913 ColumnTy::Base(_) => None,
914 })
915 .collect()
916 }
917
918 fn incremental_rebuild_rule(
919 &mut self,
920 table: FunctionId,
921 schema: &[ColumnTy],
922 col: ColumnId,
923 ) -> RuleId {
924 let subsume = self.funcs[table].can_subsume;
925 let table_id = self.funcs[table].table;
926 let uf_table = self.uf_table;
927 let mut rb = self.new_rule(&format!("incremental rebuild {table:?}, {col:?}"), true);
929 rb.set_plan_strategy(PlanStrategy::MinCover);
930 let mut vars = Vec::<QueryEntry>::with_capacity(schema.len());
931 for ty in schema {
932 vars.push(rb.new_var(*ty).into());
933 }
934 let canon_val: QueryEntry = rb.new_var(ColumnTy::Id).into();
935 let subsume_var = subsume.then(|| rb.new_var(ColumnTy::Id));
936 rb.add_atom_with_timestamp_and_func(
937 table_id,
938 Some(table),
939 subsume_var.clone().map(QueryEntry::from),
940 &vars,
941 );
942 rb.add_atom_with_timestamp_and_func(
943 uf_table,
944 None,
945 None,
946 &[vars[col.index()].clone(), canon_val.clone()],
947 );
948 rb.set_focus(1); let mut canon = Vec::<QueryEntry>::with_capacity(schema.len());
952 for (i, (var, ty)) in vars.iter().zip(schema.iter()).enumerate() {
953 canon.push(if i == col.index() {
954 canon_val.clone()
955 } else if let ColumnTy::Id = ty {
956 rb.lookup_uf(var.clone()).unwrap().into()
957 } else {
958 var.clone()
959 })
960 }
961
962 rb.rebuild_row(table, &vars, &canon, subsume_var);
964 rb.build()
965 }
966
967 fn nonincremental_rebuild(&mut self, table: FunctionId, schema: &[ColumnTy]) -> RuleId {
968 let can_subsume = self.funcs[table].can_subsume;
969 let table_id = self.funcs[table].table;
970 let mut rb = self.new_rule(&format!("nonincremental rebuild {table:?}"), false);
971 rb.set_plan_strategy(PlanStrategy::MinCover);
972 let mut vars = Vec::<QueryEntry>::with_capacity(schema.len());
973 for ty in schema {
974 vars.push(rb.new_var(*ty).into());
975 }
976 let subsume_var = can_subsume.then(|| rb.new_var(ColumnTy::Id));
977 rb.add_atom_with_timestamp_and_func(
978 table_id,
979 Some(table),
980 subsume_var.clone().map(QueryEntry::from),
981 &vars,
982 );
983 let mut lhs = SmallVec::<[QueryEntry; 4]>::new();
984 let mut rhs = SmallVec::<[QueryEntry; 4]>::new();
985 let mut canon = Vec::<QueryEntry>::with_capacity(schema.len());
986 for (var, ty) in vars.iter().zip(schema.iter()) {
987 canon.push(if let ColumnTy::Id = ty {
988 lhs.push(var.clone());
989 let canon_var = QueryEntry::from(rb.lookup_uf(var.clone()).unwrap());
990 rhs.push(canon_var.clone());
991 canon_var
992 } else {
993 var.clone()
994 })
995 }
996 rb.check_for_update(&lhs, &rhs).unwrap();
997 rb.rebuild_row(table, &vars, &canon, subsume_var);
998 rb.build()
999 }
1000
1001 pub fn with_execution_state<R>(&self, f: impl FnOnce(&mut ExecutionState<'_>) -> R) -> R {
1017 let thread_pool = self.thread_pool();
1018 install_thread_pool(thread_pool, || self.db.with_execution_state(f))
1019 }
1020
1021 pub fn with_execution_state_tracked<R>(
1026 &self,
1027 f: impl FnOnce(&mut ExecutionState<'_>) -> R,
1028 ) -> (R, bool) {
1029 self.db.with_execution_state_tracked(f)
1030 }
1031
1032 pub fn flush_updates(&mut self) -> bool {
1035 let thread_pool = self.thread_pool();
1036 install_thread_pool(thread_pool, || self.flush_updates_inner())
1037 }
1038
1039 fn flush_updates_inner(&mut self) -> bool {
1040 let uf_size_before = self.db.get_table(self.uf_table).len();
1041 let updated = self.db.merge_all();
1042 self.inc_ts();
1043 let uf_size_after = self.db.get_table(self.uf_table).len();
1044 if uf_size_before != uf_size_after {
1045 self.rebuild().unwrap();
1048 }
1049 updated
1050 }
1051
1052 pub fn set_report_level(&mut self, level: ReportLevel) {
1053 self.report_level = level;
1054 }
1055}
1056
1057#[derive(Clone)]
1058struct RuleInfo {
1059 last_run_at: Timestamp,
1060 query: rule::Query,
1061 cached_plan: Option<CachedPlanInfo>,
1062 desc: Arc<str>,
1063}
1064
1065#[derive(Clone)]
1066struct CachedPlanInfo {
1067 plan: Arc<core_relations::CachedPlan>,
1068 atom_mapping: Vec<core_relations::AtomId>,
1071}
1072
1073#[derive(Clone)]
1074struct FunctionInfo {
1075 table: TableId,
1076 schema: Vec<ColumnTy>,
1077 incremental_rebuild_rules: Vec<RuleId>,
1078 nonincremental_rebuild_rule: RuleId,
1079 default_val: DefaultVal,
1080 can_subsume: bool,
1081 name: Arc<str>,
1082}
1083
1084impl FunctionInfo {
1085 fn ret_ty(&self) -> ColumnTy {
1086 self.schema.last().copied().unwrap()
1087 }
1088}
1089
1090#[derive(Copy, Clone)]
1092pub enum DefaultVal {
1093 FreshId,
1095 Fail,
1097 Const(Value),
1099}
1100
1101pub enum MergeFn {
1103 AssertEq,
1105 UnionId,
1107 Primitive(ExternalFunctionId, Vec<MergeFn>),
1110 Function(FunctionId, Vec<MergeFn>),
1113 Old,
1115 New,
1117 Const(Value),
1121}
1122
1123impl MergeFn {
1124 fn fill_deps(
1125 &self,
1126 egraph: &EGraph,
1127 read_deps: &mut IndexSet<TableId>,
1128 write_deps: &mut IndexSet<TableId>,
1129 ) {
1130 use MergeFn::*;
1131 match self {
1132 Primitive(_, args) => {
1133 args.iter()
1134 .for_each(|arg| arg.fill_deps(egraph, read_deps, write_deps));
1135 write_deps.insert(egraph.uf_table);
1136 }
1137 Function(func, args) => {
1138 read_deps.insert(egraph.funcs[*func].table);
1139 write_deps.insert(egraph.funcs[*func].table);
1140 args.iter()
1141 .for_each(|arg| arg.fill_deps(egraph, read_deps, write_deps));
1142 }
1143 UnionId => {
1144 write_deps.insert(egraph.uf_table);
1145 }
1146 AssertEq | Old | New | Const(..) => {}
1147 }
1148 }
1149
1150 fn to_callback(
1151 &self,
1152 schema_math: SchemaMath,
1153 function_name: &str,
1154 egraph: &mut EGraph,
1155 ) -> Box<core_relations::MergeFn> {
1156 let resolved = self.resolve(function_name, egraph);
1157
1158 Box::new(move |state, cur, new, out| {
1159 let timestamp = new[schema_math.ts_col()];
1160
1161 let mut changed = false;
1162
1163 let ret_val = {
1164 let cur = cur[schema_math.ret_val_col()];
1165 let new = new[schema_math.ret_val_col()];
1166 let out = resolved.run(state, cur, new, timestamp);
1167 changed |= cur != out;
1168 out
1169 };
1170
1171 let subsume = schema_math.subsume.then(|| {
1172 let cur = cur[schema_math.subsume_col()];
1173 let new = new[schema_math.subsume_col()];
1174 let out = combine_subsumed(cur, new);
1175 changed |= cur != out;
1176 out
1177 });
1178 if changed {
1179 out.extend_from_slice(new);
1180 schema_math.write_table_row(
1181 out,
1182 RowVals {
1183 timestamp,
1184 subsume,
1185 ret_val: Some(ret_val),
1186 },
1187 );
1188 }
1189
1190 changed
1191 })
1192 }
1193
1194 fn resolve(&self, function_name: &str, egraph: &mut EGraph) -> ResolvedMergeFn {
1195 match self {
1196 MergeFn::Const(v) => ResolvedMergeFn::Const(*v),
1197 MergeFn::Old => ResolvedMergeFn::Old,
1198 MergeFn::New => ResolvedMergeFn::New,
1199 MergeFn::AssertEq => ResolvedMergeFn::AssertEq {
1200 panic: egraph.new_panic(format!(
1201 "Illegal merge attempted for function {function_name}"
1202 )),
1203 },
1204 MergeFn::UnionId => ResolvedMergeFn::UnionId {
1205 uf_table: egraph.uf_table,
1206 },
1207 MergeFn::Primitive(prim, args) => ResolvedMergeFn::Primitive {
1212 prim: *prim,
1213 args: args
1214 .iter()
1215 .map(|arg| arg.resolve(function_name, egraph))
1216 .collect::<Vec<_>>(),
1217 panic: egraph.new_panic(format!(
1218 "Merge function for {function_name} primitive call failed"
1219 )),
1220 },
1221 MergeFn::Function(func, args) => {
1222 let func_info = &egraph.funcs[*func];
1223 assert_eq!(
1224 func_info.schema.len(),
1225 args.len() + 1,
1226 "Merge function for {function_name} must match function arity for {}",
1227 func_info.name
1228 );
1229 ResolvedMergeFn::Function {
1230 func: TableAction::new(egraph, *func),
1231 panic: egraph.new_panic(format!(
1232 "Lookup on {} failed in the merge function for {function_name}",
1233 func_info.name
1234 )),
1235 args: args
1236 .iter()
1237 .map(|arg| arg.resolve(function_name, egraph))
1238 .collect::<Vec<_>>(),
1239 }
1240 }
1241 }
1242 }
1243}
1244
1245enum ResolvedMergeFn {
1250 Const(Value),
1251 Old,
1252 New,
1253 AssertEq {
1254 panic: ExternalFunctionId,
1255 },
1256 UnionId {
1257 uf_table: TableId,
1258 },
1259 Primitive {
1260 prim: ExternalFunctionId,
1261 args: Vec<ResolvedMergeFn>,
1262 panic: ExternalFunctionId,
1263 },
1264 Function {
1265 func: TableAction,
1266 args: Vec<ResolvedMergeFn>,
1267 panic: ExternalFunctionId,
1268 },
1269}
1270
1271impl ResolvedMergeFn {
1272 fn run(&self, state: &mut ExecutionState, cur: Value, new: Value, ts: Value) -> Value {
1273 match self {
1274 ResolvedMergeFn::Const(v) => *v,
1275 ResolvedMergeFn::Old => cur,
1276 ResolvedMergeFn::New => new,
1277 ResolvedMergeFn::AssertEq { panic } => {
1278 if cur != new {
1279 let res = state.call_external_func(*panic, &[]);
1280 assert_eq!(res, None);
1281 }
1282 cur
1283 }
1284 ResolvedMergeFn::UnionId { uf_table } => {
1285 if cur != new {
1286 state.stage_insert(*uf_table, &[cur, new, ts]);
1287 std::cmp::min(cur, new)
1290 } else {
1291 cur
1292 }
1293 }
1294 ResolvedMergeFn::Primitive { prim, args, panic } => {
1299 let args = args
1300 .iter()
1301 .map(|arg| arg.run(state, cur, new, ts))
1302 .collect::<Vec<_>>();
1303
1304 match state.call_external_func(*prim, &args) {
1305 Some(result) => result,
1306 None => {
1307 let res = state.call_external_func(*panic, &[]);
1308 assert_eq!(res, None);
1309 cur
1310 }
1311 }
1312 }
1313 ResolvedMergeFn::Function { func, args, panic } => {
1314 if cur == new {
1316 return cur;
1317 }
1318
1319 let args = args
1320 .iter()
1321 .map(|arg| arg.run(state, cur, new, ts))
1322 .collect::<Vec<_>>();
1323
1324 func.lookup_or_insert(state, &args).unwrap_or_else(|| {
1330 let res = state.call_external_func(*panic, &[]);
1331 assert_eq!(res, None);
1332 cur
1333 })
1334 }
1335 }
1336 }
1337}
1338
1339#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1344pub enum TableKind {
1345 Function,
1346 Constructor,
1347}
1348
1349#[derive(Debug, PartialEq, Eq, Hash, Clone)]
1353pub struct TableAction {
1354 table: TableId,
1355 table_math: SchemaMath,
1356 default: Option<MergeVal>,
1357 timestamp: CounterId,
1358 kind: TableKind,
1359}
1360
1361impl TableAction {
1362 pub fn new(egraph: &EGraph, func: FunctionId) -> TableAction {
1365 let func_info = &egraph.funcs[func];
1366 let kind = match &func_info.default_val {
1367 DefaultVal::FreshId => TableKind::Constructor,
1368 DefaultVal::Fail | DefaultVal::Const(_) => TableKind::Function,
1369 };
1370 TableAction {
1371 table: func_info.table,
1372 table_math: SchemaMath {
1373 func_cols: func_info.schema.len(),
1374 subsume: func_info.can_subsume,
1375 },
1376 default: match &func_info.default_val {
1377 DefaultVal::FreshId => Some(MergeVal::Counter(egraph.id_counter)),
1378 DefaultVal::Fail => None,
1379 DefaultVal::Const(val) => Some(MergeVal::Constant(*val)),
1380 },
1381 timestamp: egraph.timestamp_counter,
1382 kind,
1383 }
1384 }
1385
1386 pub fn kind(&self) -> TableKind {
1389 self.kind
1390 }
1391
1392 pub fn input_arity(&self) -> usize {
1395 self.table_math.func_cols - 1
1396 }
1397
1398 pub fn lookup(&self, state: &ExecutionState, key: &[Value]) -> Option<Value> {
1405 state
1406 .get_table(self.table)
1407 .get_row(key)
1408 .map(|row| row.vals[self.table_math.ret_val_col()])
1409 }
1410
1411 pub fn row_count(&self, state: &ExecutionState) -> usize {
1413 state.get_table(self.table).len()
1414 }
1415
1416 pub fn for_each(&self, state: &ExecutionState, mut f: impl FnMut(ScanEntry<'_>)) {
1421 self.for_each_while(state, |row| {
1422 f(row);
1423 true
1424 });
1425 }
1426
1427 pub fn for_each_while(&self, state: &ExecutionState, mut f: impl FnMut(ScanEntry<'_>) -> bool) {
1430 let schema_math = self.table_math;
1431 let imp = state.get_table(self.table);
1432 let all = imp.all();
1433 let mut cur = Offset::new(0);
1434 let mut buf = TaggedRowBuffer::new(imp.spec().arity());
1435 macro_rules! drain_buf {
1436 ($buf:expr) => {
1437 for (_, row) in $buf.non_stale() {
1438 let subsumed =
1439 schema_math.subsume && row[schema_math.subsume_col()] == SUBSUMED;
1440 if !f(ScanEntry {
1441 vals: &row[0..schema_math.func_cols],
1442 subsumed,
1443 }) {
1444 return;
1445 }
1446 }
1447 $buf.clear();
1448 };
1449 }
1450 while let Some(next) = imp.scan_bounded(all.as_ref(), cur, 32, &mut buf) {
1451 drain_buf!(buf);
1452 cur = next;
1453 }
1454 drain_buf!(buf);
1455 }
1456
1457 pub fn lookup_or_insert(&self, state: &mut ExecutionState, key: &[Value]) -> Option<Value> {
1465 match self.default {
1466 Some(default) => {
1467 let timestamp =
1468 MergeVal::Constant(Value::from_usize(state.read_counter(self.timestamp)));
1469 let mut merge_vals = SmallVec::<[MergeVal; 3]>::new();
1470 SchemaMath {
1471 func_cols: 1,
1472 ..self.table_math
1473 }
1474 .write_table_row(
1475 &mut merge_vals,
1476 RowVals {
1477 timestamp,
1478 subsume: self
1479 .table_math
1480 .subsume
1481 .then_some(MergeVal::Constant(NOT_SUBSUMED)),
1482 ret_val: Some(default),
1483 },
1484 );
1485 Some(
1486 state.predict_val(self.table, key, merge_vals.iter().copied())
1487 [self.table_math.ret_val_col()],
1488 )
1489 }
1490 None => self.lookup(state, key),
1491 }
1492 }
1493
1494 pub fn insert(&self, state: &mut ExecutionState, row: impl Iterator<Item = Value>) {
1496 let ts = Value::from_usize(state.read_counter(self.timestamp));
1497 let mut scratch = row.collect::<SmallVec<[_; 8]>>();
1498 self.table_math.write_table_row(
1499 &mut scratch,
1500 RowVals {
1501 timestamp: ts,
1502 subsume: self.table_math.subsume.then_some(NOT_SUBSUMED),
1503 ret_val: None,
1504 },
1505 );
1506 state.stage_insert(self.table, &scratch);
1507 }
1508
1509 pub fn remove(&self, state: &mut ExecutionState, key: &[Value]) {
1511 state.stage_remove(self.table, key);
1512 }
1513
1514 pub fn subsume(&self, state: &mut ExecutionState, key: impl Iterator<Item = Value>) {
1516 let ts = Value::from_usize(state.read_counter(self.timestamp));
1517 let mut scratch = key.collect::<SmallVec<[_; 8]>>();
1518
1519 let ret_val = self.lookup(state, &scratch).expect("subsume lookup failed");
1520
1521 self.table_math.write_table_row(
1522 &mut scratch,
1523 RowVals {
1524 timestamp: ts,
1525 subsume: Some(SUBSUMED),
1526 ret_val: Some(ret_val),
1527 },
1528 );
1529 state.stage_insert(self.table, &scratch);
1530 }
1531}
1532
1533#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1535pub struct UnionAction {
1536 table: TableId,
1537 timestamp: CounterId,
1538}
1539
1540impl UnionAction {
1541 pub fn new(egraph: &EGraph) -> UnionAction {
1544 UnionAction {
1545 table: egraph.uf_table,
1546 timestamp: egraph.timestamp_counter,
1547 }
1548 }
1549
1550 pub fn union(&self, state: &mut ExecutionState, x: Value, y: Value) {
1552 let ts = Value::from_usize(state.read_counter(self.timestamp));
1553 state.stage_insert(self.table, &[x, y, ts]);
1554 }
1555}
1556
1557fn run_rules_impl(
1558 db: &mut Database,
1559 rule_info: &mut DenseIdMapWithReuse<RuleId, RuleInfo>,
1560 rules: &[RuleId],
1561 next_ts: Timestamp,
1562 report_level: ReportLevel,
1563) -> Result<RuleSetReport> {
1564 for rule in rules {
1565 let info = &mut rule_info[*rule];
1566 if info.cached_plan.is_none() {
1567 info.cached_plan = Some(info.query.build_cached_plan(db, &info.desc)?);
1568 }
1569 }
1570 let mut rsb = db.new_rule_set();
1571 for rule in rules {
1572 let info = &mut rule_info[*rule];
1573 let cached_plan = info.cached_plan.as_ref().unwrap();
1574 info.query
1575 .add_rules_from_cached(&mut rsb, info.last_run_at, cached_plan);
1576 info.last_run_at = next_ts;
1577 }
1578 let ruleset = rsb.build();
1579 Ok(db.run_rule_set(&ruleset, report_level))
1580}
1581
1582#[inline(never)]
1586fn marker_incremental_rebuild<R>(f: impl FnOnce() -> R) -> R {
1587 f()
1588}
1589
1590#[inline(never)]
1591fn marker_nonincremental_rebuild<R>(f: impl FnOnce() -> R) -> R {
1592 f()
1593}
1594
1595pub type SideChannel<T> = Arc<Mutex<Option<T>>>;
1598
1599struct LazyPanic<F>(Arc<Lazy<String, F>>, SideChannel<String>);
1615
1616impl<F: FnOnce() -> String + Send> ExternalFunction for LazyPanic<F> {
1617 fn invoke(&self, state: &mut core_relations::ExecutionState, args: &[Value]) -> Option<Value> {
1618 assert!(args.is_empty());
1619 state.trigger_early_stop();
1620 let mut guard = self.1.lock().unwrap();
1621 if guard.is_none() {
1622 *guard = Some(Lazy::force(&self.0).clone());
1623 }
1624 None
1625 }
1626}
1627
1628impl<F> Clone for LazyPanic<F> {
1629 fn clone(&self) -> Self {
1630 LazyPanic(self.0.clone(), self.1.clone())
1631 }
1632}
1633
1634#[derive(Clone)]
1639struct Panic(String, SideChannel<String>);
1640
1641impl EGraph {
1642 pub fn new_panic(&mut self, message: String) -> ExternalFunctionId {
1644 *self
1645 .panic_funcs
1646 .entry(message.to_string())
1647 .or_insert_with(|| {
1648 let panic = Panic(message, self.panic_message.clone());
1649 self.db.add_external_function(Box::new(panic))
1650 })
1651 }
1652
1653 pub fn new_panic_lazy(
1654 &mut self,
1655 message: impl FnOnce() -> String + Send + 'static,
1656 ) -> ExternalFunctionId {
1657 let lazy = Lazy::new(message);
1658 let panic = LazyPanic(Arc::new(lazy), self.panic_message.clone());
1659 self.db.add_external_function(Box::new(panic))
1660 }
1661}
1662
1663impl ExternalFunction for Panic {
1664 fn invoke(&self, state: &mut core_relations::ExecutionState, args: &[Value]) -> Option<Value> {
1665 assert!(args.is_empty());
1667
1668 state.trigger_early_stop();
1669 let mut guard = self.1.lock().unwrap();
1670 if guard.is_none() {
1671 *guard = Some(self.0.clone());
1672 }
1673 None
1674 }
1675}
1676
1677fn incremental_rebuild(uf_size: usize, table_size: usize, parallel: bool) -> bool {
1680 if parallel {
1681 uf_size <= (table_size / 16)
1682 } else {
1683 uf_size <= (table_size / 8)
1684 }
1685}
1686
1687pub(crate) const SUBSUMED: Value = Value::new_const(1);
1688pub(crate) const NOT_SUBSUMED: Value = Value::new_const(0);
1689fn combine_subsumed(v1: Value, v2: Value) -> Value {
1690 std::cmp::max(v1, v2)
1691}
1692
1693#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1704struct SchemaMath {
1705 subsume: bool,
1707 func_cols: usize,
1709}
1710
1711struct RowVals<T> {
1717 timestamp: T,
1719 subsume: Option<T>,
1721 ret_val: Option<T>,
1724}
1725
1726#[derive(Clone, Debug)]
1733pub struct ScanEntry<'a> {
1734 pub vals: &'a [Value],
1735 pub subsumed: bool,
1736}
1737
1738impl SchemaMath {
1739 fn write_table_row<T: Clone>(
1740 &self,
1741 row: &mut impl HasResizeWith<T>,
1742 RowVals {
1743 timestamp,
1744 subsume,
1745 ret_val,
1746 }: RowVals<T>,
1747 ) {
1748 row.resize_with(self.table_columns(), || timestamp.clone());
1749 row[self.ts_col()] = timestamp;
1750 if let Some(ret_val) = ret_val {
1751 row[self.ret_val_col()] = ret_val;
1752 }
1753 if let Some(subsume) = subsume {
1754 row[self.subsume_col()] = subsume;
1755 } else {
1756 assert!(
1757 !self.subsume,
1758 "subsume flag must be provided if subsumption is enabled"
1759 );
1760 }
1761 }
1762
1763 fn num_keys(&self) -> usize {
1764 self.func_cols - 1
1765 }
1766
1767 fn table_columns(&self) -> usize {
1768 self.func_cols + 1 + if self.subsume { 1 } else { 0 }
1769 }
1770
1771 fn ret_val_col(&self) -> usize {
1772 self.func_cols - 1
1773 }
1774
1775 fn ts_col(&self) -> usize {
1776 self.func_cols
1777 }
1778
1779 #[track_caller]
1780 fn subsume_col(&self) -> usize {
1781 assert!(self.subsume);
1782 self.func_cols + 1
1783 }
1784}
1785
1786#[derive(Error, Debug)]
1787#[error("Panic: {0}")]
1788struct PanicError(String);
1789
1790trait HasResizeWith<T>:
1793 AsMut<[T]> + AsRef<[T]> + Index<usize, Output = T> + IndexMut<usize, Output = T>
1794{
1795 fn resize_with<F>(&mut self, new_size: usize, f: F)
1796 where
1797 F: FnMut() -> T;
1798}
1799
1800impl<T> HasResizeWith<T> for Vec<T> {
1801 fn resize_with<F>(&mut self, new_size: usize, f: F)
1802 where
1803 F: FnMut() -> T,
1804 {
1805 self.resize_with(new_size, f);
1806 }
1807}
1808
1809impl<T, A: smallvec::Array<Item = T>> HasResizeWith<T> for SmallVec<A> {
1810 fn resize_with<F>(&mut self, new_size: usize, f: F)
1811 where
1812 F: FnMut() -> T,
1813 {
1814 self.resize_with(new_size, f);
1815 }
1816}