1

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.

chansey
  • 1,266
  • 9
  • 20

1 Answers1

4

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.

alias
  • 28,120
  • 2
  • 23
  • 40
  • Extremely clear explanation. Thank you very much! – chansey Aug 18 '21 at 14:43
  • 1
    @chansey Added another note on how this sort of usage enables ad-hoc polymorphism, though this feature is only reserved for theory symbols for the time being. (But can be relaxed in the future for user-defined symbols as well.) – alias Aug 18 '21 at 14:51
  • Hi, Sorry to disturb you again. I seemly found an approach to temporarily remove this feature, for example, `(declare-const x Int) (declare-const x Bool)`. I hope the 2nd declare returns an error. So I can redefine these two declares as: `(declare-fun x () Int) (assert (= (as x Int) x)) (declare-fun x () Bool) (assert (= (as x Bool) x))`, then it reports an error as expected. How do you evaluate this approach? Can it be used as a convention for someone hopes a symbol can only be declared as an unique type in SMT community? Thanks. – chansey Aug 29 '21 at 13:48
  • @chansey I'm not sure. Looks like both z3 and cvc4 issue an error on this case, but it's not clear to me if this is required behavior by the standard. That is, a different solver might accept it as is without complaining, by resolving the reference correctly. You might want to ask at the smt-lib mailing list for further clarification: https://groups.google.com/g/smt-lib – alias Sep 03 '21 at 22:37