Commit 56898e43 by Richard Sandiford Committed by Richard Sandiford

[C] Avoid exposing internal details in aka types

The current aka diagnostics can sometimes leak internal details that
seem more likely to be distracting than useful.  E.g. on aarch64:

  void f (va_list *va) { *va = 1; }

gives:

  incompatible types when assigning to type ‘va_list’ {aka ‘__va_list’} from type ‘int’

where __va_list isn't something the user is expected to know about.
A similar thing happens for C++ on the arm_neon.h-based:

  float x;
  int8x8_t y = x;

which gives:

  cannot convert ‘float’ to ‘int8x8_t’ {aka ‘__Int8x8_t’} in initialization

This is accurate -- and __Int8x8_t is defined by the AArch64 PCS --
but it's not going to be meaningful to most users.

This patch stops the aka code looking through typedefs if all of
the following are true:

(1) the typedef is built into the compiler or comes from a system header

(2) the target of the typedef is anonymous or has a name in the
    implementation namespace

(3) the target type is a tag type or vector type, which have in common that:
    (a) we print their type names if they have one
    (b) what we print for anonymous types isn't all that useful
        ("struct <anonymous>" etc. for tag types, pseudo-C "__vector(N) T"
        for vector types)

The patch does this by recursively looking for the aka type, like the
C++ frontend already does.  This in turn makes "aka" work for distinct type
copies like __Int8x8_t on aarch64, fixing the ??? in aarch64/diag_aka_1.c.

2019-10-14  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (user_facing_original_type_p): Declare.
	* c-common.c: Include c-spellcheck.h.
	(user_facing_original_type_p): New function.

gcc/c/
	* c-objc-common.c (useful_aka_type_p): Replace with...
	(get_aka_type): ...this new function.  Given the original type,
	decide which aka type to print (if any).  Only look through typedefs
	if user_facing_original_type_p.
	(print_type): Update accordingly.

gcc/testsuite/
	* gcc.dg/diag-aka-5.h: New test.
	* gcc.dg/diag-aka-5a.c: Likewise.
	* gcc.dg/diag-aka-5b.c: Likewise.
	* gcc.target/aarch64/diag_aka_1.c (f): Expect an aka to be printed
	for myvec.

From-SVN: r276951
parent b9424661
2019-10-14 Richard Sandiford <richard.sandiford@arm.com>
* c-common.h (user_facing_original_type_p): Declare.
* c-common.c: Include c-spellcheck.h.
(user_facing_original_type_p): New function.
2019-10-12 Jakub Jelinek <jakub@redhat.com> 2019-10-12 Jakub Jelinek <jakub@redhat.com>
* c-common.h (c_omp_mark_declare_variant, * c-common.h (c_omp_mark_declare_variant,
......
...@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
#include "gimplify.h" #include "gimplify.h"
#include "substring-locations.h" #include "substring-locations.h"
#include "spellcheck.h" #include "spellcheck.h"
#include "c-spellcheck.h"
#include "selftest.h" #include "selftest.h"
cpp_reader *parse_in; /* Declared in c-pragma.h. */ cpp_reader *parse_in; /* Declared in c-pragma.h. */
...@@ -7713,6 +7714,52 @@ set_underlying_type (tree x) ...@@ -7713,6 +7714,52 @@ set_underlying_type (tree x)
} }
} }
/* Return true if it is worth exposing the DECL_ORIGINAL_TYPE of TYPE to
the user in diagnostics, false if it would be better to use TYPE itself.
TYPE is known to satisfy typedef_variant_p. */
bool
user_facing_original_type_p (const_tree type)
{
gcc_assert (typedef_variant_p (type));
tree decl = TYPE_NAME (type);
/* Look through any typedef in "user" code. */
if (!DECL_IN_SYSTEM_HEADER (decl) && !DECL_IS_BUILTIN (decl))
return true;
/* If the original type is also named and is in the user namespace,
assume it too is a user-facing type. */
tree orig_type = DECL_ORIGINAL_TYPE (decl);
if (tree orig_id = TYPE_IDENTIFIER (orig_type))
if (!name_reserved_for_implementation_p (IDENTIFIER_POINTER (orig_id)))
return true;
switch (TREE_CODE (orig_type))
{
/* Don't look through to an anonymous vector type, since the syntax
we use for them in diagnostics isn't real C or C++ syntax.
And if ORIG_TYPE is named but in the implementation namespace,
TYPE is likely to be more meaningful to the user. */
case VECTOR_TYPE:
return false;
/* Don't expose anonymous tag types that are presumably meant to be
known by their typedef name. Also don't expose tags that are in
the implementation namespace, such as:
typedef struct __foo foo; */
case RECORD_TYPE:
case UNION_TYPE:
case ENUMERAL_TYPE:
return false;
/* Look through to anything else. */
default:
return true;
}
}
/* Record the types used by the current global variable declaration /* Record the types used by the current global variable declaration
being parsed, so that we can decide later to emit their debug info. being parsed, so that we can decide later to emit their debug info.
Those types are in types_used_by_cur_var_decl, and we are going to Those types are in types_used_by_cur_var_decl, and we are going to
......
...@@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int, bool); ...@@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int, bool);
extern void c_common_mark_addressable_vec (tree); extern void c_common_mark_addressable_vec (tree);
extern void set_underlying_type (tree); extern void set_underlying_type (tree);
extern bool user_facing_original_type_p (const_tree);
extern void record_types_used_by_current_var_decl (tree); extern void record_types_used_by_current_var_decl (tree);
extern vec<tree, va_gc> *make_tree_vector (void); extern vec<tree, va_gc> *make_tree_vector (void);
extern void release_tree_vector (vec<tree, va_gc> *); extern void release_tree_vector (vec<tree, va_gc> *);
......
2019-10-14 Richard Sandiford <richard.sandiford@arm.com>
* c-objc-common.c (useful_aka_type_p): Replace with...
(get_aka_type): ...this new function. Given the original type,
decide which aka type to print (if any). Only look through typedefs
if user_facing_original_type_p.
(print_type): Update accordingly.
2019-10-14 Jakub Jelinek <jakub@redhat.com> 2019-10-14 Jakub Jelinek <jakub@redhat.com>
* c-parser.c (c_parser_omp_all_clauses): Change bool NESTED_P argument * c-parser.c (c_parser_omp_all_clauses): Change bool NESTED_P argument
......
...@@ -28,6 +28,8 @@ along with GCC; see the file COPYING3. If not see ...@@ -28,6 +28,8 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h" #include "langhooks.h"
#include "c-objc-common.h" #include "c-objc-common.h"
#include "gcc-rich-location.h" #include "gcc-rich-location.h"
#include "stringpool.h"
#include "attribs.h"
static bool c_tree_printer (pretty_printer *, text_info *, const char *, static bool c_tree_printer (pretty_printer *, text_info *, const char *,
int, bool, bool, bool, bool *, const char **); int, bool, bool, bool, bool *, const char **);
...@@ -62,71 +64,120 @@ c_objc_common_init (void) ...@@ -62,71 +64,120 @@ c_objc_common_init (void)
return c_common_init (); return c_common_init ();
} }
/* Return true if it's worth saying that TYPE1 is also known as TYPE2. */ /* Decide whether it's worth saying that TYPE is also known as some other
type. Return the other type if so, otherwise return TYPE. */
static bool static tree
useful_aka_type_p (tree type1, tree type2) get_aka_type (tree type)
{ {
if (type1 == type2) if (type == error_mark_node)
return false; return type;
if (type1 == error_mark_node || type2 == error_mark_node)
return false;
if (TREE_CODE (type1) != TREE_CODE (type2))
return true;
if (typedef_variant_p (type1)) tree result;
if (typedef_variant_p (type))
{ {
/* Saying that "foo" is also known as "struct foo" or /* Saying that "foo" is also known as "struct foo" or
"struct <anonymous>" is unlikely to be useful, since users of "struct <anonymous>" is unlikely to be useful, since users of
structure-like types would already know that they're structures. structure-like types would already know that they're structures.
The same applies to unions and enums; in general, printing the The same applies to unions and enums; in general, printing the
tag is only useful if it has a different name. */ tag is only useful if it has a different name. */
tree_code code = TREE_CODE (type2); tree orig_type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
tree id2 = TYPE_IDENTIFIER (type2); tree_code code = TREE_CODE (orig_type);
tree orig_id = TYPE_IDENTIFIER (orig_type);
if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE) if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
&& (!id2 || TYPE_IDENTIFIER (type1) == id2)) && (!orig_id || TYPE_IDENTIFIER (type) == orig_id))
return false; return type;
return true; if (!user_facing_original_type_p (type))
return type;
result = get_aka_type (orig_type);
} }
else else
{ {
switch (TREE_CODE (type1)) tree canonical = TYPE_CANONICAL (type);
if (canonical && TREE_CODE (type) != TREE_CODE (canonical))
return canonical;
/* Recursive calls might choose a middle ground between TYPE
(which has no typedefs stripped) and CANONICAL (which has
all typedefs stripped). So try to reuse TYPE or CANONICAL if
convenient, but be prepared to create a new type if necessary. */
switch (TREE_CODE (type))
{ {
case POINTER_TYPE: case POINTER_TYPE:
case REFERENCE_TYPE: case REFERENCE_TYPE:
return useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2)); {
tree target_type = get_aka_type (TREE_TYPE (type));
if (target_type == TREE_TYPE (type))
return type;
if (canonical && target_type == TREE_TYPE (canonical))
return canonical;
result = (TREE_CODE (type) == POINTER_TYPE
? build_pointer_type (target_type)
: build_reference_type (target_type));
break;
}
case ARRAY_TYPE: case ARRAY_TYPE:
return (useful_aka_type_p (TYPE_DOMAIN (type1), TYPE_DOMAIN (type2)) {
|| useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2))); tree element_type = get_aka_type (TREE_TYPE (type));
tree index_type = (TYPE_DOMAIN (type)
? get_aka_type (TYPE_DOMAIN (type))
: NULL_TREE);
if (element_type == TREE_TYPE (type)
&& index_type == TYPE_DOMAIN (type))
return type;
if (canonical
&& element_type == TREE_TYPE (canonical)
&& index_type == TYPE_DOMAIN (canonical))
return canonical;
result = build_array_type (element_type, index_type,
TYPE_TYPELESS_STORAGE (type));
break;
}
case FUNCTION_TYPE: case FUNCTION_TYPE:
{ {
tree args1 = TYPE_ARG_TYPES (type1); tree return_type = get_aka_type (TREE_TYPE (type));
tree args2 = TYPE_ARG_TYPES (type2);
while (args1 != args2) tree args = TYPE_ARG_TYPES (type);
if (args == error_mark_node)
return type;
auto_vec<tree, 32> arg_types;
bool type_ok_p = true;
while (args && args != void_list_node)
{ {
/* Although this shouldn't happen, it seems to wrong to assert tree arg_type = get_aka_type (TREE_VALUE (args));
for it in a diagnostic routine. */ arg_types.safe_push (arg_type);
if (!args1 || args1 == void_type_node) type_ok_p &= (arg_type == TREE_VALUE (args));
return true; args = TREE_CHAIN (args);
if (!args2 || args2 == void_type_node)
return true;
if (useful_aka_type_p (TREE_VALUE (args1), TREE_VALUE (args2)))
return true;
args1 = TREE_CHAIN (args1);
args2 = TREE_CHAIN (args2);
} }
return useful_aka_type_p (TREE_TYPE (type1), TREE_TYPE (type2));
if (type_ok_p && return_type == TREE_TYPE (type))
return type;
unsigned int i;
tree arg_type;
FOR_EACH_VEC_ELT_REVERSE (arg_types, i, arg_type)
args = tree_cons (NULL_TREE, arg_type, args);
result = build_function_type (return_type, args);
break;
} }
default: default:
return true; return canonical ? canonical : type;
} }
} }
return build_type_attribute_qual_variant (result, TYPE_ATTRIBUTES (type),
TYPE_QUALS (type));
} }
/* Print T to CPP. */ /* Print T to CPP. */
...@@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted) ...@@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted)
stripped version. But sometimes the stripped version looks stripped version. But sometimes the stripped version looks
exactly the same, so we don't want it after all. To avoid exactly the same, so we don't want it after all. To avoid
printing it in that case, we play ugly obstack games. */ printing it in that case, we play ugly obstack games. */
if (TYPE_CANONICAL (t) && useful_aka_type_p (t, TYPE_CANONICAL (t))) tree aka_type = get_aka_type (t);
if (aka_type != t)
{ {
c_pretty_printer cpp2; c_pretty_printer cpp2;
/* Print the stripped version into a temporary printer. */ /* Print the stripped version into a temporary printer. */
cpp2.type_id (TYPE_CANONICAL (t)); cpp2.type_id (aka_type);
struct obstack *ob2 = cpp2.buffer->obstack; struct obstack *ob2 = cpp2.buffer->obstack;
/* Get the stripped version from the temporary printer. */ /* Get the stripped version from the temporary printer. */
const char *aka = (char *) obstack_base (ob2); const char *aka = (char *) obstack_base (ob2);
...@@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted) ...@@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree t, bool *quoted)
pp_c_whitespace (cpp); pp_c_whitespace (cpp);
if (*quoted) if (*quoted)
pp_begin_quote (cpp, pp_show_color (cpp)); pp_begin_quote (cpp, pp_show_color (cpp));
cpp->type_id (TYPE_CANONICAL (t)); cpp->type_id (aka_type);
if (*quoted) if (*quoted)
pp_end_quote (cpp, pp_show_color (cpp)); pp_end_quote (cpp, pp_show_color (cpp));
pp_right_brace (cpp); pp_right_brace (cpp);
......
2019-10-14 Richard Sandiford <richard.sandiford@arm.com>
* gcc.dg/diag-aka-5.h: New test.
* gcc.dg/diag-aka-5a.c: Likewise.
* gcc.dg/diag-aka-5b.c: Likewise.
* gcc.target/aarch64/diag_aka_1.c (f): Expect an aka to be printed
for myvec.
2019-10-14 Jakub Jelinek <jakub@redhat.com> 2019-10-14 Jakub Jelinek <jakub@redhat.com>
* c-c++-common/gomp/declare-variant-7.c: Add tests for clauses not * c-c++-common/gomp/declare-variant-7.c: Add tests for clauses not
......
#ifdef IS_SYSTEM_HEADER
#pragma GCC system_header
#endif
typedef enum __internal_enum { A, B } user_enum;
typedef user_enum *user_enum_ptr;
typedef struct __internal_struct { int i; } user_struct;
typedef user_struct user_struct_copy;
typedef user_struct *user_struct_ptr;
typedef union __internal_union { int i; } user_union;
typedef user_union user_union_copy;
typedef user_union *user_union_ptr;
typedef unsigned int user_vector __attribute__((__vector_size__(16)));
typedef user_vector user_vector_copy;
typedef user_vector *user_vector_ptr;
typedef int user_int;
typedef user_int user_int_copy;
typedef user_int *user_int_ptr;
...@@ -8,7 +8,6 @@ void f (float x) ...@@ -8,7 +8,6 @@ void f (float x)
__Int8x8_t *ptr1 = &x; /* { dg-error {initialization of '__Int8x8_t \*' from incompatible pointer type 'float \*'} } */ __Int8x8_t *ptr1 = &x; /* { dg-error {initialization of '__Int8x8_t \*' from incompatible pointer type 'float \*'} } */
int8x8_t y2 = x; /* { dg-error {incompatible types when initializing type 'int8x8_t' using type 'float'} } */ int8x8_t y2 = x; /* { dg-error {incompatible types when initializing type 'int8x8_t' using type 'float'} } */
int8x8_t *ptr2 = &x; /* { dg-error {initialization of 'int8x8_t \*' from incompatible pointer type 'float \*'} } */ int8x8_t *ptr2 = &x; /* { dg-error {initialization of 'int8x8_t \*' from incompatible pointer type 'float \*'} } */
/* ??? For these it would be better to print an aka for 'int16x4_t'. */ myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' {aka 'int16x4_t'} using type 'float'} } */
myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' using type 'float'} } */ myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' {aka 'int16x4_t \*'} from incompatible pointer type 'float \*'} } */
myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' from incompatible pointer type 'float \*'} } */
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment