public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C][C++] Avoid exposing internal details in aka types
@ 2019-09-30 13:21 Richard Sandiford
  2019-10-01  7:41 ` Richard Biener
  2019-10-10 19:02 ` Ping: " Richard Sandiford
  0 siblings, 2 replies; 8+ messages in thread
From: Richard Sandiford @ 2019-09-30 13:21 UTC (permalink / raw)
  To: gcc-patches; +Cc: dodji, dmalcolm, joseph, polacek, jason, nathan

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 C side does this by recursively looking for the aka type, like the
C++ side 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.

On the C++ side, strip_typedefs had:

	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
	     strip typedefs with attributes.  */
	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
	  result = strip_typedefs (result);

Applying TYPE_MAIN_VARIANT predates the strip_typedefs call, with the
comment originally contrasting with plain:

	  result = TYPE_MAIN_VARIANT (t);

But the recursive call to strip_typedefs will apply TYPE_MAIN_VARIANT,
so it doesn't seem necessary to do it here too.  I think there was also
a missing "remove_attributes" argument, since wrapping something in a
typedef shouldn't change which attributes get removed.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Richard


2019-09-30  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (user_facing_original_type_p): Declare.
	* c-common.c (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/cp/
	* cp-tree.h (STF_USER_VISIBLE): New constant.
	(strip_typedefs, strip_typedefs_expr): Take a flags argument.
	* tree.c (strip_typedefs, strip_typedefs_expr): Likewise,
	updating mutual calls accordingly.  When STF_USER_VISIBLE is true,
	only look through typedefs if user_facing_original_type_p.
	* error.c (dump_template_bindings, type_to_string): Pass
	STF_USER_VISIBLE to strip_typedefs.
	(dump_type): Likewise, unless pp_c_flag_gnu_v3 is set.

gcc/testsuite/
	* g++.dg/diagnostic/aka5.h: New test.
	* g++.dg/diagnostic/aka5a.C: Likewise.
	* g++.dg/diagnostic/aka5b.C: Likewise.
	* g++.target/aarch64/diag_aka_1.C: Likewise.
	* gcc.dg/diag-aka-5.h: Likewise.
	* 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.

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2019-09-30 13:54:16.000000000 +0100
+++ gcc/c-family/c-common.h	2019-09-30 14:16:45.002103890 +0100
@@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int,
 extern void c_common_mark_addressable_vec (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 vec<tree, va_gc> *make_tree_vector (void);
 extern void release_tree_vector (vec<tree, va_gc> *);
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/c-family/c-common.c	2019-09-30 14:16:45.002103890 +0100
@@ -7713,6 +7713,55 @@ 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))
+    {
+      const char *name = IDENTIFIER_POINTER (orig_id);
+      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
+	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
    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
Index: gcc/c/c-objc-common.c
===================================================================
--- gcc/c/c-objc-common.c	2019-09-30 14:08:26.597671509 +0100
+++ gcc/c/c-objc-common.c	2019-09-30 14:16:45.002103890 +0100
@@ -28,6 +28,8 @@ Software Foundation; either version 3, o
 #include "langhooks.h"
 #include "c-objc-common.h"
 #include "gcc-rich-location.h"
+#include "stringpool.h"
+#include "attribs.h"
 
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
 			    int, bool, bool, bool, bool *, const char **);
@@ -62,71 +64,120 @@ c_objc_common_init (void)
   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
-useful_aka_type_p (tree type1, tree type2)
+static tree
+get_aka_type (tree type)
 {
-  if (type1 == type2)
-    return false;
-
-  if (type1 == error_mark_node || type2 == error_mark_node)
-    return false;
-
-  if (TREE_CODE (type1) != TREE_CODE (type2))
-    return true;
+  if (type == error_mark_node)
+    return type;
 
-  if (typedef_variant_p (type1))
+  tree result;
+  if (typedef_variant_p (type))
     {
       /* Saying that "foo" is also known as "struct foo" or
 	 "struct <anonymous>" is unlikely to be useful, since users of
 	 structure-like types would already know that they're structures.
 	 The same applies to unions and enums; in general, printing the
 	 tag is only useful if it has a different name.  */
-      tree_code code = TREE_CODE (type2);
-      tree id2 = TYPE_IDENTIFIER (type2);
+      tree orig_type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
+      tree_code code = TREE_CODE (orig_type);
+      tree orig_id = TYPE_IDENTIFIER (orig_type);
       if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
-	  && (!id2 || TYPE_IDENTIFIER (type1) == id2))
-	return false;
+	  && (!orig_id || TYPE_IDENTIFIER (type) == orig_id))
+	return type;
 
-      return true;
+      if (!user_facing_original_type_p (type))
+	return type;
+
+      result = get_aka_type (orig_type);
     }
   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 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:
-	  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:
 	  {
-	    tree args1 = TYPE_ARG_TYPES (type1);
-	    tree args2 = TYPE_ARG_TYPES (type2);
-	    while (args1 != args2)
+	    tree return_type = get_aka_type (TREE_TYPE (type));
+
+	    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
-		   for it in a diagnostic routine.  */
-		if (!args1 || args1 == void_type_node)
-		  return true;
-		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);
+		tree arg_type = get_aka_type (TREE_VALUE (args));
+		arg_types.safe_push (arg_type);
+		type_ok_p &= (arg_type == TREE_VALUE (args));
+		args = TREE_CHAIN (args);
 	      }
-	    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:
-	  return true;
+	  return canonical ? canonical : type;
 	}
     }
+  return build_type_attribute_qual_variant (result, TYPE_ATTRIBUTES (type),
+					    TYPE_QUALS (type));
 }
 
 /* Print T to CPP.  */
@@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree
      stripped version.  But sometimes the stripped version looks
      exactly the same, so we don't want it after all.  To avoid
      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;
       /* 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;
       /* Get the stripped version from the temporary printer.  */
       const char *aka = (char *) obstack_base (ob2);
@@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree
       pp_c_whitespace (cpp);
       if (*quoted)
 	pp_begin_quote (cpp, pp_show_color (cpp));
-      cpp->type_id (TYPE_CANONICAL (t));
+      cpp->type_id (aka_type);
       if (*quoted)
 	pp_end_quote (cpp, pp_show_color (cpp));
       pp_right_brace (cpp);
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/cp-tree.h	2019-09-30 14:16:45.006103863 +0100
@@ -5672,6 +5672,13 @@ #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARG
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
 
+/* These constants can be used as bit flags to control strip_typedefs.
+
+   STF_USER_VISIBLE: use heuristics to try to avoid stripping user-facing
+       aliases of internal details.  This is intended for diagnostics,
+       where it should (for example) give more useful "aka" types.  */
+const unsigned int STF_USER_VISIBLE = 1U;
+
 /* Returns the TEMPLATE_DECL associated to a TEMPLATE_TEMPLATE_PARM
    node.  */
 #define TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL(NODE)	\
@@ -7228,8 +7235,10 @@ extern int zero_init_p				(const_tree);
 extern bool check_abi_tag_redeclaration		(const_tree, const_tree,
 						 const_tree);
 extern bool check_abi_tag_args			(tree, tree);
-extern tree strip_typedefs			(tree, bool * = NULL);
-extern tree strip_typedefs_expr			(tree, bool * = NULL);
+extern tree strip_typedefs			(tree, bool * = NULL,
+						 unsigned int = 0);
+extern tree strip_typedefs_expr			(tree, bool * = NULL,
+						 unsigned int = 0);
 extern tree copy_binfo				(tree, tree, tree,
 						 tree *, int);
 extern int member_p				(const_tree);
Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/tree.c	2019-09-30 14:16:45.006103863 +0100
@@ -1431,7 +1431,10 @@ apply_identity_attributes (tree result,
   return cp_build_type_attribute_variant (result, new_attribs);
 }
 
-/* Builds a qualified variant of T that is not a typedef variant.
+/* Builds a qualified variant of T that is either not a typedef variant
+   (the default behavior) or not a typedef variant of a user-facing type
+   (if FLAGS contains STF_USER_FACING).
+
    E.g. consider the following declarations:
      typedef const int ConstInt;
      typedef ConstInt* PtrConstInt;
@@ -1456,7 +1459,7 @@ apply_identity_attributes (tree result,
    stripped.  */
 
 tree
-strip_typedefs (tree t, bool *remove_attributes)
+strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
 {
   tree result = NULL, type = NULL, t0 = NULL;
 
@@ -1471,7 +1474,7 @@ strip_typedefs (tree t, bool *remove_att
       for (; t; t = TREE_CHAIN (t))
 	{
 	  gcc_assert (!TREE_PURPOSE (t));
-	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes);
+	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes, flags);
 	  if (elt != TREE_VALUE (t))
 	    changed = true;
 	  vec_safe_push (vec, elt);
@@ -1494,28 +1497,29 @@ strip_typedefs (tree t, bool *remove_att
   switch (TREE_CODE (t))
     {
     case POINTER_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_pointer_type (type);
       break;
     case REFERENCE_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = cp_build_reference_type (type, TYPE_REF_IS_RVALUE (t));
       break;
     case OFFSET_TYPE:
-      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes);
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes, flags);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_offset_type (t0, type);
       break;
     case RECORD_TYPE:
       if (TYPE_PTRMEMFUNC_P (t))
 	{
-	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t), remove_attributes);
+	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t),
+			       remove_attributes, flags);
 	  result = build_ptrmemfunc_type (t0);
 	}
       break;
     case ARRAY_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
-      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
+      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
       result = build_cplus_array_type (type, t0);
       break;
     case FUNCTION_TYPE:
@@ -1534,7 +1538,7 @@ strip_typedefs (tree t, bool *remove_att
 	    && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
 	  is_variant = true;
 
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	tree canon_spec = (flag_noexcept_type
 			   ? canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (t))
 			   : NULL_TREE);
@@ -1548,7 +1552,7 @@ strip_typedefs (tree t, bool *remove_att
 	    if (arg_node == void_list_node)
 	      break;
 	    arg_type = strip_typedefs (TREE_VALUE (arg_node),
-				       remove_attributes);
+				       remove_attributes, flags);
 	    gcc_assert (arg_type);
 	    if (arg_type == TREE_VALUE (arg_node) && !changed)
 	      continue;
@@ -1612,9 +1616,10 @@ strip_typedefs (tree t, bool *remove_att
 		tree arg = TREE_VEC_ELT (args, i);
 		tree strip_arg;
 		if (TYPE_P (arg))
-		  strip_arg = strip_typedefs (arg, remove_attributes);
+		  strip_arg = strip_typedefs (arg, remove_attributes, flags);
 		else
-		  strip_arg = strip_typedefs_expr (arg, remove_attributes);
+		  strip_arg = strip_typedefs_expr (arg, remove_attributes,
+						   flags);
 		TREE_VEC_ELT (new_args, i) = strip_arg;
 		if (strip_arg != arg)
 		  changed = true;
@@ -1630,7 +1635,7 @@ strip_typedefs (tree t, bool *remove_att
 	    else
 	      ggc_free (new_args);
 	  }
-	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes);
+	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes, flags);
 	if (!changed && ctx == TYPE_CONTEXT (t) && !typedef_variant_p (t))
 	  return t;
 	tree name = fullname;
@@ -1643,7 +1648,7 @@ strip_typedefs (tree t, bool *remove_att
       break;
     case DECLTYPE_TYPE:
       result = strip_typedefs_expr (DECLTYPE_TYPE_EXPR (t),
-				    remove_attributes);
+				    remove_attributes, flags);
       if (result == DECLTYPE_TYPE_EXPR (t))
 	result = NULL_TREE;
       else
@@ -1653,7 +1658,8 @@ strip_typedefs (tree t, bool *remove_att
 		   tf_none));
       break;
     case UNDERLYING_TYPE:
-      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t), remove_attributes);
+      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t),
+			     remove_attributes, flags);
       result = finish_underlying_type (type);
       break;
     default:
@@ -1664,15 +1670,18 @@ strip_typedefs (tree t, bool *remove_att
     {
       if (typedef_variant_p (t))
 	{
-	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
-	     strip typedefs with attributes.  */
-	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
-	  result = strip_typedefs (result);
+	  if ((flags & STF_USER_VISIBLE)
+	      && !user_facing_original_type_p (t))
+	    return t;
+	  result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
+				   remove_attributes, flags);
 	}
       else
 	result = TYPE_MAIN_VARIANT (t);
     }
-  gcc_assert (!typedef_variant_p (result));
+  gcc_assert (!typedef_variant_p (result)
+	      || ((flags & STF_USER_VISIBLE)
+		  && !user_facing_original_type_p (result)));
 
   if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
   /* If RESULT is complete and T isn't, it's likely the case that T
@@ -1721,7 +1730,7 @@ strip_typedefs (tree t, bool *remove_att
    sizeof(TT) is replaced by sizeof(T).  */
 
 tree
-strip_typedefs_expr (tree t, bool *remove_attributes)
+strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
 {
   unsigned i,n;
   tree r, type, *ops;
@@ -1738,7 +1747,7 @@ strip_typedefs_expr (tree t, bool *remov
   /* Some expressions have type operands, so let's handle types here rather
      than check TYPE_P in multiple places below.  */
   if (TYPE_P (t))
-    return strip_typedefs (t, remove_attributes);
+    return strip_typedefs (t, remove_attributes, flags);
 
   code = TREE_CODE (t);
   switch (code)
@@ -1752,8 +1761,10 @@ strip_typedefs_expr (tree t, bool *remov
 
     case TRAIT_EXPR:
       {
-	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t), remove_attributes);
-	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t), remove_attributes);
+	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t),
+				     remove_attributes, flags);
+	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t),
+				     remove_attributes, flags);
 	if (type1 == TRAIT_EXPR_TYPE1 (t)
 	    && type2 == TRAIT_EXPR_TYPE2 (t))
 	  return t;
@@ -1770,7 +1781,8 @@ strip_typedefs_expr (tree t, bool *remov
 	tree it;
 	for (it = t; it; it = TREE_CHAIN (it))
 	  {
-	    tree val = strip_typedefs_expr (TREE_VALUE (it), remove_attributes);
+	    tree val = strip_typedefs_expr (TREE_VALUE (it),
+					    remove_attributes, flags);
 	    vec_safe_push (vec, val);
 	    if (val != TREE_VALUE (it))
 	      changed = true;
@@ -1796,7 +1808,7 @@ strip_typedefs_expr (tree t, bool *remov
 	for (i = 0; i < n; ++i)
 	  {
 	    tree op = strip_typedefs_expr (TREE_VEC_ELT (t, i),
-					   remove_attributes);
+					   remove_attributes, flags);
 	    vec->quick_push (op);
 	    if (op != TREE_VEC_ELT (t, i))
 	      changed = true;
@@ -1820,18 +1832,19 @@ strip_typedefs_expr (tree t, bool *remov
 	vec<constructor_elt, va_gc> *vec
 	  = vec_safe_copy (CONSTRUCTOR_ELTS (t));
 	n = CONSTRUCTOR_NELTS (t);
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	for (i = 0; i < n; ++i)
 	  {
 	    constructor_elt *e = &(*vec)[i];
-	    tree op = strip_typedefs_expr (e->value, remove_attributes);
+	    tree op = strip_typedefs_expr (e->value, remove_attributes, flags);
 	    if (op != e->value)
 	      {
 		changed = true;
 		e->value = op;
 	      }
 	    gcc_checking_assert
-	      (e->index == strip_typedefs_expr (e->index, remove_attributes));
+	      (e->index == strip_typedefs_expr (e->index, remove_attributes,
+						flags));
 	  }
 
 	if (!changed && type == TREE_TYPE (t))
@@ -1875,12 +1888,13 @@ strip_typedefs_expr (tree t, bool *remov
     case REINTERPRET_CAST_EXPR:
     case CAST_EXPR:
     case NEW_EXPR:
-      type = strip_typedefs (type, remove_attributes);
+      type = strip_typedefs (type, remove_attributes, flags);
       /* fallthrough */
 
     default:
       for (i = 0; i < n; ++i)
-	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i), remove_attributes);
+	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i),
+				      remove_attributes, flags);
       break;
     }
 
Index: gcc/cp/error.c
===================================================================
--- gcc/cp/error.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/error.c	2019-09-30 14:16:45.006103863 +0100
@@ -409,7 +409,7 @@ dump_template_bindings (cxx_pretty_print
       pop_deferring_access_checks ();
       /* Strip typedefs.  We can't just use TFF_CHASE_TYPEDEF because
 	 pp_simple_type_specifier doesn't know about it.  */
-      t = strip_typedefs (t);
+      t = strip_typedefs (t, NULL, STF_USER_VISIBLE);
       dump_type (pp, t, TFF_PLAIN_IDENTIFIER);
     }
 }
@@ -448,7 +448,11 @@ dump_type (cxx_pretty_printer *pp, tree
 	       || DECL_SELF_REFERENCE_P (decl)
 	       || (!flag_pretty_templates
 		   && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)))
-	t = strip_typedefs (t);
+	{
+	  unsigned int stf_flags = (!(pp->flags & pp_c_flag_gnu_v3)
+				    ? STF_USER_VISIBLE : 0);
+	  t = strip_typedefs (t, NULL, stf_flags);
+	}
       else if (alias_template_specialization_p (t))
 	{
 	  dump_alias_template_specialization (pp, t, flags);
@@ -3195,7 +3199,7 @@ type_to_string (tree typ, int verbose, b
       && !uses_template_parms (typ))
     {
       int aka_start, aka_len; char *p;
-      tree aka = strip_typedefs (typ);
+      tree aka = strip_typedefs (typ, NULL, STF_USER_VISIBLE);
       if (quote && *quote)
 	pp_end_quote (cxx_pp, show_color);
       pp_string (cxx_pp, " {aka");
Index: gcc/testsuite/g++.dg/diagnostic/aka5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5.h	2019-09-30 14:16:45.006103863 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/g++.dg/diagnostic/aka5a.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5a.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,127 @@
+#define IS_SYSTEM_HEADER
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka 'user_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka 'user_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const user_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile user_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka 'user_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka 'user_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka 'user_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const user_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka 'user_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka 'user_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const user_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka 'user_vector'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka 'user_vector\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const user_vector\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
Index: gcc/testsuite/g++.dg/diagnostic/aka5b.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5b.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,127 @@
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' {aka '__internal_enum'} in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka '__internal_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka '__internal_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' {aka '__internal_enum\*'} in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' {aka 'const __internal_enum\*'} in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const __internal_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' {aka '__internal_struct'} and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka '__internal_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka '__internal_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' {aka '__internal_struct\*'} in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' {aka 'const __internal_struct\*'} in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const __internal_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' {aka '__internal_union'} and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka '__internal_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka '__internal_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' {aka '__internal_union\*'} in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' {aka 'const __internal_union\*'} in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const __internal_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
+
Index: gcc/testsuite/g++.target/aarch64/diag_aka_1.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.target/aarch64/diag_aka_1.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,13 @@
+#include <arm_neon.h>
+
+typedef int16x4_t myvec;
+
+void f (float x)
+{
+  __Int8x8_t y1 = x; // { dg-error {cannot convert 'float' to '__Int8x8_t' in initialization} }
+  __Int8x8_t *ptr1 = &x; // { dg-error {cannot convert 'float\*' to '__Int8x8_t\*' in initialization} }
+  int8x8_t y2 = x; // { dg-error {cannot convert 'float' to 'int8x8_t' in initialization} }
+  int8x8_t *ptr2 = &x; // { dg-error {cannot convert 'float\*' to 'int8x8_t\*' in initialization} }
+  myvec y3 = x; // { dg-error {cannot convert 'float' to 'myvec' {aka 'int16x4_t'} in initialization} }
+  myvec *ptr3 = &x; // { dg-error {cannot convert 'float\*' to 'myvec\*' {aka 'int16x4_t\*'} in initialization} }
+}
Index: gcc/testsuite/gcc.dg/diag-aka-5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5.h	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/gcc.dg/diag-aka-5a.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5a.c	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,135 @@
+#define IS_SYSTEM_HEADER
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'user_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'user_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'user_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'user_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'user_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const user_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka 'user_vector'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka 'user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.dg/diag-aka-5b.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5b.c	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,134 @@
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.target/aarch64/diag_aka_1.c
===================================================================
--- gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-09-30 14:01:05.908830119 +0100
+++ gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-09-30 14:16:45.010103832 +0100
@@ -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 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 \*'} } */
-  /* ??? For these it would be better to print an aka for 'int16x4_t'.  */
-  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' using type 'float'} } */
-  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' from incompatible pointer type 'float \*'} } */
+  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' {aka 'int16x4_t'} using type 'float'} } */
+  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' {aka 'int16x4_t \*'} from incompatible pointer type 'float \*'} } */
 }

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [C][C++] Avoid exposing internal details in aka types
  2019-09-30 13:21 [C][C++] Avoid exposing internal details in aka types Richard Sandiford
@ 2019-10-01  7:41 ` Richard Biener
  2019-10-10 19:02 ` Ping: " Richard Sandiford
  1 sibling, 0 replies; 8+ messages in thread
From: Richard Biener @ 2019-10-01  7:41 UTC (permalink / raw)
  To: Richard Sandiford
  Cc: GCC Patches, Dodji Seketeli, David Malcolm, Joseph S. Myers,
	Marek Polacek, Jason Merrill, Nathan Sidwell

On Mon, Sep 30, 2019 at 3:21 PM Richard Sandiford
<richard.sandiford@arm.com> wrote:
>
> 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 C side does this by recursively looking for the aka type, like the
> C++ side 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.
>
> On the C++ side, strip_typedefs had:
>
>           /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
>              strip typedefs with attributes.  */
>           result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
>           result = strip_typedefs (result);
>
> Applying TYPE_MAIN_VARIANT predates the strip_typedefs call, with the
> comment originally contrasting with plain:
>
>           result = TYPE_MAIN_VARIANT (t);
>
> But the recursive call to strip_typedefs will apply TYPE_MAIN_VARIANT,
> so it doesn't seem necessary to do it here too.  I think there was also
> a missing "remove_attributes" argument, since wrapping something in a
> typedef shouldn't change which attributes get removed.
>
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

In other context (debug-info) we also look at DECL_ARTIFICIAL,
not sure if that is set on compiler-generated TYPE_DECLs.

> Richard
>
>
> 2019-09-30  Richard Sandiford  <richard.sandiford@arm.com>
>
> gcc/c-family/
>         * c-common.h (user_facing_original_type_p): Declare.
>         * c-common.c (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/cp/
>         * cp-tree.h (STF_USER_VISIBLE): New constant.
>         (strip_typedefs, strip_typedefs_expr): Take a flags argument.
>         * tree.c (strip_typedefs, strip_typedefs_expr): Likewise,
>         updating mutual calls accordingly.  When STF_USER_VISIBLE is true,
>         only look through typedefs if user_facing_original_type_p.
>         * error.c (dump_template_bindings, type_to_string): Pass
>         STF_USER_VISIBLE to strip_typedefs.
>         (dump_type): Likewise, unless pp_c_flag_gnu_v3 is set.
>
> gcc/testsuite/
>         * g++.dg/diagnostic/aka5.h: New test.
>         * g++.dg/diagnostic/aka5a.C: Likewise.
>         * g++.dg/diagnostic/aka5b.C: Likewise.
>         * g++.target/aarch64/diag_aka_1.C: Likewise.
>         * gcc.dg/diag-aka-5.h: Likewise.
>         * 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.
>
> Index: gcc/c-family/c-common.h
> ===================================================================
> --- gcc/c-family/c-common.h     2019-09-30 13:54:16.000000000 +0100
> +++ gcc/c-family/c-common.h     2019-09-30 14:16:45.002103890 +0100
> @@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int,
>  extern void c_common_mark_addressable_vec (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 vec<tree, va_gc> *make_tree_vector (void);
>  extern void release_tree_vector (vec<tree, va_gc> *);
> Index: gcc/c-family/c-common.c
> ===================================================================
> --- gcc/c-family/c-common.c     2019-09-30 13:54:16.000000000 +0100
> +++ gcc/c-family/c-common.c     2019-09-30 14:16:45.002103890 +0100
> @@ -7713,6 +7713,55 @@ 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))
> +    {
> +      const char *name = IDENTIFIER_POINTER (orig_id);
> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
> +       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
>     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
> Index: gcc/c/c-objc-common.c
> ===================================================================
> --- gcc/c/c-objc-common.c       2019-09-30 14:08:26.597671509 +0100
> +++ gcc/c/c-objc-common.c       2019-09-30 14:16:45.002103890 +0100
> @@ -28,6 +28,8 @@ Software Foundation; either version 3, o
>  #include "langhooks.h"
>  #include "c-objc-common.h"
>  #include "gcc-rich-location.h"
> +#include "stringpool.h"
> +#include "attribs.h"
>
>  static bool c_tree_printer (pretty_printer *, text_info *, const char *,
>                             int, bool, bool, bool, bool *, const char **);
> @@ -62,71 +64,120 @@ c_objc_common_init (void)
>    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
> -useful_aka_type_p (tree type1, tree type2)
> +static tree
> +get_aka_type (tree type)
>  {
> -  if (type1 == type2)
> -    return false;
> -
> -  if (type1 == error_mark_node || type2 == error_mark_node)
> -    return false;
> -
> -  if (TREE_CODE (type1) != TREE_CODE (type2))
> -    return true;
> +  if (type == error_mark_node)
> +    return type;
>
> -  if (typedef_variant_p (type1))
> +  tree result;
> +  if (typedef_variant_p (type))
>      {
>        /* Saying that "foo" is also known as "struct foo" or
>          "struct <anonymous>" is unlikely to be useful, since users of
>          structure-like types would already know that they're structures.
>          The same applies to unions and enums; in general, printing the
>          tag is only useful if it has a different name.  */
> -      tree_code code = TREE_CODE (type2);
> -      tree id2 = TYPE_IDENTIFIER (type2);
> +      tree orig_type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
> +      tree_code code = TREE_CODE (orig_type);
> +      tree orig_id = TYPE_IDENTIFIER (orig_type);
>        if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
> -         && (!id2 || TYPE_IDENTIFIER (type1) == id2))
> -       return false;
> +         && (!orig_id || TYPE_IDENTIFIER (type) == orig_id))
> +       return type;
>
> -      return true;
> +      if (!user_facing_original_type_p (type))
> +       return type;
> +
> +      result = get_aka_type (orig_type);
>      }
>    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 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:
> -         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:
>           {
> -           tree args1 = TYPE_ARG_TYPES (type1);
> -           tree args2 = TYPE_ARG_TYPES (type2);
> -           while (args1 != args2)
> +           tree return_type = get_aka_type (TREE_TYPE (type));
> +
> +           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
> -                  for it in a diagnostic routine.  */
> -               if (!args1 || args1 == void_type_node)
> -                 return true;
> -               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);
> +               tree arg_type = get_aka_type (TREE_VALUE (args));
> +               arg_types.safe_push (arg_type);
> +               type_ok_p &= (arg_type == TREE_VALUE (args));
> +               args = TREE_CHAIN (args);
>               }
> -           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:
> -         return true;
> +         return canonical ? canonical : type;
>         }
>      }
> +  return build_type_attribute_qual_variant (result, TYPE_ATTRIBUTES (type),
> +                                           TYPE_QUALS (type));
>  }
>
>  /* Print T to CPP.  */
> @@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree
>       stripped version.  But sometimes the stripped version looks
>       exactly the same, so we don't want it after all.  To avoid
>       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;
>        /* 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;
>        /* Get the stripped version from the temporary printer.  */
>        const char *aka = (char *) obstack_base (ob2);
> @@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree
>        pp_c_whitespace (cpp);
>        if (*quoted)
>         pp_begin_quote (cpp, pp_show_color (cpp));
> -      cpp->type_id (TYPE_CANONICAL (t));
> +      cpp->type_id (aka_type);
>        if (*quoted)
>         pp_end_quote (cpp, pp_show_color (cpp));
>        pp_right_brace (cpp);
> Index: gcc/cp/cp-tree.h
> ===================================================================
> --- gcc/cp/cp-tree.h    2019-09-30 13:54:16.000000000 +0100
> +++ gcc/cp/cp-tree.h    2019-09-30 14:16:45.006103863 +0100
> @@ -5672,6 +5672,13 @@ #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARG
>  #define TFF_NO_TEMPLATE_BINDINGS               (1 << 13)
>  #define TFF_POINTER                            (1 << 14)
>
> +/* These constants can be used as bit flags to control strip_typedefs.
> +
> +   STF_USER_VISIBLE: use heuristics to try to avoid stripping user-facing
> +       aliases of internal details.  This is intended for diagnostics,
> +       where it should (for example) give more useful "aka" types.  */
> +const unsigned int STF_USER_VISIBLE = 1U;
> +
>  /* Returns the TEMPLATE_DECL associated to a TEMPLATE_TEMPLATE_PARM
>     node.  */
>  #define TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL(NODE)     \
> @@ -7228,8 +7235,10 @@ extern int zero_init_p                           (const_tree);
>  extern bool check_abi_tag_redeclaration                (const_tree, const_tree,
>                                                  const_tree);
>  extern bool check_abi_tag_args                 (tree, tree);
> -extern tree strip_typedefs                     (tree, bool * = NULL);
> -extern tree strip_typedefs_expr                        (tree, bool * = NULL);
> +extern tree strip_typedefs                     (tree, bool * = NULL,
> +                                                unsigned int = 0);
> +extern tree strip_typedefs_expr                        (tree, bool * = NULL,
> +                                                unsigned int = 0);
>  extern tree copy_binfo                         (tree, tree, tree,
>                                                  tree *, int);
>  extern int member_p                            (const_tree);
> Index: gcc/cp/tree.c
> ===================================================================
> --- gcc/cp/tree.c       2019-09-30 13:54:16.000000000 +0100
> +++ gcc/cp/tree.c       2019-09-30 14:16:45.006103863 +0100
> @@ -1431,7 +1431,10 @@ apply_identity_attributes (tree result,
>    return cp_build_type_attribute_variant (result, new_attribs);
>  }
>
> -/* Builds a qualified variant of T that is not a typedef variant.
> +/* Builds a qualified variant of T that is either not a typedef variant
> +   (the default behavior) or not a typedef variant of a user-facing type
> +   (if FLAGS contains STF_USER_FACING).
> +
>     E.g. consider the following declarations:
>       typedef const int ConstInt;
>       typedef ConstInt* PtrConstInt;
> @@ -1456,7 +1459,7 @@ apply_identity_attributes (tree result,
>     stripped.  */
>
>  tree
> -strip_typedefs (tree t, bool *remove_attributes)
> +strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
>  {
>    tree result = NULL, type = NULL, t0 = NULL;
>
> @@ -1471,7 +1474,7 @@ strip_typedefs (tree t, bool *remove_att
>        for (; t; t = TREE_CHAIN (t))
>         {
>           gcc_assert (!TREE_PURPOSE (t));
> -         tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes);
> +         tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes, flags);
>           if (elt != TREE_VALUE (t))
>             changed = true;
>           vec_safe_push (vec, elt);
> @@ -1494,28 +1497,29 @@ strip_typedefs (tree t, bool *remove_att
>    switch (TREE_CODE (t))
>      {
>      case POINTER_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>        result = build_pointer_type (type);
>        break;
>      case REFERENCE_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>        result = cp_build_reference_type (type, TYPE_REF_IS_RVALUE (t));
>        break;
>      case OFFSET_TYPE:
> -      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes);
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes, flags);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>        result = build_offset_type (t0, type);
>        break;
>      case RECORD_TYPE:
>        if (TYPE_PTRMEMFUNC_P (t))
>         {
> -         t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t), remove_attributes);
> +         t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t),
> +                              remove_attributes, flags);
>           result = build_ptrmemfunc_type (t0);
>         }
>        break;
>      case ARRAY_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> -      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
> +      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
>        result = build_cplus_array_type (type, t0);
>        break;
>      case FUNCTION_TYPE:
> @@ -1534,7 +1538,7 @@ strip_typedefs (tree t, bool *remove_att
>             && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
>           is_variant = true;
>
> -       type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>         tree canon_spec = (flag_noexcept_type
>                            ? canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (t))
>                            : NULL_TREE);
> @@ -1548,7 +1552,7 @@ strip_typedefs (tree t, bool *remove_att
>             if (arg_node == void_list_node)
>               break;
>             arg_type = strip_typedefs (TREE_VALUE (arg_node),
> -                                      remove_attributes);
> +                                      remove_attributes, flags);
>             gcc_assert (arg_type);
>             if (arg_type == TREE_VALUE (arg_node) && !changed)
>               continue;
> @@ -1612,9 +1616,10 @@ strip_typedefs (tree t, bool *remove_att
>                 tree arg = TREE_VEC_ELT (args, i);
>                 tree strip_arg;
>                 if (TYPE_P (arg))
> -                 strip_arg = strip_typedefs (arg, remove_attributes);
> +                 strip_arg = strip_typedefs (arg, remove_attributes, flags);
>                 else
> -                 strip_arg = strip_typedefs_expr (arg, remove_attributes);
> +                 strip_arg = strip_typedefs_expr (arg, remove_attributes,
> +                                                  flags);
>                 TREE_VEC_ELT (new_args, i) = strip_arg;
>                 if (strip_arg != arg)
>                   changed = true;
> @@ -1630,7 +1635,7 @@ strip_typedefs (tree t, bool *remove_att
>             else
>               ggc_free (new_args);
>           }
> -       tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes);
> +       tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes, flags);
>         if (!changed && ctx == TYPE_CONTEXT (t) && !typedef_variant_p (t))
>           return t;
>         tree name = fullname;
> @@ -1643,7 +1648,7 @@ strip_typedefs (tree t, bool *remove_att
>        break;
>      case DECLTYPE_TYPE:
>        result = strip_typedefs_expr (DECLTYPE_TYPE_EXPR (t),
> -                                   remove_attributes);
> +                                   remove_attributes, flags);
>        if (result == DECLTYPE_TYPE_EXPR (t))
>         result = NULL_TREE;
>        else
> @@ -1653,7 +1658,8 @@ strip_typedefs (tree t, bool *remove_att
>                    tf_none));
>        break;
>      case UNDERLYING_TYPE:
> -      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t),
> +                            remove_attributes, flags);
>        result = finish_underlying_type (type);
>        break;
>      default:
> @@ -1664,15 +1670,18 @@ strip_typedefs (tree t, bool *remove_att
>      {
>        if (typedef_variant_p (t))
>         {
> -         /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
> -            strip typedefs with attributes.  */
> -         result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
> -         result = strip_typedefs (result);
> +         if ((flags & STF_USER_VISIBLE)
> +             && !user_facing_original_type_p (t))
> +           return t;
> +         result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
> +                                  remove_attributes, flags);
>         }
>        else
>         result = TYPE_MAIN_VARIANT (t);
>      }
> -  gcc_assert (!typedef_variant_p (result));
> +  gcc_assert (!typedef_variant_p (result)
> +             || ((flags & STF_USER_VISIBLE)
> +                 && !user_facing_original_type_p (result)));
>
>    if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
>    /* If RESULT is complete and T isn't, it's likely the case that T
> @@ -1721,7 +1730,7 @@ strip_typedefs (tree t, bool *remove_att
>     sizeof(TT) is replaced by sizeof(T).  */
>
>  tree
> -strip_typedefs_expr (tree t, bool *remove_attributes)
> +strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
>  {
>    unsigned i,n;
>    tree r, type, *ops;
> @@ -1738,7 +1747,7 @@ strip_typedefs_expr (tree t, bool *remov
>    /* Some expressions have type operands, so let's handle types here rather
>       than check TYPE_P in multiple places below.  */
>    if (TYPE_P (t))
> -    return strip_typedefs (t, remove_attributes);
> +    return strip_typedefs (t, remove_attributes, flags);
>
>    code = TREE_CODE (t);
>    switch (code)
> @@ -1752,8 +1761,10 @@ strip_typedefs_expr (tree t, bool *remov
>
>      case TRAIT_EXPR:
>        {
> -       tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t), remove_attributes);
> -       tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t), remove_attributes);
> +       tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t),
> +                                    remove_attributes, flags);
> +       tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t),
> +                                    remove_attributes, flags);
>         if (type1 == TRAIT_EXPR_TYPE1 (t)
>             && type2 == TRAIT_EXPR_TYPE2 (t))
>           return t;
> @@ -1770,7 +1781,8 @@ strip_typedefs_expr (tree t, bool *remov
>         tree it;
>         for (it = t; it; it = TREE_CHAIN (it))
>           {
> -           tree val = strip_typedefs_expr (TREE_VALUE (it), remove_attributes);
> +           tree val = strip_typedefs_expr (TREE_VALUE (it),
> +                                           remove_attributes, flags);
>             vec_safe_push (vec, val);
>             if (val != TREE_VALUE (it))
>               changed = true;
> @@ -1796,7 +1808,7 @@ strip_typedefs_expr (tree t, bool *remov
>         for (i = 0; i < n; ++i)
>           {
>             tree op = strip_typedefs_expr (TREE_VEC_ELT (t, i),
> -                                          remove_attributes);
> +                                          remove_attributes, flags);
>             vec->quick_push (op);
>             if (op != TREE_VEC_ELT (t, i))
>               changed = true;
> @@ -1820,18 +1832,19 @@ strip_typedefs_expr (tree t, bool *remov
>         vec<constructor_elt, va_gc> *vec
>           = vec_safe_copy (CONSTRUCTOR_ELTS (t));
>         n = CONSTRUCTOR_NELTS (t);
> -       type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +       type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>         for (i = 0; i < n; ++i)
>           {
>             constructor_elt *e = &(*vec)[i];
> -           tree op = strip_typedefs_expr (e->value, remove_attributes);
> +           tree op = strip_typedefs_expr (e->value, remove_attributes, flags);
>             if (op != e->value)
>               {
>                 changed = true;
>                 e->value = op;
>               }
>             gcc_checking_assert
> -             (e->index == strip_typedefs_expr (e->index, remove_attributes));
> +             (e->index == strip_typedefs_expr (e->index, remove_attributes,
> +                                               flags));
>           }
>
>         if (!changed && type == TREE_TYPE (t))
> @@ -1875,12 +1888,13 @@ strip_typedefs_expr (tree t, bool *remov
>      case REINTERPRET_CAST_EXPR:
>      case CAST_EXPR:
>      case NEW_EXPR:
> -      type = strip_typedefs (type, remove_attributes);
> +      type = strip_typedefs (type, remove_attributes, flags);
>        /* fallthrough */
>
>      default:
>        for (i = 0; i < n; ++i)
> -       ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i), remove_attributes);
> +       ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i),
> +                                     remove_attributes, flags);
>        break;
>      }
>
> Index: gcc/cp/error.c
> ===================================================================
> --- gcc/cp/error.c      2019-09-30 13:54:16.000000000 +0100
> +++ gcc/cp/error.c      2019-09-30 14:16:45.006103863 +0100
> @@ -409,7 +409,7 @@ dump_template_bindings (cxx_pretty_print
>        pop_deferring_access_checks ();
>        /* Strip typedefs.  We can't just use TFF_CHASE_TYPEDEF because
>          pp_simple_type_specifier doesn't know about it.  */
> -      t = strip_typedefs (t);
> +      t = strip_typedefs (t, NULL, STF_USER_VISIBLE);
>        dump_type (pp, t, TFF_PLAIN_IDENTIFIER);
>      }
>  }
> @@ -448,7 +448,11 @@ dump_type (cxx_pretty_printer *pp, tree
>                || DECL_SELF_REFERENCE_P (decl)
>                || (!flag_pretty_templates
>                    && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)))
> -       t = strip_typedefs (t);
> +       {
> +         unsigned int stf_flags = (!(pp->flags & pp_c_flag_gnu_v3)
> +                                   ? STF_USER_VISIBLE : 0);
> +         t = strip_typedefs (t, NULL, stf_flags);
> +       }
>        else if (alias_template_specialization_p (t))
>         {
>           dump_alias_template_specialization (pp, t, flags);
> @@ -3195,7 +3199,7 @@ type_to_string (tree typ, int verbose, b
>        && !uses_template_parms (typ))
>      {
>        int aka_start, aka_len; char *p;
> -      tree aka = strip_typedefs (typ);
> +      tree aka = strip_typedefs (typ, NULL, STF_USER_VISIBLE);
>        if (quote && *quote)
>         pp_end_quote (cxx_pp, show_color);
>        pp_string (cxx_pp, " {aka");
> Index: gcc/testsuite/g++.dg/diagnostic/aka5.h
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5.h      2019-09-30 14:16:45.006103863 +0100
> @@ -0,0 +1,22 @@
> +#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;
> Index: gcc/testsuite/g++.dg/diagnostic/aka5a.C
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5a.C     2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,127 @@
> +#define IS_SYSTEM_HEADER
> +#include "aka5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (s s1)
> +{
> +  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' in assignment} }
> +  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka 'user_enum'} in assignment} }
> +  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka 'user_enum\*'} in assignment} }
> +  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' in assignment} }
> +  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' in assignment} }
> +  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const user_enum\*'} in assignment} }
> +  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' in assignment} }
> +  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile user_enum\*'} in assignment} }
> +  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' in assignment} }
> +  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} in assignment} }
> +  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' in assignment} }
> +  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' in assignment} }
> +  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' in assignment} }
> +  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka 'user_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} in assignment} }
> +  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} in assignment} }
> +  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' in assignment} }
> +  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +
> +  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' and 's'\)} }
> +  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka 'user_struct'} and 's'\)} }
> +  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka 'user_struct\*'} in assignment} }
> +  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' in assignment} }
> +  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' in assignment} }
> +  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const user_struct\*'} in assignment} }
> +
> +  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' and 's'\)} }
> +  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka 'user_union'} and 's'\)} }
> +  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka 'user_union\*'} in assignment} }
> +  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' in assignment} }
> +  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' in assignment} }
> +  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const user_union\*'} in assignment} }
> +
> +  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' in assignment} }
> +  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka 'user_vector'} in assignment} }
> +  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka 'user_vector\*'} in assignment} }
> +  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' in assignment} }
> +  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' in assignment} }
> +  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const user_vector\*'} in assignment} }
> +
> +  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
> +  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
> +  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
> +  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
> +  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
> +  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
> +  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
> +  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
> +  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +}
> Index: gcc/testsuite/g++.dg/diagnostic/aka5b.C
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5b.C     2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,127 @@
> +#include "aka5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (s s1)
> +{
> +  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' {aka '__internal_enum'} in assignment} }
> +  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka '__internal_enum'} in assignment} }
> +  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka '__internal_enum\*'} in assignment} }
> +  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' {aka '__internal_enum\*'} in assignment} }
> +  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' {aka 'const __internal_enum\*'} in assignment} }
> +  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const __internal_enum\*'} in assignment} }
> +  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' {aka 'volatile __internal_enum\*'} in assignment} }
> +  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile __internal_enum\*'} in assignment} }
> +  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
> +  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
> +  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
> +  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
> +  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
> +  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
> +  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +
> +  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' {aka '__internal_struct'} and 's'\)} }
> +  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka '__internal_struct'} and 's'\)} }
> +  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka '__internal_struct\*'} in assignment} }
> +  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' {aka '__internal_struct\*'} in assignment} }
> +  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' {aka 'const __internal_struct\*'} in assignment} }
> +  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const __internal_struct\*'} in assignment} }
> +
> +  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' {aka '__internal_union'} and 's'\)} }
> +  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka '__internal_union'} and 's'\)} }
> +  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka '__internal_union\*'} in assignment} }
> +  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' {aka '__internal_union\*'} in assignment} }
> +  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' {aka 'const __internal_union\*'} in assignment} }
> +  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const __internal_union\*'} in assignment} }
> +
> +  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' {aka '__vector\([48]\) unsigned int'} in assignment} }
> +  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} in assignment} }
> +  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
> +  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
> +  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
> +  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
> +
> +  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
> +  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
> +  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
> +  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
> +  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
> +  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
> +  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
> +  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
> +  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +}
> +
> Index: gcc/testsuite/g++.target/aarch64/diag_aka_1.C
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.target/aarch64/diag_aka_1.C       2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,13 @@
> +#include <arm_neon.h>
> +
> +typedef int16x4_t myvec;
> +
> +void f (float x)
> +{
> +  __Int8x8_t y1 = x; // { dg-error {cannot convert 'float' to '__Int8x8_t' in initialization} }
> +  __Int8x8_t *ptr1 = &x; // { dg-error {cannot convert 'float\*' to '__Int8x8_t\*' in initialization} }
> +  int8x8_t y2 = x; // { dg-error {cannot convert 'float' to 'int8x8_t' in initialization} }
> +  int8x8_t *ptr2 = &x; // { dg-error {cannot convert 'float\*' to 'int8x8_t\*' in initialization} }
> +  myvec y3 = x; // { dg-error {cannot convert 'float' to 'myvec' {aka 'int16x4_t'} in initialization} }
> +  myvec *ptr3 = &x; // { dg-error {cannot convert 'float\*' to 'myvec\*' {aka 'int16x4_t\*'} in initialization} }
> +}
> Index: gcc/testsuite/gcc.dg/diag-aka-5.h
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/gcc.dg/diag-aka-5.h   2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,22 @@
> +#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;
> Index: gcc/testsuite/gcc.dg/diag-aka-5a.c
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/gcc.dg/diag-aka-5a.c  2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,135 @@
> +#define IS_SYSTEM_HEADER
> +#include "diag-aka-5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +__extension__ _Atomic user_enum *atomic_ue_ptr1;
> +__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +__extension__ _Atomic user_int *atomic_ui_ptr1;
> +__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (struct s s)
> +{
> +  ue1 = s; /* { dg-error {assigning to type 'user_enum' from type 'struct s'} } */
> +  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'user_enum'} from type 'struct s'} } */
> +  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'user_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' from incompatible pointer type 'struct s \*'} } */
> +  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' from incompatible pointer type 'struct s \*'} } */
> +  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const user_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' from incompatible pointer type 'struct s \*'} } */
> +  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile user_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' from incompatible pointer type 'struct s \*'} } */
> +  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic user_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' from incompatible pointer type 'struct s \*'} } */
> +  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'user_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +
> +  us1 = s; /* { dg-error {assigning to type 'user_struct' from type 'struct s'} } */
> +  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'user_struct'} from type 'struct s'} } */
> +  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'user_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' from incompatible pointer type 'struct s \*'} } */
> +  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' from incompatible pointer type 'struct s \*'} } */
> +  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const user_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  uu1 = s; /* { dg-error {assigning to type 'user_union' from type 'struct s'} } */
> +  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'user_union'} from type 'struct s'} } */
> +  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'user_union \*'} from incompatible pointer type 'struct s \*'} } */
> +  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' from incompatible pointer type 'struct s \*'} } */
> +  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' from incompatible pointer type 'struct s \*'} } */
> +  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const user_union \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  uv1 = s; /* { dg-error {assigning to type 'user_vector' from type 'struct s'} } */
> +  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka 'user_vector'} from type 'struct s'} } */
> +  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka 'user_vector \*'} from incompatible pointer type 'struct s \*'} } */
> +  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' from incompatible pointer type 'struct s \*'} } */
> +  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' from incompatible pointer type 'struct s \*'} } */
> +  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const user_vector \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
> +  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
> +  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
> +  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
> +  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +}
> Index: gcc/testsuite/gcc.dg/diag-aka-5b.c
> ===================================================================
> --- /dev/null   2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/gcc.dg/diag-aka-5b.c  2019-09-30 14:16:45.010103832 +0100
> @@ -0,0 +1,134 @@
> +#include "diag-aka-5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +__extension__ _Atomic user_enum *atomic_ue_ptr1;
> +__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +__extension__ _Atomic user_int *atomic_ui_ptr1;
> +__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (struct s s)
> +{
> +  ue1 = s; /* { dg-error {assigning to type 'user_enum' {aka 'enum __internal_enum'} from type 'struct s'} } */
> +  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'enum __internal_enum'} from type 'struct s'} } */
> +  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
> +  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
> +  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +
> +  us1 = s; /* { dg-error {assigning to type 'user_struct' {aka 'struct __internal_struct'} from type 'struct s'} } */
> +  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'struct __internal_struct'} from type 'struct s'} } */
> +  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  uu1 = s; /* { dg-error {assigning to type 'user_union' {aka 'union __internal_union'} from type 'struct s'} } */
> +  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'union __internal_union'} from type 'struct s'} } */
> +  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
> +  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  uv1 = s; /* { dg-error {assigning to type 'user_vector' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
> +  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
> +  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
> +  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
> +
> +  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
> +  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
> +  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
> +  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
> +  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
> +  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
> +  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
> +  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
> +  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
> +}
> Index: gcc/testsuite/gcc.target/aarch64/diag_aka_1.c
> ===================================================================
> --- gcc/testsuite/gcc.target/aarch64/diag_aka_1.c       2019-09-30 14:01:05.908830119 +0100
> +++ gcc/testsuite/gcc.target/aarch64/diag_aka_1.c       2019-09-30 14:16:45.010103832 +0100
> @@ -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 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 \*'} } */
> -  /* ??? For these it would be better to print an aka for 'int16x4_t'.  */
> -  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' using type 'float'} } */
> -  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' from incompatible pointer type 'float \*'} } */
> +  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' {aka 'int16x4_t'} using type 'float'} } */
> +  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' {aka 'int16x4_t \*'} from incompatible pointer type 'float \*'} } */
>  }

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Ping: [C][C++] Avoid exposing internal details in aka types
  2019-09-30 13:21 [C][C++] Avoid exposing internal details in aka types Richard Sandiford
  2019-10-01  7:41 ` Richard Biener
@ 2019-10-10 19:02 ` Richard Sandiford
  2019-10-10 19:55   ` Marek Polacek
  1 sibling, 1 reply; 8+ messages in thread
From: Richard Sandiford @ 2019-10-10 19:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: dodji, dmalcolm, joseph, polacek, jason, nathan

[-- Attachment #1: Type: text/plain, Size: 3719 bytes --]

Ping

Richard Sandiford <richard.sandiford@arm.com> writes:
> 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 C side does this by recursively looking for the aka type, like the
> C++ side 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.
>
> On the C++ side, strip_typedefs had:
>
> 	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
> 	     strip typedefs with attributes.  */
> 	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
> 	  result = strip_typedefs (result);
>
> Applying TYPE_MAIN_VARIANT predates the strip_typedefs call, with the
> comment originally contrasting with plain:
>
> 	  result = TYPE_MAIN_VARIANT (t);
>
> But the recursive call to strip_typedefs will apply TYPE_MAIN_VARIANT,
> so it doesn't seem necessary to do it here too.  I think there was also
> a missing "remove_attributes" argument, since wrapping something in a
> typedef shouldn't change which attributes get removed.
>
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
>
> Richard

2019-09-30  Richard Sandiford  <richard.sandiford@arm.com>

gcc/c-family/
	* c-common.h (user_facing_original_type_p): Declare.
	* c-common.c (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/cp/
	* cp-tree.h (STF_USER_VISIBLE): New constant.
	(strip_typedefs, strip_typedefs_expr): Take a flags argument.
	* tree.c (strip_typedefs, strip_typedefs_expr): Likewise,
	updating mutual calls accordingly.  When STF_USER_VISIBLE is true,
	only look through typedefs if user_facing_original_type_p.
	* error.c (dump_template_bindings, type_to_string): Pass
	STF_USER_VISIBLE to strip_typedefs.
	(dump_type): Likewise, unless pp_c_flag_gnu_v3 is set.

gcc/testsuite/
	* g++.dg/diagnostic/aka5.h: New test.
	* g++.dg/diagnostic/aka5a.C: Likewise.
	* g++.dg/diagnostic/aka5b.C: Likewise.
	* g++.target/aarch64/diag_aka_1.C: Likewise.
	* gcc.dg/diag-aka-5.h: Likewise.
	* 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.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: diag-user-facing.diff --]
[-- Type: text/x-diff, Size: 66363 bytes --]

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2019-09-30 13:54:16.000000000 +0100
+++ gcc/c-family/c-common.h	2019-09-30 14:16:45.002103890 +0100
@@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int,
 extern void c_common_mark_addressable_vec (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 vec<tree, va_gc> *make_tree_vector (void);
 extern void release_tree_vector (vec<tree, va_gc> *);
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/c-family/c-common.c	2019-09-30 14:16:45.002103890 +0100
@@ -7713,6 +7713,55 @@ 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))
+    {
+      const char *name = IDENTIFIER_POINTER (orig_id);
+      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
+	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
    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
Index: gcc/c/c-objc-common.c
===================================================================
--- gcc/c/c-objc-common.c	2019-09-30 14:08:26.597671509 +0100
+++ gcc/c/c-objc-common.c	2019-09-30 14:16:45.002103890 +0100
@@ -28,6 +28,8 @@ Software Foundation; either version 3, o
 #include "langhooks.h"
 #include "c-objc-common.h"
 #include "gcc-rich-location.h"
+#include "stringpool.h"
+#include "attribs.h"
 
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
 			    int, bool, bool, bool, bool *, const char **);
@@ -62,71 +64,120 @@ c_objc_common_init (void)
   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
-useful_aka_type_p (tree type1, tree type2)
+static tree
+get_aka_type (tree type)
 {
-  if (type1 == type2)
-    return false;
-
-  if (type1 == error_mark_node || type2 == error_mark_node)
-    return false;
-
-  if (TREE_CODE (type1) != TREE_CODE (type2))
-    return true;
+  if (type == error_mark_node)
+    return type;
 
-  if (typedef_variant_p (type1))
+  tree result;
+  if (typedef_variant_p (type))
     {
       /* Saying that "foo" is also known as "struct foo" or
 	 "struct <anonymous>" is unlikely to be useful, since users of
 	 structure-like types would already know that they're structures.
 	 The same applies to unions and enums; in general, printing the
 	 tag is only useful if it has a different name.  */
-      tree_code code = TREE_CODE (type2);
-      tree id2 = TYPE_IDENTIFIER (type2);
+      tree orig_type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
+      tree_code code = TREE_CODE (orig_type);
+      tree orig_id = TYPE_IDENTIFIER (orig_type);
       if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
-	  && (!id2 || TYPE_IDENTIFIER (type1) == id2))
-	return false;
+	  && (!orig_id || TYPE_IDENTIFIER (type) == orig_id))
+	return type;
 
-      return true;
+      if (!user_facing_original_type_p (type))
+	return type;
+
+      result = get_aka_type (orig_type);
     }
   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 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:
-	  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:
 	  {
-	    tree args1 = TYPE_ARG_TYPES (type1);
-	    tree args2 = TYPE_ARG_TYPES (type2);
-	    while (args1 != args2)
+	    tree return_type = get_aka_type (TREE_TYPE (type));
+
+	    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
-		   for it in a diagnostic routine.  */
-		if (!args1 || args1 == void_type_node)
-		  return true;
-		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);
+		tree arg_type = get_aka_type (TREE_VALUE (args));
+		arg_types.safe_push (arg_type);
+		type_ok_p &= (arg_type == TREE_VALUE (args));
+		args = TREE_CHAIN (args);
 	      }
-	    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:
-	  return true;
+	  return canonical ? canonical : type;
 	}
     }
+  return build_type_attribute_qual_variant (result, TYPE_ATTRIBUTES (type),
+					    TYPE_QUALS (type));
 }
 
 /* Print T to CPP.  */
@@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree
      stripped version.  But sometimes the stripped version looks
      exactly the same, so we don't want it after all.  To avoid
      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;
       /* 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;
       /* Get the stripped version from the temporary printer.  */
       const char *aka = (char *) obstack_base (ob2);
@@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree
       pp_c_whitespace (cpp);
       if (*quoted)
 	pp_begin_quote (cpp, pp_show_color (cpp));
-      cpp->type_id (TYPE_CANONICAL (t));
+      cpp->type_id (aka_type);
       if (*quoted)
 	pp_end_quote (cpp, pp_show_color (cpp));
       pp_right_brace (cpp);
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/cp-tree.h	2019-09-30 14:16:45.006103863 +0100
@@ -5672,6 +5672,13 @@ #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARG
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
 
+/* These constants can be used as bit flags to control strip_typedefs.
+
+   STF_USER_VISIBLE: use heuristics to try to avoid stripping user-facing
+       aliases of internal details.  This is intended for diagnostics,
+       where it should (for example) give more useful "aka" types.  */
+const unsigned int STF_USER_VISIBLE = 1U;
+
 /* Returns the TEMPLATE_DECL associated to a TEMPLATE_TEMPLATE_PARM
    node.  */
 #define TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL(NODE)	\
@@ -7228,8 +7235,10 @@ extern int zero_init_p				(const_tree);
 extern bool check_abi_tag_redeclaration		(const_tree, const_tree,
 						 const_tree);
 extern bool check_abi_tag_args			(tree, tree);
-extern tree strip_typedefs			(tree, bool * = NULL);
-extern tree strip_typedefs_expr			(tree, bool * = NULL);
+extern tree strip_typedefs			(tree, bool * = NULL,
+						 unsigned int = 0);
+extern tree strip_typedefs_expr			(tree, bool * = NULL,
+						 unsigned int = 0);
 extern tree copy_binfo				(tree, tree, tree,
 						 tree *, int);
 extern int member_p				(const_tree);
Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/tree.c	2019-09-30 14:16:45.006103863 +0100
@@ -1431,7 +1431,10 @@ apply_identity_attributes (tree result,
   return cp_build_type_attribute_variant (result, new_attribs);
 }
 
-/* Builds a qualified variant of T that is not a typedef variant.
+/* Builds a qualified variant of T that is either not a typedef variant
+   (the default behavior) or not a typedef variant of a user-facing type
+   (if FLAGS contains STF_USER_FACING).
+
    E.g. consider the following declarations:
      typedef const int ConstInt;
      typedef ConstInt* PtrConstInt;
@@ -1456,7 +1459,7 @@ apply_identity_attributes (tree result,
    stripped.  */
 
 tree
-strip_typedefs (tree t, bool *remove_attributes)
+strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
 {
   tree result = NULL, type = NULL, t0 = NULL;
 
@@ -1471,7 +1474,7 @@ strip_typedefs (tree t, bool *remove_att
       for (; t; t = TREE_CHAIN (t))
 	{
 	  gcc_assert (!TREE_PURPOSE (t));
-	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes);
+	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes, flags);
 	  if (elt != TREE_VALUE (t))
 	    changed = true;
 	  vec_safe_push (vec, elt);
@@ -1494,28 +1497,29 @@ strip_typedefs (tree t, bool *remove_att
   switch (TREE_CODE (t))
     {
     case POINTER_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_pointer_type (type);
       break;
     case REFERENCE_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = cp_build_reference_type (type, TYPE_REF_IS_RVALUE (t));
       break;
     case OFFSET_TYPE:
-      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes);
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes, flags);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_offset_type (t0, type);
       break;
     case RECORD_TYPE:
       if (TYPE_PTRMEMFUNC_P (t))
 	{
-	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t), remove_attributes);
+	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t),
+			       remove_attributes, flags);
 	  result = build_ptrmemfunc_type (t0);
 	}
       break;
     case ARRAY_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
-      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
+      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
       result = build_cplus_array_type (type, t0);
       break;
     case FUNCTION_TYPE:
@@ -1534,7 +1538,7 @@ strip_typedefs (tree t, bool *remove_att
 	    && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
 	  is_variant = true;
 
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	tree canon_spec = (flag_noexcept_type
 			   ? canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (t))
 			   : NULL_TREE);
@@ -1548,7 +1552,7 @@ strip_typedefs (tree t, bool *remove_att
 	    if (arg_node == void_list_node)
 	      break;
 	    arg_type = strip_typedefs (TREE_VALUE (arg_node),
-				       remove_attributes);
+				       remove_attributes, flags);
 	    gcc_assert (arg_type);
 	    if (arg_type == TREE_VALUE (arg_node) && !changed)
 	      continue;
@@ -1612,9 +1616,10 @@ strip_typedefs (tree t, bool *remove_att
 		tree arg = TREE_VEC_ELT (args, i);
 		tree strip_arg;
 		if (TYPE_P (arg))
-		  strip_arg = strip_typedefs (arg, remove_attributes);
+		  strip_arg = strip_typedefs (arg, remove_attributes, flags);
 		else
-		  strip_arg = strip_typedefs_expr (arg, remove_attributes);
+		  strip_arg = strip_typedefs_expr (arg, remove_attributes,
+						   flags);
 		TREE_VEC_ELT (new_args, i) = strip_arg;
 		if (strip_arg != arg)
 		  changed = true;
@@ -1630,7 +1635,7 @@ strip_typedefs (tree t, bool *remove_att
 	    else
 	      ggc_free (new_args);
 	  }
-	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes);
+	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes, flags);
 	if (!changed && ctx == TYPE_CONTEXT (t) && !typedef_variant_p (t))
 	  return t;
 	tree name = fullname;
@@ -1643,7 +1648,7 @@ strip_typedefs (tree t, bool *remove_att
       break;
     case DECLTYPE_TYPE:
       result = strip_typedefs_expr (DECLTYPE_TYPE_EXPR (t),
-				    remove_attributes);
+				    remove_attributes, flags);
       if (result == DECLTYPE_TYPE_EXPR (t))
 	result = NULL_TREE;
       else
@@ -1653,7 +1658,8 @@ strip_typedefs (tree t, bool *remove_att
 		   tf_none));
       break;
     case UNDERLYING_TYPE:
-      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t), remove_attributes);
+      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t),
+			     remove_attributes, flags);
       result = finish_underlying_type (type);
       break;
     default:
@@ -1664,15 +1670,18 @@ strip_typedefs (tree t, bool *remove_att
     {
       if (typedef_variant_p (t))
 	{
-	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
-	     strip typedefs with attributes.  */
-	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
-	  result = strip_typedefs (result);
+	  if ((flags & STF_USER_VISIBLE)
+	      && !user_facing_original_type_p (t))
+	    return t;
+	  result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
+				   remove_attributes, flags);
 	}
       else
 	result = TYPE_MAIN_VARIANT (t);
     }
-  gcc_assert (!typedef_variant_p (result));
+  gcc_assert (!typedef_variant_p (result)
+	      || ((flags & STF_USER_VISIBLE)
+		  && !user_facing_original_type_p (result)));
 
   if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
   /* If RESULT is complete and T isn't, it's likely the case that T
@@ -1721,7 +1730,7 @@ strip_typedefs (tree t, bool *remove_att
    sizeof(TT) is replaced by sizeof(T).  */
 
 tree
-strip_typedefs_expr (tree t, bool *remove_attributes)
+strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
 {
   unsigned i,n;
   tree r, type, *ops;
@@ -1738,7 +1747,7 @@ strip_typedefs_expr (tree t, bool *remov
   /* Some expressions have type operands, so let's handle types here rather
      than check TYPE_P in multiple places below.  */
   if (TYPE_P (t))
-    return strip_typedefs (t, remove_attributes);
+    return strip_typedefs (t, remove_attributes, flags);
 
   code = TREE_CODE (t);
   switch (code)
@@ -1752,8 +1761,10 @@ strip_typedefs_expr (tree t, bool *remov
 
     case TRAIT_EXPR:
       {
-	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t), remove_attributes);
-	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t), remove_attributes);
+	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t),
+				     remove_attributes, flags);
+	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t),
+				     remove_attributes, flags);
 	if (type1 == TRAIT_EXPR_TYPE1 (t)
 	    && type2 == TRAIT_EXPR_TYPE2 (t))
 	  return t;
@@ -1770,7 +1781,8 @@ strip_typedefs_expr (tree t, bool *remov
 	tree it;
 	for (it = t; it; it = TREE_CHAIN (it))
 	  {
-	    tree val = strip_typedefs_expr (TREE_VALUE (it), remove_attributes);
+	    tree val = strip_typedefs_expr (TREE_VALUE (it),
+					    remove_attributes, flags);
 	    vec_safe_push (vec, val);
 	    if (val != TREE_VALUE (it))
 	      changed = true;
@@ -1796,7 +1808,7 @@ strip_typedefs_expr (tree t, bool *remov
 	for (i = 0; i < n; ++i)
 	  {
 	    tree op = strip_typedefs_expr (TREE_VEC_ELT (t, i),
-					   remove_attributes);
+					   remove_attributes, flags);
 	    vec->quick_push (op);
 	    if (op != TREE_VEC_ELT (t, i))
 	      changed = true;
@@ -1820,18 +1832,19 @@ strip_typedefs_expr (tree t, bool *remov
 	vec<constructor_elt, va_gc> *vec
 	  = vec_safe_copy (CONSTRUCTOR_ELTS (t));
 	n = CONSTRUCTOR_NELTS (t);
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	for (i = 0; i < n; ++i)
 	  {
 	    constructor_elt *e = &(*vec)[i];
-	    tree op = strip_typedefs_expr (e->value, remove_attributes);
+	    tree op = strip_typedefs_expr (e->value, remove_attributes, flags);
 	    if (op != e->value)
 	      {
 		changed = true;
 		e->value = op;
 	      }
 	    gcc_checking_assert
-	      (e->index == strip_typedefs_expr (e->index, remove_attributes));
+	      (e->index == strip_typedefs_expr (e->index, remove_attributes,
+						flags));
 	  }
 
 	if (!changed && type == TREE_TYPE (t))
@@ -1875,12 +1888,13 @@ strip_typedefs_expr (tree t, bool *remov
     case REINTERPRET_CAST_EXPR:
     case CAST_EXPR:
     case NEW_EXPR:
-      type = strip_typedefs (type, remove_attributes);
+      type = strip_typedefs (type, remove_attributes, flags);
       /* fallthrough */
 
     default:
       for (i = 0; i < n; ++i)
-	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i), remove_attributes);
+	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i),
+				      remove_attributes, flags);
       break;
     }
 
Index: gcc/cp/error.c
===================================================================
--- gcc/cp/error.c	2019-09-30 13:54:16.000000000 +0100
+++ gcc/cp/error.c	2019-09-30 14:16:45.006103863 +0100
@@ -409,7 +409,7 @@ dump_template_bindings (cxx_pretty_print
       pop_deferring_access_checks ();
       /* Strip typedefs.  We can't just use TFF_CHASE_TYPEDEF because
 	 pp_simple_type_specifier doesn't know about it.  */
-      t = strip_typedefs (t);
+      t = strip_typedefs (t, NULL, STF_USER_VISIBLE);
       dump_type (pp, t, TFF_PLAIN_IDENTIFIER);
     }
 }
@@ -448,7 +448,11 @@ dump_type (cxx_pretty_printer *pp, tree
 	       || DECL_SELF_REFERENCE_P (decl)
 	       || (!flag_pretty_templates
 		   && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)))
-	t = strip_typedefs (t);
+	{
+	  unsigned int stf_flags = (!(pp->flags & pp_c_flag_gnu_v3)
+				    ? STF_USER_VISIBLE : 0);
+	  t = strip_typedefs (t, NULL, stf_flags);
+	}
       else if (alias_template_specialization_p (t))
 	{
 	  dump_alias_template_specialization (pp, t, flags);
@@ -3195,7 +3199,7 @@ type_to_string (tree typ, int verbose, b
       && !uses_template_parms (typ))
     {
       int aka_start, aka_len; char *p;
-      tree aka = strip_typedefs (typ);
+      tree aka = strip_typedefs (typ, NULL, STF_USER_VISIBLE);
       if (quote && *quote)
 	pp_end_quote (cxx_pp, show_color);
       pp_string (cxx_pp, " {aka");
Index: gcc/testsuite/g++.dg/diagnostic/aka5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5.h	2019-09-30 14:16:45.006103863 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/g++.dg/diagnostic/aka5a.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5a.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,127 @@
+#define IS_SYSTEM_HEADER
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka 'user_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka 'user_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const user_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile user_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka 'user_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka 'user_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka 'user_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const user_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka 'user_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka 'user_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const user_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka 'user_vector'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka 'user_vector\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const user_vector\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
Index: gcc/testsuite/g++.dg/diagnostic/aka5b.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5b.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,127 @@
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' {aka '__internal_enum'} in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka '__internal_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka '__internal_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' {aka '__internal_enum\*'} in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' {aka 'const __internal_enum\*'} in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const __internal_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' {aka '__internal_struct'} and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka '__internal_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka '__internal_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' {aka '__internal_struct\*'} in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' {aka 'const __internal_struct\*'} in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const __internal_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' {aka '__internal_union'} and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka '__internal_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka '__internal_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' {aka '__internal_union\*'} in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' {aka 'const __internal_union\*'} in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const __internal_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
+
Index: gcc/testsuite/g++.target/aarch64/diag_aka_1.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.target/aarch64/diag_aka_1.C	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,13 @@
+#include <arm_neon.h>
+
+typedef int16x4_t myvec;
+
+void f (float x)
+{
+  __Int8x8_t y1 = x; // { dg-error {cannot convert 'float' to '__Int8x8_t' in initialization} }
+  __Int8x8_t *ptr1 = &x; // { dg-error {cannot convert 'float\*' to '__Int8x8_t\*' in initialization} }
+  int8x8_t y2 = x; // { dg-error {cannot convert 'float' to 'int8x8_t' in initialization} }
+  int8x8_t *ptr2 = &x; // { dg-error {cannot convert 'float\*' to 'int8x8_t\*' in initialization} }
+  myvec y3 = x; // { dg-error {cannot convert 'float' to 'myvec' {aka 'int16x4_t'} in initialization} }
+  myvec *ptr3 = &x; // { dg-error {cannot convert 'float\*' to 'myvec\*' {aka 'int16x4_t\*'} in initialization} }
+}
Index: gcc/testsuite/gcc.dg/diag-aka-5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5.h	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/gcc.dg/diag-aka-5a.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5a.c	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,135 @@
+#define IS_SYSTEM_HEADER
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'user_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'user_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'user_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'user_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'user_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const user_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka 'user_vector'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka 'user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.dg/diag-aka-5b.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5b.c	2019-09-30 14:16:45.010103832 +0100
@@ -0,0 +1,134 @@
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.target/aarch64/diag_aka_1.c
===================================================================
--- gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-09-30 14:01:05.908830119 +0100
+++ gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-09-30 14:16:45.010103832 +0100
@@ -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 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 \*'} } */
-  /* ??? For these it would be better to print an aka for 'int16x4_t'.  */
-  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' using type 'float'} } */
-  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' from incompatible pointer type 'float \*'} } */
+  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' {aka 'int16x4_t'} using type 'float'} } */
+  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' {aka 'int16x4_t \*'} from incompatible pointer type 'float \*'} } */
 }

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Ping: [C][C++] Avoid exposing internal details in aka types
  2019-10-10 19:02 ` Ping: " Richard Sandiford
@ 2019-10-10 19:55   ` Marek Polacek
  2019-10-11 14:17     ` Richard Sandiford
  0 siblings, 1 reply; 8+ messages in thread
From: Marek Polacek @ 2019-10-10 19:55 UTC (permalink / raw)
  To: gcc-patches, dodji, dmalcolm, joseph, jason, nathan, richard.sandiford

On Thu, Oct 10, 2019 at 08:00:53PM +0100, Richard Sandiford wrote:
> Ping
> 
> Richard Sandiford <richard.sandiford@arm.com> writes:
> > 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.

Agreed.

> +/* 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))
> +    {
> +      const char *name = IDENTIFIER_POINTER (orig_id);
> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
> +	return true;

This looks like name_reserved_for_implementation_p.

The rest looks fine to me!
--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Ping: [C][C++] Avoid exposing internal details in aka types
  2019-10-10 19:55   ` Marek Polacek
@ 2019-10-11 14:17     ` Richard Sandiford
  2019-10-11 14:27       ` Richard Sandiford
  2019-10-11 14:39       ` Marek Polacek
  0 siblings, 2 replies; 8+ messages in thread
From: Richard Sandiford @ 2019-10-11 14:17 UTC (permalink / raw)
  To: Marek Polacek; +Cc: gcc-patches, dodji, dmalcolm, joseph, jason, nathan

Marek Polacek <polacek@redhat.com> writes:
> On Thu, Oct 10, 2019 at 08:00:53PM +0100, Richard Sandiford wrote:
>> Ping
>> 
>> Richard Sandiford <richard.sandiford@arm.com> writes:
>> > 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.
>
> Agreed.
>
>> +/* 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))
>> +    {
>> +      const char *name = IDENTIFIER_POINTER (orig_id);
>> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
>> +	return true;
>
> This looks like name_reserved_for_implementation_p.
>
> The rest looks fine to me!

Ah, nice!  I'd looked for a helper but missed that one.

Here's just the C parts, with that change.  Tested on aarch64-linux-gnu
and x86_64-linux-gnu.  OK to install?

Richard


2019-10-11  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.

Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	2019-10-08 09:23:43.000000000 +0100
+++ gcc/c-family/c-common.h	2019-10-11 15:12:08.552939853 +0100
@@ -1063,6 +1063,7 @@ extern tree builtin_type_for_size (int,
 extern void c_common_mark_addressable_vec (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 vec<tree, va_gc> *make_tree_vector (void);
 extern void release_tree_vector (vec<tree, va_gc> *);
Index: gcc/c-family/c-common.c
===================================================================
--- gcc/c-family/c-common.c	2019-10-08 09:23:43.000000000 +0100
+++ gcc/c-family/c-common.c	2019-10-11 15:12:08.552939853 +0100
@@ -48,6 +48,7 @@ #define GCC_C_COMMON_C
 #include "gimplify.h"
 #include "substring-locations.h"
 #include "spellcheck.h"
+#include "c-spellcheck.h"
 #include "selftest.h"
 
 cpp_reader *parse_in;		/* Declared in c-pragma.h.  */
@@ -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
    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
Index: gcc/c/c-objc-common.c
===================================================================
--- gcc/c/c-objc-common.c	2019-10-08 09:23:43.000000000 +0100
+++ gcc/c/c-objc-common.c	2019-10-11 15:12:08.552939853 +0100
@@ -28,6 +28,8 @@ Software Foundation; either version 3, o
 #include "langhooks.h"
 #include "c-objc-common.h"
 #include "gcc-rich-location.h"
+#include "stringpool.h"
+#include "attribs.h"
 
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
 			    int, bool, bool, bool, bool *, const char **);
@@ -62,71 +64,120 @@ c_objc_common_init (void)
   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
-useful_aka_type_p (tree type1, tree type2)
+static tree
+get_aka_type (tree type)
 {
-  if (type1 == type2)
-    return false;
-
-  if (type1 == error_mark_node || type2 == error_mark_node)
-    return false;
-
-  if (TREE_CODE (type1) != TREE_CODE (type2))
-    return true;
+  if (type == error_mark_node)
+    return type;
 
-  if (typedef_variant_p (type1))
+  tree result;
+  if (typedef_variant_p (type))
     {
       /* Saying that "foo" is also known as "struct foo" or
 	 "struct <anonymous>" is unlikely to be useful, since users of
 	 structure-like types would already know that they're structures.
 	 The same applies to unions and enums; in general, printing the
 	 tag is only useful if it has a different name.  */
-      tree_code code = TREE_CODE (type2);
-      tree id2 = TYPE_IDENTIFIER (type2);
+      tree orig_type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
+      tree_code code = TREE_CODE (orig_type);
+      tree orig_id = TYPE_IDENTIFIER (orig_type);
       if ((code == RECORD_TYPE || code == UNION_TYPE || code == ENUMERAL_TYPE)
-	  && (!id2 || TYPE_IDENTIFIER (type1) == id2))
-	return false;
+	  && (!orig_id || TYPE_IDENTIFIER (type) == orig_id))
+	return type;
 
-      return true;
+      if (!user_facing_original_type_p (type))
+	return type;
+
+      result = get_aka_type (orig_type);
     }
   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 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:
-	  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:
 	  {
-	    tree args1 = TYPE_ARG_TYPES (type1);
-	    tree args2 = TYPE_ARG_TYPES (type2);
-	    while (args1 != args2)
+	    tree return_type = get_aka_type (TREE_TYPE (type));
+
+	    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
-		   for it in a diagnostic routine.  */
-		if (!args1 || args1 == void_type_node)
-		  return true;
-		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);
+		tree arg_type = get_aka_type (TREE_VALUE (args));
+		arg_types.safe_push (arg_type);
+		type_ok_p &= (arg_type == TREE_VALUE (args));
+		args = TREE_CHAIN (args);
 	      }
-	    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:
-	  return true;
+	  return canonical ? canonical : type;
 	}
     }
+  return build_type_attribute_qual_variant (result, TYPE_ATTRIBUTES (type),
+					    TYPE_QUALS (type));
 }
 
 /* Print T to CPP.  */
@@ -150,11 +201,12 @@ print_type (c_pretty_printer *cpp, tree
      stripped version.  But sometimes the stripped version looks
      exactly the same, so we don't want it after all.  To avoid
      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;
       /* 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;
       /* Get the stripped version from the temporary printer.  */
       const char *aka = (char *) obstack_base (ob2);
@@ -174,7 +226,7 @@ print_type (c_pretty_printer *cpp, tree
       pp_c_whitespace (cpp);
       if (*quoted)
 	pp_begin_quote (cpp, pp_show_color (cpp));
-      cpp->type_id (TYPE_CANONICAL (t));
+      cpp->type_id (aka_type);
       if (*quoted)
 	pp_end_quote (cpp, pp_show_color (cpp));
       pp_right_brace (cpp);
Index: gcc/testsuite/gcc.dg/diag-aka-5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5.h	2019-10-11 15:12:08.552939853 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/gcc.dg/diag-aka-5a.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5a.c	2019-10-11 15:12:08.552939853 +0100
@@ -0,0 +1,135 @@
+#define IS_SYSTEM_HEADER
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'user_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic user_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'user_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'user_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const user_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'user_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'user_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const user_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka 'user_vector'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka 'user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const user_vector \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.dg/diag-aka-5b.c
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/gcc.dg/diag-aka-5b.c	2019-10-11 15:12:08.552939853 +0100
@@ -0,0 +1,134 @@
+#include "diag-aka-5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+__extension__ _Atomic user_enum *atomic_ue_ptr1;
+__extension__ _Atomic user_enum_copy *atomic_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+__extension__ _Atomic user_int *atomic_ui_ptr1;
+__extension__ _Atomic user_int_copy *atomic_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (struct s s)
+{
+  ue1 = s; /* { dg-error {assigning to type 'user_enum' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue2 = s; /* { dg-error {assigning to type 'user_enum_copy' {aka 'enum __internal_enum'} from type 'struct s'} } */
+  ue_ptr1 = &s; /* { dg-error {assignment to 'user_enum_ptr' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_ptr2 = &s; /* { dg-error {assignment to 'user_enum \*' {aka 'enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr1 = &s; /* { dg-error {assignment to 'const user_enum \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ue_ptr2 = &s; /* { dg-error {assignment to 'const user_enum_copy \*' {aka 'const enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr1 = &s; /* { dg-error {assignment to 'volatile user_enum \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ue_ptr2 = &s; /* { dg-error {assignment to 'volatile user_enum_copy \*' {aka 'volatile enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_enum \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ue_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_enum_copy \*' {aka '_Atomic enum __internal_enum \*'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_array_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\[10\]' {aka 'enum __internal_enum \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr4 = &s; /* { dg-error {assignment to 'user_enum_copy \(\*\)\(void\)' {aka 'enum __internal_enum \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(enum __internal_enum\)'} from incompatible pointer type 'struct s \*'} } */
+  ue_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(enum __internal_enum, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr1 = &s; /* { dg-error {assignment to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ue_fn_ptr2 = &s; /* { dg-error {assignment to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'enum __internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+
+  us1 = s; /* { dg-error {assigning to type 'user_struct' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us2 = s; /* { dg-error {assigning to type 'user_struct_copy' {aka 'struct __internal_struct'} from type 'struct s'} } */
+  us_ptr1 = &s; /* { dg-error {assignment to 'user_struct_ptr' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  us_ptr2 = &s; /* { dg-error {assignment to 'user_struct \*' {aka 'struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr1 = &s; /* { dg-error {assignment to 'const user_struct \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+  const_us_ptr2 = &s; /* { dg-error {assignment to 'const user_struct_copy \*' {aka 'const struct __internal_struct \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uu1 = s; /* { dg-error {assigning to type 'user_union' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu2 = s; /* { dg-error {assigning to type 'user_union_copy' {aka 'union __internal_union'} from type 'struct s'} } */
+  uu_ptr1 = &s; /* { dg-error {assignment to 'user_union_ptr' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  uu_ptr2 = &s; /* { dg-error {assignment to 'user_union \*' {aka 'union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr1 = &s; /* { dg-error {assignment to 'const user_union \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uu_ptr2 = &s; /* { dg-error {assignment to 'const user_union_copy \*' {aka 'const union __internal_union \*'} from incompatible pointer type 'struct s \*'} } */
+
+  uv1 = s; /* { dg-error {assigning to type 'user_vector' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv2 = s; /* { dg-error {assigning to type 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} from type 'struct s'} } */
+  uv_ptr1 = &s; /* { dg-error {assignment to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  uv_ptr2 = &s; /* { dg-error {assignment to 'user_vector \*' {aka '__vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr1 = &s; /* { dg-error {assignment to 'const user_vector \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_uv_ptr2 = &s; /* { dg-error {assignment to 'const user_vector_copy \*' {aka 'const __vector\([48]\) unsigned int \*'} from incompatible pointer type 'struct s \*'} } */
+
+  ui1 = s; /* { dg-error {assigning to type 'user_int' {aka 'int'} from type 'struct s'} } */
+  ui2 = s; /* { dg-error {assigning to type 'user_int_copy' {aka 'int'} from type 'struct s'} } */
+  ui_ptr1 = &s; /* { dg-error {assignment to 'user_int_ptr' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_ptr2 = &s; /* { dg-error {assignment to 'user_int \*' {aka 'int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr1 = &s; /* { dg-error {assignment to 'const user_int \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  const_ui_ptr2 = &s; /* { dg-error {assignment to 'const user_int_copy \*' {aka 'const int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr1 = &s; /* { dg-error {assignment to 'volatile user_int \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  volatile_ui_ptr2 = &s; /* { dg-error {assignment to 'volatile user_int_copy \*' {aka 'volatile int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr1 = &s; /* { dg-error {assignment to '_Atomic user_int \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  atomic_ui_ptr2 = &s; /* { dg-error {assignment to '_Atomic user_int_copy \*' {aka '_Atomic int \*'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_array_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr2 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr3 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr4 = &s; /* { dg-error {assignment to 'user_int_copy \(\*\)\(void\)' {aka 'int \(\*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr5 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} from incompatible pointer type 'struct s \*'} } */
+  ui_fn_ptr6 = &s; /* { dg-error {assignment to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr1 = &s; /* { dg-error {assignment to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+  unsafe_ui_fn_ptr2 = &s; /* { dg-error {assignment to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(void\)'} from incompatible pointer type 'struct s \*'} } */
+}
Index: gcc/testsuite/gcc.target/aarch64/diag_aka_1.c
===================================================================
--- gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-10-08 09:23:43.000000000 +0100
+++ gcc/testsuite/gcc.target/aarch64/diag_aka_1.c	2019-10-11 15:12:08.552939853 +0100
@@ -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 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 \*'} } */
-  /* ??? For these it would be better to print an aka for 'int16x4_t'.  */
-  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' using type 'float'} } */
-  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' from incompatible pointer type 'float \*'} } */
+  myvec y3 = x; /* { dg-error {incompatible types when initializing type 'myvec' {aka 'int16x4_t'} using type 'float'} } */
+  myvec *ptr3 = &x; /* { dg-error {initialization of 'myvec \*' {aka 'int16x4_t \*'} from incompatible pointer type 'float \*'} } */
 }

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Ping: [C][C++] Avoid exposing internal details in aka types
  2019-10-11 14:17     ` Richard Sandiford
@ 2019-10-11 14:27       ` Richard Sandiford
  2019-10-21 19:51         ` Jason Merrill
  2019-10-11 14:39       ` Marek Polacek
  1 sibling, 1 reply; 8+ messages in thread
From: Richard Sandiford @ 2019-10-11 14:27 UTC (permalink / raw)
  To: Marek Polacek; +Cc: gcc-patches, dodji, dmalcolm, joseph, jason, nathan

Richard Sandiford <richard.sandiford@arm.com> writes:
> Marek Polacek <polacek@redhat.com> writes:
>> On Thu, Oct 10, 2019 at 08:00:53PM +0100, Richard Sandiford wrote:
>>> Ping
>>> 
>>> Richard Sandiford <richard.sandiford@arm.com> writes:
>>> > 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.
>>
>> Agreed.
>>
>>> +/* 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))
>>> +    {
>>> +      const char *name = IDENTIFIER_POINTER (orig_id);
>>> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
>>> +	return true;
>>
>> This looks like name_reserved_for_implementation_p.
>>
>> The rest looks fine to me!
>
> Ah, nice!  I'd looked for a helper but missed that one.
>
> Here's just the C parts, with that change.

And here are the C++ parts, on top of that.  I should probably have
split them from the outset.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Richard

[Comment from the original covering note, in case it helps:

strip_typedefs had:

	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
	     strip typedefs with attributes.  */
	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
	  result = strip_typedefs (result);

Applying TYPE_MAIN_VARIANT predates the strip_typedefs call, with the
comment originally contrasting with plain:

	  result = TYPE_MAIN_VARIANT (t);

But the recursive call to strip_typedefs will apply TYPE_MAIN_VARIANT,
so it doesn't seem necessary to do it here too.  I think there was also
a missing "remove_attributes" argument, since wrapping something in a
typedef shouldn't change which attributes get removed.]

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

gcc/cp/
	* cp-tree.h (STF_USER_VISIBLE): New constant.
	(strip_typedefs, strip_typedefs_expr): Take a flags argument.
	* tree.c (strip_typedefs, strip_typedefs_expr): Likewise,
	updating mutual calls accordingly.  When STF_USER_VISIBLE is true,
	only look through typedefs if user_facing_original_type_p.
	* error.c (dump_template_bindings, type_to_string): Pass
	STF_USER_VISIBLE to strip_typedefs.
	(dump_type): Likewise, unless pp_c_flag_gnu_v3 is set.

gcc/testsuite/
	* g++.dg/diagnostic/aka5.h: New test.
	* g++.dg/diagnostic/aka5a.C: Likewise.
	* g++.dg/diagnostic/aka5b.C: Likewise.
	* g++.target/aarch64/diag_aka_1.C: Likewise.

Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	2019-10-08 09:23:43.000000000 +0100
+++ gcc/cp/cp-tree.h	2019-10-11 15:12:12.544911643 +0100
@@ -5696,6 +5696,13 @@ #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARG
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
 
+/* These constants can be used as bit flags to control strip_typedefs.
+
+   STF_USER_VISIBLE: use heuristics to try to avoid stripping user-facing
+       aliases of internal details.  This is intended for diagnostics,
+       where it should (for example) give more useful "aka" types.  */
+const unsigned int STF_USER_VISIBLE = 1U;
+
 /* Returns the TEMPLATE_DECL associated to a TEMPLATE_TEMPLATE_PARM
    node.  */
 #define TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL(NODE)	\
@@ -7254,8 +7261,10 @@ extern int zero_init_p				(const_tree);
 extern bool check_abi_tag_redeclaration		(const_tree, const_tree,
 						 const_tree);
 extern bool check_abi_tag_args			(tree, tree);
-extern tree strip_typedefs			(tree, bool * = NULL);
-extern tree strip_typedefs_expr			(tree, bool * = NULL);
+extern tree strip_typedefs			(tree, bool * = NULL,
+						 unsigned int = 0);
+extern tree strip_typedefs_expr			(tree, bool * = NULL,
+						 unsigned int = 0);
 extern tree copy_binfo				(tree, tree, tree,
 						 tree *, int);
 extern int member_p				(const_tree);
Index: gcc/cp/tree.c
===================================================================
--- gcc/cp/tree.c	2019-10-08 09:23:43.000000000 +0100
+++ gcc/cp/tree.c	2019-10-11 15:12:12.544911643 +0100
@@ -1431,7 +1431,10 @@ apply_identity_attributes (tree result,
   return cp_build_type_attribute_variant (result, new_attribs);
 }
 
-/* Builds a qualified variant of T that is not a typedef variant.
+/* Builds a qualified variant of T that is either not a typedef variant
+   (the default behavior) or not a typedef variant of a user-facing type
+   (if FLAGS contains STF_USER_FACING).
+
    E.g. consider the following declarations:
      typedef const int ConstInt;
      typedef ConstInt* PtrConstInt;
@@ -1456,7 +1459,7 @@ apply_identity_attributes (tree result,
    stripped.  */
 
 tree
-strip_typedefs (tree t, bool *remove_attributes)
+strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
 {
   tree result = NULL, type = NULL, t0 = NULL;
 
@@ -1471,7 +1474,7 @@ strip_typedefs (tree t, bool *remove_att
       for (; t; t = TREE_CHAIN (t))
 	{
 	  gcc_assert (!TREE_PURPOSE (t));
-	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes);
+	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes, flags);
 	  if (elt != TREE_VALUE (t))
 	    changed = true;
 	  vec_safe_push (vec, elt);
@@ -1494,28 +1497,29 @@ strip_typedefs (tree t, bool *remove_att
   switch (TREE_CODE (t))
     {
     case POINTER_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_pointer_type (type);
       break;
     case REFERENCE_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = cp_build_reference_type (type, TYPE_REF_IS_RVALUE (t));
       break;
     case OFFSET_TYPE:
-      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes);
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes, flags);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
       result = build_offset_type (t0, type);
       break;
     case RECORD_TYPE:
       if (TYPE_PTRMEMFUNC_P (t))
 	{
-	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t), remove_attributes);
+	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t),
+			       remove_attributes, flags);
 	  result = build_ptrmemfunc_type (t0);
 	}
       break;
     case ARRAY_TYPE:
-      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
-      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes);
+      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
+      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
       result = build_cplus_array_type (type, t0);
       break;
     case FUNCTION_TYPE:
@@ -1534,7 +1538,7 @@ strip_typedefs (tree t, bool *remove_att
 	    && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
 	  is_variant = true;
 
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	tree canon_spec = (flag_noexcept_type
 			   ? canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (t))
 			   : NULL_TREE);
@@ -1548,7 +1552,7 @@ strip_typedefs (tree t, bool *remove_att
 	    if (arg_node == void_list_node)
 	      break;
 	    arg_type = strip_typedefs (TREE_VALUE (arg_node),
-				       remove_attributes);
+				       remove_attributes, flags);
 	    gcc_assert (arg_type);
 	    if (arg_type == TREE_VALUE (arg_node) && !changed)
 	      continue;
@@ -1612,9 +1616,10 @@ strip_typedefs (tree t, bool *remove_att
 		tree arg = TREE_VEC_ELT (args, i);
 		tree strip_arg;
 		if (TYPE_P (arg))
-		  strip_arg = strip_typedefs (arg, remove_attributes);
+		  strip_arg = strip_typedefs (arg, remove_attributes, flags);
 		else
-		  strip_arg = strip_typedefs_expr (arg, remove_attributes);
+		  strip_arg = strip_typedefs_expr (arg, remove_attributes,
+						   flags);
 		TREE_VEC_ELT (new_args, i) = strip_arg;
 		if (strip_arg != arg)
 		  changed = true;
@@ -1630,7 +1635,7 @@ strip_typedefs (tree t, bool *remove_att
 	    else
 	      ggc_free (new_args);
 	  }
-	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes);
+	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes, flags);
 	if (!changed && ctx == TYPE_CONTEXT (t) && !typedef_variant_p (t))
 	  return t;
 	tree name = fullname;
@@ -1643,7 +1648,7 @@ strip_typedefs (tree t, bool *remove_att
       break;
     case DECLTYPE_TYPE:
       result = strip_typedefs_expr (DECLTYPE_TYPE_EXPR (t),
-				    remove_attributes);
+				    remove_attributes, flags);
       if (result == DECLTYPE_TYPE_EXPR (t))
 	result = NULL_TREE;
       else
@@ -1653,7 +1658,8 @@ strip_typedefs (tree t, bool *remove_att
 		   tf_none));
       break;
     case UNDERLYING_TYPE:
-      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t), remove_attributes);
+      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t),
+			     remove_attributes, flags);
       result = finish_underlying_type (type);
       break;
     default:
@@ -1664,15 +1670,18 @@ strip_typedefs (tree t, bool *remove_att
     {
       if (typedef_variant_p (t))
 	{
-	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
-	     strip typedefs with attributes.  */
-	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
-	  result = strip_typedefs (result);
+	  if ((flags & STF_USER_VISIBLE)
+	      && !user_facing_original_type_p (t))
+	    return t;
+	  result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
+				   remove_attributes, flags);
 	}
       else
 	result = TYPE_MAIN_VARIANT (t);
     }
-  gcc_assert (!typedef_variant_p (result));
+  gcc_assert (!typedef_variant_p (result)
+	      || ((flags & STF_USER_VISIBLE)
+		  && !user_facing_original_type_p (result)));
 
   if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
   /* If RESULT is complete and T isn't, it's likely the case that T
@@ -1721,7 +1730,7 @@ strip_typedefs (tree t, bool *remove_att
    sizeof(TT) is replaced by sizeof(T).  */
 
 tree
-strip_typedefs_expr (tree t, bool *remove_attributes)
+strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
 {
   unsigned i,n;
   tree r, type, *ops;
@@ -1738,7 +1747,7 @@ strip_typedefs_expr (tree t, bool *remov
   /* Some expressions have type operands, so let's handle types here rather
      than check TYPE_P in multiple places below.  */
   if (TYPE_P (t))
-    return strip_typedefs (t, remove_attributes);
+    return strip_typedefs (t, remove_attributes, flags);
 
   code = TREE_CODE (t);
   switch (code)
@@ -1752,8 +1761,10 @@ strip_typedefs_expr (tree t, bool *remov
 
     case TRAIT_EXPR:
       {
-	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t), remove_attributes);
-	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t), remove_attributes);
+	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t),
+				     remove_attributes, flags);
+	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t),
+				     remove_attributes, flags);
 	if (type1 == TRAIT_EXPR_TYPE1 (t)
 	    && type2 == TRAIT_EXPR_TYPE2 (t))
 	  return t;
@@ -1770,7 +1781,8 @@ strip_typedefs_expr (tree t, bool *remov
 	tree it;
 	for (it = t; it; it = TREE_CHAIN (it))
 	  {
-	    tree val = strip_typedefs_expr (TREE_VALUE (it), remove_attributes);
+	    tree val = strip_typedefs_expr (TREE_VALUE (it),
+					    remove_attributes, flags);
 	    vec_safe_push (vec, val);
 	    if (val != TREE_VALUE (it))
 	      changed = true;
@@ -1796,7 +1808,7 @@ strip_typedefs_expr (tree t, bool *remov
 	for (i = 0; i < n; ++i)
 	  {
 	    tree op = strip_typedefs_expr (TREE_VEC_ELT (t, i),
-					   remove_attributes);
+					   remove_attributes, flags);
 	    vec->quick_push (op);
 	    if (op != TREE_VEC_ELT (t, i))
 	      changed = true;
@@ -1820,18 +1832,19 @@ strip_typedefs_expr (tree t, bool *remov
 	vec<constructor_elt, va_gc> *vec
 	  = vec_safe_copy (CONSTRUCTOR_ELTS (t));
 	n = CONSTRUCTOR_NELTS (t);
-	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
+	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
 	for (i = 0; i < n; ++i)
 	  {
 	    constructor_elt *e = &(*vec)[i];
-	    tree op = strip_typedefs_expr (e->value, remove_attributes);
+	    tree op = strip_typedefs_expr (e->value, remove_attributes, flags);
 	    if (op != e->value)
 	      {
 		changed = true;
 		e->value = op;
 	      }
 	    gcc_checking_assert
-	      (e->index == strip_typedefs_expr (e->index, remove_attributes));
+	      (e->index == strip_typedefs_expr (e->index, remove_attributes,
+						flags));
 	  }
 
 	if (!changed && type == TREE_TYPE (t))
@@ -1875,12 +1888,13 @@ strip_typedefs_expr (tree t, bool *remov
     case REINTERPRET_CAST_EXPR:
     case CAST_EXPR:
     case NEW_EXPR:
-      type = strip_typedefs (type, remove_attributes);
+      type = strip_typedefs (type, remove_attributes, flags);
       /* fallthrough */
 
     default:
       for (i = 0; i < n; ++i)
-	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i), remove_attributes);
+	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i),
+				      remove_attributes, flags);
       break;
     }
 
Index: gcc/cp/error.c
===================================================================
--- gcc/cp/error.c	2019-10-08 09:23:43.000000000 +0100
+++ gcc/cp/error.c	2019-10-11 15:12:12.544911643 +0100
@@ -409,7 +409,7 @@ dump_template_bindings (cxx_pretty_print
       pop_deferring_access_checks ();
       /* Strip typedefs.  We can't just use TFF_CHASE_TYPEDEF because
 	 pp_simple_type_specifier doesn't know about it.  */
-      t = strip_typedefs (t);
+      t = strip_typedefs (t, NULL, STF_USER_VISIBLE);
       dump_type (pp, t, TFF_PLAIN_IDENTIFIER);
     }
 }
@@ -448,7 +448,11 @@ dump_type (cxx_pretty_printer *pp, tree
 	       || DECL_SELF_REFERENCE_P (decl)
 	       || (!flag_pretty_templates
 		   && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)))
-	t = strip_typedefs (t);
+	{
+	  unsigned int stf_flags = (!(pp->flags & pp_c_flag_gnu_v3)
+				    ? STF_USER_VISIBLE : 0);
+	  t = strip_typedefs (t, NULL, stf_flags);
+	}
       else if (alias_template_specialization_p (t))
 	{
 	  dump_alias_template_specialization (pp, t, flags);
@@ -3195,7 +3199,7 @@ type_to_string (tree typ, int verbose, b
       && !uses_template_parms (typ))
     {
       int aka_start, aka_len; char *p;
-      tree aka = strip_typedefs (typ);
+      tree aka = strip_typedefs (typ, NULL, STF_USER_VISIBLE);
       if (quote && *quote)
 	pp_end_quote (cxx_pp, show_color);
       pp_string (cxx_pp, " {aka");
Index: gcc/testsuite/g++.dg/diagnostic/aka5.h
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5.h	2019-10-11 15:12:12.544911643 +0100
@@ -0,0 +1,22 @@
+#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;
Index: gcc/testsuite/g++.dg/diagnostic/aka5a.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5a.C	2019-10-11 15:12:12.544911643 +0100
@@ -0,0 +1,127 @@
+#define IS_SYSTEM_HEADER
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka 'user_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka 'user_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const user_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile user_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka 'user_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka 'user_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka 'user_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const user_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka 'user_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka 'user_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const user_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka 'user_vector'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka 'user_vector\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const user_vector\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
Index: gcc/testsuite/g++.dg/diagnostic/aka5b.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.dg/diagnostic/aka5b.C	2019-10-11 15:12:12.544911643 +0100
@@ -0,0 +1,127 @@
+#include "aka5.h"
+
+typedef user_enum user_enum_copy;
+
+struct s { int i; };
+
+user_enum ue1;
+user_enum_copy ue2;
+user_enum_ptr ue_ptr1;
+user_enum *ue_ptr2;
+const user_enum *const_ue_ptr1;
+const user_enum_copy *const_ue_ptr2;
+volatile user_enum *volatile_ue_ptr1;
+volatile user_enum_copy *volatile_ue_ptr2;
+user_enum (*ue_array_ptr1)[10];
+user_enum_copy (*ue_array_ptr2)[10];
+user_enum (*ue_fn_ptr1) (void);
+void (*ue_fn_ptr2) (user_enum);
+void (*ue_fn_ptr3) (user_enum, ...);
+user_enum_copy (*ue_fn_ptr4) (void);
+void (*ue_fn_ptr5) (user_enum_copy);
+void (*ue_fn_ptr6) (user_enum_copy, ...);
+user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
+user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
+
+user_struct us1;
+user_struct_copy us2;
+user_struct_ptr us_ptr1;
+user_struct *us_ptr2;
+const user_struct *const_us_ptr1;
+const user_struct_copy *const_us_ptr2;
+
+user_union uu1;
+user_union_copy uu2;
+user_union_ptr uu_ptr1;
+user_union *uu_ptr2;
+const user_union *const_uu_ptr1;
+const user_union_copy *const_uu_ptr2;
+
+user_vector uv1;
+user_vector_copy uv2;
+user_vector_ptr uv_ptr1;
+user_vector *uv_ptr2;
+const user_vector *const_uv_ptr1;
+const user_vector_copy *const_uv_ptr2;
+
+user_int ui1;
+user_int_copy ui2;
+user_int_ptr ui_ptr1;
+user_int *ui_ptr2;
+const user_int *const_ui_ptr1;
+const user_int_copy *const_ui_ptr2;
+volatile user_int *volatile_ui_ptr1;
+volatile user_int_copy *volatile_ui_ptr2;
+user_int (*ui_array_ptr1)[10];
+user_int_copy (*ui_array_ptr2)[10];
+user_int (*ui_fn_ptr1) (void);
+void (*ui_fn_ptr2) (user_int);
+void (*ui_fn_ptr3) (user_int, ...);
+user_int_copy (*ui_fn_ptr4) (void);
+void (*ui_fn_ptr5) (user_int_copy);
+void (*ui_fn_ptr6) (user_int_copy, ...);
+user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
+user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
+
+void f (s s1)
+{
+  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' {aka '__internal_enum'} in assignment} }
+  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka '__internal_enum'} in assignment} }
+  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka '__internal_enum\*'} in assignment} }
+  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' {aka '__internal_enum\*'} in assignment} }
+  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' {aka 'const __internal_enum\*'} in assignment} }
+  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const __internal_enum\*'} in assignment} }
+  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile __internal_enum\*'} in assignment} }
+  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
+  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
+  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
+  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
+  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+
+  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' {aka '__internal_struct'} and 's'\)} }
+  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka '__internal_struct'} and 's'\)} }
+  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka '__internal_struct\*'} in assignment} }
+  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' {aka '__internal_struct\*'} in assignment} }
+  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' {aka 'const __internal_struct\*'} in assignment} }
+  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const __internal_struct\*'} in assignment} }
+
+  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' {aka '__internal_union'} and 's'\)} }
+  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka '__internal_union'} and 's'\)} }
+  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka '__internal_union\*'} in assignment} }
+  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' {aka '__internal_union\*'} in assignment} }
+  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' {aka 'const __internal_union\*'} in assignment} }
+  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const __internal_union\*'} in assignment} }
+
+  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} in assignment} }
+  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
+
+  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
+  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
+  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
+  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
+  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
+  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
+  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
+  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
+  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
+  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
+  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
+  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
+  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
+}
+
Index: gcc/testsuite/g++.target/aarch64/diag_aka_1.C
===================================================================
--- /dev/null	2019-09-17 11:41:18.176664108 +0100
+++ gcc/testsuite/g++.target/aarch64/diag_aka_1.C	2019-10-11 15:12:12.544911643 +0100
@@ -0,0 +1,13 @@
+#include <arm_neon.h>
+
+typedef int16x4_t myvec;
+
+void f (float x)
+{
+  __Int8x8_t y1 = x; // { dg-error {cannot convert 'float' to '__Int8x8_t' in initialization} }
+  __Int8x8_t *ptr1 = &x; // { dg-error {cannot convert 'float\*' to '__Int8x8_t\*' in initialization} }
+  int8x8_t y2 = x; // { dg-error {cannot convert 'float' to 'int8x8_t' in initialization} }
+  int8x8_t *ptr2 = &x; // { dg-error {cannot convert 'float\*' to 'int8x8_t\*' in initialization} }
+  myvec y3 = x; // { dg-error {cannot convert 'float' to 'myvec' {aka 'int16x4_t'} in initialization} }
+  myvec *ptr3 = &x; // { dg-error {cannot convert 'float\*' to 'myvec\*' {aka 'int16x4_t\*'} in initialization} }
+}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Ping: [C][C++] Avoid exposing internal details in aka types
  2019-10-11 14:17     ` Richard Sandiford
  2019-10-11 14:27       ` Richard Sandiford
@ 2019-10-11 14:39       ` Marek Polacek
  1 sibling, 0 replies; 8+ messages in thread
From: Marek Polacek @ 2019-10-11 14:39 UTC (permalink / raw)
  To: gcc-patches, dodji, dmalcolm, joseph, jason, nathan, richard.sandiford

On Fri, Oct 11, 2019 at 03:14:09PM +0100, Richard Sandiford wrote:
> Marek Polacek <polacek@redhat.com> writes:
> > On Thu, Oct 10, 2019 at 08:00:53PM +0100, Richard Sandiford wrote:
> >> Ping
> >> 
> >> Richard Sandiford <richard.sandiford@arm.com> writes:
> >> > 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.
> >
> > Agreed.
> >
> >> +/* 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))
> >> +    {
> >> +      const char *name = IDENTIFIER_POINTER (orig_id);
> >> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
> >> +	return true;
> >
> > This looks like name_reserved_for_implementation_p.
> >
> > The rest looks fine to me!
> 
> Ah, nice!  I'd looked for a helper but missed that one.
> 
> Here's just the C parts, with that change.  Tested on aarch64-linux-gnu
> and x86_64-linux-gnu.  OK to install?

Ok, thanks!

--
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Ping: [C][C++] Avoid exposing internal details in aka types
  2019-10-11 14:27       ` Richard Sandiford
@ 2019-10-21 19:51         ` Jason Merrill
  0 siblings, 0 replies; 8+ messages in thread
From: Jason Merrill @ 2019-10-21 19:51 UTC (permalink / raw)
  To: Marek Polacek, gcc-patches, dodji, dmalcolm, joseph, nathan,
	richard.sandiford

On 10/11/19 10:17 AM, Richard Sandiford wrote:
> Richard Sandiford <richard.sandiford@arm.com> writes:
>> Marek Polacek <polacek@redhat.com> writes:
>>> On Thu, Oct 10, 2019 at 08:00:53PM +0100, Richard Sandiford wrote:
>>>> Ping
>>>>
>>>> Richard Sandiford <richard.sandiford@arm.com> writes:
>>>>> 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.
>>>
>>> Agreed.
>>>
>>>> +/* 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))
>>>> +    {
>>>> +      const char *name = IDENTIFIER_POINTER (orig_id);
>>>> +      if (name[0] != '_' || (name[1] != '_' && !ISUPPER (name[1])))
>>>> +	return true;
>>>
>>> This looks like name_reserved_for_implementation_p.
>>>
>>> The rest looks fine to me!
>>
>> Ah, nice!  I'd looked for a helper but missed that one.
>>
>> Here's just the C parts, with that change.
> 
> And here are the C++ parts, on top of that.  I should probably have
> split them from the outset.
> 
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

OK.

Jason
> 
> Richard
> 
> [Comment from the original covering note, in case it helps:
> 
> strip_typedefs had:
> 
> 	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
> 	     strip typedefs with attributes.  */
> 	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
> 	  result = strip_typedefs (result);
> 
> Applying TYPE_MAIN_VARIANT predates the strip_typedefs call, with the
> comment originally contrasting with plain:
> 
> 	  result = TYPE_MAIN_VARIANT (t);
> 
> But the recursive call to strip_typedefs will apply TYPE_MAIN_VARIANT,
> so it doesn't seem necessary to do it here too.  I think there was also
> a missing "remove_attributes" argument, since wrapping something in a
> typedef shouldn't change which attributes get removed.]
> 
> 2019-10-11  Richard Sandiford  <richard.sandiford@arm.com>
> 
> gcc/cp/
> 	* cp-tree.h (STF_USER_VISIBLE): New constant.
> 	(strip_typedefs, strip_typedefs_expr): Take a flags argument.
> 	* tree.c (strip_typedefs, strip_typedefs_expr): Likewise,
> 	updating mutual calls accordingly.  When STF_USER_VISIBLE is true,
> 	only look through typedefs if user_facing_original_type_p.
> 	* error.c (dump_template_bindings, type_to_string): Pass
> 	STF_USER_VISIBLE to strip_typedefs.
> 	(dump_type): Likewise, unless pp_c_flag_gnu_v3 is set.
> 
> gcc/testsuite/
> 	* g++.dg/diagnostic/aka5.h: New test.
> 	* g++.dg/diagnostic/aka5a.C: Likewise.
> 	* g++.dg/diagnostic/aka5b.C: Likewise.
> 	* g++.target/aarch64/diag_aka_1.C: Likewise.
> 
> Index: gcc/cp/cp-tree.h
> ===================================================================
> --- gcc/cp/cp-tree.h	2019-10-08 09:23:43.000000000 +0100
> +++ gcc/cp/cp-tree.h	2019-10-11 15:12:12.544911643 +0100
> @@ -5696,6 +5696,13 @@ #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARG
>   #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
>   #define TFF_POINTER		                (1 << 14)
>   
> +/* These constants can be used as bit flags to control strip_typedefs.
> +
> +   STF_USER_VISIBLE: use heuristics to try to avoid stripping user-facing
> +       aliases of internal details.  This is intended for diagnostics,
> +       where it should (for example) give more useful "aka" types.  */
> +const unsigned int STF_USER_VISIBLE = 1U;
> +
>   /* Returns the TEMPLATE_DECL associated to a TEMPLATE_TEMPLATE_PARM
>      node.  */
>   #define TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL(NODE)	\
> @@ -7254,8 +7261,10 @@ extern int zero_init_p				(const_tree);
>   extern bool check_abi_tag_redeclaration		(const_tree, const_tree,
>   						 const_tree);
>   extern bool check_abi_tag_args			(tree, tree);
> -extern tree strip_typedefs			(tree, bool * = NULL);
> -extern tree strip_typedefs_expr			(tree, bool * = NULL);
> +extern tree strip_typedefs			(tree, bool * = NULL,
> +						 unsigned int = 0);
> +extern tree strip_typedefs_expr			(tree, bool * = NULL,
> +						 unsigned int = 0);
>   extern tree copy_binfo				(tree, tree, tree,
>   						 tree *, int);
>   extern int member_p				(const_tree);
> Index: gcc/cp/tree.c
> ===================================================================
> --- gcc/cp/tree.c	2019-10-08 09:23:43.000000000 +0100
> +++ gcc/cp/tree.c	2019-10-11 15:12:12.544911643 +0100
> @@ -1431,7 +1431,10 @@ apply_identity_attributes (tree result,
>     return cp_build_type_attribute_variant (result, new_attribs);
>   }
>   
> -/* Builds a qualified variant of T that is not a typedef variant.
> +/* Builds a qualified variant of T that is either not a typedef variant
> +   (the default behavior) or not a typedef variant of a user-facing type
> +   (if FLAGS contains STF_USER_FACING).
> +
>      E.g. consider the following declarations:
>        typedef const int ConstInt;
>        typedef ConstInt* PtrConstInt;
> @@ -1456,7 +1459,7 @@ apply_identity_attributes (tree result,
>      stripped.  */
>   
>   tree
> -strip_typedefs (tree t, bool *remove_attributes)
> +strip_typedefs (tree t, bool *remove_attributes, unsigned int flags)
>   {
>     tree result = NULL, type = NULL, t0 = NULL;
>   
> @@ -1471,7 +1474,7 @@ strip_typedefs (tree t, bool *remove_att
>         for (; t; t = TREE_CHAIN (t))
>   	{
>   	  gcc_assert (!TREE_PURPOSE (t));
> -	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes);
> +	  tree elt = strip_typedefs (TREE_VALUE (t), remove_attributes, flags);
>   	  if (elt != TREE_VALUE (t))
>   	    changed = true;
>   	  vec_safe_push (vec, elt);
> @@ -1494,28 +1497,29 @@ strip_typedefs (tree t, bool *remove_att
>     switch (TREE_CODE (t))
>       {
>       case POINTER_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>         result = build_pointer_type (type);
>         break;
>       case REFERENCE_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>         result = cp_build_reference_type (type, TYPE_REF_IS_RVALUE (t));
>         break;
>       case OFFSET_TYPE:
> -      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes);
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +      t0 = strip_typedefs (TYPE_OFFSET_BASETYPE (t), remove_attributes, flags);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>         result = build_offset_type (t0, type);
>         break;
>       case RECORD_TYPE:
>         if (TYPE_PTRMEMFUNC_P (t))
>   	{
> -	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t), remove_attributes);
> +	  t0 = strip_typedefs (TYPE_PTRMEMFUNC_FN_TYPE (t),
> +			       remove_attributes, flags);
>   	  result = build_ptrmemfunc_type (t0);
>   	}
>         break;
>       case ARRAY_TYPE:
> -      type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> -      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes);
> +      type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
> +      t0  = strip_typedefs (TYPE_DOMAIN (t), remove_attributes, flags);
>         result = build_cplus_array_type (type, t0);
>         break;
>       case FUNCTION_TYPE:
> @@ -1534,7 +1538,7 @@ strip_typedefs (tree t, bool *remove_att
>   	    && (TYPE_ATTRIBUTES (t) || TYPE_USER_ALIGN (t)))
>   	  is_variant = true;
>   
> -	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>   	tree canon_spec = (flag_noexcept_type
>   			   ? canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (t))
>   			   : NULL_TREE);
> @@ -1548,7 +1552,7 @@ strip_typedefs (tree t, bool *remove_att
>   	    if (arg_node == void_list_node)
>   	      break;
>   	    arg_type = strip_typedefs (TREE_VALUE (arg_node),
> -				       remove_attributes);
> +				       remove_attributes, flags);
>   	    gcc_assert (arg_type);
>   	    if (arg_type == TREE_VALUE (arg_node) && !changed)
>   	      continue;
> @@ -1612,9 +1616,10 @@ strip_typedefs (tree t, bool *remove_att
>   		tree arg = TREE_VEC_ELT (args, i);
>   		tree strip_arg;
>   		if (TYPE_P (arg))
> -		  strip_arg = strip_typedefs (arg, remove_attributes);
> +		  strip_arg = strip_typedefs (arg, remove_attributes, flags);
>   		else
> -		  strip_arg = strip_typedefs_expr (arg, remove_attributes);
> +		  strip_arg = strip_typedefs_expr (arg, remove_attributes,
> +						   flags);
>   		TREE_VEC_ELT (new_args, i) = strip_arg;
>   		if (strip_arg != arg)
>   		  changed = true;
> @@ -1630,7 +1635,7 @@ strip_typedefs (tree t, bool *remove_att
>   	    else
>   	      ggc_free (new_args);
>   	  }
> -	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes);
> +	tree ctx = strip_typedefs (TYPE_CONTEXT (t), remove_attributes, flags);
>   	if (!changed && ctx == TYPE_CONTEXT (t) && !typedef_variant_p (t))
>   	  return t;
>   	tree name = fullname;
> @@ -1643,7 +1648,7 @@ strip_typedefs (tree t, bool *remove_att
>         break;
>       case DECLTYPE_TYPE:
>         result = strip_typedefs_expr (DECLTYPE_TYPE_EXPR (t),
> -				    remove_attributes);
> +				    remove_attributes, flags);
>         if (result == DECLTYPE_TYPE_EXPR (t))
>   	result = NULL_TREE;
>         else
> @@ -1653,7 +1658,8 @@ strip_typedefs (tree t, bool *remove_att
>   		   tf_none));
>         break;
>       case UNDERLYING_TYPE:
> -      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t), remove_attributes);
> +      type = strip_typedefs (UNDERLYING_TYPE_TYPE (t),
> +			     remove_attributes, flags);
>         result = finish_underlying_type (type);
>         break;
>       default:
> @@ -1664,15 +1670,18 @@ strip_typedefs (tree t, bool *remove_att
>       {
>         if (typedef_variant_p (t))
>   	{
> -	  /* Explicitly get the underlying type, as TYPE_MAIN_VARIANT doesn't
> -	     strip typedefs with attributes.  */
> -	  result = TYPE_MAIN_VARIANT (DECL_ORIGINAL_TYPE (TYPE_NAME (t)));
> -	  result = strip_typedefs (result);
> +	  if ((flags & STF_USER_VISIBLE)
> +	      && !user_facing_original_type_p (t))
> +	    return t;
> +	  result = strip_typedefs (DECL_ORIGINAL_TYPE (TYPE_NAME (t)),
> +				   remove_attributes, flags);
>   	}
>         else
>   	result = TYPE_MAIN_VARIANT (t);
>       }
> -  gcc_assert (!typedef_variant_p (result));
> +  gcc_assert (!typedef_variant_p (result)
> +	      || ((flags & STF_USER_VISIBLE)
> +		  && !user_facing_original_type_p (result)));
>   
>     if (COMPLETE_TYPE_P (result) && !COMPLETE_TYPE_P (t))
>     /* If RESULT is complete and T isn't, it's likely the case that T
> @@ -1721,7 +1730,7 @@ strip_typedefs (tree t, bool *remove_att
>      sizeof(TT) is replaced by sizeof(T).  */
>   
>   tree
> -strip_typedefs_expr (tree t, bool *remove_attributes)
> +strip_typedefs_expr (tree t, bool *remove_attributes, unsigned int flags)
>   {
>     unsigned i,n;
>     tree r, type, *ops;
> @@ -1738,7 +1747,7 @@ strip_typedefs_expr (tree t, bool *remov
>     /* Some expressions have type operands, so let's handle types here rather
>        than check TYPE_P in multiple places below.  */
>     if (TYPE_P (t))
> -    return strip_typedefs (t, remove_attributes);
> +    return strip_typedefs (t, remove_attributes, flags);
>   
>     code = TREE_CODE (t);
>     switch (code)
> @@ -1752,8 +1761,10 @@ strip_typedefs_expr (tree t, bool *remov
>   
>       case TRAIT_EXPR:
>         {
> -	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t), remove_attributes);
> -	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t), remove_attributes);
> +	tree type1 = strip_typedefs (TRAIT_EXPR_TYPE1 (t),
> +				     remove_attributes, flags);
> +	tree type2 = strip_typedefs (TRAIT_EXPR_TYPE2 (t),
> +				     remove_attributes, flags);
>   	if (type1 == TRAIT_EXPR_TYPE1 (t)
>   	    && type2 == TRAIT_EXPR_TYPE2 (t))
>   	  return t;
> @@ -1770,7 +1781,8 @@ strip_typedefs_expr (tree t, bool *remov
>   	tree it;
>   	for (it = t; it; it = TREE_CHAIN (it))
>   	  {
> -	    tree val = strip_typedefs_expr (TREE_VALUE (it), remove_attributes);
> +	    tree val = strip_typedefs_expr (TREE_VALUE (it),
> +					    remove_attributes, flags);
>   	    vec_safe_push (vec, val);
>   	    if (val != TREE_VALUE (it))
>   	      changed = true;
> @@ -1796,7 +1808,7 @@ strip_typedefs_expr (tree t, bool *remov
>   	for (i = 0; i < n; ++i)
>   	  {
>   	    tree op = strip_typedefs_expr (TREE_VEC_ELT (t, i),
> -					   remove_attributes);
> +					   remove_attributes, flags);
>   	    vec->quick_push (op);
>   	    if (op != TREE_VEC_ELT (t, i))
>   	      changed = true;
> @@ -1820,18 +1832,19 @@ strip_typedefs_expr (tree t, bool *remov
>   	vec<constructor_elt, va_gc> *vec
>   	  = vec_safe_copy (CONSTRUCTOR_ELTS (t));
>   	n = CONSTRUCTOR_NELTS (t);
> -	type = strip_typedefs (TREE_TYPE (t), remove_attributes);
> +	type = strip_typedefs (TREE_TYPE (t), remove_attributes, flags);
>   	for (i = 0; i < n; ++i)
>   	  {
>   	    constructor_elt *e = &(*vec)[i];
> -	    tree op = strip_typedefs_expr (e->value, remove_attributes);
> +	    tree op = strip_typedefs_expr (e->value, remove_attributes, flags);
>   	    if (op != e->value)
>   	      {
>   		changed = true;
>   		e->value = op;
>   	      }
>   	    gcc_checking_assert
> -	      (e->index == strip_typedefs_expr (e->index, remove_attributes));
> +	      (e->index == strip_typedefs_expr (e->index, remove_attributes,
> +						flags));
>   	  }
>   
>   	if (!changed && type == TREE_TYPE (t))
> @@ -1875,12 +1888,13 @@ strip_typedefs_expr (tree t, bool *remov
>       case REINTERPRET_CAST_EXPR:
>       case CAST_EXPR:
>       case NEW_EXPR:
> -      type = strip_typedefs (type, remove_attributes);
> +      type = strip_typedefs (type, remove_attributes, flags);
>         /* fallthrough */
>   
>       default:
>         for (i = 0; i < n; ++i)
> -	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i), remove_attributes);
> +	ops[i] = strip_typedefs_expr (TREE_OPERAND (t, i),
> +				      remove_attributes, flags);
>         break;
>       }
>   
> Index: gcc/cp/error.c
> ===================================================================
> --- gcc/cp/error.c	2019-10-08 09:23:43.000000000 +0100
> +++ gcc/cp/error.c	2019-10-11 15:12:12.544911643 +0100
> @@ -409,7 +409,7 @@ dump_template_bindings (cxx_pretty_print
>         pop_deferring_access_checks ();
>         /* Strip typedefs.  We can't just use TFF_CHASE_TYPEDEF because
>   	 pp_simple_type_specifier doesn't know about it.  */
> -      t = strip_typedefs (t);
> +      t = strip_typedefs (t, NULL, STF_USER_VISIBLE);
>         dump_type (pp, t, TFF_PLAIN_IDENTIFIER);
>       }
>   }
> @@ -448,7 +448,11 @@ dump_type (cxx_pretty_printer *pp, tree
>   	       || DECL_SELF_REFERENCE_P (decl)
>   	       || (!flag_pretty_templates
>   		   && DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)))
> -	t = strip_typedefs (t);
> +	{
> +	  unsigned int stf_flags = (!(pp->flags & pp_c_flag_gnu_v3)
> +				    ? STF_USER_VISIBLE : 0);
> +	  t = strip_typedefs (t, NULL, stf_flags);
> +	}
>         else if (alias_template_specialization_p (t))
>   	{
>   	  dump_alias_template_specialization (pp, t, flags);
> @@ -3195,7 +3199,7 @@ type_to_string (tree typ, int verbose, b
>         && !uses_template_parms (typ))
>       {
>         int aka_start, aka_len; char *p;
> -      tree aka = strip_typedefs (typ);
> +      tree aka = strip_typedefs (typ, NULL, STF_USER_VISIBLE);
>         if (quote && *quote)
>   	pp_end_quote (cxx_pp, show_color);
>         pp_string (cxx_pp, " {aka");
> Index: gcc/testsuite/g++.dg/diagnostic/aka5.h
> ===================================================================
> --- /dev/null	2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5.h	2019-10-11 15:12:12.544911643 +0100
> @@ -0,0 +1,22 @@
> +#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;
> Index: gcc/testsuite/g++.dg/diagnostic/aka5a.C
> ===================================================================
> --- /dev/null	2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5a.C	2019-10-11 15:12:12.544911643 +0100
> @@ -0,0 +1,127 @@
> +#define IS_SYSTEM_HEADER
> +#include "aka5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (s s1)
> +{
> +  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' in assignment} }
> +  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka 'user_enum'} in assignment} }
> +  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka 'user_enum\*'} in assignment} }
> +  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' in assignment} }
> +  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' in assignment} }
> +  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const user_enum\*'} in assignment} }
> +  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' in assignment} }
> +  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile user_enum\*'} in assignment} }
> +  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' in assignment} }
> +  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka 'user_enum \(\*\)\[10\]'} in assignment} }
> +  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' in assignment} }
> +  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' in assignment} }
> +  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' in assignment} }
> +  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka 'user_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(user_enum\)'} in assignment} }
> +  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(user_enum, \.\.\.\)'} in assignment} }
> +  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' in assignment} }
> +  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +
> +  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' and 's'\)} }
> +  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka 'user_struct'} and 's'\)} }
> +  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka 'user_struct\*'} in assignment} }
> +  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' in assignment} }
> +  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' in assignment} }
> +  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const user_struct\*'} in assignment} }
> +
> +  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' and 's'\)} }
> +  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka 'user_union'} and 's'\)} }
> +  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka 'user_union\*'} in assignment} }
> +  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' in assignment} }
> +  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' in assignment} }
> +  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const user_union\*'} in assignment} }
> +
> +  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' in assignment} }
> +  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka 'user_vector'} in assignment} }
> +  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka 'user_vector\*'} in assignment} }
> +  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' in assignment} }
> +  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' in assignment} }
> +  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const user_vector\*'} in assignment} }
> +
> +  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
> +  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
> +  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
> +  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
> +  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
> +  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
> +  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
> +  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
> +  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +}
> Index: gcc/testsuite/g++.dg/diagnostic/aka5b.C
> ===================================================================
> --- /dev/null	2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.dg/diagnostic/aka5b.C	2019-10-11 15:12:12.544911643 +0100
> @@ -0,0 +1,127 @@
> +#include "aka5.h"
> +
> +typedef user_enum user_enum_copy;
> +
> +struct s { int i; };
> +
> +user_enum ue1;
> +user_enum_copy ue2;
> +user_enum_ptr ue_ptr1;
> +user_enum *ue_ptr2;
> +const user_enum *const_ue_ptr1;
> +const user_enum_copy *const_ue_ptr2;
> +volatile user_enum *volatile_ue_ptr1;
> +volatile user_enum_copy *volatile_ue_ptr2;
> +user_enum (*ue_array_ptr1)[10];
> +user_enum_copy (*ue_array_ptr2)[10];
> +user_enum (*ue_fn_ptr1) (void);
> +void (*ue_fn_ptr2) (user_enum);
> +void (*ue_fn_ptr3) (user_enum, ...);
> +user_enum_copy (*ue_fn_ptr4) (void);
> +void (*ue_fn_ptr5) (user_enum_copy);
> +void (*ue_fn_ptr6) (user_enum_copy, ...);
> +user_enum (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr1) (void);
> +user_enum_copy (*__attribute__((__transaction_unsafe__)) unsafe_ue_fn_ptr2) (void);
> +
> +user_struct us1;
> +user_struct_copy us2;
> +user_struct_ptr us_ptr1;
> +user_struct *us_ptr2;
> +const user_struct *const_us_ptr1;
> +const user_struct_copy *const_us_ptr2;
> +
> +user_union uu1;
> +user_union_copy uu2;
> +user_union_ptr uu_ptr1;
> +user_union *uu_ptr2;
> +const user_union *const_uu_ptr1;
> +const user_union_copy *const_uu_ptr2;
> +
> +user_vector uv1;
> +user_vector_copy uv2;
> +user_vector_ptr uv_ptr1;
> +user_vector *uv_ptr2;
> +const user_vector *const_uv_ptr1;
> +const user_vector_copy *const_uv_ptr2;
> +
> +user_int ui1;
> +user_int_copy ui2;
> +user_int_ptr ui_ptr1;
> +user_int *ui_ptr2;
> +const user_int *const_ui_ptr1;
> +const user_int_copy *const_ui_ptr2;
> +volatile user_int *volatile_ui_ptr1;
> +volatile user_int_copy *volatile_ui_ptr2;
> +user_int (*ui_array_ptr1)[10];
> +user_int_copy (*ui_array_ptr2)[10];
> +user_int (*ui_fn_ptr1) (void);
> +void (*ui_fn_ptr2) (user_int);
> +void (*ui_fn_ptr3) (user_int, ...);
> +user_int_copy (*ui_fn_ptr4) (void);
> +void (*ui_fn_ptr5) (user_int_copy);
> +void (*ui_fn_ptr6) (user_int_copy, ...);
> +user_int (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr1) (void);
> +user_int_copy (*__attribute__((__transaction_unsafe__)) unsafe_ui_fn_ptr2) (void);
> +
> +void f (s s1)
> +{
> +  ue1 = s1; // { dg-error {cannot convert 's' to 'user_enum' {aka '__internal_enum'} in assignment} }
> +  ue2 = s1; // { dg-error {cannot convert 's' to 'user_enum_copy' {aka '__internal_enum'} in assignment} }
> +  ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_ptr' {aka '__internal_enum\*'} in assignment} }
> +  ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum\*' {aka '__internal_enum\*'} in assignment} }
> +  const_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum\*' {aka 'const __internal_enum\*'} in assignment} }
> +  const_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_enum_copy\*' {aka 'const __internal_enum\*'} in assignment} }
> +  volatile_ue_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum\*' {aka 'volatile __internal_enum\*'} in assignment} }
> +  volatile_ue_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_enum_copy\*' {aka 'volatile __internal_enum\*'} in assignment} }
> +  ue_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
> +  ue_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\[10\]' {aka '__internal_enum \(\*\)\[10\]'} in assignment} }
> +  ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
> +  ue_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
> +  ue_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(\*\)\(\)' {aka '__internal_enum \(\*\)\(\)'} in assignment} }
> +  ue_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy\)' {aka 'void \(\*\)\(__internal_enum\)'} in assignment} }
> +  ue_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_enum_copy, \.\.\.\)' {aka 'void \(\*\)\(__internal_enum, \.\.\.\)'} in assignment} }
> +  unsafe_ue_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ue_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_enum_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka '__internal_enum \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +
> +  us1 = s1; // { dg-error {no match for 'operator=' in 'us1 = s1' \(operand types are 'user_struct' {aka '__internal_struct'} and 's'\)} }
> +  us2 = s1; // { dg-error {no match for 'operator=' in 'us2 = s1' \(operand types are 'user_struct_copy' {aka '__internal_struct'} and 's'\)} }
> +  us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct_ptr' {aka '__internal_struct\*'} in assignment} }
> +  us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_struct\*' {aka '__internal_struct\*'} in assignment} }
> +  const_us_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct\*' {aka 'const __internal_struct\*'} in assignment} }
> +  const_us_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_struct_copy\*' {aka 'const __internal_struct\*'} in assignment} }
> +
> +  uu1 = s1; // { dg-error {no match for 'operator=' in 'uu1 = s1' \(operand types are 'user_union' {aka '__internal_union'} and 's'\)} }
> +  uu2 = s1; // { dg-error {no match for 'operator=' in 'uu2 = s1' \(operand types are 'user_union_copy' {aka '__internal_union'} and 's'\)} }
> +  uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_union_ptr' {aka '__internal_union\*'} in assignment} }
> +  uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_union\*' {aka '__internal_union\*'} in assignment} }
> +  const_uu_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union\*' {aka 'const __internal_union\*'} in assignment} }
> +  const_uu_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_union_copy\*' {aka 'const __internal_union\*'} in assignment} }
> +
> +  uv1 = s1; // { dg-error {cannot convert 's' to 'user_vector' {aka '__vector\([48]\) unsigned int'} in assignment} }
> +  uv2 = s1; // { dg-error {cannot convert 's' to 'user_vector_copy' {aka '__vector\([48]\) unsigned int'} in assignment} }
> +  uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector_ptr' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
> +  uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_vector\*' {aka '__vector\([48]\) unsigned int\*'} in assignment} }
> +  const_uv_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
> +  const_uv_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_vector_copy\*' {aka 'const __vector\([48]\) unsigned int\*'} in assignment} }
> +
> +  ui1 = s1; // { dg-error {cannot convert 's' to 'user_int' {aka 'int'} in assignment} }
> +  ui2 = s1; // { dg-error {cannot convert 's' to 'user_int_copy' {aka 'int'} in assignment} }
> +  ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_ptr' {aka 'int\*'} in assignment} }
> +  ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int\*' {aka 'int\*'} in assignment} }
> +  const_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int\*' {aka 'const int\*'} in assignment} }
> +  const_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'const user_int_copy\*' {aka 'const int\*'} in assignment} }
> +  volatile_ui_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int\*' {aka 'volatile int\*'} in assignment} }
> +  volatile_ui_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'volatile user_int_copy\*' {aka 'volatile int\*'} in assignment} }
> +  ui_array_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_array_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\[10\]' {aka 'int \(\*\)\[10\]'} in assignment} }
> +  ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr3 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  ui_fn_ptr4 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(\*\)\(\)' {aka 'int \(\*\)\(\)'} in assignment} }
> +  ui_fn_ptr5 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy\)' {aka 'void \(\*\)\(int\)'} in assignment} }
> +  ui_fn_ptr6 = &s1; // { dg-error {cannot convert 's\*' to 'void \(\*\)\(user_int_copy, \.\.\.\)' {aka 'void \(\*\)\(int, \.\.\.\)'} in assignment} }
> +  unsafe_ui_fn_ptr1 = &s1; // { dg-error {cannot convert 's\*' to 'user_int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +  unsafe_ui_fn_ptr2 = &s1; // { dg-error {cannot convert 's\*' to 'user_int_copy \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)' {aka 'int \(__attribute__\(\(transaction_unsafe\)\) \*\)\(\)'} in assignment} }
> +}
> +
> Index: gcc/testsuite/g++.target/aarch64/diag_aka_1.C
> ===================================================================
> --- /dev/null	2019-09-17 11:41:18.176664108 +0100
> +++ gcc/testsuite/g++.target/aarch64/diag_aka_1.C	2019-10-11 15:12:12.544911643 +0100
> @@ -0,0 +1,13 @@
> +#include <arm_neon.h>
> +
> +typedef int16x4_t myvec;
> +
> +void f (float x)
> +{
> +  __Int8x8_t y1 = x; // { dg-error {cannot convert 'float' to '__Int8x8_t' in initialization} }
> +  __Int8x8_t *ptr1 = &x; // { dg-error {cannot convert 'float\*' to '__Int8x8_t\*' in initialization} }
> +  int8x8_t y2 = x; // { dg-error {cannot convert 'float' to 'int8x8_t' in initialization} }
> +  int8x8_t *ptr2 = &x; // { dg-error {cannot convert 'float\*' to 'int8x8_t\*' in initialization} }
> +  myvec y3 = x; // { dg-error {cannot convert 'float' to 'myvec' {aka 'int16x4_t'} in initialization} }
> +  myvec *ptr3 = &x; // { dg-error {cannot convert 'float\*' to 'myvec\*' {aka 'int16x4_t\*'} in initialization} }
> +}
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-10-21 19:38 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-30 13:21 [C][C++] Avoid exposing internal details in aka types Richard Sandiford
2019-10-01  7:41 ` Richard Biener
2019-10-10 19:02 ` Ping: " Richard Sandiford
2019-10-10 19:55   ` Marek Polacek
2019-10-11 14:17     ` Richard Sandiford
2019-10-11 14:27       ` Richard Sandiford
2019-10-21 19:51         ` Jason Merrill
2019-10-11 14:39       ` Marek Polacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).