ld's response to an error
Major Section: MISCELLANEOUS
Ld-error-action is an ld special (see ld). The accessor is
(ld-error-action state) and the updater is
(set-ld-error-action val state). Ld-error-action must be
:continue, :return, :return!, or :error. The initial value
of ld-error-action is :continue, which means that the top-level ACL2
command loop will not exit when an error is caused by user-typein.
But the default value for ld-error-action on calls of ld is
:return!.
The general-purpose ACL2 read-eval-print loop, ld, reads forms from
standard-oi, evaluates them and prints the result to standard-co.
However, there are various flags that control ld's behavior and
ld-error-action is one of them. Suppose that ld-error-triples is
t and a form evaluates to an error triple (mv erp val state);
see error-triples. If the ``error component'', erp, is non-nil,
then an error is said to have occurred. If an error occurs, ld's
action depends on ld-error-action. If it is :continue, ld just
continues processing the forms in standard-oi. If it is :return or
:return!, ld stops and returns as though it had emptied the
channel. If it is :error, ld stops and returns, signalling an
error to its caller by returning an error triple with non-nil error
component, and reverting the logical world to its value just before that
call of ld.
To see this effect of :ERROR for ld-error-action, consider the
following example.
(ld '((defun f (x) x) (defun bad (x)) (defun g (x) x)))When the
defun of bad fails (because its body is missing),
evaluation of the ld call stops; thus, the defun of g is not
evaluated. The definition of f will be removed from the logical
world before the call of ld returns.However, by default each user call of ld is made with a
ld-error-action of :RETURN! (not :ERROR). In the common case
that all nested calls of ld inside the ACL2 loop are made this way, an
error will not roll back the logical world. However, it will still halt
evaluation of forms for the current call of ld and any parent calls of
ld (other than the call made on behalf of lp that entered the ACL2
loop in the first place), as though there were no more forms to evaluate.
We have already discussed the behavior of ld when an error occurs. But
there is another case when ld can stop processing more forms: when
ld-error-action is not :CONTINUE, ld-error-triples is t,
and evaluation of a form returns an error triple (mv nil val state),
where nil is the error component and whose ``value component'', val
is a cons pair whose car is the symbol :STOP-LD. Let val
be the pair (:STOP-LD . x). Then the call of ld returns the error
triple (mv nil (:STOP-LD n . x) state), where n is the value of
state global variable 'ld-level at the time of termination. The
following example illustrates how this works.
(ld '((defun f1 (x) x)
(ld '((defun f2 (x) x)
(mv nil '(:STOP-LD my-error more-info) state)
(defun g2 (x) x)))
(defun g1 (x) x)))
The outer call of ld returns the value
(:STOP-LD 2 3 MY-ERROR MORE-INFO)and leaves us in a world the includes definitions for
f1 and f2, but
no definition for g1 or g2 since neither of their two defun forms
was evaluated. The value of state global 'ld-level is incremented
from 1 to 2 when the outer ld is entered and then again to 3 when the
inner ld is entered. When the inner ld escounters the error triple
(mv nil (:STOP-LD my-error more-info) state), it sees :STOP-LD in the
car of the value component and pushes the current value of 'ld-level,
3, onto the cdr of that value, to return the value triple
(mv nil (:STOP-LD my-error 3 more-info) state). The outer of ld then
sees this value and returns
(mv nil (:STOP-LD my-error 2 3 more-info) state), since its current value
of 'ld-level is 2 after the inner ld exits.That concludes our discussion of how these special :STOP-LD values are
handled; but how are they created? While they can be created directly by
evaluation results as suggested in the example above, that is not the
standard way. Rather, ld returns an error triple
(mv nil (:STOP-LD n) state), where n is the value of variable
ld-level at the time of termination, when the following conditions hold:
an error occurs, ld-error-action is RETURN! (which is the default),
and ld-error-triples is t (the default). The following example,
which is a bit similar to the preceding one, illustrates both creation and
handling of the special :STOP-LD values.
(ld '((defun f1 (x) x)
(ld '((defun f2 (x) x)
(ld '((defun f3 (x) x)
(defun bad (x)) ; ERROR -- missing the body
(defun g3 (x) x)))
(defun g2 (x) x)))
(defun g1 (x) x)))
The result is that f1, f2, and f3 are defined, but none of
g1, g2, or g3 is defined. Let's see why. The innermost call
of ld has a default :ld-error-action of :RETURN! (as do the
other calls). So when the definition of bad fails, then the innermost
ld returns (mv nil (:STOP-LD 4) state). The middle ld sees this
value, and since its :ld-error-action is not :CONTINUE (because it
has the default value of :RETURN!), it returns before considering the
definition of g2, with value (mv nil (:STOP-LD 3 4) state). The
topmost call of ld similarly sees the :STOP-LD; stops evaluation
of forms, without defining g1; and returns
(mv nil (:STOP-LD 2 3 4) state).