Skip to main content

formally_smt/
decl.rs

1//
2// ::formally - the open-source formal methods toolchain
3//
4// Copyright (c) 2025 Nicola Gigante
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23//
24
25use crate::*;
26
27use formally::support::*;
28
29use derive_more::{Deref, From};
30use transitive::Transitive;
31
32use std::{fmt::Debug, sync::Arc};
33
34/// Associativity attribute of a primitive function.
35///
36/// These correspond to the `:left-assoc`, `:right-assoc`, `:chainable` and `:pairwise`
37/// annotations supported by SMT-LIBv2 theory declarations. They are mostly used to express
38/// associativity and similar syntactic properties of functions such as binary arithmetic
39/// operations, equality, etc.
40///
41/// See Section 3.7 "Theory Declarations" of the SMT-LIBv2
42/// [specification manual](https://smt-lib.org/language.shtml).
43///
44/// These annotations look like syntactic properties, but in fact have nothing to do with the
45/// parser. When parsing `(+ a b c d)`, the parser only sees a term applying function `+` to four
46/// arguments. Then, the name resolver ([Resolve::resolve()]) and the type checker
47/// ([TypeCheck::type_check()]) look for those attributes and understand that, even if `+` is
48/// declared as a *binary* function, it can take more than two arguments in the way that the
49/// annotation specifies.
50///
51/// See also the [theories!] macro to see how to specify these annotations when declaring theory
52/// symbols.
53#[derive(Copy, Clone, Debug)]
54pub enum Associativity {
55    /// Equivalent to the `:left-assoc` annotation.
56    LeftAssoc,
57    /// Equivalent to the `:right-assoc` annotation.
58    RightAssoc,
59    /// Equivalent to the `:chainable` annotation.
60    Chainable,
61    /// Equivalent to the `:pairwise` annotation.
62    Pairwise,
63}
64
65/// A free variable.
66///
67/// [Variable] represents a free variable in a term that can be bound by `let` expressions,
68/// quantified by `forall` or `exists` quantifiers, or bound to function parameters.
69///
70/// [Variable] objects can be created using the [Variable::new()] constructor or with the [var!] or
71/// [vars!] macros.
72///
73/// For example:
74/// ```
75/// # mod formally {
76/// #    pub extern crate formally_support as support;
77/// #    pub extern crate formally_smt as smt;
78/// # }
79/// # use formally::{smt::*, support::*};
80/// # fn main() -> Result<()> {
81/// let config = Config::default();
82/// let mut solver = Solver::new(&config)?;
83///
84/// solver.define(Definition::function("f", [var!(x Int)], sort!(Int), term!(* x 2)));
85///
86/// solver.require(term!(not (= (f 21) 42)))?;
87///
88/// assert_eq!(solver.check()?, Answer::No);
89///
90/// # Ok(())
91/// # }
92/// ```
93///
94/// The [Variable] type is parametric in a [ToSort] type `S` used to represent the sort of the
95/// variable, such as [Sort] itself or the result of the [sort!] macro. See the documentation of
96/// [ToSort] for details.
97///
98/// Comparison of [Variable] objects is *nominal*, that is, equality and hashing operate on the
99/// identity of the objects, not on their values. In other words, two [Variable] objects with
100/// exactly the same fields (and in particular the same *name*) created by two different calls to
101/// [Variable::new()] will compare *different* and have a possibly different hash. Moreover,
102/// cloning a [Variable] object is a cheap operation and produces a second object which compares
103/// *equal* to the first. Under the hood, this is the behavior of `Nominal<Arc<T>>` for some inner
104/// type `T`, so we also refer to the [Nominal] type for details.
105#[derive(Clone, Debug, Hash, PartialEq, Eq, Located)]
106pub struct Variable<S: ToSort = Sort>(Nominal<Arc<VariableData<S>>>);
107
108#[derive(Clone, Debug, Located, Locatable)]
109struct VariableData<S: ToSort> {
110    pub name: Identifier<'static>,
111    pub sort: S,
112    pub span: Option<Span>,
113}
114
115impl<S: ToSort> Variable<S> {
116    /// Create a new [Variable].
117    pub fn new<'a>(name: impl Into<Identifier<'a>>, sort: S) -> Variable<S> {
118        Variable(Nominal(Arc::new(VariableData {
119            name: name.into().into_owned(),
120            sort,
121            span: None,
122        })))
123    }
124
125    /// Get the variable's name.
126    pub fn name(&self) -> &Identifier<'static> {
127        &self.0.name
128    }
129
130    /// Get the variable's sort.
131    pub fn sort(&self) -> &S {
132        &self.0.sort
133    }
134}
135
136impl<S: Clone + ToSort> Locatable for Variable<S> {
137    type Located = Variable<S>;
138
139    fn over(self, span: impl Into<Option<Span>>) -> Self::Located {
140        Variable(Nominal(Arc::new((**self.0).clone().over(span.into()))))
141    }
142}
143
144/// A primitive function (or constant, or sort).
145///
146/// [Primitive] represents a primitive symbol provided by a theory. As such it is usually not
147/// created directly by users of the framework but indirectly by the [theories!] macro.
148///
149/// Comparison of [Primitive] objects is *nominal*, that is, equality and hashing operate on the
150/// identity of the objects, not on their values. In other words, two [Primitive] objects with
151/// exactly the same fields (and in particular the same *name*) created by two different invocations
152/// of the [theories!] macro will compare *different* and have a possibly different hash. Moreover,
153/// cloning a [Primitive] object is a cheap operation and produces a second object which compares
154/// *equal* to the first. Under the hood, this is the behavior of `Nominal<Arc<T>>` for some inner
155/// type `T`, so we also refer to the [Nominal] type for details.
156#[derive(Clone, Debug, Hash, PartialEq, Eq)]
157pub struct Primitive(pub(crate) Nominal<SArc<PrimitiveData>>);
158
159#[derive(Clone, Debug)]
160pub(crate) struct PrimitiveData {
161    pub name: Identifier<'static>,
162    pub parameters: Vec<Variable>,
163    pub domain: Vec<Sort>,
164    pub range: Sort,
165    pub associativity: Option<Associativity>,
166}
167
168impl Located for Primitive {
169    fn span(&self) -> Option<Span> {
170        None
171    }
172}
173
174impl Primitive {
175    /// Create a new [Primitive].
176    ///
177    /// Using this method directly should very seldom be necessary, as the preferred way of
178    /// introducing primitive symbols if through the [theories!] macro.
179    pub fn new<'a>(
180        name: impl Into<Identifier<'a>>,
181        parameters: Vec<Variable>,
182        domain: Vec<Sort>,
183        range: Sort,
184        associativity: Option<Associativity>,
185    ) -> Primitive {
186        Primitive(Nominal(SArc::Arc(Arc::new(PrimitiveData {
187            name: name.into().into_owned(),
188            parameters,
189            domain,
190            range,
191            associativity,
192        }))))
193    }
194
195    /// Get the name of the primitive function.
196    pub fn name(&self) -> &Identifier<'static> {
197        &self.0.name
198    }
199
200    /// Get the *type-level parameters* of the primitive function.
201    ///
202    /// Only primitive functions can be parametric and the parameters returned here are the
203    /// ones that SMT-LIBv2 theory declarations introduce with the `par` keyword.
204    ///
205    /// These are *not* the sorts of the arguments of the function. For that, call
206    /// [domain()](Primitive::domain()).
207    pub fn parameters(&self) -> &[Variable] {
208        &self.0.parameters
209    }
210
211    /// Get the domain of the primitive function, i.e. the sorts of its arguments.
212    pub fn domain(&self) -> &[Sort] {
213        &self.0.domain
214    }
215
216    /// Get the range of the primitive function, i.e. the sort of its return value.
217    pub fn range(&self) -> &Sort {
218        &self.0.range
219    }
220
221    /// Get the associativity annotation, if any, of the primitive function.
222    ///
223    /// See [Associativity] for details.
224    pub fn associativity(&self) -> Option<Associativity> {
225        self.0.associativity
226    }
227}
228
229/// Specification for declarations of functions (and constants, and sorts).
230///
231/// [Declaration] objects are passed to the [Solver::declare()] method to declare entities in a
232/// solver, by specifying all the attributes of such declarations. Then, the method returns a
233/// [Declared] object which is an opaque shared reference to a specific [Declaration] object which
234/// represents the actual entity the solver is keeping track of.
235///
236/// The [Declaration<D, R>] type is parametric in two [ToSort] types `D` and `R` used to represent
237/// the sort of the domain and of the range of the function, respectively. These can be e.g.,
238/// [Sort] itself or the result of the [sort!] macro. See the documentation of [ToSort] for details.
239///
240/// The fields are public and the type can be constructed freely, but some constructors are also
241/// provided ([function()](Declaration::function), [constant()](Declaration::constant), and
242/// [sort()](Declaration::sort)), for common cases.
243#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, Locatable)]
244pub struct Declaration<D: ToSort, R: ToSort> {
245    /// The name of the declared function.
246    pub name: Identifier<'static>,
247    /// The domain of the declared function, i.e. the sorts of its arguments.
248    pub domain: Vec<D>,
249    /// The range of the declared function, i.e. its return type.
250    pub range: R,
251    /// The optional source span the declaration comes from.
252    pub span: Option<Span>,
253}
254
255impl<D: ToSort, R: ToSort> Declaration<D, R> {
256    /// Declare a function (or a constant, or a sort).
257    ///
258    /// This is the most general constructor. It is more convenient than directly constructing the
259    /// [Declaration] struct because of implicit conversions of the arguments and the absence
260    /// of the `span` argument which is put to `None` by default, since most of the time when
261    /// using the API directly, declarations do not have a source span.
262    ///
263    /// If the declaration do have a span it can be set using [over()](Declaration::over()).
264    ///
265    /// In the following example we declare a function taking a integer argument and returning a
266    /// real and we set it to a given span.
267    /// ```
268    /// # mod formally {
269    /// #     pub extern crate formally_smt as smt;
270    /// #     pub extern crate formally_support as support;
271    /// # }
272    /// # use formally::{smt::*, support::*};
273    /// #
274    /// # fn main() -> Result<()> {
275    /// # let mut solver = Solver::new(&Config::new())?;
276    /// solver.declare(Declaration::function("f", [sort!(Int)], sort!(Real)))?;
277    /// # Ok(())
278    /// # }
279    /// ```
280    pub fn function<'a>(
281        name: impl Into<Identifier<'a>>,
282        domain: impl IntoIterator<Item = D>,
283        range: R,
284    ) -> Declaration<D, R> {
285        Declaration {
286            name: name.into().into_owned(),
287            domain: domain.into_iter().collect(),
288            range,
289            span: None,
290        }
291    }
292}
293
294impl<D: ToSort> Declaration<D, Sort> {
295    /// Declare a predicate (i.e. a function returning [Core::Bool()](theories::Core::Bool()).
296    ///
297    /// This is equivalent to `Declaration::function(name, domain, theories::Core::Bool())`.
298    pub fn predicate<'a>(
299        name: impl Into<Identifier<'a>>,
300        domain: impl IntoIterator<Item = D>,
301    ) -> Declaration<D, Sort> {
302        Declaration::function(name, domain, theories::Core::Bool())
303    }
304}
305
306impl<R: ToSort> Declaration<Sort, R> {
307    /// Declare a constant (i.e. a function with no arguments).
308    ///
309    /// This is equivalent to `Declaration::function(name, Vec::<Sort>::new(), sort)`.
310    pub fn constant<'a>(name: impl Into<Identifier<'a>>, sort: R) -> Declaration<Sort, R> {
311        Declaration::function(name, Vec::<Sort>::new(), sort)
312    }
313}
314
315impl Declaration<Sort, Sort> {
316    /// Declare a sort (i.e. a constant of the special sort [Sort::sort()]).
317    ///
318    /// This is equivalent to `Declaration::constant(name, Sort::sort())`.
319    pub fn sort<'a>(name: impl Into<Identifier<'a>>) -> Declaration<Sort, Sort> {
320        Declaration::constant(name, Sort::sort())
321    }
322
323    /// Declare a Boolean constant (i.e. a constant of sort [Core::Bool()](theories::Core::Bool()).
324    ///
325    /// This is equivalent to `Declaration::constant(name, theories::Core::Bool())`.
326    pub fn boolean<'a>(name: impl Into<Identifier<'a>>) -> Declaration<Sort, Sort> {
327        Declaration::constant(name, theories::Core::Bool())
328    }
329
330    /// Declare an integer constant (i.e. a constant of sort [Ints::Int()](theories::Ints::Int()).
331    ///
332    /// This is equivalent to `Declaration::constant(name, theories::Ints::Int())`.
333    pub fn integer<'a>(name: impl Into<Identifier<'a>>) -> Declaration<Sort, Sort> {
334        Declaration::constant(name, theories::Ints::Int())
335    }
336
337    /// Declare a real constant (i.e. a constant of sort [Reals::Real()](theories::Reals::Real()).
338    ///
339    /// This is equivalent to `Declaration::constant(name, theories::Reals::Real())`.
340    pub fn real<'a>(name: impl Into<Identifier<'a>>) -> Declaration<Sort, Sort> {
341        Declaration::constant(name, theories::Reals::Real())
342    }
343}
344
345/// Represent a specific function (or constant, or sort) declared in a [Solver].
346///
347/// [Declared] is an opaque shared reference to a [Declaration] declared in a [Solver].
348/// The only way to obtain a [Declared] is through [Solver::declare()]. [Declared] derefs to the
349/// underlying [Declaration] to immutably access its fields.
350///
351/// Comparison of [Declared] objects is *nominal*, that is, equality and hashing operate on the
352/// identity of the objects, not on their values. In other words, two [Declared] objects with
353/// exactly the same fields (and in particular the same *name*), created by two different calls to
354/// [Solver::declare()] will compare *different* and have a possibly different hash. Moreover,
355/// cloning a [Declared] object is a cheap operation and produces a second object which compares
356/// *equal* to the first. Under the hood, this is the behavior of `Nominal<Arc<Declaration>>`, so we
357/// also refer to the [Nominal] type for details.
358#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, Deref)]
359pub struct Declared(pub(crate) Nominal<Arc<Declaration<Sort, Sort>>>);
360
361/// Specification for definitions of functions (and constants, and sorts).
362///
363/// [Definition] objects are passed to the [Solver::define()] method to define entities in a
364/// solver, by specifying all the attributes of such definitions. Then, the method returns a
365/// [Defined] object which is an opaque shared reference to a specific [Definition] object which
366/// represents the actual entity the solver is keeping track of.
367///
368/// The [Definition<V, R, B>] type is parametric in two [ToSort] types `V` and `R` used to
369/// represent the sort of the variables and of the range of the definition, respectively, and in
370/// a [ToTerm] `B` type used to represent the term of the body of the definition. See the
371/// documentation of [ToSort] and [ToTerm] for details.
372///
373/// The fields are public and the type can be constructed freely, but some constructors are also
374/// provided ([function()](Definition::function), [constant()](Definition::constant), and
375/// [sort()](Definition::sort)), for common cases.
376#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, Locatable)]
377pub struct Definition<V: ToSort, R: ToSort, B: ToTerm> {
378    pub name: Identifier<'static>,
379    pub domain: Vec<Variable<V>>,
380    pub range: R,
381    pub body: B,
382    pub span: Option<Span>,
383}
384
385impl<V: ToSort, R: ToSort, B: ToTerm> Definition<V, R, B> {
386    /// Define a function (or a constant, or a sort).
387    ///
388    /// This is the most general constructor. It is more convenient than directly constructing the
389    /// [Definition] struct because of implicit conversions of the arguments and the absence
390    /// of the `span` argument which is put to `None` by default, since most of the time when
391    /// using the API directly, declarations do not have a source span.
392    ///
393    /// If the declaration do have a span it can be set using [over()](Definition::over()).
394    ///
395    /// The function parameters are created as objects of type [Variable] which are given to the
396    /// `domain` field and freely used in the body.
397    ///
398    /// In the following example we define a function `mult-add` taking three real arguments,
399    /// computing a *multiply-add* operation argument and returning it. Note that when defining
400    /// the body with the `term!` macro, the parameters are in scope and can just be referred to by
401    /// name (the same name given to the constructor of [Variable]).
402    /// ```
403    /// # mod formally {
404    /// #    pub extern crate formally_smt as smt;
405    /// #    pub extern crate formally_support as support;
406    /// # }
407    /// # use formally::{smt::{*, theories::*}, support::*};
408    /// # fn main() -> Result<()> {
409    /// # let mut solver = Solver::new(&Config::new().logic("NRA"))?;
410    ///
411    /// let a = var!(a Real);
412    /// let b = var!(b Real);
413    /// let c = var!(c Real);
414    ///
415    /// solver.define(
416    ///     Definition::function("mult-add", [a, b, c], Reals::Real(), term!(+ (* a b) c))
417    /// )?;
418    /// # Ok(())
419    /// # }
420    /// ```
421    pub fn function<'a>(
422        name: impl Into<Identifier<'a>>,
423        domain: impl IntoIterator<Item = Variable<V>>,
424        range: R,
425        body: B,
426    ) -> Definition<V, R, B> {
427        Definition {
428            name: name.into().into_owned(),
429            domain: domain.into_iter().collect(),
430            range,
431            body,
432            span: None,
433        }
434    }
435}
436
437impl<V: ToSort, B: ToTerm> Definition<V, Sort, B> {
438    /// Define a predicate (i.e. a function returning [Core::Bool()](theories::Core::Bool()).
439    ///
440    /// This is equivalent to `Definition::function(name, domain, theories::Core::Bool(), body)`.
441    pub fn predicate<'a>(
442        name: impl Into<Identifier<'a>>,
443        domain: impl IntoIterator<Item = Variable<V>>,
444        body: B,
445    ) -> Definition<V, Sort, B> {
446        Definition::function(name, domain, theories::Core::Bool(), body)
447    }
448
449    /// Define a parametric sort (i.e. a function returning the special sort [Sort::sort()]).
450    ///
451    /// This is equivalent to `Definition::function(name, domain, Sort::sort(), body)`.
452    pub fn sort<'a>(
453        name: impl Into<Identifier<'a>>,
454        domain: impl IntoIterator<Item = Variable<V>>,
455        body: B,
456    ) -> Definition<V, Sort, B> {
457        Definition::function(name, domain, Sort::sort(), body)
458    }
459}
460
461impl<R: ToSort, B: ToTerm> Definition<Sort, R, B> {
462    /// Define a constant (i.e. a function with no arguments).
463    ///
464    /// This is equivalent to `Definition::function(name, [], sort, body)`.
465    pub fn constant<'a>(
466        name: impl Into<Identifier<'a>>,
467        sort: R,
468        body: B,
469    ) -> Definition<Sort, R, B> {
470        Definition::function(name, [], sort, body)
471    }
472}
473
474impl<B: ToTerm> Definition<Sort, Sort, B> {
475    /// Define a Boolean constant (i.e. a constant of sort [Core::Bool()](theories::Core::Bool()).
476    ///
477    /// This is equivalent to `Definition::constant(name, theories::Core::Bool(), value)`.
478    pub fn boolean<'a>(name: impl Into<Identifier<'a>>, value: B) -> Definition<Sort, Sort, B> {
479        Definition::constant(name, theories::Core::Bool(), value)
480    }
481
482    /// Define an integer constant (i.e. a constant of sort [Ints::Int()](theories::Ints::Int()).
483    ///
484    /// This is equivalent to `Definition::constant(name, theories::Ints::Int(), value)`.
485    pub fn integer<'a>(name: impl Into<Identifier<'a>>, value: B) -> Definition<Sort, Sort, B> {
486        Definition::constant(name, theories::Ints::Int(), value)
487    }
488
489    /// Define a real constant (i.e. a constant of sort [Reals::Real()](theories::Reals::Real()).
490    ///
491    /// This is equivalent to `Definition::constant(name, theories::Reals::Real(), value)`.
492    pub fn real<'a>(name: impl Into<Identifier<'a>>, value: B) -> Definition<Sort, Sort, B> {
493        Definition::constant(name, theories::Reals::Real(), value)
494    }
495}
496
497/// Represent a specific function (or constant, or sort) defined in a [Solver].
498///
499/// [Defined] is an opaque shared reference to a [Definition] defined in a [Solver].
500/// The only way to obtain a [Defined] is through [Solver::define()]. [Defined] derefs to the
501/// underlying [Definition] to immutably access its fields.
502///
503/// Comparison of [Defined] objects is *nominal*, that is, equality and hashing operate on the
504/// identity of the objects, not on their values. In other words, two [Defined] objects with
505/// exactly the same fields (and in particular the same *name*) created by two different calls to
506/// [Solver::define()] will compare *different* and have a possibly different hash. Moreover,
507/// cloning a [Defined] object is a cheap operation and produces a second object which compares
508/// *equal* to the first. Under the hood, this is the behavior of `Nominal<Arc<Definition>>`, so we
509/// also refer to the [Nominal] type for details.
510#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, Deref)]
511pub struct Defined(pub(crate) Nominal<Arc<Definition<Sort, Sort, Term>>>);
512
513/// A function.
514///
515/// The [Function] type represent any function usable to build terms.
516///
517/// A function can be a *user function*, i.e. a [UserFunction] object which in turn can be either
518/// [Declared] or [Defined], a [Variable], or a [Primitive].
519///
520/// Some accessor methods are provided to get fields in common between the variants avoiding
521/// redundant pattern matches.
522#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, From, Transitive)]
523#[allow(clippy::duplicated_attributes)]
524#[transitive(from(Declared, UserFunction))]
525#[transitive(from(Defined, UserFunction))]
526pub enum Function {
527    /// A variable.
528    Variable(Variable),
529    /// A primitive function declared by some theory.
530    Primitive(Primitive),
531    /// A user function, i.e. either [Declared] or [Defined].
532    User(UserFunction),
533}
534
535impl Function {
536    /// Get the name of the function.
537    pub fn name(&self) -> &Identifier<'static> {
538        match self {
539            Function::Variable(var) => var.name(),
540            Function::Primitive(prim) => prim.name(),
541            Function::User(user) => user.name(),
542        }
543    }
544
545    /// Get the *type-level parameters* of a function.
546    ///
547    /// Only primitive functions can be parametric and the parameters returned here are the
548    /// ones that SMT-LIBv2 theory declarations introduce with the `par` keyword.
549    ///
550    /// These are *not* the sorts of the arguments of the function. For that, call
551    /// [domain()](Function::domain()).
552    pub fn parameters(&self) -> Vec<Sort> {
553        match self {
554            Function::Variable(_) => Vec::new(),
555            Function::Primitive(prim) => {
556                prim.parameters().iter().map(|p| p.sort().clone()).collect()
557            }
558            Function::User(_) => Vec::new(),
559        }
560    }
561
562    /// Get the domain of the function, i.e. the sorts of its arguments.
563    pub fn domain(&self) -> Vec<Sort> {
564        match self {
565            Function::Variable(_) => Vec::new(),
566            Function::Primitive(prim) => prim.domain().to_vec(),
567            Function::User(user) => user.domain(),
568        }
569    }
570
571    /// Get the range of the function, i.e. the sort of its return value.
572    pub fn range(&self) -> &Sort {
573        match self {
574            Function::Variable(var) => var.sort(),
575            Function::Primitive(prim) => prim.range(),
576            Function::User(user) => user.range(),
577        }
578    }
579}
580
581/// A *user function*, i.e. a function either declared or defined in some [Solver].
582///
583/// Some accessor methods are provided to get fields in common between the variants avoiding
584/// redundant pattern matches.
585#[derive(Clone, Debug, Hash, PartialEq, Eq, Located, From)]
586pub enum UserFunction {
587    /// A declared function.
588    Declared(Declared),
589    /// A defined function.
590    Defined(Defined),
591}
592
593impl UserFunction {
594    /// Get the name of the function.
595    pub fn name(&self) -> &Identifier<'static> {
596        match self {
597            UserFunction::Declared(decl) => &decl.name,
598            UserFunction::Defined(def) => &def.name,
599        }
600    }
601
602    /// Get the domain of the function, i.e. the sorts of its arguments.
603    pub fn domain(&self) -> Vec<Sort> {
604        match self {
605            UserFunction::Declared(decl) => decl.domain.clone(),
606            UserFunction::Defined(def) => def.domain.iter().map(|p| p.sort().clone()).collect(),
607        }
608    }
609
610    /// Get the range of the function, i.e. the sort of its return value.
611    pub fn range(&self) -> &Sort {
612        match self {
613            UserFunction::Declared(decl) => &decl.range,
614            UserFunction::Defined(def) => &def.range,
615        }
616    }
617}