Files
open-gpu-kernel-modules/src/common/inc/nvctassert.h
Bernhard Stoeckner 3084c04453 555.42.02
(cherry picked from commit 5a1c474040)
2024-07-19 15:38:00 -07:00

190 lines
9.0 KiB
C++

/*
* SPDX-FileCopyrightText: Copyright (c) 1997-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __NV_CTASSERT_H
#define __NV_CTASSERT_H
/*****************************************************************************/
/* Compile Time assert
* -------------------
* Use ct_assert(b) instead of assert(b) whenever the condition 'b' is constant,
* i.e. when 'b' can be determined at compile time.
*
* e.g.: check array size:
* ct_assert(__GL_ARRAYSIZE(arrayName) == constArraySize);
* e.g.: check struct size alignment:
* ct_assert(sizeof(struct xy) % 64 == 0);
*
* When available, standard C or C++ language constructs are used:
* - ISO C++11 defines the static_assert keyword
* - ISO C11 defines the _Static_assert keyword
*
* Note that recent versions of Clang support _Static_assert in all compiler modes
* - not just C11 mode - so we test for that in addition to checking explicitly for
* C11 and C++11 support.
*
* Those new language standards aren't available on all supported platforms; an
* alternate method which involves array declarations is employed in that case,
* described below.
*
* In C, there is a restriction where ct_assert() can be placed:
* It can be placed wherever a variable declaration can be placed, i.e.:
* - either anywhere at file scope
* - or inside a function at the beginning of any {} block; it may be mixed
* with variable declarations.
* e.g.:
* void function()
* {
* ct_assert(...); <-- ok \
* int a; |
* ct_assert(...); <-- ok | declaration section
* int b; |
* ct_assert(...); <-- ok /
*
* a = 0; -- first statement
*
* int c; <-- error
* ct_assert(...); <-- error
*
* {ct_assert(...);} <-- ok (uses its own block for ct_assert())
* }
*
* In CPP, there is no such restriction, i.e. it can be placed at file scope
* or anywhere inside a function or namespace or class (i.e., wherever
* a variable declaration may be placed).
*
* For C code, the mechanism of this ct_assert() is to declare a prototype
* of a function (e.g. compile_time_assertion_failed_in_line_555, if current
* line number is 555), which gets an array as argument:
* (1) the size of this array is +1, if b != 0 (ok)
* (2) the size of this array is -1, if b == 0 (error)
*
* In case (2) the compiler throws an error.
* e.g. msvc compiler:
* error C2118: negative subscript or subscript is too large
* e.g. gcc 2.95.3:
* size of array '_compile_time_assertion_failed_in_line_555' is negative
*
* In case the condition 'b' is not constant, the msvc compiler throws
* an error:
* error C2057: expected constant expression
* In this case the run time assert() must be used.
*
* For C++ code, we use a different technique because the function prototype
* declaration can have function linkage conflicts. If a single compilation
* unit has ct_assert() statements on the same line number in two different
* files, we would have:
*
* compile_time_assertion_failed_in_line_777(...); from xxx.cpp
* compile_time_assertion_failed_in_line_777(...); from xxx.h
*
* That is valid C++. But if either declaration were in an extern "C" block,
* the same function would be declared with two different linkage types and an
* error would ensue.
*
* Instead, ct_assert() for C++ simply declares an array typedef. As in the C
* version, we will get a compilation error if a typedef with a negative size
* is specified. Line numbers are not needed because C++ allows redundant
* typedefs as long as they are all defined the same way. But we tack them on
* anyway in case the typedef name is reported in compiler errors. C does not
* permit redundant typedefs, so this version should not be used in true C
* code. It can be used in extern "C" blocks of C++ code, however. As with
* the C version, MSVC will throw a "negative subscript" or "expected constant
* expression" error if the expression asserted is false or non-constant.
*
* Notes:
* - This ct_assert() does *not* generate any code or variable.
* Therefore there is no need to define it away for RELEASE builds.
* - The integration of the current source file number (__LINE__) ...
* ... would be required in C++ to allow multiple use inside the same
* class/namespace (if we used the C-style expansion), because the id
* must be unique.
* ... is nice to have in C or C++ if the compiler's error message contains
* the id (this is not the case for msvc)
* - Using three nested macros instead of only one is necessary to get the id
* compile_time_assertion_failed_in_line_555
* instead of
* compile_time_assertion_failed_in_line___LINE__
*/
#if defined(__clang__)
# ifndef __has_extension
# define __has_extension __has_feature // Compatibility with Clang pre-3.0 compilers.
# endif
# define CLANG_C_STATIC_ASSERT __has_extension(c_static_assert)
#else
# define CLANG_C_STATIC_ASSERT 0
#endif
// Adding this macro to fix MISRA 2012 rule 20.12
#define NV_CTASSERT_STRINGIFY_MACRO(b) #b
#if (defined(__cplusplus) && __cplusplus >= 201103L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
// ISO C++11 defines the static_assert keyword
# define ct_assert(b) static_assert((b), "Compile time assertion failed: " NV_CTASSERT_STRINGIFY_MACRO(b))
# define ct_assert_i(b,line) static_assert((b), "Compile time assertion failed: " NV_CTASSERT_STRINGIFY_MACRO(b)NV_CTASSERT_STRINGIFY_MACRO(line))
#elif !defined(NVOC) && ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || CLANG_C_STATIC_ASSERT)
// ISO C11 defines the _Static_assert keyword
# define ct_assert(b) _Static_assert((b), "Compile time assertion failed: " NV_CTASSERT_STRINGIFY_MACRO(b))
# define ct_assert_i(b,line) _Static_assert((b), "Compile time assertion failed: " NV_CTASSERT_STRINGIFY_MACRO(b)NV_CTASSERT_STRINGIFY_MACRO(line))
#else
// For compilers which don't support ISO C11 or C++11, we fall back to an
// array (type) declaration
# define ct_assert(b) ct_assert_i(b,__LINE__)
# define ct_assert_i(b,line) ct_assert_ii(b,line)
# ifdef __cplusplus
# define ct_assert_ii(b,line) typedef char compile_time_assertion_failed_in_line_##line[(b)?1:-1]
# else
/*
* The use of a function prototype "void compile_time_assertion_failed_in_line_##line(..)
* above violates MISRA-C 2012 Rule 8.6 since the rule disallows a function
* declaration without a definition. To fix the MISRA rule, the cplusplus style
* 'typdef char compile_time_assertion_failed_in_line_##line'
* is acceptable, but doesn't work for typical C code since there can be duplicate
* line numbers leading to duplicate typedefs which C doesn't allow.
*
* The following macro uses the predefined macro __COUNTER__ to create unique
* typedefs that fixes the MISRA violations. However, not all C compilers support
* that macro and even for compilers that support it, the underlying code makes
* use of variably modified identifiers in ct_assert that makes the use of this
* unviable.
*
* For now restrict the use of MACRO only on
* i) GCC 4.3.0 and above that supports __COUNTER__ macro
* ii) Specifically the Falcon port of the compiler since the use of variably
* modified identifiers have been removed on those projects
*
* TBD: Enable the macro on MSVC and CLANG pending
*/
# if defined(__GNUC__) && ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40300) && defined(GCC_FALCON)
# define ct_assert_ii(b,line) ct_assert_iii(b,line,__COUNTER__)
# define ct_assert_iii(b,line,cntr) ct_assert_cntr(b,line,cntr)
# define ct_assert_cntr(b,line,cntr) typedef char cnt##cntr##_compile_time_assertion_failed_in_line_##line[(b)?1:-1] __attribute__((unused))
# else
# define ct_assert_ii(b,line) void compile_time_assertion_failed_in_line_##line(int _compile_time_assertion_failed_in_line_##line[(b) ? 1 : -1])
# endif
# endif
#endif
#endif // __NV_CTASSERT_H