1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-04 19:10:37 -08:00
emacs/mps/code/check.h
Peter Jackson 1d00afac81 Publish licence updates in code directory.
Publish minor changes to readme and configure files in main directory.
Main directory and code directory licence texts are now fully updated.

Copied from Perforce
 Change: 196994
2020-06-11 17:05:40 +01:00

402 lines
14 KiB
C

/* check.h: ASSERTION INTERFACE
*
* $Id$
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
* Portions copyright (C) 2002 Global Graphics Software.
*
* .aver: This header defines a family of AVER and NOTREACHED macros.
* These macros should be used to instrument and annotate code with
* invariants, and so provide both interface and internal consistency
* checks.
*
* .comment: Non-obvious AVER statements should always be accompanied by
* a comment.
*
* .disable: When assertions are disabled, AVER expands to something
* which contains the condition but discards the result. Compilers
* will throw the code away, but check its syntax.
*
* .trans.level-check: CheckLevel itself is not checked anywhere.
*
* .careful: BE CAREFUL when changing this file. It is easy to make mistakes
* and change the checking level in a variety and thereby its performance
* without realising it. This has happened before. Eyeball the preprocessor
* output for each variety. For example:
*
* cc -E -DCONFIG_VAR_RASH trace.c
* cc -E -DCONFIG_VAR_HOT trace.c
* cc -E -DCONFIG_VAR_COOL trace.c
*
* Then look at TraceCheck to make sure checking is right, TraceAddWhite
* for general assertions, and TraceFix for the critical path assertions.
*/
#ifndef check_h
#define check_h
#include "config.h"
#include "misc.h"
#include "mpslib.h"
#include "protocol.h"
/* ASSERT -- basic assertion
*
* The ASSERT macro is equivalent to the ISO C assert() except that it is
* always defined, and uses the assertion handler from the MPS plinth, which
* can be replaced by the client code.
*
* It is not intended for direct use within the MPS. Use AVER and CHECK
* macros, which can be controlled by both build and run-time configuration.
*/
#define ASSERT(cond, condstring) \
BEGIN \
if (LIKELY(cond)) NOOP; else \
mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)); \
END
#define ASSERTP(cond, condstring, default_) \
((void)(LIKELY(cond) \
|| (mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)), FALSE)), \
(default_))
#define ASSERT_ISTYPE(type, val) (type ## Check(val))
#define ASSERT_TYPECHECK(type, val) \
ASSERT(ASSERT_ISTYPE(type, val), "TypeCheck " #type ": " #val)
#define ASSERT_ISCLASS(klass, val) (klass ## Check(CouldBeA(klass, val)))
#define ASSERT_CLASSCHECK(klass, val) \
ASSERT(ASSERT_ISCLASS(klass, val), "ClassCheck " #klass ": " #val)
#define ASSERT_NULLCHECK(type, val) \
ASSERT((val) != NULL, "NullCheck " #type ": " #val)
/* CheckLevel -- control for check method behaviour
*
* When the MPS is build with AVER_AND_CHECK_ALL (in a "cool" variety) the
* static variable CheckLevel controls the frequency and detail of
* consistency checking on structures.
*
* By default, CHECKLEVEL is defined to a static value in config.h, though
* it can be overridden on the compiler command line, e.g.
* cc -DCHECKLEVEL=CheckLevelSHALLOW ...
*
* However, if CHECKLEVEL_DYNAMIC is defined we use a variable to control
* the level of checking. The run-time overhead for this is quite high
* (observed double run-time on amcss when the variable is set to SHALLOW).
* CHECKLEVEL_DYNAMIC should be set to the initial level for the variable,
* which is in mpm.c.
*
* In general, it's better to adjust the check level by defining CHECKLEVEL
* but this is intended to meet the case where a run-time adjustable
* checking level is required -- where recompilation or relinking is
* undesirable or impossible.
*
* TODO: Should also allow the check level variable to come from an
* environment variable.
*/
enum {
CheckLevelMINIMAL = 0, /* local sig check only */
CheckLevelSHALLOW = 1, /* local invariants, */
/* and adjacent (up, down) sig checks */
CheckLevelDEEP = 2 /* local invariants, */
/* and adjacent up sig checks */
/* and recursive down full type checks */
};
#ifdef CHECKLEVEL_DYNAMIC
extern unsigned CheckLevel;
#undef CHECKLEVEL
#define CHECKLEVEL CheckLevel
#endif
/* AVER, AVERT, AVERC, AVERP -- MPM assertions
*
* AVER and friends are used to assert conditions in the code.
*
* AVER checks an expression.
*
* AVERT checks that a value is of the correct type and may perform
* consistency checks on the value by calling a check function.
*
* AVERC checks that a value is of the correct class (including
* subclasses) and may perform consistency checks on the value by
* calling a check function.
*
* AVERP checks an expression but is itself a void * expression, and
* so can be used in expression macros.
*
* AVER etc. are on by default, and check conditions even in "hot"
* varieties intended to work in production. To avoid the cost of a
* check in critical parts of the code, use AVER_CRITICAL etc., but
* only when you've *proved* that this makes a difference to
* performance that affects requirements.
*/
#if defined(AVER_AND_CHECK_NONE)
#define AVER(cond) DISCARD(cond)
#define AVERT(type, val) DISCARD(ASSERT_ISTYPE(type, val))
#define AVERC(klass, val) DISCARD(ASSERT_ISCLASS(klass, val))
#define AVERP(cond, dflt) (DISCARD_EXP(cond), dflt)
#define AVERPC(cond, condstring, dflt) (DISCARD_EXP(cond), dflt)
#else
#define AVER(cond) ASSERT(cond, #cond)
#define AVERT ASSERT_TYPECHECK
#define AVERC ASSERT_CLASSCHECK
#define AVERP(cond, dflt) ASSERTP(cond, #cond, dflt)
#define AVERPC ASSERTP
#endif
#if defined(AVER_AND_CHECK_ALL)
#define AVER_CRITICAL(cond) ASSERT(cond, #cond)
#define AVERT_CRITICAL ASSERT_TYPECHECK
#define AVERC_CRITICAL ASSERT_CLASSCHECK
#define AVERP_CRITICAL(cond, dflt) ASSERTP(cond, #cond, dflt)
#define AVERPC_CRITICAL ASSERTP
#else
#define AVER_CRITICAL DISCARD
#define AVERT_CRITICAL(type, val) DISCARD(ASSERT_ISTYPE(type, val))
#define AVERC_CRITICAL(klass, val) DISCARD(ASSERT_ISCLASS(klass, val))
#define AVERP_CRITICAL(cond, dflt) (DISCARD_EXP(cond), dflt)
#define AVERPC_CRITICAL(cond, condstring, dflt) (DISCARD_EXP(cond), dflt)
#endif
/* NOTREACHED -- control should never reach this statement
*
* This is a sort of AVER; it is equivalent to AVER(FALSE), but will produce
* a more informative message.
*/
#if defined(AVER_AND_CHECK_NONE)
#define NOTREACHED NOOP
#else
#define NOTREACHED \
BEGIN \
mps_lib_assert_fail(__FILE__, __LINE__, "unreachable code"); \
END
#endif
/* TESTT -- check type simply
*
* Must be thread safe. <design/interface-c#.thread-safety>
* and <design/interface-c#.check.space>.
*/
#define TESTT(type, val) ((val) != NULL && (val)->sig == type ## Sig)
/* TESTC -- check class simply
*
* TODO: Does this need to be thread safe like TESTT?
*/
#define TESTC(klass, val) ((val) != NULL && IsA(klass, val))
/* CHECKS, CHECKC -- Check Signature, Check Class
*
* (if CHECKLEVEL == CheckLevelMINIMAL, this is all we check)
*/
#if defined(AVER_AND_CHECK_NONE)
#define CHECKS(type, val) DISCARD(TESTT(type, val))
#define CHECKC(klass, val) DISCARD(MustBeA(klass, val))
#else
#define CHECKS(type, val) \
ASSERT(TESTT(type, val), "SigCheck " #type ": " #val)
#define CHECKC(klass, val) \
ASSERT(TESTC(klass, val), "ClassCheck " #klass ": " #val)
#endif
/* CHECKL, CHECKD, CHECKU -- local, "down", and "up" checks
*
* Each type should have a function defined called <type>Check that checks
* the consistency of the type. This function should return TRUE iff the
* value passes consistency checks. In general, it should assert otherwise,
* but we allow for the possibility of returning FALSE in this case for
* configuration adaptability.
*
* For structure types, the check function should:
*
* - check its own signature with CHECKS
*
* - check fields that it "owns" with CHECKL, like asserts
*
* - check "down" values which are its "children" with CHECKD
*
* - check "up" values which are its "parents" with CHECKU.
*
* These various checks will be compiled out or compiled to be controlled
* by CHECKLEVEL.
*
* For example:
*
* Bool MessageCheck(Message message)
* {
* CHECKS(Message, message);
* CHECKU(Arena, message->arena);
* CHECKD(MessageClass, message->class);
* CHECKL(RingCheck(&message->queueRing));
* CHECKL(MessageIsClocked(message) || (message->postedClock == 0));
* return TRUE;
* }
*
* The parent/child distinction depends on the structure, but in the MPS
* the Arena has no parents, and has children which are Pools, which have
* children which are Segments, etc.
*
* The important thing is to have a partial order on types so that recursive
* checking will terminate. When CHECKLEVEL is set to DEEP, checking will
* recurse into check methods for children, but will only do a shallow
* signature check on parents, avoiding infinite regression.
*/
#if defined(AVER_AND_CHECK_ALL)
#define CHECK_BY_LEVEL(minimal, shallow, deep) \
BEGIN \
switch (CHECKLEVEL) { \
case CheckLevelDEEP: deep; break; \
case CheckLevelSHALLOW: shallow; break; \
default: NOTREACHED; /* fall through */ \
case CheckLevelMINIMAL: minimal; break; \
} \
END
#define CHECKL(cond) \
CHECK_BY_LEVEL(NOOP, \
ASSERT(cond, #cond), \
ASSERT(cond, #cond))
#define CHECKD(type, val) \
CHECK_BY_LEVEL(NOOP, \
CHECKS(type, val), \
ASSERT_TYPECHECK(type, val))
#define CHECKD_NOSIG(type, val) \
CHECK_BY_LEVEL(NOOP, \
ASSERT_NULLCHECK(type, val), \
ASSERT_TYPECHECK(type, val))
#define CHECKD_CLASS(klass, val) \
CHECK_BY_LEVEL(NOOP, \
CHECKC(klass, val) \
ASSERT_CLASSCHECK(klass, val))
#define CHECKU(type, val) \
CHECK_BY_LEVEL(NOOP, \
CHECKS(type, val), \
CHECKS(type, val))
#define CHECKU_NOSIG(type, val) \
CHECK_BY_LEVEL(NOOP, \
ASSERT_NULLCHECK(type, val), \
ASSERT_NULLCHECK(type, val))
#else /* AVER_AND_CHECK_ALL, not */
/* TODO: This gives comparable performance to RASH when compiling
using mps.c and -O2 (to get check methods inlined), but is it a bit
too minimal? How much do we rely on check methods? */
#define CHECKL(cond) DISCARD(cond)
#define CHECKD(type, val) DISCARD(TESTT(type, val))
#define CHECKD_NOSIG(type, val) DISCARD((val) != NULL)
#define CHECKD_CLASS(klass, val) DISCARD((val) != NULL)
#define CHECKU(type, val) DISCARD(TESTT(type, val))
#define CHECKU_NOSIG(type, val) DISCARD((val) != NULL)
#endif /* AVER_AND_CHECK_ALL */
/* COMPAT* -- type compatibility checking
*
* .check.macros: The COMPAT* macros use some C trickery to attempt to
* verify that certain types and fields are equivalent. They do not do
* a complete job. This trickery is justified by the security gained
* in knowing that <code/mps.h> matches the MPM. See
* <design/interface-c#.check.types>. [This paragraph is intended to
* satisfy rule.impl.trick.]
*/
/* compile-time check */
#define COMPATLVALUE(lv1, lv2) \
((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE)
/* aims to test whether t1 and t2 are assignment-compatible */
#define COMPATTYPE(t1, t2) \
(sizeof(t1) == sizeof(t2) && \
COMPATLVALUE(*((t1 *)0), *((t2 *)0)))
#define COMPATFIELDAPPROX(s1, f1, s2, f2) \
(sizeof(((s1 *)0)->f1) == sizeof(((s2 *)0)->f2) && \
offsetof(s1, f1) == offsetof(s2, f2))
#define COMPATFIELD(s1, f1, s2, f2) \
(COMPATFIELDAPPROX(s1, f1, s2, f2) && \
COMPATLVALUE(((s1 *)0)->f1, ((s2 *)0)->f2))
/* NONNEGATIVE -- test that value is greater than or equal to zero
*
* We'd like to write "x >= 0" but when x belongs to an unsigned
* integral type then this results in a "comparison of unsigned
* expression >= 0 is always true" warning from GCC if -Wextra is
* specified. We also don't want to remove these assertions because
* they protect us against errors if the type of x should ever be
* changed to a signed type on some platform.
*
* Note that this macro evaluates its argument twice.
*/
#define NONNEGATIVE(x) ((x) == 0 || (x) > 0)
#endif /* check_h */
/* C. COPYRIGHT AND LICENSE
*
* Copyright (C) 2001-2020 Ravenbrook Limited <http://www.ravenbrook.com/>.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/