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] [PR2204] Check for parameters of abstract types - Take 4


Jason Merrill wrote:

>> Anyhow, why not just keep a list of all VAR_DECLs and FUNCTION_DECLs
>> that have a parameter of an incomplete type, attached to that
>> incomplete type? Then, when a type is completed, if it is abstract,
>> run down the list
>> issuing error messages.  After the type is complete, clear the list.
>> To
>> save space, use a hash table mapping class types to these lists,
>> rather
>> than using space in every class type.
>
> Sounds like this checking could be combine with the existing use of
> incomplete_vars.

Yup, in fact it is not so different from what I was doing before.

This is the updated patch. This time, I am hiding the deferral of the check for
abstractness in case of incomplete type within abstract_virtuals_error, so that
callers do not have to bother checking. I am also following Mark's advice to
use a hash table instead of a list, and Jason's advice to insert a check within
create_array_type_for_decl to handle array types (which creates a less accurate
diagnostic, but still good enough).

The new structure I created (struct pending_abstract_type) is needed basically
because I have to store the locus to emit a diagnostic at the right place. For
DECLs, the locus is already present within the DECL itself, but for
IDENTIFIER_NODEs it is not. In case you wonder, it is
create_array_type_for_decl which passes IDENTIFIER_NODEs to
abstract_virtuals_error. Also, there are some situations where the declaration
can be NULL (for instance, when creating an array type within a functional
cast).

As a follow-up patch, we can change incomplete_vars to use a hash table too, or
merge it with pending_abstract_type.

Bootstrapped & tested on i686-pc-linux-gnu with no new regressions, OK for
mainline?

Giovanni Bajo


cp/
        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.


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


Index: config-lang.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/config-lang.in,v
retrieving revision 1.21
diff -c -3 -p -r1.21 config-lang.in
*** config-lang.in 27 Feb 2004 00:31:47 -0000 1.21
--- config-lang.in 21 Jun 2004 10:32:45 -0000
*************** stagestuff="g++\$(exeext) g++-cross\$(ex
*** 34,37 ****

  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/lex.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"
--- 34,37 ----

  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/lex.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: Make-lang.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/Make-lang.in,v
retrieving revision 1.183
diff -c -3 -p -r1.183 Make-lang.in
*** Make-lang.in 24 May 2004 10:50:43 -0000 1.183
--- Make-lang.in 21 Jun 2004 10:32:45 -0000
*************** $(srcdir)/cp/cfns.h: $(srcdir)/cp/cfns.g
*** 99,105 ****

  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

  #
  # Build hooks:
--- 99,105 ----

  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 gt-cp-typeck2.h: s-gtype;
@true

  #
  # Build hooks:
*************** cp/decl.o: cp/decl.c $(CXX_TREE_H) $(TM_
*** 227,233 ****
  cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/lex.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
  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
--- 227,233 ----
  cp/decl2.o: cp/decl2.c $(CXX_TREE_H) $(TM_H) flags.h cp/lex.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 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: cp-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/cp-tree.h,v
retrieving revision 1.977
diff -c -3 -p -r1.977 cp-tree.h
*** cp-tree.h 16 Jun 2004 01:21:29 -0000 1.977
--- cp-tree.h 21 Jun 2004 10:32:46 -0000
*************** extern void cxx_incomplete_type_error  (
*** 4275,4280 ****
--- 4275,4281 ----
  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: typeck2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/typeck2.c,v
retrieving revision 1.160
diff -c -3 -p -r1.160 typeck2.c
*** typeck2.c 16 Jun 2004 01:21:35 -0000 1.160
--- typeck2.c 21 Jun 2004 10:32:46 -0000
*************** readonly_error (tree arg, const char* st
*** 118,123 ****
--- 118,236 ----
      (*fn) ("%s of read-only location", string);
  }

+ 
+ /* 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 (CLASS_TYPE_P (type) || 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
*************** abstract_virtuals_error (tree decl, tree
*** 129,135 ****
    tree u;
    tree tu;

!   if (!CLASS_TYPE_P (type) || !CLASSTYPE_PURE_VIRTUALS (type))
      return 0;

    if (!TYPE_SIZE (type))
--- 242,286 ----
    tree u;
    tree tu;

!   /* 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))
*************** abstract_virtuals_error (tree decl, tree
*** 137,147 ****
         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)
      {
--- 288,293 ----
*************** abstract_virtuals_error (tree decl, tree
*** 164,169 ****
--- 310,319 ----
        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);
      }
*************** require_complete_eh_spec_types (tree fnt
*** 1405,1407 ****
--- 1555,1560 ----
   }
      }
  }
+
+ 
+ #include "gt-cp-typeck2.h"
Index: decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.1218
diff -c -3 -p -r1.1218 decl.c
*** decl.c 16 Jun 2004 01:21:30 -0000 1.1218
--- decl.c 21 Jun 2004 10:32:49 -0000
*************** cp_finish_decl (tree decl, tree init, tr
*** 4866,4886 ****

        make_rtl_for_nonlocal_decl (decl, init, asmspec);

        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));
!       }
        else
   abstract_virtuals_error (decl, type);

--- 4866,4877 ----

        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, TREE_TYPE (type));
        else
   abstract_virtuals_error (decl, type);

*************** create_array_type_for_decl (tree name, t
*** 6254,6259 ****
--- 6245,6255 ----
    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);
  }

*************** complete_vars (tree type)
*** 11051,11056 ****
--- 11047,11055 ----
        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: class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.616
diff -c -3 -p -r1.616 class.c
*** class.c 31 May 2004 08:42:31 -0000 1.616
--- class.c 21 Jun 2004 10:32:51 -0000
*************** finish_struct (tree t, tree attributes)
*** 5260,5267 ****
--- 5260,5283 ----

    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);



// { 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" }
}



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