1use std::any::TypeId;
13use std::sync::Mutex;
14
15use crate::exec_state::Internal;
16use enum_map::EnumMap;
17
18use super::*;
19
20#[derive(Clone, Debug)]
21pub struct FunctionContainer(
22 pub ResolvedFunctionId,
23 pub Vec<(ArcSort, Value)>,
24 pub String,
25 pub ExternalFunctionId,
30);
31
32impl PartialEq for FunctionContainer {
36 fn eq(&self, other: &Self) -> bool {
37 self.0 == other.0
38 && self.1.iter().map(|(_, v)| *v).collect::<Vec<_>>()
39 == other.1.iter().map(|(_, v)| *v).collect::<Vec<_>>()
40 && self.2 == other.2
41 }
42}
43
44impl Eq for FunctionContainer {}
45
46impl Hash for FunctionContainer {
47 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
48 self.0.hash(state);
49 for (_, v) in &self.1 {
50 v.hash(state);
51 }
52 self.2.hash(state);
53 }
54}
55
56impl ContainerValue for FunctionContainer {
57 fn rebuild_contents(&mut self, rebuilder: &dyn ValueRebuilder) -> bool {
58 let mut changed = false;
59 for (s, old) in &mut self.1 {
60 if s.is_eq_sort() || s.is_eq_container_sort() {
61 let new = rebuilder.rebuild_val(*old);
62 changed |= *old != new;
63 *old = new;
64 }
65 }
66 changed
67 }
68 fn iter(&self) -> impl Iterator<Item = Value> + '_ {
69 self.1.iter().map(|(_, v)| v).copied()
70 }
71}
72#[derive(Debug)]
73pub struct FunctionSort {
74 name: String,
75 inputs: Vec<ArcSort>,
76 output: ArcSort,
77 partial_arcsorts: Arc<Mutex<Vec<ArcSort>>>,
80}
81
82impl FunctionSort {
83 pub fn name(&self) -> &str {
84 &self.name
85 }
86
87 pub fn inputs(&self) -> &[ArcSort] {
88 &self.inputs
89 }
90
91 pub fn output(&self) -> ArcSort {
92 self.output.clone()
93 }
94}
95
96impl Presort for FunctionSort {
97 fn presort_name() -> &'static str {
98 "UnstableFn"
99 }
100
101 fn reserved_primitives() -> Vec<&'static str> {
102 vec!["unstable-fn", "unstable-app"]
103 }
104
105 fn make_sort(
106 typeinfo: &mut TypeInfo,
107 name: String,
108 args: &[Expr],
109 ) -> Result<ArcSort, TypeError> {
110 if let [inputs, Expr::Var(span, output)] = args {
111 let output_sort = typeinfo
112 .get_sort_by_name(output)
113 .ok_or(TypeError::UndefinedSort(output.clone(), span.clone()))?;
114
115 let input_sorts = match inputs {
116 Expr::Call(_, first, rest_args) => {
117 let all_args = once(first).chain(rest_args.iter().map(|arg| {
118 if let Expr::Var(_, arg) = arg {
119 arg
120 } else {
121 panic!("function sort must be called with list of input sorts");
122 }
123 }));
124 all_args
125 .map(|arg| {
126 typeinfo
127 .get_sort_by_name(arg)
128 .ok_or(TypeError::UndefinedSort(arg.clone(), span.clone()))
129 .cloned()
130 })
131 .collect::<Result<Vec<_>, _>>()?
132 }
133 Expr::Lit(_, Literal::Unit) => vec![],
135 _ => panic!("function sort must be called with list of input sorts"),
136 };
137
138 Ok(Arc::new(Self {
139 name,
140 inputs: input_sorts,
141 output: output_sort.clone(),
142 partial_arcsorts: Arc::new(Mutex::new(vec![])),
143 }))
144 } else {
145 panic!("function sort must be called with list of input args and output sort");
146 }
147 }
148}
149
150impl Sort for FunctionSort {
151 fn name(&self) -> &str {
152 &self.name
153 }
154
155 fn column_ty(&self, _backend: &egglog_bridge::EGraph) -> ColumnTy {
156 ColumnTy::Id
157 }
158
159 fn register_type(&self, backend: &mut egglog_bridge::EGraph) {
160 backend.register_container_ty::<FunctionContainer>();
161 backend
162 .base_values_mut()
163 .register_type::<ResolvedFunction>();
164 }
165
166 fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync + 'static> {
167 self
168 }
169
170 fn is_container_sort(&self) -> bool {
171 true
172 }
173
174 fn is_eq_container_sort(&self) -> bool {
175 self.inputs
176 .iter()
177 .any(|s| s.is_eq_sort() || s.is_eq_container_sort())
178 }
179
180 fn serialized_name(&self, container_values: &ContainerValues, value: Value) -> String {
181 let val = container_values
182 .get_val::<FunctionContainer>(value)
183 .unwrap();
184 val.2.clone()
185 }
186
187 fn inner_sorts(&self) -> Vec<ArcSort> {
188 self.partial_arcsorts.lock().unwrap().clone()
189 }
190
191 fn inner_values(
192 &self,
193 container_values: &ContainerValues,
194 value: Value,
195 ) -> Vec<(ArcSort, Value)> {
196 let val = container_values
197 .get_val::<FunctionContainer>(value)
198 .unwrap();
199 val.1.clone()
200 }
201
202 fn register_primitives(self: Arc<Self>, eg: &mut EGraph) {
203 eg.add_pure_primitive(
204 Ctor {
205 name: "unstable-fn".into(),
206 function: self.clone(),
207 },
208 None,
209 );
210 eg.add_pure_primitive(
211 Apply {
212 name: "unstable-app".into(),
213 function: self.clone(),
214 },
215 None,
216 );
217
218 register_vec_primitives_for_function(eg, self.clone());
219 register_multiset_primitives_for_function(eg, self.clone());
220 }
221
222 fn value_type(&self) -> Option<TypeId> {
223 Some(TypeId::of::<FunctionContainer>())
224 }
225
226 fn reconstruct_termdag_container(
227 &self,
228 container_values: &ContainerValues,
229 value: Value,
230 termdag: &mut TermDag,
231 mut element_terms: Vec<TermId>,
232 ) -> TermId {
233 let name = &container_values
234 .get_val::<FunctionContainer>(value)
235 .unwrap()
236 .2;
237 let head = termdag.lit(Literal::String(name.clone()));
238 element_terms.insert(0, head);
239 termdag.app("unstable-fn".to_owned(), element_terms)
240 }
241}
242
243struct FunctionCTorTypeConstraint {
245 name: String,
246 function: Arc<FunctionSort>,
247 span: Span,
248}
249
250impl TypeConstraint for FunctionCTorTypeConstraint {
251 fn get(
252 &self,
253 arguments: &[AtomTerm],
254 typeinfo: &TypeInfo,
255 ) -> Vec<Box<dyn Constraint<AtomTerm, ArcSort>>> {
256 if arguments.len() < 2 {
258 return vec![constraint::impossible(
259 constraint::ImpossibleConstraint::ArityMismatch {
260 atom: core::Atom {
261 span: self.span.clone(),
262 head: self.name.clone(),
263 args: arguments.to_vec(),
264 },
265 expected: 2,
266 },
267 )];
268 }
269 let output_sort_constraint: Box<dyn Constraint<_, ArcSort>> = constraint::assign(
270 arguments[arguments.len() - 1].clone(),
271 self.function.clone(),
272 );
273 if let AtomTerm::Literal(_, Literal::String(ref name)) = arguments[0] {
276 let n_partial_args = arguments.len() - 2;
278 if let Some(func_type) = typeinfo.get_func_type(name) {
279 if self.function.inputs.len() + n_partial_args != func_type.input.len() {
282 return vec![constraint::impossible(
283 constraint::ImpossibleConstraint::ArityMismatch {
284 atom: core::Atom {
285 span: self.span.clone(),
286 head: self.name.clone(),
287 args: arguments.to_vec(),
288 },
289 expected: self.function.inputs.len() + func_type.input.len() + 1,
290 },
291 )];
292 }
293 let expected_output = self.function.output.clone();
295 let expected_input = self.function.inputs.clone();
296 let actual_output = func_type.output.clone();
297 let actual_input: Vec<ArcSort> = func_type
298 .input
299 .iter()
300 .skip(n_partial_args)
301 .cloned()
302 .collect();
303 if expected_output.name() != actual_output.name()
304 || expected_input
305 .iter()
306 .map(|s| s.name())
307 .ne(actual_input.iter().map(|s| s.name()))
308 {
309 return vec![constraint::impossible(
310 constraint::ImpossibleConstraint::FunctionMismatch {
311 expected_output,
312 expected_input,
313 actual_output,
314 actual_input,
315 },
316 )];
317 }
318 return func_type
320 .input
321 .iter()
322 .take(n_partial_args)
323 .zip(arguments.iter().skip(1))
324 .map(|(expected_sort, actual_term)| {
325 constraint::assign(actual_term.clone(), expected_sort.clone())
326 })
327 .chain(once(output_sort_constraint))
328 .collect();
329 }
330
331 if let Some(primitives) = typeinfo.get_prims(name) {
332 let mut primitive_constraints = Vec::with_capacity(primitives.len());
346 for primitive in primitives {
347 let mut primitive_args = arguments[1..arguments.len() - 1].to_vec();
348 primitive_constraints.push(Vec::new());
349 let alternative_constraints = primitive_constraints.last_mut().unwrap();
350 for (index, sort) in self
351 .function
352 .inputs
353 .iter()
354 .chain(once(&self.function.output))
355 .enumerate()
356 {
357 let term = AtomTerm::Var(
358 self.span.clone(),
359 format!(
360 "__unstable_fn_target_{}_{}_arg_{index}",
361 name,
362 self.function.name()
363 ),
364 );
365 alternative_constraints
366 .push(constraint::assign(term.clone(), sort.clone()));
367 primitive_args.push(term);
368 }
369 alternative_constraints.extend(
370 primitive
371 .primitive
372 .get_type_constraints(&self.span)
373 .get(&primitive_args, typeinfo),
374 );
375 }
376
377 return match primitive_constraints.len() {
382 0 => vec![constraint::impossible(
383 constraint::ImpossibleConstraint::ArityMismatch {
384 atom: core::Atom {
385 span: self.span.clone(),
386 head: self.name.clone(),
387 args: arguments.to_vec(),
388 },
389 expected: n_partial_args + self.function.inputs.len() + 2,
390 },
391 )],
392 1 => once(output_sort_constraint)
393 .chain(primitive_constraints.pop().unwrap())
394 .collect(),
395 _ => vec![
396 output_sort_constraint,
397 constraint::xor(
398 primitive_constraints
399 .into_iter()
400 .map(constraint::and)
401 .collect(),
402 ),
403 ],
404 };
405 }
406 }
407
408 vec![
410 constraint::assign(arguments[0].clone(), StringSort.to_arcsort()),
411 output_sort_constraint,
412 ]
413 }
414}
415
416#[derive(Clone)]
418struct Ctor {
419 name: String,
420 function: Arc<FunctionSort>,
421}
422
423impl Primitive for Ctor {
429 fn name(&self) -> &str {
430 &self.name
431 }
432
433 fn get_type_constraints(&self, span: &Span) -> Box<dyn TypeConstraint> {
434 Box::new(FunctionCTorTypeConstraint {
435 name: self.name.clone(),
436 function: self.function.clone(),
437 span: span.clone(),
438 })
439 }
440}
441
442impl PurePrim for Ctor {
443 fn apply<'a, 'db>(
444 &self,
445 mut state: crate::PureState<'a, 'db>,
446 args: &[Value],
447 ) -> Option<Value> {
448 let (rf, args) = args.split_first().unwrap();
449 let ResolvedFunction {
450 id,
451 partial_arcsorts,
452 name,
453 panic_id,
454 } = state.base_values().unwrap(*rf);
455 self.function
456 .partial_arcsorts
457 .lock()
458 .unwrap()
459 .extend(partial_arcsorts.iter().cloned());
460 let args = partial_arcsorts
461 .iter()
462 .zip(args)
463 .map(|(b, x)| (b.clone(), *x))
464 .collect();
465 let y = FunctionContainer(id, args, name, panic_id);
466 Some(state.register_container(y))
467 }
468}
469
470#[derive(Clone, Debug)]
471pub struct ResolvedFunction {
472 pub id: ResolvedFunctionId,
473 pub partial_arcsorts: Vec<ArcSort>,
474 pub name: String,
475 pub panic_id: ExternalFunctionId,
483}
484impl PartialEq for ResolvedFunction {
487 fn eq(&self, other: &Self) -> bool {
488 self.id == other.id
489 && self
490 .partial_arcsorts
491 .iter()
492 .map(|s| s.name())
493 .collect::<Vec<_>>()
494 == other
495 .partial_arcsorts
496 .iter()
497 .map(|s| s.name())
498 .collect::<Vec<_>>()
499 }
500}
501
502impl Eq for ResolvedFunction {}
503
504impl Hash for ResolvedFunction {
505 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
506 self.id.hash(state);
507 for s in &self.partial_arcsorts {
508 s.name().hash(state);
509 }
510 }
511}
512
513impl BaseValue for ResolvedFunction {}
514
515#[derive(Clone, Debug, PartialEq, Eq, Hash)]
516pub enum ResolvedFunctionId {
517 Constructor(egglog_bridge::TableAction),
525 Function(egglog_bridge::TableAction),
530 Primitive {
537 context_ids: EnumMap<crate::Context, Option<ExternalFunctionId>>,
538 },
539}
540
541#[derive(Clone)]
549struct Apply {
550 name: String,
551 function: Arc<FunctionSort>,
552}
553
554impl Primitive for Apply {
555 fn name(&self) -> &str {
556 &self.name
557 }
558
559 fn get_type_constraints(&self, span: &Span) -> Box<dyn TypeConstraint> {
560 let mut sorts: Vec<ArcSort> = vec![self.function.clone()];
561 sorts.extend(self.function.inputs.clone());
562 sorts.push(self.function.output.clone());
563 SimpleTypeConstraint::new(&self.name, sorts, span.clone()).into_box()
564 }
565}
566
567impl PurePrim for Apply {
568 fn apply<'a, 'db>(
569 &self,
570 mut state: crate::PureState<'a, 'db>,
571 args: &[Value],
572 ) -> Option<Value> {
573 let (fc_val, args) = args.split_first().unwrap();
574 let fc = state
575 .container_values()
576 .get_val::<FunctionContainer>(*fc_val)
577 .unwrap()
578 .clone();
579 state.apply_function(&fc, args)
580 }
581}
582
583impl FunctionContainer {
584 pub(crate) fn apply<'a, 'db>(
589 &self,
590 state: &mut crate::PureState<'a, 'db>,
591 args: &[Value],
592 ) -> Option<Value>
593 where
594 'db: 'a,
595 {
596 let ctx = state.ctx();
597 let args: Vec<_> = self.1.iter().map(|(_, x)| x).chain(args).copied().collect();
598 let can_mint = matches!(ctx, crate::Context::Write | crate::Context::Full);
599 let can_read = matches!(ctx, crate::Context::Read | crate::Context::Full);
600 let panic_id = self.3;
601 let mismatch = |state: &mut crate::PureState<'a, 'db>| -> Option<Value> {
607 state.call_external_func(panic_id, &[])
608 };
609 match &self.0 {
610 ResolvedFunctionId::Constructor(action) => {
611 if can_mint {
612 action.lookup_or_insert(state.raw_exec_state(), &args)
613 } else {
614 mismatch(state)
615 }
616 }
617 ResolvedFunctionId::Function(action) => {
618 if can_read {
619 action.lookup(state.raw_exec_state(), &args)
620 } else {
621 mismatch(state)
622 }
623 }
624 ResolvedFunctionId::Primitive { context_ids } => {
625 match context_ids[ctx] {
628 Some(id) => state.call_external_func(id, &args),
629 None => mismatch(state),
630 }
631 }
632 }
633 }
634}