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 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 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 let sol = self.solver_solution_obj();
384 crate::solver::SolveResult::Satisfied(sol)
385 }
386 20 => {
387 let fail = self.solver_fail_obj();
389 crate::solver::SolveResult::Unsatisfiable(fail)
390 },
391 _ => {
392 debug_assert_eq!(res, 0); 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)] 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 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); 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}