diff options
author | Jakub Jelinek <jakub@redhat.com> | 2023-09-20 18:37:29 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2023-09-20 18:37:29 +0200 |
commit | 53d834a7fae3afffebb45a2d66908f705773a7fc (patch) | |
tree | 4af979256b3f85649ada1d5cabc08c21cf6947db /gcc | |
parent | 27282dc0931484c31fa391772499d878afcc746a (diff) | |
download | gcc-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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/builtins.cc | 3 | ||||
-rw-r--r-- | gcc/builtins.h | 1 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 23 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 42 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 20 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 24 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/builtin-classify-type-1.c | 105 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C | 149 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C | 11 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/builtin-classify-type-1.c | 11 |
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, ""); +} |