binutils-gdb/gdbsupport/valid-expr.h
Pedro Alves de38d64ad2 Tweak gdbsupport/valid-expr.h for GCC 6, fix build
With GCC 6.4 and 6.5 (at least), unit tests that use
gdbsupport/valid-expr.h's CHECK_VALID fail to compile, with:

 In file included from src/gdb/unittests/offset-type-selftests.c:24:0:
 src/gdb/unittests/offset-type-selftests.c: In substitution of 'template<class Expected, template<class ...> class Op, class ... Args> using is_detected_exact = std::is_same<Expected, typename gdb::detection_detail::detector<gdb::nonesuch, void, Op, Args ...>::type> [with Expected = selftests::offset_type::off_A&; Op = selftests::offset_type::check_valid_expr75::archetype; Args = {selftests::offset_type::off_A, selftests::offset_type::off_B}]':
 src/gdb/unittests/offset-type-selftests.c:75:1:   required from here
 src/gdb/../gdbsupport/valid-expr.h:65:20: error: type/value mismatch at argument 2 in template parameter list for 'template<class Expected, template<class ...> class Op, class ... Args> using is_detected_exact = std::is_same<Expected, typename gdb::detection_detail::detector<gdb::nonesuch, void, Op, Args ...>::type>'
     archetype, TYPES>::value == VALID,   \
		     ^

The important part is the "error: type/value mismatch" error.  Seems
like that GCC doesn't understand that archetype is an alias template,
and is being strict in requiring a template class.

The fix here is then to make archetype a template class, to pacify
GCC.  The resulting code looks like this:

  template <TYPENAMES, typename = decltype (EXPR)>
  struct archetype
  {
  };

  static_assert (gdb::is_detected_exact<archetype<TYPES, EXPR_TYPE>,
 		 archetype, TYPES>::value == VALID, "");

is_detected_exact<Expected, Op, Args> checks whether Op<Args> is type
Expected:

 - For Expected, we pass the explicit EXPR_TYPE, overriding the
   default parameter type of archetype.

 - For Args we don't pass the last template parameter, so archtype
   defaults to the EXPR's decltype.

So in essence, we're really checking whether EXPR_TYPE is the same as
decltype(EXPR).

We need to do the decltype in a template context in order to trigger
SFINAE instead of failing to compile.


The hunk in unittests/enum-flags-selftests.c becomes necessary,
because unlike with the current alias template version, this new
version makes GCC trigger -Wenum-compare warnings as well:

 src/gdb/unittests/enum-flags-selftests.c:328:33: error: comparison between 'enum selftests::enum_flags_tests::RE' and 'enum selftests::enum_flags_tests::RE2' [-Werror=enum-compare]
  CHECK_VALID (true,  bool, RE () != RE2 ())
				  ^
 src/gdb/../gdbsupport/valid-expr.h:61:45: note: in definition of macro 'CHECK_VALID_EXPR_INT'
    template <TYPENAMES, typename = decltype (EXPR)>   \
					      ^

Build-tested with:

 - GCC {4.8.5, 6.4, 6.5, 7.3.1, 9.3.0, 11.0.0-20200910}
 - Clang 10.0.0

gdbsupport/ChangeLog:

	* valid-expr.h (CHECK_VALID_EXPR_INT): Make archetype a template
	class instead of an alias template and adjust static_assert.

gdb/ChangeLog:

	* unittests/enum-flags-selftests.c: Check whether __GNUC__ is
	defined before using '#pragma GCC diagnostic' instead of checking
	__clang__.
2020-09-29 23:48:04 +01:00

112 lines
4.4 KiB
C++

/* Compile-time valid expression checker for GDB, the GNU debugger.
Copyright (C) 2017-2020 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Helper macros used to build compile-time unit tests that make sure
that invalid expressions that should not compile would not compile,
and that expressions that should compile do compile, and have the
right type. This is mainly used to verify that some utility's API
is really as safe as intended. */
#ifndef COMMON_VALID_EXPR_H
#define COMMON_VALID_EXPR_H
#include "gdbsupport/preprocessor.h"
#include "gdbsupport/traits.h"
/* Macro that uses SFINAE magic to detect whether the EXPR expression
is either valid or ill-formed, at compile time, without actually
producing compile-time errors. I.e., check that bad uses of the
types (e.g., involving mismatching types) would be caught at
compile time. If the expression is valid, also check whether the
expression has the right type.
EXPR must be defined in terms of some of the template parameters,
so that template substitution failure discards the overload instead
of causing a real compile error. TYPES is thus the list of types
involved in the expression, and TYPENAMES is the same list, but
with each element prefixed by "typename". These are passed as
template parameter types to the templates within the macro.
VALID is a boolean that indicates whether the expression is
supposed to be valid or invalid.
EXPR_TYPE is the expected type of EXPR. Only meaningful iff VALID
is true. If VALID is false, then you must pass "void" as expected
type.
Each invocation of the macro is wrapped in its own namespace to
avoid ODR violations. The generated namespace only includes the
line number, so client code should wrap sets of calls in a
test-specific namespace too, to fully guarantee uniqueness between
the multiple clients in the codebase. */
#define CHECK_VALID_EXPR_INT(TYPENAMES, TYPES, VALID, EXPR_TYPE, EXPR) \
namespace CONCAT (check_valid_expr, __LINE__) { \
\
template <TYPENAMES, typename = decltype (EXPR)> \
struct archetype \
{ \
}; \
\
static_assert (gdb::is_detected_exact<archetype<TYPES, EXPR_TYPE>, \
archetype, TYPES>::value == VALID, \
""); \
} /* namespace */
/* A few convenience macros that support expressions involving a
varying numbers of types. If you need more types, feel free to add
another variant. */
#define CHECK_VALID_EXPR_1(T1, VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1), \
ESC_PARENS (T1), \
VALID, EXPR_TYPE, EXPR)
#define CHECK_VALID_EXPR_2(T1, T2, VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS(typename T1, typename T2), \
ESC_PARENS (T1, T2), \
VALID, EXPR_TYPE, EXPR)
#define CHECK_VALID_EXPR_3(T1, T2, T3, VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, typename T3), \
ESC_PARENS (T1, T2, T3), \
VALID, EXPR_TYPE, EXPR)
#define CHECK_VALID_EXPR_4(T1, T2, T3, T4, VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \
typename T3, typename T4), \
ESC_PARENS (T1, T2, T3, T4), \
VALID, EXPR_TYPE, EXPR)
#define CHECK_VALID_EXPR_5(T1, T2, T3, T4, T5, VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \
typename T3, typename T4, \
typename T5), \
ESC_PARENS (T1, T2, T3, T4, T5), \
VALID, EXPR_TYPE, EXPR)
#define CHECK_VALID_EXPR_6(T1, T2, T3, T4, T5, T6, \
VALID, EXPR_TYPE, EXPR) \
CHECK_VALID_EXPR_INT (ESC_PARENS (typename T1, typename T2, \
typename T3, typename T4, \
typename T5, typename T6), \
ESC_PARENS (T1, T2, T3, T4, T5, T6), \
VALID, EXPR_TYPE, EXPR)
#endif /* COMMON_VALID_EXPR_H */