• Top
    • Documentation
    • Books
    • Boolean-reasoning
    • Debugging
    • Projects
    • Std
    • Proof-automation
    • Macro-libraries
      • B*
      • Defunc
      • Fty
      • Apt
      • Std/util
      • Defdata
      • Defrstobj
      • Seq
      • Match-tree
      • Defrstobj
      • With-supporters
        • Def-partial-measure
        • Template-subst
        • Soft
        • Defthm-domain
        • Event-macros
        • Def-universal-equiv
        • Def-saved-obligs
        • With-supporters-after
        • Definec
        • Sig
        • Outer-local
        • Data-structures
      • ACL2
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Testing-utilities
      • Math
    • Macro-libraries

    With-supporters

    Automatically define necessary redundant definitions from locally included books

    When local include-book forms are used in support of definitions and theorems, the resulting book or encapsulate event may be ill-formed because of missing definitions. The macro, with-supporters, is intended to avoid this problem. See also with-supporters-after for a related utility.

    General forms:

    (with-supporters local-event event-1 ... event-k)
    (with-supporters local-event
                     :names (name-1 ... name-m)
                     event-1 ... event-k)

    where local-event and each event event-i and (if supplied) name-i are events and local-event is of the form (local <event>). The effect is the same as

    ((encapsulate () local-event EXTRA event-1 ... event-k)

    where EXTRA is a sequence of events that includes the following, in an attempt to re-create the environment produced by local-event in order to process each event-i during the second pass of the encapsulate event.

    • function definitions
    • definitions of macros that are aliases for additional functions being defined; see macro-aliases-table
    • in-theory events so that the rules introduced by the EXTRA definitions are suitably enable or disabled
    • If :names is supplied, then the first events in EXTRA are the events named by the name-i, in order.

    We now illustrate with examples, starting with one that does not use the :names keyword.

    Consider the following event.

    (encapsulate
     ()
     (local (include-book "std/lists/duplicity" :dir :system))
     (defthm duplicity-append
       (equal (duplicity a (append x y))
              (+ (duplicity a x) (duplicity a y)))))

    This event fails because the function duplicity is defined in the locally included book, and hence that function is undefined when the above defthm form is processed during the second pass of the encapsulate event. (Recall that local events are skipped during that pass; see encapsulate.)

    One solution is to move the include-book form so that it appears non-locally in front of the encapsulate event. But we may not want to include other events from that book, out of concern that rules defined in that book could affect our proof development.

    A more suitable solution may thus be to use the macro, with-supporters, in place of encapsulate, as follows.

    (with-supporters
     (local (include-book "std/lists/duplicity" :dir :system))
     (defthm duplicity-append
       (equal (duplicity a (append x y))
              (+ (duplicity a x) (duplicity a y)))))

    That macro determines automatically that the function duplicity needs to be defined, so it generates an encapsulate event like the original one above but with the definition of duplicity added non-locally. In this example, duplicity is actually defined in terms of another function, duplicity-exec, so its definition is needed as well. The generated event is initially as follows.

    (encapsulate
     ()
     (local (include-book "std/lists/duplicity"
                          :dir :system))
     (std::defredundant
       :names (duplicity-exec duplicity))
     (defthm duplicity-append
       (equal (duplicity a (append x y))
              (+ (duplicity a x) (duplicity a y)))))

    Notice that with-supporters is implemented using the macro std::defredundant. (Also see redundant-events.) When the call above of std::defredundant is expanded, the result is essentially as follows. Note that in-theory events are generated in an attempt to set the enable/disable status of each rule introduced by each function to match the status after the original include-book event.

    (encapsulate
     ()
     (set-enforce-redundancy t)
     (defun duplicity-exec (a x n)
       (declare (xargs :mode :logic :verify-guards t))
       (declare (xargs :measure (:? x)))
       (declare (xargs :guard (natp n)))
       (if (atom x)
           n
         (duplicity-exec a (cdr x)
                         (if (equal (car x) a) (+ 1 n) n))))
     (in-theory (e/d ((:type-prescription duplicity-exec)
                      (:executable-counterpart duplicity-exec))
                     ((:induction duplicity-exec)
                      (:definition duplicity-exec))))
     (defun duplicity (a x)
       (declare (xargs :mode :logic :verify-guards t))
       (declare (xargs :measure (:? x)))
       (declare (xargs :guard t))
       (mbe :logic (cond ((atom x) 0)
                         ((equal (car x) a)
                          (+ 1 (duplicity a (cdr x))))
                         (t (duplicity a (cdr x))))
            :exec (duplicity-exec a x 0)))
     (in-theory (e/d ((:type-prescription duplicity)
                      (:executable-counterpart duplicity))
                     ((:induction duplicity)
                      (:definition duplicity)))))

    For a second example, consider the following form from the community-books, file tools/with-supporters-test-top.lisp.

    (with-supporters
     (local (include-book "with-supporters-test-sub"))
     :names (mac1 mac1-fn)
     (defun h2 (x)
       (g3 x)))

    Here are the events in the locally included book.

    (defun mac1-fn (x)
      x)
    
    (defmacro mac1 (x)
      (mac1-fn x))
    
    (defun g1 (x)
      (declare (xargs :guard t))
      (mac1 x))
    
    (defun mac2-fn-b (x)
      x)
    
    (defun mac2-fn (x)
      (mac2-fn-b x))
    
    (defmacro mac2 (x)
      (mac2-fn x))
    
    (add-macro-alias mac2 g2)
    
    (defun g2 (x)
      (declare (xargs :guard (g1 x)))
      (mac2 x))
    
    (defun g3 (x)
      (g2 x))

    Notice that g3 in the top-level book calls g2, whose guard mentions g1. Now although g1 calls the macro mac1, with-supporters is not clever enough to notice this, because it tracks dependencies using translated terms (see term), for which macros have been expanded away. Thus, macros like mac1, as well as functions used in their definitions (like mac1-fn), must be specified explicitly. This specification is made with :names (mac1 mac1-fn) in the call of with-supporters above.

    There is an exception to this required use of the :names keyword argument: macros that are aliases for functions that support the events. Returning to our example, notice that g3 is defined using the function g2, which in turn calls the macro, mac2. So we might expect, as described for mac1 above, that mac2 must be included in :names. But fortunately, mac2 is a macro alias for a function that supports the definition of h2 specified in the call of with-supporters: g2 supports that definition and mac2 is a macro alias for g2. Thus mac2 and its supporting function mac2-fn are added to the list of generated events (called EXTRA, above).

    In summary, the above call of with-supporters generates the following event.

    (ENCAPSULATE NIL
                  (LOCAL (INCLUDE-BOOK "with-supporters-test-sub"))
                  (DEFUN MAC1-FN (X) X)
                  (DEFMACRO MAC1 (X) (MAC1-FN X))
                  (STD::DEFREDUNDANT G1 MAC2-FN-B MAC2-FN MAC2 G2 G3)
                  (DEFUN H2 (X) (G3 X)))

    As in the first example, std::defredundant generates definitions for the indicated names.