re PR c++/2204 (G++ doesn't check (member) function parameter for abstract-ness.)
authorGiovanni Bajo <giovannibajo@gcc.gnu.org>
Mon, 12 Jul 2004 10:07:30 +0000 (10:07 +0000)
committerGiovanni Bajo <giovannibajo@gcc.gnu.org>
Mon, 12 Jul 2004 10:07:30 +0000 (10:07 +0000)
PR c++/2204
* config-lang.in (gtfiles): Add typeck2.c.
* Make-lang.in: Tweak typeck2.c dependencies, and add rule for
gt-cp-typeck2.h.
* cp-tree.h: Declare complete_type_check_abstract.
* typeck2.c (pat_calc_hash, pat_compare,
complete_type_check_abstract): New functions.
(abstract_virtuals_error): If the type is abstract, register the
declaration within abstract_pending_vars for further checks.
Inspect also dependent types. Handle IDENTIFIER_NODEs as decl.
* decl.c (cp_finish_decl): Do not strip array types.
(create_array_type_for_decl): Check for abstractness of the element
type.
(complete_vars): Call complete_type_check_abstract.
* class.c (finish_struct): Prepare a list of virtual functions for
template types, and call complete_vars on it to check for abstractness.

PR c++/2204
* g++.dg/other/abstract2.C: New test.

From-SVN: r84552

gcc/cp/ChangeLog
gcc/cp/Make-lang.in
gcc/cp/class.c
gcc/cp/config-lang.in
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/typeck2.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/other/abstract2.C [new file with mode: 0644]

index 099b1bbb87bc78dce7e893ef2793651543956dd5..4b80aa6d5aa48a9269e3b175320497af1211585a 100644 (file)
@@ -1,3 +1,22 @@
+2004-07-12  Giovanni Bajo  <giovannibajo@gcc.gnu.org>
+
+       PR c++/2204
+       * config-lang.in (gtfiles): Add typeck2.c.
+       * Make-lang.in: Tweak typeck2.c dependencies, and add rule for
+       gt-cp-typeck2.h.
+       * cp-tree.h: Declare complete_type_check_abstract.
+       * typeck2.c (pat_calc_hash, pat_compare,
+       complete_type_check_abstract): New functions.
+       (abstract_virtuals_error): If the type is abstract, register the
+       declaration within abstract_pending_vars for further checks.
+       Inspect also dependent types. Handle IDENTIFIER_NODEs as decl.
+       * decl.c (cp_finish_decl): Do not strip array types.
+       (create_array_type_for_decl): Check for abstractness of the element
+       type.
+       (complete_vars): Call complete_type_check_abstract.
+       * class.c (finish_struct): Prepare a list of virtual functions for
+       template types, and call complete_vars on it to check for abstractness.
+
 2004-07-12  Paolo Bonzini  <bonzini@gnu.org>
 
        PR tree-optimization/14107
index 864b51a96554c188c7cf8c6ea465da594af16f34..1ce05dac03c95f5a60e3a70e235a05ef811e09d7 100644 (file)
@@ -99,7 +99,7 @@ $(srcdir)/cp/cfns.h: $(srcdir)/cp/cfns.gperf
 
 gtype-cp.h gt-cp-call.h gt-cp-decl.h gt-cp-decl2.h : s-gtype; @true
 gt-cp-pt.h gt-cp-repo.h gt-cp-parser.h gt-cp-method.h : s-gtype; @true
-gt-cp-tree.h gt-cp-mangle.h gt-cp-name-lookup.h: s-gtype; @true
+gt-cp-tree.h gt-cp-mangle.h gt-cp-name-lookup.h gt-cp-typeck2.h: s-gtype; @true
 
 #\f
 # Build hooks:
@@ -231,7 +231,7 @@ cp/decl.o: cp/decl.c $(CXX_TREE_H) $(TM_H) flags.h cp/decl.h stack.h \
 cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/decl.h $(EXPR_H) \
   output.h except.h toplev.h $(RTL_H) c-common.h gt-cp-decl2.h cgraph.h
 cp/typeck2.o: cp/typeck2.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h output.h $(TM_P_H) \
-   diagnostic.h
+   diagnostic.h gt-cp-typeck2.h
 cp/typeck.o: cp/typeck.c $(CXX_TREE_H) $(TM_H) flags.h $(RTL_H) $(EXPR_H) toplev.h \
    diagnostic.h convert.h
 cp/class.o: cp/class.c $(CXX_TREE_H) $(TM_H) flags.h toplev.h $(RTL_H) $(TARGET_H) convert.h
index f13ccac378bc9eaba1dd5f49a574c05930f55d84..080e5e9bfa9c19cd7f722f27e388bd25ae52cb95 100644 (file)
@@ -5270,8 +5270,24 @@ finish_struct (tree t, tree attributes)
 
   if (processing_template_decl)
     {
+      tree x;
+
       finish_struct_methods (t);
       TYPE_SIZE (t) = bitsize_zero_node;
+
+      /* We need to emit an error message if this type was used as a parameter
+        and it is an abstract type, even if it is a template. We construct
+        a simple CLASSTYPE_PURE_VIRTUALS list without taking bases into
+        account and we call complete_vars with this type, which will check
+        the PARM_DECLS. Note that while the type is being defined,
+        CLASSTYPE_PURE_VIRTUALS contains the list of the inline friends
+        (see CLASSTYPE_INLINE_FRIENDS) so we need to clear it.  */
+      CLASSTYPE_PURE_VIRTUALS (t) = NULL_TREE;
+      for (x = TYPE_METHODS (t); x; x = TREE_CHAIN (x))
+       if (DECL_PURE_VIRTUAL_P (x))
+         CLASSTYPE_PURE_VIRTUALS (t)
+           = tree_cons (NULL_TREE, x, CLASSTYPE_PURE_VIRTUALS (t));
+      complete_vars (t);
     }
   else
     finish_struct_1 (t);
index 0d0c38f0f4290b41efe9ca6a907760e84357ae2a..563cfb4d1c02e231aa64b30b0c29deca53827336 100644 (file)
@@ -34,4 +34,4 @@ stagestuff="g++\$(exeext) g++-cross\$(exeext) cc1plus\$(exeext)"
 
 target_libs="target-libstdc++-v3 target-gperf"
 
-gtfiles="\$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-lex.c \$(srcdir)/c-pragma.c"
+gtfiles="\$(srcdir)/cp/mangle.c \$(srcdir)/cp/name-lookup.h \$(srcdir)/cp/name-lookup.c \$(srcdir)/cp/cp-tree.h \$(srcdir)/cp/decl.h \$(srcdir)/cp/call.c \$(srcdir)/cp/decl.c \$(srcdir)/cp/decl2.c \$(srcdir)/cp/pt.c \$(srcdir)/cp/repo.c \$(srcdir)/cp/semantics.c \$(srcdir)/cp/tree.c \$(srcdir)/cp/parser.c \$(srcdir)/cp/method.c \$(srcdir)/cp/typeck2.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-lex.c \$(srcdir)/c-pragma.c"
index 6cfe2805caa3f8fab51f04605eaf345cc75ead77..500ad112f45dab5badbf3af44cde6cb04a4ed51c 100644 (file)
@@ -4315,6 +4315,7 @@ extern void cxx_incomplete_type_error             (tree, tree);
 extern tree error_not_base_type                        (tree, tree);
 extern tree binfo_or_else                      (tree, tree);
 extern void readonly_error                     (tree, const char *, int);
+extern void complete_type_check_abstract       (tree);
 extern int abstract_virtuals_error             (tree, tree);
 
 extern tree store_init_value                   (tree, tree);
index ab5dd84531a224ba32fa3746a482b3bb9839d9bd..cc905eb251120321212493bb0f3c1b7f0a809c35 100644 (file)
@@ -4821,21 +4821,12 @@ cp_finish_decl (tree decl, tree init, tree asmspec_tree, int flags)
 
       make_rtl_for_nonlocal_decl (decl, init, asmspec);
 
+      /* Check for abstractness of the type. Notice that there is no
+        need to strip array types here since the check for those types
+        is already done within create_array_type_for_decl.  */
       if (TREE_CODE (type) == FUNCTION_TYPE
          || TREE_CODE (type) == METHOD_TYPE)
-       abstract_virtuals_error (decl,
-                                strip_array_types (TREE_TYPE (type)));
-      else if (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)
-      {
-       /* If it's either a pointer or an array type, strip through all
-          of them but the last one. If the last is an array type, issue 
-          an error if the element type is abstract.  */
-       while (POINTER_TYPE_P (TREE_TYPE (type)) 
-              || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
-         type = TREE_TYPE (type);
-       if (TREE_CODE (type) == ARRAY_TYPE)
-         abstract_virtuals_error (decl, TREE_TYPE (type));
-      }
+       abstract_virtuals_error (decl, TREE_TYPE (type));
       else
        abstract_virtuals_error (decl, type);
 
@@ -6162,6 +6153,11 @@ create_array_type_for_decl (tree name, tree type, tree size)
   if (size)
     itype = compute_array_index_type (name, size);
 
+  /* [dcl.array]
+     T is called the array element type; this type shall not be [...] an
+     abstract class type.  */
+  abstract_virtuals_error (name, type);
+
   return build_cplus_array_type (type, itype);
 }
 
@@ -10592,6 +10588,9 @@ complete_vars (tree type)
       else
        list = &TREE_CHAIN (*list);
     }
+
+  /* Check for pending declarations which may have abstract type.  */
+  complete_type_check_abstract (type);
 }
 
 /* If DECL is of a type which needs a cleanup, build that cleanup
index 5ddfdd5bcee764c1b84d3f4f15f65b00df090189..6e32377c4a35f70c0eae3ed42f8359bb761fed6c 100644 (file)
@@ -114,6 +114,119 @@ readonly_error (tree arg, const char* string, int soft)
     (*fn) ("%s of read-only location", string);
 }
 
+\f
+/* Structure that holds information about declarations whose type was
+   incomplete and we could not check whether it was abstract or not.  */
+
+struct pending_abstract_type GTY((chain_next ("%h.next")))
+{
+  /* Declaration which we are checking for abstractness. It is either
+     a DECL node, or an IDENTIFIER_NODE if we do not have a full
+     declaration available.  */
+  tree decl;
+
+  /* Type which will be checked for abstractness.  */
+  tree type;
+
+  /* Position of the declaration. This is only needed for IDENTIFIER_NODEs,
+     because DECLs already carry locus information.  */
+  location_t locus;
+
+  /* Link to the next element in list.  */
+  struct pending_abstract_type* next;
+};
+
+
+/* Compute the hash value of the node VAL. This function is used by the
+   hash table abstract_pending_vars.  */
+
+static hashval_t
+pat_calc_hash (const void* val)
+{
+  const struct pending_abstract_type* pat = val;
+  return (hashval_t) TYPE_UID (pat->type);
+}
+
+
+/* Compare node VAL1 with the type VAL2. This function is used by the
+   hash table abstract_pending_vars.  */
+
+static int
+pat_compare (const void* val1, const void* val2)
+{
+  const struct pending_abstract_type* pat1 = val1;
+  tree type2 = (tree)val2;
+
+  return (pat1->type == type2);
+}
+
+/* Hash table that maintains pending_abstract_type nodes, for which we still
+   need to check for type abstractness.  The key of the table is the type
+   of the declaration.  */
+static GTY ((param_is (struct pending_abstract_type)))
+htab_t abstract_pending_vars = NULL;
+
+
+/* This function is called after TYPE is completed, and will check if there
+   are pending declarations for which we still need to verify the abstractness
+   of TYPE, and emit a diagnostic (through abstract_virtuals_error) if TYPE
+   turned out to be incomplete.  */
+
+void
+complete_type_check_abstract (tree type)
+{
+  void **slot;
+  struct pending_abstract_type *pat;
+  location_t cur_loc = input_location;
+
+  my_friendly_assert (COMPLETE_TYPE_P (type), 20040620_3);
+
+  if (!abstract_pending_vars)
+    return;
+
+  /* Retrieve the list of pending declarations for this type.  */
+  slot = htab_find_slot_with_hash (abstract_pending_vars, type,
+                                  (hashval_t)TYPE_UID (type), NO_INSERT);
+  if (!slot)
+    return;
+  pat = (struct pending_abstract_type*)*slot;
+  my_friendly_assert (pat, 20040620_2);
+
+  /* If the type is not abstract, do not do anything.  */
+  if (CLASSTYPE_PURE_VIRTUALS (type))
+    {
+      struct pending_abstract_type *prev = 0, *next;
+
+      /* Reverse the list to emit the errors in top-down order.  */
+      for (; pat; pat = next)
+       {
+         next = pat->next;
+         pat->next = prev;
+         prev = pat;
+       }
+      pat = prev;
+
+      /* Go through the list, and call abstract_virtuals_error for each
+       element: it will issue a diagostic if the type is abstract.  */
+      while (pat)
+       {
+         my_friendly_assert (type == pat->type, 20040620_4);
+
+         /* Tweak input_location so that the diagnostic appears at the correct
+           location. Notice that this is only needed if the decl is an
+           IDENTIFIER_NODE, otherwise cp_error_at. */
+         input_location = pat->locus;
+         abstract_virtuals_error (pat->decl, pat->type);
+         pat = pat->next;
+       }
+    }
+
+  htab_clear_slot (abstract_pending_vars, slot);
+
+  input_location = cur_loc;
+}
+
+
 /* If TYPE has abstract virtual functions, issue an error about trying
    to create an object of that type.  DECL is the object declared, or
    NULL_TREE if the declaration is unavailable.  Returns 1 if an error
@@ -125,7 +238,45 @@ abstract_virtuals_error (tree decl, tree type)
   tree u;
   tree tu;
 
-  if (!CLASS_TYPE_P (type) || !CLASSTYPE_PURE_VIRTUALS (type))
+  /* This function applies only to classes. Any other entity can never
+     be abstract.  */
+  if (!CLASS_TYPE_P (type))
+    return 0;
+
+  /* If the type is incomplete, we register it within a hash table,
+     so that we can check again once it is completed. This makes sense
+     only for objects for which we have a declaration or at least a
+     name.  */
+  if (!COMPLETE_TYPE_P (type))
+    {
+      void **slot;
+      struct pending_abstract_type *pat;
+
+      my_friendly_assert (!decl || (DECL_P (decl) 
+                                   || TREE_CODE (decl) == IDENTIFIER_NODE),
+                         20040620_1);
+
+      if (!abstract_pending_vars)
+       abstract_pending_vars = htab_create_ggc (31, &pat_calc_hash, 
+                                               &pat_compare, NULL);
+
+      slot = htab_find_slot_with_hash (abstract_pending_vars, type,
+                                     (hashval_t)TYPE_UID (type), INSERT);
+
+      pat = ggc_alloc (sizeof (struct pending_abstract_type));
+      pat->type = type;
+      pat->decl = decl;
+      pat->locus = ((decl && DECL_P (decl))
+                   ? DECL_SOURCE_LOCATION (decl)
+                   : input_location);
+
+      pat->next = *slot;
+      *slot = pat;
+
+      return 0;
+    }
+
+  if (!CLASSTYPE_PURE_VIRTUALS (type))
     return 0;
 
   if (!TYPE_SIZE (type))
@@ -133,11 +284,6 @@ abstract_virtuals_error (tree decl, tree type)
        CLASSTYPE_PURE_VIRTUALS holds the inline friends.  */
     return 0;
 
-  if (dependent_type_p (type))
-    /* For a dependent type, we do not yet know which functions are pure
-       virtuals.  */
-    return 0;
-
   u = CLASSTYPE_PURE_VIRTUALS (type);
   if (decl)
     {
@@ -160,6 +306,10 @@ abstract_virtuals_error (tree decl, tree type)
       else if (TREE_CODE (decl) == FUNCTION_DECL)
        cp_error_at ("invalid abstract return type for function `%+#D'", 
                     decl);
+      else if (TREE_CODE (decl) == IDENTIFIER_NODE)
+       /* Here we do not have location information, so use error instead
+          of cp_error_at.  */
+       error ("invalid abstract type `%T' for `%E'", type, decl);
       else
        cp_error_at ("invalid abstract type for `%+D'", decl);
     }
@@ -1405,3 +1555,6 @@ require_complete_eh_spec_types (tree fntype, tree decl)
        }
     }
 }
+
+\f
+#include "gt-cp-typeck2.h"
index 0ee84ee5ca29697de7f08ef8e3a8ffde042fc758..e9d788d1739e32b397dc3f3fe40335467faee066 100644 (file)
@@ -1,3 +1,8 @@
+2004-07-12  Giovanni Bajo  <giovannibajo@gcc.gnu.org>
+
+       PR c++/2204
+       * g++.dg/other/abstract2.C: New test.
+
 2004-07-12  Paul Brook  <paul@codesourcery.com>
 
        * gfortran.dg/pointer_init_1.f90: New test.
diff --git a/gcc/testsuite/g++.dg/other/abstract2.C b/gcc/testsuite/g++.dg/other/abstract2.C
new file mode 100644 (file)
index 0000000..d242ffd
--- /dev/null
@@ -0,0 +1,58 @@
+// { dg-do compile }
+// Contributed by Gabriel Dos Reis <gdr at integrable-solutions dot net>
+// PR c++/2204: Check for parameters of abstract type in function declarations.
+
+namespace N1 {
+  struct X;
+
+  struct Y1 {
+    void g(X parm1);         // { dg-error "abstract" }
+    void g(X parm2[2]);      // { dg-error "abstract" }
+    void g(X (*parm3)[2]);   // { dg-error "abstract" }
+  };
+
+
+  template <int N>
+  struct Y2 {
+    void g(X parm4);         // { dg-error "abstract" }
+    void g(X parm5[2]);      // { dg-error "abstract" }
+    void g(X (*parm6)[2]);   // { dg-error "abstract" }
+  };
+
+  struct X {  // { dg-error "note" }
+    virtual void xfunc(void) = 0;  // { dg-error "note" }
+  };
+}
+
+namespace N2 {
+  struct X1 { // { dg-error "note" }
+    virtual void xfunc(void) = 0;  // { dg-error "note" }
+    void g(X1 parm7);        // { dg-error "abstract" }
+    void g(X1 parm8[2]);     // { dg-error "abstract" }
+    void g(X1 (*parm9)[2]);  // { dg-error "abstract" }
+  };
+
+  template <int N>
+  struct X2 { // { dg-error "note" }
+    virtual void xfunc(void) = 0; // { dg-error "note" }
+    void g(X2 parm10);        // { dg-error "abstract" }
+    void g(X2 parm11[2]);     // { dg-error "abstract" }
+    void g(X2 (*parm12)[2]);  // { dg-error "abstract" }
+  };
+}
+
+namespace N3 {
+  struct X { // { dg-error "note" "" }
+    virtual void xfunc(void) = 0;  // { dg-error "note" }
+  };
+  void g(X parm13);          // { dg-error "abstract" }
+  void g(X parm14[2]);       // { dg-error "abstract" }
+  void g(X (*parm15)[2]);    // { dg-error "abstract" }
+
+  template <int N> 
+  void g(X parm16);          // { dg-error "abstract" }
+  template <int N> 
+  void g(X parm17[2]);       // { dg-error "abstract" }
+  template <int N> 
+  void g(X (*parm18)[2]);    // { dg-error "abstract" }
+}