aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2023-09-20 18:37:29 +0200
committerJakub Jelinek <jakub@redhat.com>2023-09-20 18:37:29 +0200
commit53d834a7fae3afffebb45a2d66908f705773a7fc (patch)
tree4af979256b3f85649ada1d5cabc08c21cf6947db
parent27282dc0931484c31fa391772499d878afcc746a (diff)
downloadgcc-53d834a7fae3afffebb45a2d66908f705773a7fc.zip
gcc-53d834a7fae3afffebb45a2d66908f705773a7fc.tar.gz
gcc-53d834a7fae3afffebb45a2d66908f705773a7fc.tar.bz2
c, c++: Accept __builtin_classify_type (typename)
As mentioned in my stdckdint.h mail, __builtin_classify_type has a problem that argument promotion (the argument is passed to ... prototyped builtin function) means that certain type classes will simply never appear. I think it is too late to change how it behaves, lots of code in the wild might rely on the current behavior. So, the following patch adds option to use a typename rather than expression as the operand to the builtin, making it behave similarly to sizeof, typeof or say the clang _Generic extension where the first argument can be there not just expression, but also typename. I think we have other prior art here, e.g. __builtin_va_arg also expects typename. I've added this to both C and C++, because it would be weird if it supported it only in C and not in C++. 2023-09-20 Jakub Jelinek <jakub@redhat.com> gcc/ * builtins.h (type_to_class): Declare. * builtins.cc (type_to_class): No longer static. Return int rather than enum. * doc/extend.texi (__builtin_classify_type): Document. gcc/c/ * c-parser.cc (c_parser_postfix_expression_after_primary): Parse __builtin_classify_type call with typename as argument. gcc/cp/ * parser.cc (cp_parser_postfix_expression): Parse __builtin_classify_type call with typename as argument. * pt.cc (tsubst_copy_and_build): Handle __builtin_classify_type with dependent typename as argument. gcc/testsuite/ * c-c++-common/builtin-classify-type-1.c: New test. * g++.dg/ext/builtin-classify-type-1.C: New test. * g++.dg/ext/builtin-classify-type-2.C: New test. * gcc.dg/builtin-classify-type-1.c: New test.
-rw-r--r--gcc/builtins.cc3
-rw-r--r--gcc/builtins.h1
-rw-r--r--gcc/c/c-parser.cc23
-rw-r--r--gcc/cp/parser.cc42
-rw-r--r--gcc/cp/pt.cc20
-rw-r--r--gcc/doc/extend.texi24
-rw-r--r--gcc/testsuite/c-c++-common/builtin-classify-type-1.c105
-rw-r--r--gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C149
-rw-r--r--gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C11
-rw-r--r--gcc/testsuite/gcc.dg/builtin-classify-type-1.c11
10 files changed, 387 insertions, 2 deletions
diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index 3b453b3..6e4274b 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -113,7 +113,6 @@ static rtx expand_builtin_apply_args (void);
static rtx expand_builtin_apply_args_1 (void);
static rtx expand_builtin_apply (rtx, rtx, rtx);
static void expand_builtin_return (rtx);
-static enum type_class type_to_class (tree);
static rtx expand_builtin_classify_type (tree);
static rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
static rtx expand_builtin_mathfn_ternary (tree, rtx, rtx);
@@ -1853,7 +1852,7 @@ expand_builtin_return (rtx result)
/* Used by expand_builtin_classify_type and fold_builtin_classify_type. */
-static enum type_class
+int
type_to_class (tree type)
{
switch (TREE_CODE (type))
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 6a43de4..3b5c34c 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -156,5 +156,6 @@ extern internal_fn associated_internal_fn (tree);
extern internal_fn replacement_internal_fn (gcall *);
extern bool builtin_with_linkage_p (tree);
+extern int type_to_class (tree);
#endif /* GCC_BUILTINS_H */
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index b9a1b75..0d468b8 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -11606,6 +11606,29 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
literal_zero_mask = 0;
if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
exprlist = NULL;
+ else if (TREE_CODE (expr.value) == FUNCTION_DECL
+ && fndecl_built_in_p (expr.value, BUILT_IN_CLASSIFY_TYPE)
+ && c_parser_next_tokens_start_typename (parser,
+ cla_prefer_id))
+ {
+ /* __builtin_classify_type (type) */
+ c_inhibit_evaluation_warnings++;
+ in_alignof++;
+ struct c_type_name *type = c_parser_type_name (parser);
+ c_inhibit_evaluation_warnings--;
+ in_alignof--;
+ struct c_typespec ret;
+ ret.expr = NULL_TREE;
+ ret.spec = error_mark_node;
+ ret.expr_const_operands = false;
+ if (type != NULL)
+ ret.spec = groktypename (type, &ret.expr,
+ &ret.expr_const_operands);
+ parens.skip_until_found_close (parser);
+ expr.value = build_int_cst (integer_type_node,
+ type_to_class (ret.spec));
+ break;
+ }
else
exprlist = c_parser_expr_list (parser, true, false, &origtypes,
sizeof_arg_loc, sizeof_arg,
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8cac3dc..0e1cbbf 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/known-headers.h"
#include "contracts.h"
#include "bitmap.h"
+#include "builtins.h"
/* The lexer. */
@@ -7906,6 +7907,47 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
= parser->non_integral_constant_expression_p;
parser->integral_constant_expression_p = false;
}
+ else if (TREE_CODE (stripped_expression) == FUNCTION_DECL
+ && fndecl_built_in_p (stripped_expression,
+ BUILT_IN_CLASSIFY_TYPE))
+ {
+ /* __builtin_classify_type (type) */
+ auto cl1 = make_temp_override
+ (parser->type_definition_forbidden_message,
+ G_("types may not be defined in "
+ "%<__builtin_classify_type%> calls"));
+ auto cl2 = make_temp_override
+ (parser->type_definition_forbidden_message_arg,
+ NULL);
+ auto cl3 = make_temp_override (parser->in_type_id_in_expr_p,
+ true);
+ cp_unevaluated uev;
+ cp_parser_parse_tentatively (parser);
+ matching_parens parens;
+ parens.consume_open (parser);
+ tree type = cp_parser_type_id (parser);
+ parens.require_close (parser);
+ if (cp_parser_parse_definitely (parser))
+ {
+ if (dependent_type_p (type))
+ {
+ postfix_expression = build_vl_exp (CALL_EXPR, 4);
+ CALL_EXPR_FN (postfix_expression)
+ = stripped_expression;
+ CALL_EXPR_STATIC_CHAIN (postfix_expression) = type;
+ CALL_EXPR_ARG (postfix_expression, 0)
+ = build_min (SIZEOF_EXPR, size_type_node, type);
+ TREE_TYPE (postfix_expression) = integer_type_node;
+ }
+ else
+ {
+ postfix_expression
+ = build_int_cst (integer_type_node,
+ type_to_class (type));
+ }
+ break;
+ }
+ }
args = (cp_parser_parenthesized_expression_list
(parser, non_attr,
/*cast_p=*/false, /*allow_expansion_p=*/true,
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 8758e21..5c300f4 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "selftest.h"
#include "target.h"
+#include "builtins.h"
/* The type of functions taking a tree, and some additional data, and
returning an int. */
@@ -21037,6 +21038,25 @@ tsubst_copy_and_build (tree t,
/*done=*/false,
/*address_p=*/false);
}
+ else if (CALL_EXPR_STATIC_CHAIN (t)
+ && TREE_CODE (function) == FUNCTION_DECL
+ && fndecl_built_in_p (function, BUILT_IN_CLASSIFY_TYPE))
+ {
+ tree type = tsubst (CALL_EXPR_STATIC_CHAIN (t), args, complain,
+ in_decl);
+ if (dependent_type_p (type))
+ {
+ ret = build_vl_exp (CALL_EXPR, 4);
+ CALL_EXPR_FN (ret) = function;
+ CALL_EXPR_STATIC_CHAIN (ret) = type;
+ CALL_EXPR_ARG (ret, 0)
+ = build_min (SIZEOF_EXPR, size_type_node, type);
+ TREE_TYPE (ret) = integer_type_node;
+ }
+ else
+ ret = build_int_cst (integer_type_node, type_to_class (type));
+ RETURN (ret);
+ }
else if (koenig_p
&& (identifier_p (function)
|| (TREE_CODE (function) == TEMPLATE_ID_EXPR
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index be04604..b4770f1 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -14560,6 +14560,30 @@ need not be a constant. @xref{Object Size Checking}, for a detailed
description of the function.
@enddefbuiltin
+@defbuiltin{int __builtin_classify_type (@var{arg})}
+@defbuiltinx{int __builtin_classify_type (@var{type})}
+The @code{__builtin_classify_type} returns a small integer with a category
+of @var{arg} argument's type, like void type, integer type, enumeral type,
+boolean type, pointer type, reference type, offset type, real type, complex
+type, function type, method type, record type, union type, array type,
+string type, etc. When the argument is an expression, for
+backwards compatibility reason the argument is promoted like arguments
+passed to @code{...} in varargs function, so some classes are never returned
+in certain languages. Alternatively, the argument of the built-in
+function can be a typename, such as the @code{typeof} specifier.
+
+@smallexample
+int a[2];
+__builtin_classify_type (a) == __builtin_classify_type (int[5]);
+__builtin_classify_type (a) == __builtin_classify_type (void*);
+__builtin_classify_type (typeof (a)) == __builtin_classify_type (int[5]);
+@end smallexample
+
+The first comparison will never be true, as @var{a} is implicitly converted
+to pointer. The last two comparisons will be true as they classify
+pointers in the second case and arrays in the last case.
+@enddefbuiltin
+
@defbuiltin{double __builtin_huge_val (void)}
Returns a positive infinity, if supported by the floating-point format,
else @code{DBL_MAX}. This function is suitable for implementing the
diff --git a/gcc/testsuite/c-c++-common/builtin-classify-type-1.c b/gcc/testsuite/c-c++-common/builtin-classify-type-1.c
new file mode 100644
index 0000000..a41dbe1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-classify-type-1.c
@@ -0,0 +1,105 @@
+/* { dg-do run { target { c || c++11 } } } */
+
+#if !defined(__cplusplus) && __STDC_VERSION__ <= 201710L
+#define static_assert _Static_assert
+#define bool _Bool
+#define false ((_Bool) 0)
+#endif
+#ifdef __cplusplus
+extern "C" void abort ();
+#else
+extern void abort (void);
+#endif
+
+int
+main ()
+{
+ enum E { E1 } e = E1;
+ struct S { int s; } s = { 0 };
+ union U { int u; } u = { 0 };
+ int a[2] = { 0, 0 };
+ bool b = false;
+ const char *p = (const char *) 0;
+ float f = 0.0;
+ _Complex double c = 0.0;
+#ifdef __cplusplus
+ struct T { void foo (); };
+ int &r = a[0];
+ int S::*q = &S::s;
+#endif
+ static_assert (__builtin_classify_type (void) == 0, "");
+ static_assert (__builtin_classify_type (int) == 1, "");
+ static_assert (__builtin_classify_type (enum E) == 3, "");
+ static_assert (__builtin_classify_type (bool) == 4, "");
+ static_assert (__builtin_classify_type (const char *) == 5, "");
+#ifdef __cplusplus
+ static_assert (__builtin_classify_type (int &) == 6, "");
+ static_assert (__builtin_classify_type (int &&) == 6, "");
+ static_assert (__builtin_classify_type (int S::*) == 7, "");
+#endif
+ static_assert (__builtin_classify_type (float) == 8, "");
+ static_assert (__builtin_classify_type (_Complex double) == 9, "");
+ static_assert (__builtin_classify_type (int (int, int)) == 10, "");
+ static_assert (__builtin_classify_type (struct S) == 12, "");
+ static_assert (__builtin_classify_type (union U) == 13, "");
+ static_assert (__builtin_classify_type (int [2]) == 14, "");
+ static_assert (__builtin_classify_type (__typeof__ (a[0])) == 1, "");
+ static_assert (__builtin_classify_type (__typeof__ (e)) == 3, "");
+ static_assert (__builtin_classify_type (__typeof__ (b)) == 4, "");
+ static_assert (__builtin_classify_type (__typeof__ (p)) == 5, "");
+#ifdef __cplusplus
+ static_assert (__builtin_classify_type (decltype (r)) == 6, "");
+ static_assert (__builtin_classify_type (__typeof__ (q)) == 7, "");
+#endif
+ static_assert (__builtin_classify_type (__typeof__ (f)) == 8, "");
+ static_assert (__builtin_classify_type (__typeof__ (c)) == 9, "");
+ static_assert (__builtin_classify_type (__typeof__ (main)) == 10, "");
+ static_assert (__builtin_classify_type (__typeof__ (s)) == 12, "");
+ static_assert (__builtin_classify_type (__typeof__ (u)) == 13, "");
+ static_assert (__builtin_classify_type (__typeof__ (a)) == 14, "");
+#ifndef __cplusplus
+ static_assert (__builtin_classify_type (a[0]) == 1, "");
+ static_assert (__builtin_classify_type (e) == 1, "");
+ static_assert (__builtin_classify_type (b) == 1, "");
+ static_assert (__builtin_classify_type (p) == 5, "");
+ static_assert (__builtin_classify_type (f) == 8, "");
+ static_assert (__builtin_classify_type (c) == 9, "");
+ static_assert (__builtin_classify_type (main) == 5, "");
+ static_assert (__builtin_classify_type (s) == 12, "");
+ static_assert (__builtin_classify_type (u) == 13, "");
+ static_assert (__builtin_classify_type (a) == 5, "");
+#endif
+ if (__builtin_classify_type (a[0]) != 1)
+ abort ();
+#ifdef __cplusplus
+ if (__builtin_classify_type (e) != 3)
+ abort ();
+ if (__builtin_classify_type (b) != 4)
+ abort ();
+#else
+ if (__builtin_classify_type (e) != 1)
+ abort ();
+ if (__builtin_classify_type (b) != 1)
+ abort ();
+#endif
+ if (__builtin_classify_type (p) != 5)
+ abort ();
+#ifdef __cplusplus
+ if (__builtin_classify_type (r) != 1)
+ abort ();
+ if (__builtin_classify_type (q) != 7)
+ abort ();
+#endif
+ if (__builtin_classify_type (f) != 8)
+ abort ();
+ if (__builtin_classify_type (c) != 9)
+ abort ();
+ if (__builtin_classify_type (main) != 5)
+ abort ();
+ if (__builtin_classify_type (s) != 12)
+ abort ();
+ if (__builtin_classify_type (u) != 13)
+ abort ();
+ if (__builtin_classify_type (a) != 5)
+ abort ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C b/gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C
new file mode 100644
index 0000000..0d145f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C
@@ -0,0 +1,149 @@
+// { dg-do run { target c++11 } }
+
+extern "C" void abort ();
+
+template <int N>
+void
+foo ()
+{
+ enum E { E1 } e = E1;
+ struct S { int s; } s = { 0 };
+ union U { int u; } u = { 0 };
+ int a[2] = { 0, 0 };
+ bool b = false;
+ const char *p = (const char *) 0;
+ float f = 0.0;
+ _Complex double c = 0.0;
+ struct T { void foo (); };
+ int &r = a[0];
+ int S::*q = &S::s;
+ static_assert (__builtin_classify_type (void) == 0, "");
+ static_assert (__builtin_classify_type (int) == 1, "");
+ static_assert (__builtin_classify_type (enum E) == 3, "");
+ static_assert (__builtin_classify_type (bool) == 4, "");
+ static_assert (__builtin_classify_type (const char *) == 5, "");
+ static_assert (__builtin_classify_type (int &) == 6, "");
+ static_assert (__builtin_classify_type (int &&) == 6, "");
+ static_assert (__builtin_classify_type (int S::*) == 7, "");
+ static_assert (__builtin_classify_type (float) == 8, "");
+ static_assert (__builtin_classify_type (_Complex double) == 9, "");
+ static_assert (__builtin_classify_type (int (int, int)) == 10, "");
+ static_assert (__builtin_classify_type (struct S) == 12, "");
+ static_assert (__builtin_classify_type (union U) == 13, "");
+ static_assert (__builtin_classify_type (int [2]) == 14, "");
+ static_assert (__builtin_classify_type (__typeof__ (a[0])) == 1, "");
+ static_assert (__builtin_classify_type (__typeof__ (e)) == 3, "");
+ static_assert (__builtin_classify_type (__typeof__ (b)) == 4, "");
+ static_assert (__builtin_classify_type (__typeof__ (p)) == 5, "");
+ static_assert (__builtin_classify_type (decltype (r)) == 6, "");
+ static_assert (__builtin_classify_type (__typeof__ (q)) == 7, "");
+ static_assert (__builtin_classify_type (__typeof__ (f)) == 8, "");
+ static_assert (__builtin_classify_type (__typeof__ (c)) == 9, "");
+ static_assert (__builtin_classify_type (__typeof__ (abort)) == 10, "");
+ static_assert (__builtin_classify_type (__typeof__ (s)) == 12, "");
+ static_assert (__builtin_classify_type (__typeof__ (u)) == 13, "");
+ static_assert (__builtin_classify_type (__typeof__ (a)) == 14, "");
+ if (__builtin_classify_type (a[0]) != 1)
+ abort ();
+ if (__builtin_classify_type (e) != 3)
+ abort ();
+ if (__builtin_classify_type (b) != 4)
+ abort ();
+ if (__builtin_classify_type (p) != 5)
+ abort ();
+ if (__builtin_classify_type (r) != 1)
+ abort ();
+ if (__builtin_classify_type (q) != 7)
+ abort ();
+ if (__builtin_classify_type (f) != 8)
+ abort ();
+ if (__builtin_classify_type (c) != 9)
+ abort ();
+ if (__builtin_classify_type (abort) != 5)
+ abort ();
+ if (__builtin_classify_type (s) != 12)
+ abort ();
+ if (__builtin_classify_type (u) != 13)
+ abort ();
+ if (__builtin_classify_type (a) != 5)
+ abort ();
+}
+
+template <typename V, typename I, typename E, typename B, typename P,
+ typename R1, typename R2, typename PM, typename F,
+ typename C, typename FN, typename S, typename U, typename A>
+void
+bar ()
+{
+ E e = (E) 0;
+ S s = { 0 };
+ U u = { 0 };
+ A a = { 0, 0 };
+ B b = false;
+ P p = (P) 0;
+ F f = 0.0;
+ C c = 0.0;
+ R1 r = a[0];
+ PM q = &S::s;
+ static_assert (__builtin_classify_type (V) == 0, "");
+ static_assert (__builtin_classify_type (I) == 1, "");
+ static_assert (__builtin_classify_type (E) == 3, "");
+ static_assert (__builtin_classify_type (B) == 4, "");
+ static_assert (__builtin_classify_type (P) == 5, "");
+ static_assert (__builtin_classify_type (R1) == 6, "");
+ static_assert (__builtin_classify_type (R2) == 6, "");
+ static_assert (__builtin_classify_type (PM) == 7, "");
+ static_assert (__builtin_classify_type (F) == 8, "");
+ static_assert (__builtin_classify_type (C) == 9, "");
+ static_assert (__builtin_classify_type (FN) == 10, "");
+ static_assert (__builtin_classify_type (S) == 12, "");
+ static_assert (__builtin_classify_type (U) == 13, "");
+ static_assert (__builtin_classify_type (A) == 14, "");
+ static_assert (__builtin_classify_type (__typeof__ (a[0])) == 1, "");
+ static_assert (__builtin_classify_type (__typeof__ (e)) == 3, "");
+ static_assert (__builtin_classify_type (__typeof__ (b)) == 4, "");
+ static_assert (__builtin_classify_type (__typeof__ (p)) == 5, "");
+ static_assert (__builtin_classify_type (decltype (r)) == 6, "");
+ static_assert (__builtin_classify_type (__typeof__ (q)) == 7, "");
+ static_assert (__builtin_classify_type (__typeof__ (f)) == 8, "");
+ static_assert (__builtin_classify_type (__typeof__ (c)) == 9, "");
+ static_assert (__builtin_classify_type (__typeof__ (abort)) == 10, "");
+ static_assert (__builtin_classify_type (__typeof__ (s)) == 12, "");
+ static_assert (__builtin_classify_type (__typeof__ (u)) == 13, "");
+ static_assert (__builtin_classify_type (__typeof__ (a)) == 14, "");
+ if (__builtin_classify_type (a[0]) != 1)
+ abort ();
+ if (__builtin_classify_type (e) != 3)
+ abort ();
+ if (__builtin_classify_type (b) != 4)
+ abort ();
+ if (__builtin_classify_type (p) != 5)
+ abort ();
+ if (__builtin_classify_type (r) != 1)
+ abort ();
+ if (__builtin_classify_type (q) != 7)
+ abort ();
+ if (__builtin_classify_type (f) != 8)
+ abort ();
+ if (__builtin_classify_type (c) != 9)
+ abort ();
+ if (__builtin_classify_type (abort) != 5)
+ abort ();
+ if (__builtin_classify_type (s) != 12)
+ abort ();
+ if (__builtin_classify_type (u) != 13)
+ abort ();
+ if (__builtin_classify_type (a) != 5)
+ abort ();
+}
+
+int
+main ()
+{
+ enum E { E1 };
+ struct S { int s; };
+ union U { int u; };
+ foo <0> ();
+ bar <void, int, E, bool, const char *, int &, int &&, int S::*,
+ float, _Complex double, int (int, int), S, U, int [2]> ();
+}
diff --git a/gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C b/gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C
new file mode 100644
index 0000000..acebd3f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+void
+foo (int n)
+{
+ __builtin_classify_type (enum E { E1, E2 }); // { dg-error "types may not be defined in '__builtin_classify_type' calls" }
+ __builtin_classify_type (struct S { int s; });// { dg-error "types may not be defined in '__builtin_classify_type' calls" }
+ __builtin_classify_type (union U { int u; }); // { dg-error "types may not be defined in '__builtin_classify_type' calls" }
+ __builtin_classify_type (int [2 * n + 36]);
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-classify-type-1.c b/gcc/testsuite/gcc.dg/builtin-classify-type-1.c
new file mode 100644
index 0000000..2268c23
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-classify-type-1.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+void
+foo (int n)
+{
+ _Static_assert (__builtin_classify_type (enum E { E1, E2 }) == 3, "");
+ _Static_assert (__builtin_classify_type (struct S { int s; }) == 12, "");
+ _Static_assert (__builtin_classify_type (union U { int u; }) == 13, "");
+ _Static_assert (__builtin_classify_type (int [2 * n + 36]) == 14, "");
+}