pindakaas_derive/
lib.rs

1use darling::FromDeriveInput;
2use proc_macro::TokenStream;
3use quote::{format_ident, quote};
4use syn::{parse_macro_input, DeriveInput, Ident};
5
6#[derive(FromDeriveInput)]
7#[darling(attributes(ipasir))]
8struct IpasirOpts {
9	krate: Ident,
10	#[darling(default)]
11	ptr: Option<Ident>,
12	#[darling(default)]
13	vars: Option<Ident>,
14	#[darling(default)]
15	assumptions: bool,
16	#[darling(default)]
17	learn_callback: bool,
18	#[darling(default)]
19	learn_callback_ident: Option<Ident>,
20	#[darling(default)]
21	term_callback: bool,
22	#[darling(default)]
23	term_callback_ident: Option<Ident>,
24	#[darling(default)]
25	ipasir_up: bool,
26	#[darling(default = "default_true")]
27	has_default: bool,
28}
29
30#[proc_macro_derive(IpasirSolver, attributes(ipasir))]
31pub fn ipasir_solver_derive(input: TokenStream) -> TokenStream {
32	let input = parse_macro_input!(input);
33	let opts = IpasirOpts::from_derive_input(&input).expect("Invalid options");
34	let DeriveInput { ident, .. } = input;
35
36	let krate = opts.krate;
37	let ptr_attr = match opts.ptr {
38		Some(x) => quote! {  #x },
39		None => quote! { ptr },
40	};
41	let ptr = quote! { self. #ptr_attr };
42	let vars = match opts.vars.clone() {
43		Some(x) => quote! { self. #x },
44		None => quote! { self.vars },
45	};
46	let sol_ident = format_ident!("{}Sol", ident);
47
48	let (assumptions, fail_type) = if opts.assumptions {
49		let fail_ident = format_ident!("{}Failed", ident);
50		(
51			quote! {
52				impl #ident {
53					fn solver_fail_obj(&self) -> #fail_ident {
54						#fail_ident { slv: self }
55					}
56				}
57
58				impl crate::solver::SolveAssuming for #ident {
59					#[expect(refining_impl_trait)]
60					fn solve_assuming<I: IntoIterator<Item = crate::Lit>>(
61						&mut self,
62						assumptions: I,
63					) -> crate::solver::SolveResult<#sol_ident <'_>, #fail_ident <'_>> {
64						use crate::solver::Solver;
65						for i in assumptions {
66							unsafe { #krate::ipasir_assume(#ptr, i.into()) }
67						}
68						self.solve()
69					}
70				}
71
72				pub struct #fail_ident <'a> {
73					slv: &'a #ident,
74				}
75				impl crate::solver::FailedAssumtions for #fail_ident <'_> {
76					fn fail(&self, lit: crate::Lit) -> bool {
77						let lit: i32 = lit.into();
78						let failed = unsafe { #krate::ipasir_failed( self.slv. #ptr_attr, lit) };
79						failed != 0
80					}
81				}
82			},
83			quote! { #fail_ident <'_> },
84		)
85	} else {
86		(
87			quote! {
88				impl #ident {
89					fn solver_fail_obj(&self) {}
90				}
91			},
92			quote! { () },
93		)
94	};
95
96	let term_callback = if opts.term_callback {
97		let term_cb = match opts.term_callback_ident {
98			Some(x) => quote! { self. #x },
99			None => quote! { self.term_cb },
100		};
101		quote! {
102			impl crate::solver::TermCallback for #ident {
103				fn set_terminate_callback<F: FnMut() -> crate::solver::SlvTermSignal + 'static>(
104					&mut self,
105					cb: Option<F>,
106				) {
107					if let Some(mut cb) = cb {
108						let wrapped_cb = move || -> std::ffi::c_int {
109							match cb() {
110								crate::solver::SlvTermSignal::Continue => std::ffi::c_int::from(0),
111								crate::solver::SlvTermSignal::Terminate => std::ffi::c_int::from(1),
112							}
113						};
114						let trampoline = crate::solver::get_trampoline0(&wrapped_cb);
115						#term_cb = crate::solver::FFIPointer::new(wrapped_cb);
116						unsafe {
117							#krate::ipasir_set_terminate(
118								#ptr,
119								#term_cb .get_ptr(),
120								Some(trampoline),
121							)
122						}
123					} else {
124						#term_cb = crate::solver::FFIPointer::default();
125						unsafe { #krate::ipasir_set_terminate(#ptr, std::ptr::null_mut(), None) }
126					}
127				}
128			}
129		}
130	} else {
131		quote!()
132	};
133
134	let learn_callback = if opts.learn_callback {
135		let learn_cb = match opts.learn_callback_ident {
136			Some(x) => quote! { self. #x },
137			None => quote! { self.learn_cb },
138		};
139
140		quote! {
141			impl crate::solver::LearnCallback for #ident {
142				fn set_learn_callback<F: FnMut(&mut dyn Iterator<Item = crate::Lit>) + 'static>(
143					&mut self,
144					cb: Option<F>,
145				) {
146					const MAX_LEN: std::ffi::c_int = 512;
147					if let Some(mut cb) = cb {
148						let wrapped_cb = move |clause: *const i32| {
149							let mut iter = crate::solver::ExplIter(clause)
150								.map(|i: i32| crate::Lit(std::num::NonZeroI32::new(i).unwrap()));
151							cb(&mut iter)
152						};
153						let trampoline = crate::solver::get_trampoline1(&wrapped_cb);
154						#learn_cb = crate::solver::FFIPointer::new(wrapped_cb);
155						unsafe {
156							#krate::ipasir_set_learn(
157								#ptr,
158								#learn_cb .get_ptr(),
159								MAX_LEN,
160								Some(trampoline),
161							)
162						}
163					} else {
164						#learn_cb = crate::solver::FFIPointer::default();
165						unsafe { #krate::ipasir_set_learn(#ptr, std::ptr::null_mut(), MAX_LEN, None) }
166					}
167				}
168			}
169		}
170	} else {
171		quote!()
172	};
173
174	let ipasir_up = if opts.ipasir_up {
175		let prop_slv = format_ident!("Propagating{}", ident);
176		quote! {
177			#[cfg(feature = "external-propagation")]
178			pub struct #prop_slv<P> {
179				container: Box<crate::solver::propagation::IpasirPropStore <P, #ident>>,
180			}
181
182			#[cfg(feature = "external-propagation")]
183			impl<P: crate::solver::propagation::Propagator> crate::solver::propagation::WithPropagator<P> for #ident {
184				type PropSlv = #prop_slv <P>;
185				fn with_propagator(self, prop: P) -> Self::PropSlv {
186					let is_lazy = prop.is_check_only();
187					let forgettable_reasons = prop.reason_persistence() == crate::solver::propagation::ClausePersistence::Forgettable;
188					let notify_fixed = prop.enable_persistent_assignments();
189
190					let mut container = Box::new(crate::solver::propagation::IpasirPropStore::new(prop, self));
191					unsafe {
192						#krate::ipasir_connect_external_propagator(
193							container.slv. #ptr_attr,
194							&mut *container as *mut _ as *mut std::ffi::c_void,
195							crate::solver::propagation::ipasir_notify_assignments_cb::<P, #ident>,
196							crate::solver::propagation::ipasir_notify_new_decision_level_cb::<P, #ident>,
197							crate::solver::propagation::ipasir_notify_backtrack_cb::<P, #ident>,
198							crate::solver::propagation::ipasir_check_model_cb::<P, #ident>,
199							crate::solver::propagation::ipasir_has_external_clause_cb::<P, #ident>,
200							crate::solver::propagation::ipasir_add_external_clause_lit_cb::<P, #ident>,
201							is_lazy,
202							forgettable_reasons,
203							notify_fixed,
204							crate::solver::propagation::ipasir_decide_cb::<P, #ident>,
205							crate::solver::propagation::ipasir_propagate_cb::<P, #ident>,
206							crate::solver::propagation::ipasir_add_reason_clause_lit_cb::<P, #ident>,
207							crate::solver::propagation::ipasir_notify_persistent_assignments_cb::<P, #ident>,
208						)
209					};
210
211					#prop_slv { container }
212				}
213			}
214
215			#[cfg(feature = "external-propagation")]
216			impl<P: crate::solver::propagation::Propagator> crate::solver::propagation::PropagatingSolver<P> for #prop_slv <P> {
217				type Slv = #ident;
218
219				fn access_solving(&mut self) -> (&mut dyn crate::solver::propagation::SolvingActions, &mut P) {
220					(&mut self.container.slv, &mut self.container.prop)
221				}
222
223				fn add_observed_var(&mut self, var: crate::Var) {
224					unsafe { #krate::ipasir_add_observed_var( self.container.slv. #ptr_attr, var.0.get()) };
225				}
226
227				fn into_parts(self) -> (Self::Slv, P) {
228					unsafe { #krate::ipasir_disconnect_external_propagator( self.container.slv. #ptr_attr ) };
229					(self.container.slv, self.container.prop)
230				}
231
232				fn propagator(&self) -> &P {
233					&self.container.prop
234				}
235
236				fn propagator_mut(&mut self) -> &mut P {
237					&mut self.container.prop
238				}
239
240				fn remove_observed_var(&mut self, var: crate::Var) {
241					unsafe { #krate::ipasir_remove_observed_var( self.container.slv. #ptr_attr, var.0.get()) };
242				}
243
244				fn reset_observed_vars(&mut self) {
245					unsafe { #krate::ipasir_reset_observed_vars( self.container.slv. #ptr_attr ) };
246				}
247
248				fn solve(&mut self) -> (&P, crate::solver::SolveResult<#sol_ident <'_>, #fail_type >) {
249					use crate::solver::Solver;
250					let res = self.container.slv.solve();
251					(&self.container.prop, res)
252				}
253
254				#[expect(
255					refining_impl_trait,
256					reason = "user can use more specific type if needed"
257				)]
258				fn solve_assuming<I: IntoIterator<Item = crate::Lit>>(
259					&mut self,
260					assumptions: I,
261				) -> (
262					&P,
263					crate::solver::SolveResult<#sol_ident <'_>, #fail_type >,
264				) {
265					use crate::solver::SolveAssuming;
266					let res = self.container.slv.solve_assuming(assumptions);
267					(&self.container.prop, res)
268				}
269
270				fn solver(&self) -> &Self::Slv {
271					&self.container.slv
272				}
273
274				fn solver_mut(&mut self) -> &mut Self::Slv {
275					&mut self.container.slv
276				}
277			}
278
279			#[cfg(feature = "external-propagation")]
280			impl<P> crate::ClauseDatabase for #prop_slv <P> {
281				fn add_clause_from_slice(&mut self, clause: &[crate::Lit]) -> crate::Result {
282					self.container.slv.add_clause_from_slice(clause)
283				}
284
285				fn new_var_range(&mut self, len: usize) -> crate::VarRange {
286					self.container.slv.new_var_range(len)
287				}
288			}
289
290			#[cfg(feature = "external-propagation")]
291			impl crate::solver::propagation::SolvingActions for #ident {
292				fn new_var(&mut self) -> crate::Var {
293					let var = <Self as crate::ClauseDatabaseTools>::new_var(self);
294					unsafe { #krate::ipasir_add_observed_var( #ptr , var.0.get()) };
295					var
296				}
297				fn is_decision(&mut self, lit: crate::Lit) -> bool {
298					unsafe { #krate::ipasir_is_decision( #ptr, lit.0.get() ) }
299				}
300			}
301
302			#[cfg(feature = "external-propagation")]
303			impl crate::solver::propagation::ExtendedSolvingActions for #ident {
304				fn force_backtrack(&mut self, new_level: usize) {
305					unsafe { #krate::ipasir_force_backtrack( #ptr, new_level ) }
306				}
307			}
308		}
309	} else {
310		quote!()
311	};
312
313	let from_cnf = if opts.has_default {
314		let var_member = match opts.vars {
315			Some(x) => quote! { #x },
316			None => quote! { vars },
317		};
318
319		quote! {
320			impl From<&crate::Cnf> for #ident {
321				fn from(value: &crate::Cnf) -> #ident {
322					let mut slv: #ident = Default::default();
323					slv. #var_member = value.nvar;
324					for cl in value.iter() {
325						// Ignore early detected unsatisfiability
326						let _ = crate::ClauseDatabaseTools::add_clause(&mut slv, cl.iter().copied());
327					}
328					slv
329				}
330			}
331		}
332	} else {
333		quote!()
334	};
335
336	quote! {
337		impl Drop for #ident {
338			fn drop(&mut self) {
339				unsafe { #krate::ipasir_release( #ptr ) }
340			}
341		}
342
343		// Safety: No one besides us has the raw solver pointer, so we can safely
344		// transfer the solver to another thread.
345		unsafe impl Send for #ident {}
346
347		impl crate::ClauseDatabase for #ident {
348			fn add_clause_from_slice(&mut self, clause: &[crate::Lit]) -> crate::Result{
349				let mut empty = true;
350				for &lit in clause {
351					unsafe { #krate::ipasir_add( #ptr , lit.into()) };
352					empty = false;
353				}
354				unsafe { #krate::ipasir_add( #ptr , 0) };
355				if empty {
356					Err(crate::Unsatisfiable)
357				} else {
358					Ok(())
359				}
360			}
361
362			fn new_var_range(&mut self, len: usize) -> crate::VarRange {
363				#vars .next_var_range(len)
364			}
365		}
366
367		impl crate::solver::Solver for #ident {
368			fn signature(&self) -> &str {
369				unsafe { std::ffi::CStr::from_ptr(#krate::ipasir_signature()) }
370					.to_str()
371					.unwrap()
372			}
373
374			#[expect(
375				refining_impl_trait,
376				reason = "user can use more specific type if needed"
377			)]
378			fn solve(&mut self) -> crate::solver::SolveResult<#sol_ident <'_>, #fail_type > {
379				let res = unsafe { #krate::ipasir_solve( #ptr ) };
380				match res {
381					10 => {
382						// 10 -> Sat
383						let sol = self.solver_solution_obj();
384						crate::solver::SolveResult::Satisfied(sol)
385					}
386					20 => {
387						// 20 -> Unsat
388						let fail = self.solver_fail_obj();
389						crate::solver::SolveResult::Unsatisfiable(fail)
390					},
391					_ => {
392						debug_assert_eq!(res, 0); // According to spec should be 0, unknown
393						crate::solver::SolveResult::Unknown
394					}
395				}
396			}
397		}
398
399		pub struct #sol_ident <'a> {
400			slv: &'a #ident,
401		}
402
403		impl #ident {
404			#[doc(hidden)] // TODO: Unsure whether this is a good idea.
405			pub fn emitted_vars(&self) -> crate::VarRange {
406				#vars .emitted_vars()
407			}
408
409			fn solver_solution_obj(&self) -> #sol_ident {
410				#sol_ident { slv: self }
411			}
412		}
413
414		impl crate::Valuation for #sol_ident <'_> {
415			fn value(&self, lit: crate::Lit) -> bool {
416				let var: i32 = lit.var().into();
417				// WARN: Always ask about variable (positive) literal, otherwise solvers sometimes seem incorrect
418				let ret = unsafe { #krate::ipasir_val( self.slv. #ptr_attr, var) };
419				match ret {
420					_ if ret == var => !lit.is_negated(),
421					_ if ret == -var => lit.is_negated(),
422					_ => {
423						debug_assert_eq!(ret, 0); // zero according to spec, both value are valid
424						false
425					}
426				}
427			}
428		}
429
430		#from_cnf
431		#assumptions
432		#term_callback
433		#learn_callback
434		#ipasir_up
435	}
436	.into()
437}
438
439fn default_true() -> bool {
440	true
441}