• Top
    • Documentation
    • Books
    • Recursion-and-induction
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
      • Theories
      • Rule-classes
        • Type-prescription
        • Rewrite
        • Meta
        • Linear
        • Definition
        • Clause-processor
        • Tau-system
        • Forward-chaining
        • Equivalence
        • Free-variables
        • Congruence
        • Executable-counterpart
        • Induction
        • Compound-recognizer
        • Elim
        • Well-founded-relation-rule
        • Rewrite-quoted-constant
        • Built-in-clause
        • Well-formedness-guarantee
          • Patterned-congruence
          • Rule-classes-introduction
          • Guard-holders
          • Refinement
          • Type-set-inverter
          • Generalize
          • Corollary
          • Induction-heuristics
          • Backchaining
          • Default-backchain-limit
        • Proof-builder
        • Hons-and-memoization
        • Events
        • History
        • Parallelism
        • Programming
        • Start-here
        • Real
        • Debugging
        • Miscellaneous
        • Output-controls
        • Macros
        • Interfacing-tools
      • Interfacing-tools
      • Hardware-verification
      • Software-verification
      • Testing-utilities
      • Math
    • Rule-classes

    Well-formedness-guarantee

    Guarantee that a metafunction or clause-processor returns a well-formed answer

    A :well-formedness-guarantee is a keyword attribute available in the :meta and :clause-processor :rule-classes. By default, when a metafunction or clause processor is applied, ACL2 checks that the output is well-formed (and does not contain certain ``forbidden'' functions; see set-skip-meta-termp-checks). By providing a :well-formedness-guarantee when you store a :meta or :clause-processor rule, you can cause ACL2 to skip these runtime checks.

    This is considered an advanced feature that requires that you prove certain theorems; this is probably only worthwhile if your metafunctions or clause processors produce very large terms. Henceforth, we assume you are familiar with metafunctions and clause processors.

    This topic first exhibits a simple example of a well-formedness guarantee for a metafunction. This example is really an outline; a self-contained, runnable example may be found in the community-books in file books/demos/meta-wf-guarantee-example.lisp. Then we describe the acceptable values of the :well-formedness-guarantee keyword of both the :meta and :clause-processor rule-classes. Next we show the forms of the theorems you must prove to provide such guarantees. Finally, we discuss the runtime effects of providing such guarantees.

    Example

    Suppose that fn is a vanilla metafunction that rewrites terms with top function symbol TARGET, of arity 1. The event storing fn as a metafunction would normally be something like this:

    (defthm fn-is-correct
      (implies (and (pseudo-termp x)
                    (alistp a))
               (equal (evl x a)
                      (evl (fn x) a)))
      :rule-classes
      ((:meta :trigger-fns (TARGET))))
    where evl is an evaluator for all the function symbols known to fn. Suppose that to prove this theorem, evl must be able to interpret the symbols TARGET, CONS, and IF. While the metatheorem for fn establishes that fn returns something with the same ``meaning'' (under evl) as its input, it does not answer the question: does fn return a well-formed term, all of whose function symbols are in :logic mode? Given the above :rule-class, whenever the simplifier fires the :meta rule fn-is-correct by applying fn to a term, it checks that the value, val, is well-formed, by evaluating (logic-termp val (w state)). The function logic-termp checks that its argument is a term all of whose function symbols are in :logic mode.

    Function: logic-termp

    (defun logic-termp (x wrld)
           (declare (xargs :guard (plist-worldp-with-formals wrld)))
           (and (termp x wrld)
                (logic-fnsp x wrld)))

    The logic-termp check can be avoided if, before you store the rule fn-is-correct, you prove:

    (defthm fn-is-well-formed
      (implies (and (logic-termp x w)
                    (arities-okp '((TARGET . 1)
                                   (CONS . 2)
                                   (IF . 3))
                                 w))
               (logic-termp (fn x) w))
      :rule-classes nil)
    and then you store fn-is-correct this way:
    (defthm fn-is-correct
      (implies (and (pseudo-termp x)
                    (alistp a))
               (equal (evl x a)
                      (evl (fn x) a)))
      :rule-classes
      ((:meta :trigger-fns (TARGET)
              :well-formedness-guarantee fn-is-well-formed)))

    Acceptable Values

    Now we describe the values you may provide with the :well-founded-guarantee keyword of the :meta and the :clause-processor rule-classes.

    General Forms:
    :well-formedness-guarantee thm-name1
    :well-formedness-guarantee (thm-name1)
    :well-formedness-guarantee (thm-name1 thm-name2)
    where both thm-name1 and thm-name2 are the names of previously proved ``well-formedness theorems'' (see the next section); furthermore, thm-name1 must be about the metafunction or clause processor being introduced, and thm-name2 must be about the hypothesis metafunction (if any) associated with the metafunction. For :meta rules, all three forms are accepted, but the last form is required if the :meta rule involves a hypothesis metafunction. That is, to provide a :well-formedness-guarantee for a metatheorem with a hypothesis metafunction, you must supply both thm-name1 and thm-name2. For :clause-processor rules, you must use the first form.

    Each well-formedness theorem provides an ``arity alist'' that specifies the assumed arities of the function symbols known to the metafunction or clause processor. The arities shown for the function symbols listed in that alist must be the same as the arities of those functions in the actual ACL2 logical world current at the time the :meta or :clause-processor is stored. Moreover, the function symbols in that alist must all be in :logic mode.

    Furthermore, none of the function symbols in the arity alists should be ``forbidden'' function symbols as explained in set-skip-meta-termp-checks.

    When the :meta or :clause-processor rule is stored, notes are made that will prevent the function symbols on the arity alists from becoming untouchable (and thus forbidden). See push-untouchable.

    The only way a function's arity or forbidden status can change, or that a function can transition out of :logic mode, is if the user has engaged in redefinition or activated a trust tag to add or remove untouchables. Thus, the restrictions above are unimportant to most users.

    Well-Formedness Theorems

    Theorems must be proved to establish that metafunctions and clause processors return well-formed results. These are called ``well-formedness theorems.'' Note: To say a theorem is a ``well-formedness theorem'' is a remark about the shape of the formula, not its rule-class. There is no :well-formedness-theorem rule-class and a well-formedness theorem may be stored with any :rule-classes that accept the syntax of the formula or as :rule-classes nil. Indeed, if one of your metafunctions uses another function to produce a subterm of the metafunction's answer, you might need to prove a well-formedness theorem for the sub-function and make it a :rewrite rule.

    Well-formedness theorems for metafunctions and clause processors are similar but slightly different. We deal with metafunctions and their corresponding hypothesis metafunctions (if any) first. The shapes of the well-formedness theorems for a metafunction and hypothesis metafunction are identical, but remember that you must prove a well-formedness theorem for both the metafunction and the associated hypothesis metafunction. So suppose fn is a metafunction or a hypothesis metafunction and let (fn x ...) be a legal call on distinct variable symbols. (Recall that extended metafunctions and their hypothesis functions can take three arguments.) Then the general form a well-formedness theorem for fn states that fn returns a termp when given one, provided the arities of certain function symbols are as expected:

    General Form of Well-Formedness Theorem for a Metafunction:
    (implies (and (logic-termp x w)
                  (arities-okp '<alist> w))
             (logic-termp (fn x ...) w))
    where <alist> is an alist pairing function symbols with their assumed arities as illustrated in the opening example. Note that the first argument of arities-okp in the theorem is a quoted constant. You may omit or reorder the hypotheses above and you may use different variable symbols in place of x and w, but they must be distinct and different from the variables in the ``....''

    An example of <alist> is ((TARGET . 1) (CONS . 2) (IF . 3)). Generally, the <alist> you provide should assign arities to every symbol known to fn, i.e., every function symbol known to the evaluator used in your correctness theorem. If you inspect the definition of termp you will see that it uses w to obtain arities of the function symbols in x. The arities-okp hypothesis restricts w to worlds where the function symbols known to fn have the arities you expect and are in :logic-mode. For example, given the (arities-okp '<alist> w) hypothesis for the <alist> above, the theorem prover will rewrite (arity 'IF w) to 3. By default arity and arities-okp are disabled; and the following rewrite rules, which are part of ACL2's initial world, will take care of calls like (arity 'IF w) and (logicp fn w).

    Theorem: arities-okp-implies-arity

    (defthm arities-okp-implies-arity
            (implies (and (arities-okp user-table w)
                          (assoc fn user-table))
                     (equal (arity fn w)
                            (cdr (assoc fn user-table)))))

    Theorem: arities-okp-implies-logicp

    (defthm arities-okp-implies-logicp
            (implies (and (arities-okp user-table w)
                          (assoc fn user-table))
                     (logicp fn w)))

    Now we turn to the well-formedness theorem for a clause processor, cl-proc. Let (cl-proc x ...) be a legal call on distinct variable symbols. The theorem establishes that cl-proc returns a list of clauses when given a clause, provided certain functions have the expected arities. But the recognizer for a clause is the function logic-term-listp and the recognizer for a list of clauses is logic-term-list-listp:

    General Forms of Well-Formedness Theorem for a Clause Processor
    (implies (and (logic-term-listp x w)
                  (arities-okp '<alist> w))
             (logic-term-list-listp (cl-proc x ...) w))
    
    (implies (and (logic-term-listp x w)
                  (arities-okp '<alist> w))
             (logic-term-list-listp (clauses-result (cl-proc x ...)) w))

    The first form above is for a cl-proc that returns a single value and the second form is for a cl-proc that returns multiple values.

    The discussion about metafunctions, above, applies here as well. <Alist> is an alist pairing function symbols with their assumed arities. You may omit or reorder the hypotheses and you may use different variable symbols in place of x and w, but they must be distinct and different from the variables in the ``....''

    Runtime Effects

    In the absence of a :well-formedness-guarantee, if a metafunction or clause processor is applied during a proof and produces val, then certain checks are made on val before it is used in the proof attempt. In the case of a metafunction, (logic-termp val (w state)) is checked and val is scanned to ensure that it contains no forbidden function symbols. In the case of a clause processor, (logic-term-list-listp val (w state)) is checked and val is scanned to ensure that it contains no forbidden function symbols.

    If val is large (e.g., because the input is large), these checks can take more time than the metafunction or clause processor did to produce val! It is for this reason that we provide for :well-formedness-guarantees.

    When a :well-formedness-guarantee is provided no checks are made on val. However, ACL2 will check that the arity alist(s) involved in the guarantee still correctly shows the arities of the function symbols listed. Because those function symbols were not forbidden when the guarantee was made and are prohibited from being forbidden thereafter, no check is necessary to ensure that no forbidden symbols are introduced into the proof.