.. sources:
`<https://info.ravenbrook.com/project/mps/master/design/check/>`_
.. mps:prefix:: design.mps.check
Checking
========
Introduction
------------
This documents the design of structure checking within the MPS.
History
-------
:mps:tag:`hist.0` Incomplete design. Gavin Matthews, 1996-08-05.
:mps:tag:`hist.1` Converted from MMInfo database design document.
Richard Brooksby, 2002-06-07.
:mps:tag:`hist.2` Converted to reStructuredText. Gareth Rees,
2013-03-12.
Implementation
--------------
:mps:tag:`level` There are three levels of checking:
1. :mps:tag:`level.sig` The lowest level checks only that the
structure has a valid :c:type:`Signature` (see
:mps:ref:`design.mps.sig`).
2. :mps:tag:`level.shallow` Shallow checking checks all local fields
(including signature) and also checks the signatures of any parent
or child structures.
3. :mps:tag:`level.deep` Deep checking checks all local fields
(including signatures), the signatures of any parent structures,
and does full recursive checking on any child structures.
:mps:tag:`level.control` Control over the levels of checking is via
the definition of at most one of the macros
:c:macro:`TARGET_CHECK_SHALLOW` (which if defined gives
:mps:ref:`.level.shallow`), :c:macro:`TARGET_CHECK_DEEP` (which if
defined gives :mps:ref:`.level.deep`). If neither macro is defined
then :mps:ref:`.level.sig` is used. These macros are not intended to
be manipulated directly by developers, they should use the interface
in :mps:ref:`impl.h.target`.
:mps:tag:`order` Because deep checking (:mps:ref:`.level.deep`) uses
unchecked recursion, it is important that child relationships are
acyclic (:mps:ref:`.macro.down`).
:mps:tag:`fun` Every abstract data type which is a structure pointer
should have a function ``<type>Check`` which takes a pointer of type
``<type>`` and returns a :c:type:`Bool`. It should check all fields in
order, using one of the macros in :mps:ref:`.macro`, or document why
not.
:mps:tag:`fun.omit` The only fields which should be omitted from a
check function are those for which there is no meaningful check (for
example, an unlimited unsigned integer with no relation to other fields).
:mps:tag:`fun.return` Although the function returns a :c:type:`Bool`,
if the assert handler returns (or there is no assert handler), then
this is taken to mean "ignore and continue", and the check function
hence returns ``TRUE``.
:mps:tag:`macro` Checking is implemented by invoking four macros in :mps:ref:`impl.h.assert`:
* :mps:tag:`macro.sig` ``CHECKS(type, val)`` checks the signature
only, and should be called precisely on ``type`` and the received
object pointer.
* :mps:tag:`macro.local` ``CHECKL(cond)`` checks a local field
(depending on level; see :mps:ref:`.level`), and should be called on
each local field that is not an abstract data type structure pointer
itself (apart from the signature), with an appropriate normally-true
test condition.
* :mps:tag:`macro.up` ``CHECKU(type, val)`` checks a parent abstract
data type structure pointer, performing at most signature checks
(depending on level; see :mps:ref:`.level`). It should be called
with the parent type and pointer.
* :mps:tag:`macro.down` ``CHECKD(type, val)`` checks a child abstract
data type structure pointer, possibly invoking ``<type>Check``
(depending on level; see :mps:ref:`.level`). It should be called
with the child type and pointer.
:mps:tag:`full-type` ``CHECKS``, ``CHECKD``, ``CHECKU``, all operate
only on fully fledged types. This means the type has to provide a
function ``Bool TypeCheck(Type type)`` where ``Type`` is substituted
for the name of the type (for example, :c:func:`PoolCheck`), and the
expression ``obj->sig`` must be a valid value of type :c:type:`Sig`
whenever ``obj`` is a valid value of type ``Type``.
:mps:tag:`type.no-sig` This tag is to be referenced in implementations
whenever the form ``CHECKL(ThingCheck(thing))`` is used instead of
``CHECK{U,D}(Thing, thing)`` because ``Thing`` is not a fully fledged
type (:mps:ref:`.full-type`).