From 53d834a7fae3afffebb45a2d66908f705773a7fc Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Wed, 20 Sep 2023 18:37:29 +0200 Subject: [PATCH] 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 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. --- gcc/builtins.cc | 3 +- gcc/builtins.h | 1 + gcc/c/c-parser.cc | 23 +++ gcc/cp/parser.cc | 42 +++++ gcc/cp/pt.cc | 20 +++ gcc/doc/extend.texi | 24 +++ .../c-c++-common/builtin-classify-type-1.c | 105 ++++++++++++ .../g++.dg/ext/builtin-classify-type-1.C | 149 ++++++++++++++++++ .../g++.dg/ext/builtin-classify-type-2.C | 11 ++ .../gcc.dg/builtin-classify-type-1.c | 11 ++ 10 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/builtin-classify-type-1.c create mode 100644 gcc/testsuite/g++.dg/ext/builtin-classify-type-1.C create mode 100644 gcc/testsuite/g++.dg/ext/builtin-classify-type-2.C create mode 100644 gcc/testsuite/gcc.dg/builtin-classify-type-1.c diff --git a/gcc/builtins.cc b/gcc/builtins.cc index 3b453b3ec8c3..6e4274bb2a4e 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 6a43de4b3efe..3b5c34c48023 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 b9a1b75ca43c..0d468b86bd8c 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 8cac3dc94b9f..0e1cbbfe051b 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 8758e218ce4c..5c300f4f0c86 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 be04604e8f0b..b4770f1a149a 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 000000000000..a41dbe163015 --- /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 000000000000..0d145f14bab6 --- /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 +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 +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 (); +} 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 000000000000..acebd3f67955 --- /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 000000000000..2268c2303768 --- /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, ""); +} -- 2.47.2