[gcc r17-2657] c++: Diagnose declaration of objects with anonymous union or struct types [CWG3130]

Jakub Jelinek via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:ad1f516c31965657aa16c6b601db111f57276d45

commit r17-2657-gad1f516c31965657aa16c6b601db111f57276d45
Author: Jakub Jelinek <[email protected]>
Date:   Thu Jul 23 11:14:21 2026 +0200

    c++: Diagnose declaration of objects with anonymous union or struct types [CWG3130]
    
    https://wg21.link/CWG3130 added
    "All objects of such an unnamed type shall be such an unnamed object."
    sentence which should prevent reusing anonymous union types (and by
    extension anonymous struct types too) as types of some other declarations,
    which in some cases ICEs, in other cases is just really weird and could
    misbehave when trying to look things up etc.
    That wording is IMHO bad, because creation of objects is what happens
    at runtime, so it is something that really can't be diagnosed by the compiler
    because it doesn't know if a variable, function parameter, temporary etc.
    will be actually encountered during runtime or not.
    This patch assumes this sentence is changed to something reasonable that
    would disallow anything that can create such objects at compile time,
    so, declaration of variables, function parameters, class members,
    constructs that need temporaries of this type, or all of those with
    a type (perhaps multidimensional) array of anonmous union/struct.
    In addition to that, I've added an error trying to make derived types from
    anonymous struct types (one can't make derived types from union types, so
    that case isn't a problem).
    
    2026-07-23  Jakub Jelinek  <[email protected]>
    
    gcc/cp/
            * typeck2.cc: Implement part of CWG3130 - Naming function members of
            anonymous unions.
            (abstract_virtuals_error): Diagnose trying to create
            an object with anonymous union or struct type.
            * semantics.cc (finish_compound_literal): Call abstract_virtuals_error.
            (finish_member_declaration): Diagnose named members with anonymous
            union or struct type.
            * decl.cc (xref_basetypes): Diagnose anonymous structs as bases.
    gcc/testsuite/
            * g++.dg/cpp0x/anon-union4.C: New test.
            * g++.dg/reflect/anon6.C: New test.
            * g++.dg/reflect/anon7.C: New test.
            * g++.dg/reflect/anon8.C: New test.
            * g++.dg/reflect/anon9.C: New test.
            * g++.dg/template/anonunion3.C: Expect an error.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/decl.cc                             |   5 ++
 gcc/cp/semantics.cc                        |  69 +++++++++++++++++--
 gcc/cp/typeck2.cc                          |  95 ++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/anon-union4.C   |  10 +++
 gcc/testsuite/g++.dg/reflect/anon6.C       | 105 +++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/reflect/anon7.C       |  86 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/reflect/anon8.C       |  24 +++++++
 gcc/testsuite/g++.dg/reflect/anon9.C       |  24 +++++++
 gcc/testsuite/g++.dg/template/anonunion3.C |   2 +-
 9 files changed, 414 insertions(+), 6 deletions(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 639eb85c0760..41aae747c955 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -18758,6 +18758,11 @@ xref_basetypes (tree ref, tree base_list)
 		 basetype);
 	  goto dropped_base;
 	}
+      else if (ANON_AGGR_TYPE_P (basetype))
+	{
+	  error ("base type %qT is anonymous struct type", basetype);
+	  goto dropped_base;
+	}
 
       base_binfo = NULL_TREE;
       if (CLASS_TYPE_P (basetype) && !dependent_scope_p (basetype))
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 4806acbcdc66..9ee28b7ace08 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3946,6 +3946,8 @@ finish_compound_literal (tree type, tree compound_literal,
       if (type == error_mark_node)
 	return error_mark_node;
     }
+  if (abstract_virtuals_error (ACU_UNKNOWN, type, complain))
+    return error_mark_node;
   compound_literal = digest_init_flags (type, compound_literal,
 					LOOKUP_NORMAL | LOOKUP_NO_NARROWING,
 					complain);
@@ -4279,12 +4281,69 @@ finish_member_declaration (tree decl)
   if (TREE_CODE (decl) != CONST_DECL)
     DECL_CONTEXT (decl) = current_class_type;
 
-  /* Remember the single FIELD_DECL an anonymous aggregate type is used for.  */
-  if (TREE_CODE (decl) == FIELD_DECL
-      && ANON_AGGR_TYPE_P (TREE_TYPE (decl)))
+  if (TREE_TYPE (decl)
+      && ANON_AGGR_TYPE_P (TREE_TYPE (decl))
+      && TREE_CODE (decl) != TYPE_DECL)
+    {
+      /* Remember the single FIELD_DECL an anonymous aggregate type is used
+	 for.  */
+      if (TREE_CODE (decl) == FIELD_DECL && DECL_NAME (decl) == NULL_TREE)
+	{
+	  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+	  gcc_assert (!ANON_AGGR_TYPE_FIELD (type));
+	  SET_ANON_AGGR_TYPE_FIELD (type, decl);
+	}
+      /* [class.union.anon]/1: Each object of such an unnamed type shall
+	 be such an unnamed object.  */
+      else if (ANON_UNION_TYPE_P (TREE_TYPE (decl)))
+	{
+	  tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+	  auto_diagnostic_group d;
+	  error_at (location_of (decl),
+		    "declaration of member %qD with anonymous union type %qT",
+		    decl, TREE_TYPE (decl));
+	  inform (DECL_SOURCE_LOCATION (adecl),
+		  "anonymous union declared here");
+	}
+      else
+	{
+	  tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+	  auto_diagnostic_group d;
+	  error_at (location_of (decl),
+		    "declaration of member %qD with anonymous struct type %qT",
+		    decl, TREE_TYPE (decl));
+	  inform (DECL_SOURCE_LOCATION (adecl),
+		  "anonymous struct declared here");
+	}
+    }
+  else if (TREE_TYPE (decl)
+	   && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE
+	   && TREE_CODE (decl) != TYPE_DECL)
     {
-      gcc_assert (!ANON_AGGR_TYPE_FIELD (TYPE_MAIN_VARIANT (TREE_TYPE (decl))));
-      SET_ANON_AGGR_TYPE_FIELD (TYPE_MAIN_VARIANT (TREE_TYPE (decl)), decl);
+      tree type = strip_array_types (TREE_TYPE (decl));
+      if (ANON_AGGR_TYPE_P (type))
+	{
+	  /* [class.union.anon]/1: Each object of such an unnamed type shall
+	     be such an unnamed object.  */
+	  tree adecl = TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (type));
+	  auto_diagnostic_group d;
+	  if (ANON_UNION_TYPE_P (type))
+	    {
+	      error_at (location_of (decl),
+			"declaration of member %qD with array of anonymous "
+			"union type %qT", decl, TREE_TYPE (decl));
+	      inform (DECL_SOURCE_LOCATION (adecl),
+		      "anonymous union declared here");
+	    }
+	  else
+	    {
+	      error_at (location_of (decl),
+			"declaration of member %qD with array of anonymous "
+			"struct type %qT", decl, TREE_TYPE (decl));
+	      inform (DECL_SOURCE_LOCATION (adecl),
+		      "anonymous struct declared here");
+	    }
+	}
     }
 
   if (TREE_CODE (decl) == USING_DECL)
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 12c34ba6b3a7..2d7c65c3a51a 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -151,6 +151,101 @@ abstract_virtuals_error (tree decl, tree type, abstract_class_use use,
      be abstract.  */
   if (!CLASS_TYPE_P (type))
     return 0;
+
+  if (ANON_AGGR_TYPE_P (type))
+    {
+      /* [class.union.anon]/1: Each object of such an unnamed type shall be
+	 such an unnamed object.  */
+      auto_diagnostic_group d;
+      location_t aloc
+	= DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (TYPE_MAIN_VARIANT (type)));
+      if (decl
+	  && VAR_P (decl)
+	  && DECL_NAME (decl) == NULL_TREE
+	  && ANON_UNION_TYPE_P (type))
+	/* Unnamed variables are ok, those are assumed to be the variable
+	   created for namespace scope anonymous union.  For temporaries
+	   even when in the end they create VAR_DECLs with NULL DECL_NAME,
+	   this function is called first with !decl and so invalid code
+	   can be diagnosed below at that point.  */;
+      else if (!(complain & tf_error))
+	return 1;
+      else if (ANON_UNION_TYPE_P (type))
+	{
+	  if (!decl)
+	    switch (use)
+	      {
+	      default:
+		error ("temporary object with anonymous union type %qT", type);
+		break;
+	      case ACU_CATCH:
+		error ("%<catch%> parameter with anonymous union type %qT",
+		       type);
+		break;
+	      case ACU_THROW:
+		error ("%<throw%> operand has anonymous union type %qT", type);
+		break;
+	      case ACU_ARRAY:
+		error ("object with array of anonymous union type %qT", type);
+		break;
+	      }
+	  else if (VAR_P (decl))
+	    error_at (location_of (decl),
+		      "declaration of variable %qD with anonymous union type "
+		      "%qT", decl, type);
+	  else if (TREE_CODE (decl) == PARM_DECL && DECL_NAME (decl))
+	    error_at (location_of (decl),
+		      "declaration of parameter %qD with anonymous union type "
+		      "%qT", decl, type);
+	  else if (TREE_CODE (decl) == PARM_DECL)
+	    error_at (location_of (decl),
+		      "declaration of a parameter with anonymous union type "
+		      "%qT", type);
+	  inform (aloc, "anonymous union declared here");
+	  if (decl)
+	    TREE_TYPE (decl) = error_mark_node;
+	  return 1;
+	}
+      else
+	{
+	  if (!decl)
+	    switch (use)
+	      {
+	      default:
+		error ("temporary object with anonymous struct type %qT",
+		       type);
+		break;
+	      case ACU_CATCH:
+		error ("%<catch%> parameter with anonymous struct type %qT",
+		       type);
+		break;
+	      case ACU_THROW:
+		error ("%<throw%> operand has anonymous struct type %qT",
+		       type);
+		break;
+	      case ACU_ARRAY:
+		error ("object with array of anonymous struct type %qT", type);
+		break;
+	      }
+	  else if (VAR_P (decl))
+	    error_at (location_of (decl),
+		      "declaration of variable %qD with anonymous struct type "
+		      "%qT", decl, type);
+	  else if (TREE_CODE (decl) == PARM_DECL && DECL_NAME (decl))
+	    error_at (location_of (decl),
+		      "declaration of parameter %qD with anonymous struct type "
+		      "%qT", decl, type);
+	  else if (TREE_CODE (decl) == PARM_DECL)
+	    error_at (location_of (decl),
+		      "declaration of a parameter with anonymous struct type "
+		      "%qT", type);
+	  inform (aloc, "anonymous struct declared here");
+	  if (decl)
+	    TREE_TYPE (decl) = error_mark_node;
+	  return 1;
+	}
+    }
+
   type = TYPE_MAIN_VARIANT (type);
 
 #if 0
diff --git a/gcc/testsuite/g++.dg/cpp0x/anon-union4.C b/gcc/testsuite/g++.dg/cpp0x/anon-union4.C
new file mode 100644
index 000000000000..db498009cb32
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/anon-union4.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+struct A { A () = default; A (int); int a; };
+struct B { union { int a; A b; }; };
+
+B *
+foo ()
+{
+  return new B ();
+}
diff --git a/gcc/testsuite/g++.dg/reflect/anon6.C b/gcc/testsuite/g++.dg/reflect/anon6.C
new file mode 100644
index 000000000000..0de8f596794d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/anon6.C
@@ -0,0 +1,105 @@
+// CWG3130 - Naming function members of anonymous unions
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct A { union { int a; long b; }; };	// { dg-message "anonymous union declared here" }
+using U = typename [: parent_of (^^A::a) :];
+U b;					// { dg-error "declaration of variable 'b' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+struct B { U b; };			// { dg-error "declaration of member 'B::b' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+U &plugh ();
+auto c = plugh ();			// { dg-error "declaration of variable 'c' with anonymous union type 'A::<unnamed union>'" }
+					// { dg-error "temporary object with anonymous union type 'A::<unnamed union>'" "" { target *-*-* } .-1 }
+static union { int d; long e; };	// { dg-message "anonymous union declared here" }
+using V = typename [: parent_of (^^e) :];
+V &thud () { throw 1; }
+auto f = thud ();			// { dg-error "declaration of variable 'f' with anonymous union type '<unnamed union>'" }
+					// { dg-error "temporary object with anonymous union type '<unnamed union>'" "" { target *-*-* } .-1 }
+V g;					// { dg-error "declaration of variable 'g' with anonymous union type 'V' {aka '<unnamed union>'}" }
+struct D { V d; };			// { dg-error "declaration of member 'D::d' with anonymous union type 'V' {aka '<unnamed union>'}" }
+struct E { U e[2]; };			// { dg-error "declaration of member 'E::e' with array of anonymous union type 'U \\\[2\\\]' {aka 'A::<unnamed union> \\\[2\\\]'}" }
+struct F { V f[2]; };			// { dg-error "declaration of member 'F::f' with array of anonymous union type 'V \\\[2\\\]' {aka '<unnamed union> \\\[2\\\]'}" }
+struct G { static U g; };		// { dg-error "declaration of member 'G::g' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+struct H { static V h; };		// { dg-error "declaration of member 'H::h' with anonymous union type 'V' {aka '<unnamed union>'}" }
+struct I { static U i[1][2]; };		// { dg-error "declaration of member 'I::i' with array of anonymous union type 'U \\\[1\\\]\\\[2\\\]' {aka 'A::<unnamed union> \\\[1\\\]\\\[2\\\]'}" }
+struct J { static V j[2][1]; };		// { dg-error "declaration of member 'J::j' with array of anonymous union type 'V \\\[2\\\]\\\[1\\\]' {aka '<unnamed union> \\\[2\\\]\\\[1\\\]'}" }
+using UA = U[2];
+using VA = V[3];
+
+void
+foo (U x)				// { dg-error "declaration of parameter 'x' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+{
+  U ({ .a = 1 });			// { dg-error "temporary object with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+}
+
+void
+foo (U &x)
+{
+  throw x;				// { dg-error "'throw' operand has anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+}
+
+void
+bar ()
+{
+  U y;					// { dg-error "declaration of variable 'y' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  U r[4];				// { dg-error "object with array of anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  V s[2];				// { dg-error "object with array of anonymous union type 'V'" }
+  union W { U u; } v;			// { dg-error "declaration of member 'bar\\\(\\\)::W::u' with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  try
+    {
+    }
+  catch (U z)				// { dg-error "'catch' parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+    {
+    }
+  try
+    {
+    }
+  catch (U)				// { dg-error "'catch' parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+    {
+    }
+}
+
+void
+baz (U)					// { dg-error "declaration of a parameter with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+{
+}
+
+long
+garply (const U &x)
+{
+  return x.b;
+}
+
+void
+corge ()
+{
+  garply (U { .b = 42 });		// { dg-error "temporary object with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+}
+
+V *
+xyzzy ()
+{
+  return new V { .e = 42 };		// { dg-error "temporary object with anonymous union type 'V' {aka '<unnamed union>'}" }
+}
+
+void
+foo (V &x)
+{
+  V ({ .d = 1 });			// { dg-error "temporary object with anonymous union type 'V' {aka '<unnamed union>'}" }
+  V v;					// { dg-error "declaration of variable 'v' with anonymous union type 'V' {aka '<unnamed union>'}" }
+  throw x;				// { dg-error "'throw' operand has anonymous union type 'V' {aka '<unnamed union>'}" }
+}
+
+void qux (U);
+void fred (V x);
+void waldo (int, ...);
+
+void
+boo (U &x, V &y)
+{
+  qux (x);				// { dg-error "temporary object with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  fred (y);				// { dg-error "temporary object with anonymous union type 'V' {aka '<unnamed union>'}" }
+  waldo (1, x);				// { dg-error "temporary object with anonymous union type 'U' {aka 'A::<unnamed union>'}" }
+  waldo (2, y);				// { dg-error "temporary object with anonymous union type 'V' {aka '<unnamed union>'}" }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/anon7.C b/gcc/testsuite/g++.dg/reflect/anon7.C
new file mode 100644
index 000000000000..2e546f46d761
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/anon7.C
@@ -0,0 +1,86 @@
+// { dg-do compile { target c++26 } }
+// { dg-options "-freflection" }
+
+#include <meta>
+
+struct A { struct { int a; long b; }; }; // { dg-message "anonymous struct declared here" }
+using U = typename [: parent_of (^^A::a) :];
+U b;					// { dg-error "declaration of variable 'b' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct B { U b; };			// { dg-error "declaration of member 'B::b' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+U &plugh ();
+auto c = plugh ();			// { dg-error "declaration of variable 'c' with anonymous struct type 'A::<unnamed struct>'" }
+					// { dg-error "temporary object with anonymous struct type 'A::<unnamed struct>'" "" { target *-*-* } .-1 }
+U g;					// { dg-error "declaration of variable 'g' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct E { U e[2]; };			// { dg-error "declaration of member 'E::e' with array of anonymous struct type 'U \\\[2\\\]' {aka 'A::<unnamed struct> \\\[2\\\]'}" }
+struct F { static U f; };		// { dg-error "declaration of member 'F::f' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+struct G { static U g[2][42]; };	// { dg-error "declaration of member 'G::g' with array of anonymous struct type 'U \\\[2\\\]\\\[42\\\]' {aka 'A::<unnamed struct> \\\[2\\\]\\\[42\\\]'}" }
+using UA = U[2];
+
+void
+foo (U x)				// { dg-error "declaration of parameter 'x' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+{
+  U ({ .a = 1 });			// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+void
+foo (U &x)
+{
+  throw x;				// { dg-error "'throw' operand has anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+void
+bar ()
+{
+  U y;					// { dg-error "declaration of variable 'y' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  U r[4];				// { dg-error "object with array of anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  union W { U u; } v;			// { dg-error "declaration of member 'bar\\\(\\\)::W::u' with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  try
+    {
+    }
+  catch (U z)				// { dg-error "'catch' parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+    {
+    }
+  try
+    {
+    }
+  catch (U)				// { dg-error "'catch' parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+    {
+    }
+}
+
+void
+baz (U)					// { dg-error "declaration of a parameter with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+{
+}
+
+struct D : public U {};			// { dg-error "base type 'U' {aka 'A::<unnamed struct>'} is anonymous struct type" }
+
+long
+garply (const U &x)
+{
+  return x.b;
+}
+
+void
+corge ()
+{
+  garply (U { .b = 42 });		// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+U *
+xyzzy ()
+{
+  return new U { .b = 42 };		// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
+
+void qux (U);
+void fred (U x);
+void waldo (int, ...);
+
+void
+boo (U &x)
+{
+  qux (x);				// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  fred (x);				// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+  waldo (1, x);				// { dg-error "temporary object with anonymous struct type 'U' {aka 'A::<unnamed struct>'}" }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/anon8.C b/gcc/testsuite/g++.dg/reflect/anon8.C
new file mode 100644
index 000000000000..cf902e1886cd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/anon8.C
@@ -0,0 +1,24 @@
+// CWG3130 - Naming function members of anonymous unions
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+struct A { union { int a; long b; }; };
+using U = typename [: parent_of (^^A::a) :];
+
+long
+foo (U &x)
+{
+  return x.b;
+}
+
+int
+main ()
+{
+  A a;
+  a.b = 42;
+  constexpr auto ctx = std::meta::access_context::unchecked ();
+  if (foo (a.[: members_of (^^A, ctx)[1] :]) != 42)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/reflect/anon9.C b/gcc/testsuite/g++.dg/reflect/anon9.C
new file mode 100644
index 000000000000..f21084d13d5e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/anon9.C
@@ -0,0 +1,24 @@
+// { dg-do run { target c++26 } }
+// { dg-options "-freflection" }
+
+#include <meta>
+
+struct A { struct { int a; long b; }; };
+using U = typename [: parent_of (^^A::a) :];
+
+long
+foo (U &x)
+{
+  return x.a + x.b;
+}
+
+int
+main ()
+{
+  A a;
+  a.a = 2;
+  a.b = 42;
+  constexpr auto ctx = std::meta::access_context::unchecked ();
+  if (foo (a.[: members_of (^^A, ctx)[1] :]) != 44)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/template/anonunion3.C b/gcc/testsuite/g++.dg/template/anonunion3.C
index 6ab7d1007640..1c5a7275c0c3 100644
--- a/gcc/testsuite/g++.dg/template/anonunion3.C
+++ b/gcc/testsuite/g++.dg/template/anonunion3.C
@@ -4,7 +4,7 @@
 extern "C" int printf (const char *, ...);
 
 template<typename T> static char const * f(T *t) {
- T u(*t);
+ T u(*t);			// { dg-error "declaration of variable 'u' with anonymous union type 'main\\\(\\\)::<unnamed union>'" }
  u.x = "hello world";
  printf("%s\n", u.x);
  return "initialized";
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.