This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH for c++/55877 (names for linkage purposes)


My earlier patch didn't go far enough; when we apply a name for linkage purposes and fix up the visibility of the type, we also need to fix up the visibility and names of any members and implementation bits.

At first I thought we would need to deal with static data members as well as functions, but it turns out that an unnamed class can't have static data members. So the second patch enforces that better.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit f32d03db15009c0ee07b33bda6083a163bf87fa7
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jan 31 17:39:17 2014 -0500

    	PR c++/55877
    	* decl2.c (no_linkage_error): Handle C++98 semantics.
    	(reset_type_linkage): Move from decl.c.
    	(reset_type_linkage_1, reset_type_linkage_2, bt_reset_linkage_1)
    	(bt_reset_linkage_2, reset_decl_linkage): New.
    	(tentative_decl_linkage): Factor out of expand_or_defer_fn_1.
    	(cp_write_global_declarations): Move condition into no_linkage_error.
    	* decl.c (grokfndecl, grokvardecl): Use no_linkage_error.
    	* semantics.c (expand_or_defer_fn_1): Factor out
    	tentative_decl_linkage.
    	* cp-tree.h: Adjust.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7681b27..3db18f3 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5313,12 +5313,15 @@ extern tree coerce_delete_type			(tree);
 extern void comdat_linkage			(tree);
 extern void determine_visibility		(tree);
 extern void constrain_class_visibility		(tree);
+extern void reset_type_linkage			(tree);
+extern void tentative_decl_linkage		(tree);
 extern void import_export_decl			(tree);
 extern tree build_cleanup			(tree);
 extern tree build_offset_ref_call_from_tree	(tree, vec<tree, va_gc> **,
 						 tsubst_flags_t);
 extern bool decl_constant_var_p			(tree);
 extern bool decl_maybe_constant_var_p		(tree);
+extern void no_linkage_error			(tree);
 extern void check_default_args			(tree);
 extern bool mark_used				(tree);
 extern bool mark_used			        (tree, tsubst_flags_t);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 04c4cf5..db86d97 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -7569,29 +7569,7 @@ grokfndecl (tree ctype,
 	 declare an entity with linkage.
 
 	 DR 757 relaxes this restriction for C++0x.  */
-      t = no_linkage_check (TREE_TYPE (decl),
-			    /*relaxed_p=*/false);
-      if (t)
-	{
-	  if (TYPE_ANONYMOUS_P (t))
-	    {
-	      if (DECL_EXTERN_C_P (decl))
-		/* Allow this; it's pretty common in C.  */;
-	      else
-		{
-		  permerror (input_location, "anonymous type with no linkage "
-			     "used to declare function %q#D with linkage",
-			     decl);
-		  if (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))
-		    permerror (input_location, "%q+#D does not refer to the unqualified "
-			       "type, so it is not used for linkage",
-			       TYPE_NAME (t));
-		}
-	    }
-	  else
-	    permerror (input_location, "type %qT with no linkage used to "
-		       "declare function %q#D with linkage", t, decl);
-	}
+      no_linkage_error (decl);
     }
 
   TREE_PUBLIC (decl) = publicp;
@@ -7874,7 +7852,7 @@ set_linkage_for_static_data_member (tree decl)
 
    If SCOPE is non-NULL, it is the class type or namespace containing
    the variable.  If SCOPE is NULL, the variable should is created in
-   the innermost enclosings scope.  */
+   the innermost enclosing scope.  */
 
 static tree
 grokvardecl (tree type,
@@ -7972,33 +7950,8 @@ grokvardecl (tree type,
 	 declare an entity with linkage.
 
 	 DR 757 relaxes this restriction for C++0x.  */
-      tree t = (cxx_dialect > cxx98 ? NULL_TREE
-		: no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false));
-      if (t)
-	{
-	  if (TYPE_ANONYMOUS_P (t))
-	    {
-	      if (DECL_EXTERN_C_P (decl))
-		/* Allow this; it's pretty common in C.  */
-		;
-	      else
-		{
-		  /* DRs 132, 319 and 389 seem to indicate types with
-		     no linkage can only be used to declare extern "C"
-		     entities.  Since it's not always an error in the
-		     ISO C++ 90 Standard, we only issue a warning.  */
-		  warning (0, "anonymous type with no linkage used to declare "
-			   "variable %q#D with linkage", decl);
-		  if (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))
-		    warning (0, "%q+#D does not refer to the unqualified "
-			     "type, so it is not used for linkage",
-			     TYPE_NAME (t));
-		}
-	    }
-	  else
-	    warning (0, "type %qT with no linkage used to declare variable "
-		     "%q#D with linkage", t, decl);
-	}
+      if (cxx_dialect < cxx11)
+	no_linkage_error (decl);
     }
   else
     DECL_INTERFACE_KNOWN (decl) = 1;
@@ -8670,23 +8623,6 @@ check_var_type (tree identifier, tree type)
   return type;
 }
 
-/* Functions for adjusting the visibility of a tagged type and its nested
-   types when it gets a name for linkage purposes from a typedef.  */
-
-static void bt_reset_linkage (binding_entry, void *);
-static void
-reset_type_linkage (tree type)
-{
-  set_linkage_according_to_type (type, TYPE_MAIN_DECL (type));
-  if (CLASS_TYPE_P (type))
-    binding_table_foreach (CLASSTYPE_NESTED_UTDS (type), bt_reset_linkage, NULL);
-}
-static void
-bt_reset_linkage (binding_entry b, void */*data*/)
-{
-  reset_type_linkage (b->type);
-}
-
 /* Given declspecs and a declarator (abstract or otherwise), determine
    the name and type of the object declared and construct a DECL node
    for it.
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index f512541..1afe16e 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -2475,6 +2475,125 @@ constrain_class_visibility (tree type)
     }
 }
 
+/* Functions for adjusting the visibility of a tagged type and its nested
+   types and declarations when it gets a name for linkage purposes from a
+   typedef.  */
+
+static void bt_reset_linkage_1 (binding_entry, void *);
+static void bt_reset_linkage_2 (binding_entry, void *);
+
+/* First reset the visibility of all the types.  */
+
+static void
+reset_type_linkage_1 (tree type)
+{
+  set_linkage_according_to_type (type, TYPE_MAIN_DECL (type));
+  if (CLASS_TYPE_P (type))
+    binding_table_foreach (CLASSTYPE_NESTED_UTDS (type),
+			   bt_reset_linkage_1, NULL);
+}
+static void
+bt_reset_linkage_1 (binding_entry b, void */*data*/)
+{
+  reset_type_linkage_1 (b->type);
+}
+
+/* Then reset the visibility of any static data members or member
+   functions that use those types.  */
+
+static void
+reset_decl_linkage (tree decl)
+{
+  if (TREE_PUBLIC (decl))
+    return;
+  if (DECL_CLONED_FUNCTION_P (decl))
+    return;
+  TREE_PUBLIC (decl) = true;
+  DECL_INTERFACE_KNOWN (decl) = false;
+  determine_visibility (decl);
+  tentative_decl_linkage (decl);
+}
+static void
+reset_type_linkage_2 (tree type)
+{
+  if (CLASS_TYPE_P (type))
+    {
+      if (tree vt = CLASSTYPE_VTABLES (type))
+	{
+	  tree name = mangle_vtbl_for_type (type);
+	  DECL_NAME (vt) = name;
+	  SET_DECL_ASSEMBLER_NAME (vt, name);
+	  reset_decl_linkage (vt);
+	}
+      if (tree ti = CLASSTYPE_TYPEINFO_VAR (type))
+	{
+	  tree name = mangle_typeinfo_for_type (type);
+	  DECL_NAME (ti) = name;
+	  SET_DECL_ASSEMBLER_NAME (ti, name);
+	  TREE_TYPE (name) = type;
+	  reset_decl_linkage (ti);
+	}
+      for (tree m = TYPE_FIELDS (type); m; m = DECL_CHAIN (m))
+	if (TREE_CODE (m) == VAR_DECL)
+	  reset_decl_linkage (m);
+      for (tree m = TYPE_METHODS (type); m; m = DECL_CHAIN (m))
+	reset_decl_linkage (m);
+      binding_table_foreach (CLASSTYPE_NESTED_UTDS (type),
+			     bt_reset_linkage_2, NULL);
+    }
+}
+static void
+bt_reset_linkage_2 (binding_entry b, void */*data*/)
+{
+  reset_type_linkage_2 (b->type);
+}
+void
+reset_type_linkage (tree type)
+{
+  reset_type_linkage_1 (type);
+  reset_type_linkage_2 (type);
+}
+
+/* Set up our initial idea of what the linkage of DECL should be.  */
+
+void
+tentative_decl_linkage (tree decl)
+{
+  if (DECL_INTERFACE_KNOWN (decl))
+    /* We've already made a decision as to how this function will
+       be handled.  */;
+  else if (vague_linkage_p (decl))
+    {
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	{
+	  DECL_EXTERNAL (decl) = 1;
+	  DECL_NOT_REALLY_EXTERN (decl) = 1;
+	  note_vague_linkage_fn (decl);
+	  /* A non-template inline function with external linkage will
+	     always be COMDAT.  As we must eventually determine the
+	     linkage of all functions, and as that causes writes to
+	     the data mapped in from the PCH file, it's advantageous
+	     to mark the functions at this point.  */
+	  if (DECL_DECLARED_INLINE_P (decl)
+	      && (!DECL_IMPLICIT_INSTANTIATION (decl)
+		  || DECL_DEFAULTED_FN (decl)))
+	    {
+	      /* This function must have external linkage, as
+		 otherwise DECL_INTERFACE_KNOWN would have been
+		 set.  */
+	      gcc_assert (TREE_PUBLIC (decl));
+	      comdat_linkage (decl);
+	      DECL_INTERFACE_KNOWN (decl) = 1;
+	    }
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (decl) == VAR_DECL);
+	  maybe_commonize_var (decl);
+	}
+    }
+}
+
 /* DECL is a FUNCTION_DECL or VAR_DECL.  If the object file linkage
    for DECL has not already been determined, do so now by setting
    DECL_EXTERNAL, DECL_COMDAT and other related flags.  Until this
@@ -3966,23 +4085,57 @@ decl_maybe_constant_var_p (tree decl)
 	  && INTEGRAL_OR_ENUMERATION_TYPE_P (type));
 }
 
-/* Complain that DECL uses a type with no linkage but is never defined.  */
+/* Complain that DECL uses a type with no linkage.  In C++98 mode this is
+   called from grokfndecl and grokvardecl; in all modes it is called from
+   cp_write_global_declarations.  */
 
-static void
+void
 no_linkage_error (tree decl)
 {
+  if (cxx_dialect >= cxx11 && decl_defined_p (decl))
+    /* In C++11 it's ok if the decl is defined.  */
+    return;
   tree t = no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/false);
-  if (TYPE_ANONYMOUS_P (t))
+  if (t == NULL_TREE)
+    /* The type that got us on no_linkage_decls must have gotten a name for
+       linkage purposes.  */;
+  else if (CLASS_TYPE_P (t) && TYPE_BEING_DEFINED (t))
+    /* The type might end up having a typedef name for linkage purposes.  */
+    vec_safe_push (no_linkage_decls, decl);
+  else if (TYPE_ANONYMOUS_P (t))
     {
-      permerror (0, "%q+#D, declared using anonymous type, "
-		 "is used but never defined", decl);
-      if (is_typedef_decl (TYPE_NAME (t)))
-	permerror (0, "%q+#D does not refer to the unqualified type, "
-		   "so it is not used for linkage", TYPE_NAME (t));
+      bool d = false;
+      if (cxx_dialect >= cxx11)
+	d = permerror (DECL_SOURCE_LOCATION (decl), "%q#D, declared using "
+		       "anonymous type, is used but never defined", decl);
+      else if (DECL_EXTERN_C_P (decl))
+	/* Allow this; it's pretty common in C.  */;
+      else if (TREE_CODE (decl) == VAR_DECL)
+	/* DRs 132, 319 and 389 seem to indicate types with
+	   no linkage can only be used to declare extern "C"
+	   entities.  Since it's not always an error in the
+	   ISO C++ 90 Standard, we only issue a warning.  */
+	d = warning_at (DECL_SOURCE_LOCATION (decl), 0, "anonymous type "
+			"with no linkage used to declare variable %q#D with "
+			"linkage", decl);
+      else
+	d = permerror (DECL_SOURCE_LOCATION (decl), "anonymous type with no "
+		       "linkage used to declare function %q#D with linkage",
+		       decl);
+      if (d && is_typedef_decl (TYPE_NAME (t)))
+	inform (DECL_SOURCE_LOCATION (TYPE_NAME (t)), "%q#D does not refer "
+		"to the unqualified type, so it is not used for linkage",
+		TYPE_NAME (t));
     }
+  else if (cxx_dialect >= cxx11)
+    permerror (DECL_SOURCE_LOCATION (decl), "%q#D, declared using local type "
+	       "%qT, is used but never defined", decl, t);
+  else if (TREE_CODE (decl) == VAR_DECL)
+    warning_at (DECL_SOURCE_LOCATION (decl), 0, "type %qT with no linkage "
+		"used to declare variable %q#D with linkage", t, decl);
   else
-    permerror (0, "%q+#D, declared using local type %qT, "
-	       "is used but never defined", decl, t);
+    permerror (DECL_SOURCE_LOCATION (decl), "type %qT with no linkage used "
+	       "to declare function %q#D with linkage", t, decl);
 }
 
 /* Collect declarations from all namespaces relevant to SOURCE_FILE.  */
@@ -4407,8 +4560,7 @@ cp_write_global_declarations (void)
 
   /* So must decls that use a type with no linkage.  */
   FOR_EACH_VEC_SAFE_ELT (no_linkage_decls, i, decl)
-    if (!decl_defined_p (decl))
-      no_linkage_error (decl);
+    no_linkage_error (decl);
 
   /* Then, do the Objective-C stuff.  This is where all the
      Objective-C module stuff gets generated (symtab,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 85d6807..9c1c29d 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3977,25 +3977,7 @@ expand_or_defer_fn_1 (tree fn)
 	/* We've already made a decision as to how this function will
 	   be handled.  */;
       else if (!at_eof)
-	{
-	  DECL_EXTERNAL (fn) = 1;
-	  DECL_NOT_REALLY_EXTERN (fn) = 1;
-	  note_vague_linkage_fn (fn);
-	  /* A non-template inline function with external linkage will
-	     always be COMDAT.  As we must eventually determine the
-	     linkage of all functions, and as that causes writes to
-	     the data mapped in from the PCH file, it's advantageous
-	     to mark the functions at this point.  */
-	  if (!DECL_IMPLICIT_INSTANTIATION (fn) || DECL_DEFAULTED_FN (fn))
-	    {
-	      /* This function must have external linkage, as
-		 otherwise DECL_INTERFACE_KNOWN would have been
-		 set.  */
-	      gcc_assert (TREE_PUBLIC (fn));
-	      comdat_linkage (fn);
-	      DECL_INTERFACE_KNOWN (fn) = 1;
-	    }
-	}
+	tentative_decl_linkage (fn);
       else
 	import_export_decl (fn);
 
diff --git a/gcc/testsuite/g++.dg/abi/anon2.C b/gcc/testsuite/g++.dg/abi/anon2.C
new file mode 100644
index 0000000..5d72d8e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/anon2.C
@@ -0,0 +1,65 @@
+// PR c++/55877
+
+namespace N1 {
+  typedef struct {
+    typedef enum { X, Y } A;
+    typedef struct { } B;
+    struct C {
+      // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN2N11D1C3fn1ENS0_1BE" } }
+      static void fn1 (B) { }
+      // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN2N11D1C3fn2ES1_" } }
+      static void fn2 (C) { }
+    };
+  } D;
+
+  void *p = (void *) D::C::fn1;
+  void *q = (void *) D::C::fn2;
+}
+
+namespace N2 {
+  typedef struct {
+    typedef enum { X, Y } A;
+    typedef struct { } B;
+    struct C {
+      // { dg-final { scan-assembler-not ".weak\(_definition\)?\[ \t\]_?_ZN2N23._31C3fn1ENS0_1BE" } }
+      static void fn1 (B) { } // { dg-error "no linkage" "" { target c++98 } }
+      // { dg-final { scan-assembler-not ".weak\(_definition\)?\[ \t\]_?_ZN2N23._31C3fn2ES1_" } }
+      static void fn2 (C) { } // { dg-error "no linkage" "" { target c++98 } }
+    };
+  } const D;
+
+  void *p = (void *) D::C::fn1;
+  void *q = (void *) D::C::fn2;
+}
+
+namespace N3 {
+  typedef struct {
+    typedef enum { X, Y } A;
+    typedef struct { } B;
+    template <class T> struct C {
+      // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN2N31D1CIiE3fn1ENS0_1BE" } }
+      static void fn1 (B) { }
+      // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN2N31D1CIiE3fn2ES2_" } }
+      static void fn2 (C) { }
+    };
+  } D;
+
+  void *p = (void *) D::C<int>::fn1;
+  void *q = (void *) D::C<int>::fn2;
+}
+
+namespace N4 {
+  typedef struct {
+    typedef enum { X, Y } A;
+    typedef struct { } B;
+    template <class T> struct C {
+      // { dg-final { scan-assembler-not ".weak\(_definition\)?\[ \t\]_?_ZN2N43._91CIiE3fn1ENS0_1BE" } }
+      static void fn1 (B) { } // { not-dg-error "no linkage" "" { target c++98 } }
+      // { dg-final { scan-assembler-not ".weak\(_definition\)?\[ \t\]_?_ZN2N43._91CIiE3fn2ES2_" } }
+      static void fn2 (C) { } // { not-dg-error "no linkage" "" { target c++98 } }
+    };
+  } const D;
+
+  void *p = (void *) D::C<int>::fn1;
+  void *q = (void *) D::C<int>::fn2;
+}
diff --git a/gcc/testsuite/g++.dg/abi/anon3.C b/gcc/testsuite/g++.dg/abi/anon3.C
new file mode 100644
index 0000000..38bc145
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/anon3.C
@@ -0,0 +1,17 @@
+typedef struct {
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN4Heya4blahEv" } }
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZTI4Heya" } }
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZTV4Heya" } }
+  virtual const char *blah() {
+    return "Heya::blah";
+  }
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZN4Heya1A1fEv" } }
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZTIN4Heya1AE" } }
+  // { dg-final { scan-assembler ".weak\(_definition\)?\[ \t\]_?_ZTVN4Heya1AE" } }
+  struct A {
+    virtual void f() { }
+  };
+} Heya;
+
+Heya h;
+Heya::A a;
diff --git a/gcc/testsuite/g++.old-deja/g++.oliva/linkage1.C b/gcc/testsuite/g++.old-deja/g++.oliva/linkage1.C
index 23295ea..6c4874b 100644
--- a/gcc/testsuite/g++.old-deja/g++.oliva/linkage1.C
+++ b/gcc/testsuite/g++.old-deja/g++.oliva/linkage1.C
@@ -11,8 +11,6 @@
 // checking that another translation unit can call it.  We don't do
 // the right things on functions, but we do on data members.
 
-// { dg-bogus "" "" { xfail *-*-* } 0 }
-
 typedef struct {
   void f();
 } S;
diff --git a/gcc/testsuite/g++.old-deja/g++.other/anon9.C b/gcc/testsuite/g++.old-deja/g++.other/anon9.C
index d458b6b..acb4f46 100644
--- a/gcc/testsuite/g++.old-deja/g++.other/anon9.C
+++ b/gcc/testsuite/g++.old-deja/g++.other/anon9.C
@@ -2,5 +2,5 @@
 // Test that we properly diagnose an attempt to use an anonymous class
 // in declaring an external function.
 
-typedef const struct { int i; } T; // { dg-error "" } referenced below
+typedef const struct { int i; } T; // { dg-message "" } referenced below
 void f (T* t);			// { dg-error "" } uses unnamed type
commit 8bf3ffa515e08c9fb815e6663c16acd39af9056a
Author: Jason Merrill <jason@redhat.com>
Date:   Sun Feb 9 19:31:28 2014 -0500

    	* decl2.c (finish_static_data_member_decl): Diagnose static data
    	member in unnamed class.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index e861e4d..f61dc9d 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -2851,7 +2851,9 @@ finish_struct_anon_r (tree field, bool complain)
 
       if (TREE_CODE (elt) != FIELD_DECL)
 	{
-	  if (complain)
+	  /* We already complained about static data members in
+	     finish_static_data_member_decl.  */
+	  if (complain && TREE_CODE (elt) != VAR_DECL)
 	    {
 	      if (is_union)
 		permerror (input_location,
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 35707a0..f512541 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -802,6 +802,17 @@ finish_static_data_member_decl (tree decl,
       && !DECL_TEMPLATE_INSTANTIATION (decl))
     permerror (input_location, "local class %q#T shall not have static data member %q#D",
 	       current_class_type, decl);
+  else
+    for (tree t = current_class_type; TYPE_P (t);
+	 t = CP_TYPE_CONTEXT (t))
+      if (TYPE_ANONYMOUS_P (t))
+	{
+	  if (permerror (DECL_SOURCE_LOCATION (decl),
+			 "static data member %qD in unnamed class", decl))
+	    inform (DECL_SOURCE_LOCATION (TYPE_NAME (t)),
+		    "unnamed class defined here");
+	  break;
+	}
 
   DECL_IN_AGGR_P (decl) = 1;
 
diff --git a/gcc/testsuite/g++.dg/ext/anon-struct5.C b/gcc/testsuite/g++.dg/ext/anon-struct5.C
index 8b697cc..ec02225 100644
--- a/gcc/testsuite/g++.dg/ext/anon-struct5.C
+++ b/gcc/testsuite/g++.dg/ext/anon-struct5.C
@@ -2,12 +2,12 @@
 
 struct A
 {
-  struct { static int i; }; // { dg-error "prohibits anonymous structs|an anonymous struct" }
+  struct { static int i; }; // { dg-error "prohibits anonymous structs|an anonymous struct|unnamed class" }
   void foo() { i; }
 };
 
 struct B
 {
-  union { static int i; }; // { dg-error "an anonymous union|member of a union" }
+  union { static int i; }; // { dg-error "an anonymous union|member of a union|unnamed class" }
   void foo() { i; }
 };
diff --git a/gcc/testsuite/g++.dg/ext/anon-struct6.C b/gcc/testsuite/g++.dg/ext/anon-struct6.C
index 11a7bbd..66d4b32 100644
--- a/gcc/testsuite/g++.dg/ext/anon-struct6.C
+++ b/gcc/testsuite/g++.dg/ext/anon-struct6.C
@@ -4,7 +4,7 @@ struct A
 {
   struct
   {  // { dg-error "anonymous struct cannot have function members" }
-    struct { static int i; }; // { dg-error "prohibits anonymous structs|non-static data members" } 
+    struct { static int i; }; // { dg-error "prohibits anonymous structs|non-static data members|unnamed class" }
     void foo() { i; }
   }; // { dg-error "prohibits anonymous structs" }
 };
diff --git a/gcc/testsuite/g++.dg/parse/unnamed1.C b/gcc/testsuite/g++.dg/parse/unnamed1.C
new file mode 100644
index 0000000..f5972f1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/unnamed1.C
@@ -0,0 +1,6 @@
+// 9.4.2/4: Unnamed classes and classes contained directly or indirectly
+// within unnamed classes shall not contain static data members.
+
+typedef struct {		// { dg-message "unnamed" }
+  static int i;			// { dg-error "static data member" }
+} A;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]