diff mbox series

[C++] cleanup check_field_decls

Message ID 46ddbffb-c562-5976-155e-40b1687b239a@acm.org
State New
Headers show
Series [C++] cleanup check_field_decls | expand

Commit Message

Nathan Sidwell Nov. 1, 2019, 12:58 p.m. UTC
In implementing C++20 using enum changes, I wandered into 
check_field_decls, and was confused.

This patch makes it better than it was.
*) the main loop showed signs that we used to remove bad fields.  but we 
don't any more.
*) ... so we can call the field 'field', not 'x'.  (still not perfect, 
but hey, it comes from TYPE_FIELDS.)
*) The 'clever' use of 'continue' to get to the next iteration is now in 
only one place.  Less churn than indenting the entirety of the 
FIELD_DECL case
*) We now note the first default-init and pointer-type field, when 
issuing diagnostics -- better than 'hey, somewhere in here is a field 
with these bad properties'.
*) While grokdeclarator doesn't appear to emit FIELD_DECLS of function 
or method type, we can still get there via template instantiation. 
Added a test case for that, but didn't move the check into pt.c, which 
may be a better place.

committing to trunk.

nathan
diff mbox series

Patch

2019-11-01  Nathan Sidwell  <nathan@acm.org>

	cp/
	* class.c (check_field_decls): Refactor.

	testsuite/
	* g++.dg/template/fn.C: New.

Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	(revision 277705)
+++ gcc/cp/class.c	(working copy)
@@ -3449,8 +3449,5 @@  check_field_decl (tree field,
 
    All of these flags should be initialized before calling this
-   function.
-
-   Returns a pointer to the end of the TYPE_FIELDs chain; additional
-   fields can be added by adding to this chain.  */
+   function.   */
 
 static void
@@ -3459,102 +3456,99 @@  check_field_decls (tree t, tree *access_
 		   int *no_const_asn_ref_p)
 {
-  tree *field;
-  tree *next;
   int cant_pack = 0;
-  int field_access = -1;
 
   /* Assume there are no access declarations.  */
   *access_decls = NULL_TREE;
-  /* Assume this class has no pointer members.  */
-  bool has_pointers = false;
-  /* Assume none of the members of this class have default
-     initializations.  */
-  bool any_default_members = false;
-  /* Assume none of the non-static data members are of non-volatile literal
-     type.  */
+  /* Effective C has things to say about classes with pointer members.  */
+  tree pointer_member = NULL_TREE;
+  /* Default initialized members affect the whole class.  */
+  tree default_init_member = NULL_TREE;
+  /* Lack of any non-static data member of non-volatile literal
+     type affects a union.  */
   bool found_nv_literal_p = false;
+  /* Standard layout requires all FIELDS have same access.  */
+  int field_access = -1;
 
-  for (field = &TYPE_FIELDS (t); *field; field = next)
+  for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field))
     {
-      tree x = *field;
-      tree type = TREE_TYPE (x);
-      int this_field_access;
+      tree type = TREE_TYPE (field);
 
-      next = &DECL_CHAIN (x);
-
-      if (TREE_CODE (x) == USING_DECL)
+      switch (TREE_CODE (field))
 	{
-	  /* Save the access declarations for our caller.  */
-	  *access_decls = tree_cons (NULL_TREE, x, *access_decls);
-	  continue;
-	}
-
-      if (TREE_CODE (x) == TYPE_DECL
-	  || TREE_CODE (x) == TEMPLATE_DECL)
-	continue;
-
-      if (TREE_CODE (x) == FUNCTION_DECL)
-	/* FIXME: We should fold in the checking from check_methods.  */
-	continue;
+	default:
+	  gcc_unreachable ();
 
-      /* If we've gotten this far, it's a data member, possibly static,
-	 or an enumerator.  */
-      if (TREE_CODE (x) != CONST_DECL)
-	DECL_CONTEXT (x) = t;
+	case USING_DECL:
+	  /* Save the access declarations for our caller.  */
+	  *access_decls = tree_cons (NULL_TREE, field, *access_decls);
+	  break;
 
-      /* When this goes into scope, it will be a non-local reference.  */
-      DECL_NONLOCAL (x) = 1;
+	case TYPE_DECL:
+	case TEMPLATE_DECL:
+	  break;
+
+	case FUNCTION_DECL:
+	  /* FIXME: We should fold in the checking from check_methods.  */
+	  break;
+
+	case CONST_DECL:
+	  DECL_NONLOCAL (field) = 1;
+	  break;
+	  
+	case VAR_DECL:
+	  if (TREE_CODE (t) == UNION_TYPE
+	      && cxx_dialect < cxx11)
+	    {
+	      /* [class.union]
 
-      if (TREE_CODE (t) == UNION_TYPE)
-	{
-	  /* [class.union] (C++98)
+		 (C++98) If a union contains a static data member,
+		 ... the program is ill-formed.  */
+	      if (cxx_dialect < cxx11)
+		error ("in C++98 %q+D may not be static because it is "
+		       "a member of a union", field);
+	    }
+	  goto data_member;
+	  
+	case FIELD_DECL:
+	  if (TREE_CODE (t) == UNION_TYPE)
+	    {
+	      /* [class.union]
 
-	     If a union contains a static data member, or a member of
-	     reference type, the program is ill-formed.
+		 If a union contains ... or a [non-static data] member
+		 of reference type, the program is ill-formed.  */
+	      if (TYPE_REF_P (type))
+		error ("non-static data member %q+D in a union may not "
+		       "have reference type %qT", field, type);
+	    }
 
-	     In C++11 [class.union] says:
-	     If a union contains a non-static data member of reference type
-	     the program is ill-formed.  */
-	  if (VAR_P (x) && cxx_dialect < cxx11)
+	data_member:
+	  /* Common VAR_DECL & FIELD_DECL processing.  */
+	  DECL_CONTEXT (field) = t;
+	  DECL_NONLOCAL (field) = 1;
+
+	  /* Template instantiation can cause this.  Perhaps this
+	     should be a specific instantiation check?  */
+	  if (TREE_CODE (type) == FUNCTION_TYPE)
 	    {
-	      error ("in C++98 %q+D may not be static because it is "
-		     "a member of a union", x);
-	      continue;
+	      error ("data member %q+D invalidly declared function type", field);
+	      type = build_pointer_type (type);
+	      TREE_TYPE (field) = type;
 	    }
-	  if (TYPE_REF_P (type)
-	      && TREE_CODE (x) == FIELD_DECL)
+	  else if (TREE_CODE (type) == METHOD_TYPE)
 	    {
-	      error ("non-static data member %q+D in a union may not "
-		     "have reference type %qT", x, type);
-	      continue;
+	      error ("data member %q+D invalidly declared method type", field);
+	      type = build_pointer_type (type);
+	      TREE_TYPE (field) = type;
 	    }
-	}
 
-      /* Perform error checking that did not get done in
-	 grokdeclarator.  */
-      if (TREE_CODE (type) == FUNCTION_TYPE)
-	{
-	  error ("field %q+D invalidly declared function type", x);
-	  type = build_pointer_type (type);
-	  TREE_TYPE (x) = type;
-	}
-      else if (TREE_CODE (type) == METHOD_TYPE)
-	{
-	  error ("field %q+D invalidly declared method type", x);
-	  type = build_pointer_type (type);
-	  TREE_TYPE (x) = type;
+	  break;
 	}
 
-      if (type == error_mark_node)
+      if (TREE_CODE (field) != FIELD_DECL)
 	continue;
 
-      if (TREE_CODE (x) == CONST_DECL || VAR_P (x))
+      if (type == error_mark_node)
 	continue;
 
-      /* Now it can only be a FIELD_DECL.  */
-
-      if (TREE_PRIVATE (x) || TREE_PROTECTED (x))
-	CLASSTYPE_NON_AGGREGATE (t) = 1;
-
       /* If it is not a union and at least one non-static data member is
 	 non-literal, the whole class becomes non-literal.  Per Core/1453,
@@ -3571,13 +3565,21 @@  check_field_decls (tree t, tree *access_
 	}
 
-      /* A standard-layout class is a class that:
-	 ...
-	 has the same access control (Clause 11) for all non-static data members,
-         ...  */
-      this_field_access = TREE_PROTECTED (x) ? 1 : TREE_PRIVATE (x) ? 2 : 0;
-      if (field_access == -1)
-	field_access = this_field_access;
-      else if (this_field_access != field_access)
-	CLASSTYPE_NON_STD_LAYOUT (t) = 1;
+      int this_field_access = (TREE_PROTECTED (field) ? 1
+			       : TREE_PRIVATE (field) ? 2 : 0);
+      if (field_access != this_field_access)
+	{
+	  /* A standard-layout class is a class that:
+
+	     ... has the same access control (Clause 11) for all
+	     non-static data members, */
+	  if (field_access < 0)
+	    field_access = this_field_access;
+	  else
+	    CLASSTYPE_NON_STD_LAYOUT (t) = 1;
+
+	  /* Aggregates must be public.  */
+	  if (this_field_access)
+	    CLASSTYPE_NON_AGGREGATE (t) = 1;
+	}
 
       /* If this is of reference type, check if it needs an init.  */
@@ -3586,5 +3588,5 @@  check_field_decls (tree t, tree *access_
 	  CLASSTYPE_NON_LAYOUT_POD_P (t) = 1;
 	  CLASSTYPE_NON_STD_LAYOUT (t) = 1;
-	  if (DECL_INITIAL (x) == NULL_TREE)
+	  if (DECL_INITIAL (field) == NULL_TREE)
 	    SET_CLASSTYPE_REF_FIELDS_NEED_INIT (t, 1);
 	  if (cxx_dialect < cxx11)
@@ -3605,25 +3607,22 @@  check_field_decls (tree t, tree *access_
 	  if (!layout_pod_type_p (type) && !TYPE_PACKED (type))
 	    {
-	      warning_at
-		(DECL_SOURCE_LOCATION (x), 0,
-		 "ignoring packed attribute because of unpacked non-POD field %q#D",
-		 x);
+	      warning_at (DECL_SOURCE_LOCATION (field), 0,
+			  "ignoring packed attribute because of"
+			  " unpacked non-POD field %q#D", field);
 	      cant_pack = 1;
 	    }
-	  else if (DECL_C_BIT_FIELD (x)
-		   || TYPE_ALIGN (TREE_TYPE (x)) > BITS_PER_UNIT)
-	    DECL_PACKED (x) = 1;
+	  else if (DECL_C_BIT_FIELD (field)
+		   || TYPE_ALIGN (TREE_TYPE (field)) > BITS_PER_UNIT)
+	    DECL_PACKED (field) = 1;
 	}
 
-      if (DECL_C_BIT_FIELD (x)
-	  && integer_zerop (DECL_BIT_FIELD_REPRESENTATIVE (x)))
+      if (DECL_C_BIT_FIELD (field)
+	  && integer_zerop (DECL_BIT_FIELD_REPRESENTATIVE (field)))
 	/* We don't treat zero-width bitfields as making a class
 	   non-empty.  */
 	;
-      else if (field_poverlapping_p (x) && is_empty_class (type))
-	{
-	  /* Empty data members also don't make a class non-empty.  */
-	  CLASSTYPE_CONTAINS_EMPTY_CLASS_P (t) = 1;
-	}
+      else if (field_poverlapping_p (field) && is_empty_class (type))
+	/* Empty data members also don't make a class non-empty.  */
+	CLASSTYPE_CONTAINS_EMPTY_CLASS_P (t) = 1;
       else
 	{
@@ -3632,6 +3631,6 @@  check_field_decls (tree t, tree *access_
 	  /* The class is not even nearly empty.  */
 	  CLASSTYPE_NEARLY_EMPTY_P (t) = 0;
-	  /* If one of the data members contains an empty class,
-	     so does T.  */
+	  /* If one of the data members contains an empty class, so
+	     does T.  */
 	  if (CLASS_TYPE_P (type)
 	      && CLASSTYPE_CONTAINS_EMPTY_CLASS_P (type))
@@ -3644,5 +3643,5 @@  check_field_decls (tree t, tree *access_
       if (TYPE_PTR_P (type)
 	  && !TYPE_PTRFN_P (type))
-	has_pointers = true;
+	pointer_member = field;
 
       if (CLASS_TYPE_P (type))
@@ -3654,21 +3653,15 @@  check_field_decls (tree t, tree *access_
 	}
 
-      if (DECL_MUTABLE_P (x) || TYPE_HAS_MUTABLE_P (type))
+      if (DECL_MUTABLE_P (field) || TYPE_HAS_MUTABLE_P (type))
 	CLASSTYPE_HAS_MUTABLE (t) = 1;
 
-      if (DECL_MUTABLE_P (x))
+      if (DECL_MUTABLE_P (field))
 	{
-	  if (CP_TYPE_CONST_P (type))
-	    {
-	      error ("member %q+D cannot be declared both %<const%> "
-		     "and %<mutable%>", x);
-	      continue;
-	    }
 	  if (TYPE_REF_P (type))
-	    {
-	      error ("member %q+D cannot be declared as a %<mutable%> "
-		     "reference", x);
-	      continue;
-	    }
+	    error ("member %q+D cannot be declared as a %<mutable%> "
+		   "reference", field);
+	  else if (CP_TYPE_CONST_P (type))
+	    error ("member %q+D cannot be declared both %<const%> "
+		   "and %<mutable%>", field);
 	}
 
@@ -3678,5 +3671,5 @@  check_field_decls (tree t, tree *access_
 	CLASSTYPE_NON_LAYOUT_POD_P (t) = 1;
 
-      if (field_poverlapping_p (x))
+      if (field_poverlapping_p (field))
 	/* A potentially-overlapping non-static data member makes the class
 	   non-layout-POD.  */
@@ -3691,13 +3684,19 @@  check_field_decls (tree t, tree *access_
       /* We set DECL_C_BIT_FIELD in grokbitfield.
 	 If the type and width are valid, we'll also set DECL_BIT_FIELD.  */
-      if (DECL_C_BIT_FIELD (x))
-	check_bitfield_decl (x);
+      if (DECL_C_BIT_FIELD (field))
+	check_bitfield_decl (field);
 
-      if (check_field_decl (x, t, cant_have_const_ctor_p, no_const_asn_ref_p))
+      if (check_field_decl (field, t,
+			    cant_have_const_ctor_p, no_const_asn_ref_p))
 	{
-	  if (any_default_members
+	  if (default_init_member
 	      && TREE_CODE (t) == UNION_TYPE)
-	    error ("multiple fields in union %qT initialized", t);
-	  any_default_members = true;
+	    {
+	      error ("multiple fields in union %qT initialized", t);
+	      inform (DECL_SOURCE_LOCATION (default_init_member),
+		      "initialized member %q+D declared here",
+		      default_init_member);
+	    }
+	  default_init_member = field;
 	}
 
@@ -3705,12 +3704,12 @@  check_field_decls (tree t, tree *access_
 	 anything left in DECL_INITIAL is an NSDMI that makes the class
 	 non-aggregate in C++11.  */
-      if (DECL_INITIAL (x) && cxx_dialect < cxx14)
+      if (DECL_INITIAL (field) && cxx_dialect < cxx14)
 	CLASSTYPE_NON_AGGREGATE (t) = true;
 
-      /* If any field is const, the structure type is pseudo-const.  */
       if (CP_TYPE_CONST_P (type))
 	{
+	  /* If any field is const, the structure type is pseudo-const.  */
 	  C_TYPE_FIELDS_READONLY (t) = 1;
-	  if (DECL_INITIAL (x) == NULL_TREE)
+	  if (DECL_INITIAL (field) == NULL_TREE)
 	    SET_CLASSTYPE_READONLY_FIELDS_NEED_INIT (t, 1);
 	  if (cxx_dialect < cxx11)
@@ -3736,8 +3735,8 @@  check_field_decls (tree t, tree *access_
 	 different name from the class iff the class has a
 	 user-declared constructor.  */
-      if (constructor_name_p (DECL_NAME (x), t)
+      if (constructor_name_p (DECL_NAME (field), t)
 	  && TYPE_HAS_USER_CONSTRUCTOR (t))
-	permerror (DECL_SOURCE_LOCATION (x),
-		   "field %q#D with same name as class", x);
+	permerror (DECL_SOURCE_LOCATION (field),
+		   "field %q#D with same name as class", field);
     }
 
@@ -3762,26 +3761,29 @@  check_field_decls (tree t, tree *access_
      This seems enough for practical purposes.  */
   if (warn_ecpp
-      && has_pointers
+      && pointer_member
       && TYPE_HAS_USER_CONSTRUCTOR (t)
       && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t)
       && !(TYPE_HAS_COPY_CTOR (t) && TYPE_HAS_COPY_ASSIGN (t)))
     {
-      warning (OPT_Weffc__, "%q#T has pointer data members", t);
-
-      if (! TYPE_HAS_COPY_CTOR (t))
+      if (warning (OPT_Weffc__, "%q#T has pointer data members", t))
 	{
-	  warning (OPT_Weffc__,
-		   "  but does not override %<%T(const %T&)%>", t, t);
-	  if (!TYPE_HAS_COPY_ASSIGN (t))
-	    warning (OPT_Weffc__, "  or %<operator=(const %T&)%>", t);
-	}
-      else if (! TYPE_HAS_COPY_ASSIGN (t))
-	warning (OPT_Weffc__,
-		 "  but does not override %<operator=(const %T&)%>", t);
+	  if (! TYPE_HAS_COPY_CTOR (t))
+	    {
+	      warning (OPT_Weffc__,
+		       "  but does not override %<%T(const %T&)%>", t, t);
+	      if (!TYPE_HAS_COPY_ASSIGN (t))
+		warning (OPT_Weffc__, "  or %<operator=(const %T&)%>", t);
+	    }
+	  else if (! TYPE_HAS_COPY_ASSIGN (t))
+	    warning (OPT_Weffc__,
+		     "  but does not override %<operator=(const %T&)%>", t);
+	  inform (DECL_SOURCE_LOCATION (pointer_member),
+		  "pointer member %q+D declared here", pointer_member);
+	}
     }
 
   /* Non-static data member initializers make the default constructor
      non-trivial.  */
-  if (any_default_members)
+  if (default_init_member)
     {
       TYPE_NEEDS_CONSTRUCTING (t) = true;
Index: gcc/testsuite/g++.dg/template/fn.C
===================================================================
--- gcc/testsuite/g++.dg/template/fn.C	(revision 0)
+++ gcc/testsuite/g++.dg/template/fn.C	(working copy)
@@ -0,0 +1,10 @@ 
+// instantiation cannot turn a data member into a function!
+
+typedef int (frib) (int);
+
+template <typename T> class X 
+{
+  T v; // { dg-error "declared function" }
+};
+
+X<frib> v;