Skip to main content

conjure_cp_core/ast/
reference.rs

1use crate::ast::serde::{AsId, HasId};
2use crate::representation::types::ReprGetOrInitResult;
3use crate::representation::{
4    ReferenceReprError, ReprError, ReprRule, ReprRulePtr, ReprSelectError, ReprStateStored,
5};
6use crate::{ast::DeclarationPtr, bug};
7use derivative::Derivative;
8use parking_lot::{MappedRwLockReadGuard, RwLockReadGuard};
9use serde::{Deserialize, Serialize};
10use serde_with::serde_as;
11use std::fmt::{Display, Formatter};
12use uniplate::Uniplate;
13
14use super::{
15    Atom, DeclarationKind, DomainPtr, Expression, GroundDomain, Literal, Metadata, Moo, Name,
16    ReturnType,
17    categories::{Category, CategoryOf},
18    domains::HasDomain,
19};
20
21/// A reference to a declaration (variable, parameter, etc.)
22///
23/// This is a thin wrapper around [`DeclarationPtr`] with two main purposes:
24/// 1. Encapsulate the serde pragmas (e.g., serializing as IDs rather than full objects)
25/// 2. Enable type-directed traversals of references via uniplate
26#[serde_as]
27#[derive(
28    Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Uniplate, Derivative,
29)]
30#[derivative(Hash)]
31#[uniplate()]
32#[biplate(to=DeclarationPtr)]
33#[biplate(to=Name)]
34pub struct Reference {
35    #[serde_as(as = "AsId")]
36    pub ptr: DeclarationPtr,
37    pub repr: Option<ReprRulePtr>,
38}
39
40impl Reference {
41    pub fn new(ptr: DeclarationPtr) -> Self {
42        Reference { ptr, repr: None }
43    }
44
45    pub fn ptr(&self) -> &DeclarationPtr {
46        &self.ptr
47    }
48
49    pub fn into_ptr(self) -> DeclarationPtr {
50        self.ptr
51    }
52
53    pub fn name(&self) -> MappedRwLockReadGuard<'_, Name> {
54        self.ptr.name()
55    }
56
57    pub fn id(&self) -> crate::ast::serde::ObjId {
58        self.ptr.id()
59    }
60
61    pub fn domain(&self) -> Option<DomainPtr> {
62        self.ptr.domain()
63    }
64
65    pub fn resolved_domain(&self) -> Option<Moo<GroundDomain>> {
66        self.domain()?.resolve().ok()
67    }
68
69    /// Select the given representation for this reference, if it is currently unrepresented
70    /// and the representation exists for the underlying variable.
71    ///
72    /// # Errors
73    /// - [ReprSelectError::AlreadySelected] if a different representation is already selected for this reference
74    /// - [ReprSelectError::DoesNotExist] if the representation does not exist for this variable
75    ///
76    /// # Returns
77    /// State of the initialised representation
78    pub fn select_repr<R: ReprRule + ?Sized>(
79        &mut self,
80    ) -> Result<MappedRwLockReadGuard<'_, R::DeclLevel>, ReprSelectError> {
81        let _ = self.select_repr_via(R::STORED);
82        Ok(self.repr_state_as_unchecked::<R>())
83    }
84
85    /// Same as [Reference::select_repr], but type-erased
86    pub fn select_repr_via(
87        &mut self,
88        rule: ReprRulePtr,
89    ) -> Result<MappedRwLockReadGuard<'_, dyn ReprStateStored>, ReprSelectError> {
90        if let Some(repr) = self.repr
91            && repr != rule
92        {
93            return Err(ReprSelectError::AlreadySelected(repr));
94        }
95        if !self.ptr.reprs().has_repr(rule) {
96            return Err(ReprSelectError::DoesNotExist(self.ptr.clone(), rule.name()));
97        }
98        self.repr = Some(rule);
99        Ok(self.repr_state_unchecked())
100    }
101
102    /// Same as [Reference::select_or_init_repr], but type-erased
103    pub fn select_or_init_repr_via(
104        &mut self,
105        rule: ReprRulePtr,
106    ) -> ReprGetOrInitResult<'_, dyn ReprStateStored, ReferenceReprError> {
107        if let Some(repr) = self.repr
108            && repr != rule
109        {
110            return Err(ReprSelectError::AlreadySelected(repr).into());
111        }
112        self.update_or_init_repr_via(rule).map_err(Into::into)
113    }
114
115    /// Select the given representation for this reference, initialising it if necessary.
116    /// Will fail if a different representation is already selected.
117    ///
118    /// # Errors
119    /// - [ReprSelectError] if a different representation is already selected for this reference
120    /// - [ReprInitError] | [ReprInstantiateError] if the representation could not be initialised
121    ///
122    /// # Returns
123    ///
124    /// `(state, symbols, constraints)`
125    /// where:
126    /// - `state` is an instance of the given representation
127    /// - `symbols` are new variables created by the representation
128    /// - `constraints` are new top-level constraints created by the representation
129    pub fn select_or_init_repr<R: ReprRule + ?Sized>(
130        &mut self,
131    ) -> ReprGetOrInitResult<'_, R::DeclLevel, ReferenceReprError> {
132        let (_, symbols, constraints) = self.select_or_init_repr_via(R::STORED)?;
133        let state = self.repr_state_as_unchecked::<R>();
134        Ok((state, symbols, constraints))
135    }
136
137    /// Select the given representation for this reference, initialising it if necessary.
138    /// Will overwrite the existing selection.
139    ///
140    /// # Errors
141    /// - [ReprInitError] | [ReprInstantiateError] if the representation could not be initialised
142    ///
143    /// # Returns
144    ///
145    /// `(state, symbols, constraints)`
146    /// where:
147    /// - `state` is an instance of the given representation
148    /// - `symbols` are new variables created by the representation
149    /// - `constraints` are new top-level constraints created by the representation
150    pub fn update_or_init_repr<R: ReprRule + ?Sized>(
151        &mut self,
152    ) -> ReprGetOrInitResult<'_, R::DeclLevel, ReprError> {
153        let (_, symbols, constraints) = self.update_or_init_repr_via(R::STORED)?;
154        let state = self.repr_state_as_unchecked::<R>();
155        Ok((state, symbols, constraints))
156    }
157
158    /// Same as [Reference::update_or_init_repr], but type-erased
159    pub fn update_or_init_repr_via(
160        &mut self,
161        rule: ReprRulePtr,
162    ) -> ReprGetOrInitResult<'_, dyn ReprStateStored, ReprError> {
163        let (symbols, constraints) = rule.init_for_if_not_exists(&mut self.ptr)?;
164        self.repr = Some(rule);
165        let state = self.repr_state_unchecked();
166        Ok((state, symbols, constraints))
167    }
168
169    /// If this reference has a representation selected, return `(rule, state)`
170    /// where
171    /// - `rule` is a pointer to the representation rule
172    /// - `state` is an instance of that representation
173    pub fn get_repr(
174        &self,
175    ) -> Option<(ReprRulePtr, MappedRwLockReadGuard<'_, dyn ReprStateStored>)> {
176        let rule = self.repr?;
177        Some((rule, self.repr_state_unchecked()))
178    }
179
180    /// If this reference has this specific representation selected, get its state as a concrete type
181    pub fn get_repr_as<R: ReprRule + ?Sized>(
182        &self,
183    ) -> Option<MappedRwLockReadGuard<'_, R::DeclLevel>> {
184        if let Some(rule) = self.repr
185            && rule == R::STORED
186        {
187            return Some(self.repr_state_as_unchecked::<R>());
188        }
189        None
190    }
191
192    /// If this reference has a representation selected, return its state, otherwise crash
193    fn repr_state_unchecked(&self) -> MappedRwLockReadGuard<'_, dyn ReprStateStored> {
194        let rule = self
195            .repr
196            .unwrap_or_else(|| bug!("`{}` had no representation", self.name()));
197        RwLockReadGuard::map(self.ptr.reprs(), |reprs| {
198            reprs.get_by_rule(rule).unwrap_or_else(|| {
199                bug!(
200                    "Representation '{}' was selected for '{}' but its state was not stored!",
201                    rule.name(),
202                    self.name()
203                )
204            })
205        })
206    }
207
208    /// If this reference has this specific representation selected, return its state
209    /// as a concrete type, otherwise crash
210    fn repr_state_as_unchecked<R: ReprRule + ?Sized>(
211        &self,
212    ) -> MappedRwLockReadGuard<'_, R::DeclLevel> {
213        R::get_for(&self.ptr).unwrap_or_else(|| {
214            bug!(
215                "`{}` did not have the expected representation `{}`",
216                self.name(),
217                R::NAME
218            )
219        })
220    }
221
222    /// Returns the expression behind a value-letting reference, if this is one.
223    ///
224    /// Prefer [`Reference::with_resolved_expression`] when the expression only needs to be
225    /// inspected. Returning an owned value here necessarily clones collection-bearing
226    /// expressions such as matrix literals.
227    pub fn resolve_expression(&self) -> Option<Expression> {
228        if let Some(expr) = self.ptr().as_value_letting() {
229            return Some(expr.clone());
230        }
231
232        let generator = {
233            let kind = self.ptr.kind();
234            if let DeclarationKind::Quantified(inner) = &*kind {
235                inner.generator().cloned()
236            } else {
237                None
238            }
239        };
240
241        if let Some(generator) = generator
242            && let Some(expr) = generator.as_value_letting()
243        {
244            return Some(expr.clone());
245        }
246
247        None
248    }
249
250    /// The formula named by this boolean value letting, when that formula is not an atom.
251    ///
252    /// Constants and aliases of decision variables return [`None`]: those names already stand for
253    /// a value. An alias of another value letting returns that reference, so the caller can follow
254    /// the chain. Unit `or`/`and` removal rewrites a singleton connective down to the remaining
255    /// name, and that name is still a connective.
256    pub fn boolean_formula_letting_body(&self) -> Option<Expression> {
257        let body = self.resolve_expression()?;
258        boolean_formula_letting(&body).then_some(body)
259    }
260
261    /// Calls `inspect` with the expression behind a value-letting reference.
262    ///
263    /// The declaration is kept read-locked for the duration of `inspect`, allowing callers to ask
264    /// questions about large value lettings without cloning them. Quantified declarations may
265    /// delegate their value to an expression generator; those are handled here as well.
266    pub fn with_resolved_expression<T>(&self, inspect: impl FnOnce(&Expression) -> T) -> Option<T> {
267        if let Some(expr) = self.ptr().as_value_letting() {
268            return Some(inspect(&expr));
269        }
270
271        let generator = {
272            let kind = self.ptr.kind();
273            if let DeclarationKind::Quantified(inner) = &*kind {
274                inner.generator().cloned()
275            } else {
276                None
277            }
278        }?;
279        let expr = generator.as_value_letting()?;
280        Some(inspect(&expr))
281    }
282
283    /// Evaluates this reference to a literal if it resolves to a constant.
284    pub fn resolve_constant(&self) -> Option<Literal> {
285        self.with_resolved_expression(super::eval::eval_constant)
286            .flatten()
287    }
288
289    /// Resolves this reference to an atomic expression, if possible.
290    pub fn resolve_atomic(&self) -> Option<Atom> {
291        self.with_resolved_expression(|expr| match expr {
292            Expression::Atomic(_, atom) => Some(atom.clone()),
293            _ => None,
294        })
295        .flatten()
296    }
297}
298
299/// True when a value-letting body should be read as a boolean formula rather than as an atom.
300fn boolean_formula_letting(body: &Expression) -> bool {
301    match body {
302        Expression::Atomic(_, Atom::Reference(next)) => next.resolve_expression().is_some(),
303        Expression::Atomic(_, _) => false,
304        other => {
305            other.try_return_type() == Some(ReturnType::Bool)
306                || matches!(
307                    other,
308                    Expression::Not(_, _)
309                        | Expression::Or(_, _)
310                        | Expression::And(_, _)
311                        | Expression::Imply(_, _, _)
312                        | Expression::Iff(_, _, _)
313                        | Expression::Eq(_, _, _)
314                        | Expression::Neq(_, _, _)
315                        | Expression::Geq(_, _, _)
316                        | Expression::Leq(_, _, _)
317                        | Expression::Gt(_, _, _)
318                        | Expression::Lt(_, _, _)
319                        | Expression::FlatIneq(_, _, _, _)
320                        | Expression::MinionReify(_, _, _)
321                        | Expression::MinionReifyImply(_, _, _)
322                )
323        }
324    }
325}
326
327impl From<Reference> for Expression {
328    fn from(value: Reference) -> Self {
329        Expression::Atomic(Metadata::new(), value.into())
330    }
331}
332
333impl From<Reference> for Moo<Expression> {
334    fn from(value: Reference) -> Self {
335        Moo::new(value.into())
336    }
337}
338
339impl From<DeclarationPtr> for Reference {
340    fn from(ptr: DeclarationPtr) -> Self {
341        Reference::new(ptr)
342    }
343}
344
345impl CategoryOf for Reference {
346    fn category_of(&self) -> Category {
347        self.ptr.category_of()
348    }
349}
350
351impl HasDomain for Reference {
352    fn domain_of(&self) -> DomainPtr {
353        self.ptr
354            .domain()
355            .or_else(|| self.resolve_constant().map(|literal| literal.domain_of()))
356            .unwrap_or_else(|| {
357                bug!(
358                    "reference ({name}) should have a domain",
359                    name = self.ptr.name()
360                )
361            })
362    }
363}
364
365impl Display for Reference {
366    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
367        self.ptr.name().fmt(f)
368    }
369}