I have a question about declare-const
in smtlib.
For example,
In z3/cvc4, the following program doesn't report an error:
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
In the smt-lib-reference, it says that
(declare-fun f (s1 ... sn) s) ... The command reports an error if a function symbol with name f is already present in the current signature.
So the sort s
is included in the entire signature of the x
, is that right?
But why is it so? What is the motivation behind it?
In my understanding, the x
is variable identifier and in general (e.g. in some general programming languages) we are not allowed to declare the same variable with different types. So I think the above code is best to report an error.
I once thought that perhaps z3/smtlib can support redefinition?, but not...
C:\Users\Chansey>z3 -in
(declare-const x Int)
(declare-const x Bool)
(assert (= x true))
(error "line 3 column 11: ambiguous constant reference, more than one constant with the same sort, use a qualified expre
ssion (as <symbol> <sort>) to disambiguate x")
So the above code is definitely wrong, why not report the error earlier?
PS. If I use the same sort, then it will report an error (that great, I hope the Bool
case can also report the error):
C:\Users\Chansey>z3 -in
(declare-fun x () Int)
(declare-fun x () Int)
(error "line 2 column 21: invalid declaration, constant 'x' (with the given signature) already declared")
Thanks.
In SMTLib, a symbol is identified not just by its name, but also by its sort. And it's perfectly fine to use the same name, so long as you have a different sort, as you observed. Here's an example:
(set-logic ALL)
(set-option :produce-models true)
(declare-fun x () Int)
(declare-fun x () Bool)
(assert (= (as x Int) 4))
(assert (= (as x Bool) true))
(check-sat)
(get-model)
(get-value ((as x Int)))
(get-value ((as x Bool)))
This prints:
sat
(
(define-fun x () Bool
true)
(define-fun x () Int
4)
)
(((as x Int) 4))
(((as x Bool) true))
Note how we use the as
construct to disambiguate between the two x
's. This is explained in Section 3.6.4 of http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2021-05-12.pdf
Having said that, I do agree the part of the document you quoted isn't very clear about this, and perhaps can use a bit of a clarifying text.
Regarding what the motivation is for allowing this sort of usage: There are two main reasons. The first is o simplify generating SMTLib. Note that SMTLib is usually not intended to be hand-written. It's often generated from a higher-level system that uses an SMT solver underneath. So being flexible in allowing symbols to share name so long as they can be distinguished by explicit sort annotations can be beneficial when you use SMTLib as an intermediate language between a higher-level system and the solver itself. But when you're writing SMTLib by hand, you should probably avoid this sort of duplication if you can, for clarity if nothing else.
The second reason is to allow for a limited form of "overloading" to be used. For instance, think about the SMTLib function distinct
. This function can operate on any type of object (Int
, Bool
, Real
etc.), yet it's always called distinct
. (We don't have distinct-int
, distinct-bool
etc.) The solver "distinguishes" which one you meant by doing a bit of an analysis, but in cases it cannot, you can help it along with an as
declaration as well. So, theory symbols can be overloaded in this way, which is also the case for =
, +
, *
, etc. Of course, SMTLib does not allow for users to define such overloaded names, but as the document states in footnote 29 on page 91, this restriction might be removed in the future.