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 (partial fix)


Hello,

this is a partial fix for PR2204, which is about checking if PARM_DECLs in
function declarations have abstract types. Currently, the check is being
performed in grokparms, which is fine for many cases, but it's way too early if
the type is the class being defined, or an incomplete type. In that case, we
need to defer the check later, when/if the type is completed.

To do this, I allow PARM_DECLS to be registered in the incomplete_vars list (by
grokparms) and check for abstractness upon type completition (finish_struct_1,
which calls complete_vars). Notice that we cannot check it in start_function,
because the program is ill-formed even if there is no definition for the
function.

Alas, this solution is partial, mainly because it doesn't get into account
array types. In fact, parameters with array type are decayed into pointer types
within grokdeclarator (even before a PARM_DECL is built), so they never get
registered into the incomplete_vars list. A solution for this might be
extracting the standard decays out of grokdeclarator, and having grokparms call
it. There is also another user of grokdeclarator(PARM) (in pt.c, to build
template parameters), which should be updated as well. Comments on this plan
are highly appreciated.

Another case we don't handle yet is when the type of the parameter is
dependent. In that case, we probably need to check for abstractness upon
instantiation, while tsubsting. I'll investigate.

Meanwhile, this tested succesfully on i686-pc-linux-gnu. The testcase have a
handful of xfails for the cases we don't handle yet. OK for mainline?

Giovanni Bajo



2004-02-10  Giovanni Bajo  <giovannibajo@gcc.gnu.org>

        PR c++/2204
        * decl.c (cp_finish_decl): Extract logic to strip array and pointer
        to array types into...
        (strip_array_and_pointer_to_array_types): New function.
        (grokparms): Register PARM_DECLs of incomplete types for later check,
        using...
        (maybe_register_incomplete_parms): New function.
        (complete_vars): When completing the type for a PARM_DECL, check
        for abstractness. Fix a typo in the comment.
        * typeck2.c (abstract_virtual_errors): Reword diagnostics, make them
        appear at the correct location.

2004-02-10  Giovanni Bajo  <giovannibajo@gcc.gnu.org>

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


Index: decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/decl.c,v
retrieving revision 1.1179
diff -c -3 -p -r1.1179 decl.c
*** decl.c 2 Feb 2004 16:53:02 -0000 1.1179
--- decl.c 9 Feb 2004 23:27:43 -0000
*************** static void expand_static_init (tree, tr
*** 120,125 ****
--- 120,127 ----
  static tree next_initializable_field (tree);
  static tree reshape_init (tree, tree *);
  static tree build_typename_type (tree, tree, tree);
+ static void maybe_register_incomplete_parm (tree);
+ static tree strip_array_and_pointer_to_array_types (tree);

  /* Erroneous argument lists can use this *IFF* they do not modify it.  */
  tree error_mark_list;
*************** cp_finish_decl (tree decl, tree init, tr
*** 4883,4901 ****
     || 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);

        if (TREE_CODE (decl) == FUNCTION_DECL
     || TREE_TYPE (decl) == error_mark_node)
--- 4885,4894 ----
     || TREE_CODE (type) == METHOD_TYPE)
   abstract_virtuals_error (decl,
       strip_array_types (TREE_TYPE (type)));
!       else
!  abstract_virtuals_error (decl,
!      strip_array_and_pointer_to_array_types
!       (type));

        if (TREE_CODE (decl) == FUNCTION_DECL
     || TREE_TYPE (decl) == error_mark_node)
*************** grokparms (tree first_parm)
*** 8669,8674 ****
--- 8660,8667 ----
         type = build_pointer_type (type);
         TREE_TYPE (decl) = type;
       }
+    else if (!COMPLETE_TYPE_P (type) || currently_open_class (type))
+      maybe_register_incomplete_parm (decl);
     else if (abstract_virtuals_error (decl, type))
       any_error = 1;  /* Seems like a good idea.  */
     else if (POINTER_TYPE_P (type))
*************** finish_method (tree decl)
*** 11049,11055 ****

    return decl;
  }
! 

  /* VAR is a VAR_DECL.  If its type is incomplete, remember VAR so that
     we can lay it out later, when and if its type becomes complete.  */
--- 11042,11098 ----

    return decl;
  }
!
! /* Strip all the array/pointer-to-array types from TYPE. This is used to
!    extract the underlying type from declarations such as:
!
!    A a[2];
!    A (*a)[2];
!    A (**a[2])[2];
!
!    which are invalid if 'A' is an abstract type.  */
!
! static tree
! strip_array_and_pointer_to_array_types (tree type)
! {
!   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, then
!   we can return its element type.  */
!       while (POINTER_TYPE_P (TREE_TYPE (type))
!       || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
!  type = TREE_TYPE (type);
!       if (TREE_CODE (type) == ARRAY_TYPE)
!  return TREE_TYPE (type);
!     }
!   return type;
! }
!
! /* PARM is a PARM_DECL, whose type is incomplete at the point of parsing.
!    We keep track of these because we must issue an error if, upon
!    completition, the type ends up being abstract.  */
!
! static void
! maybe_register_incomplete_parm (tree var)
! {
!   my_friendly_assert (TREE_CODE (var) == PARM_DECL, 20040208);
!
!   /* If the type is dependent, there is nothing we can do here. The
!      diagnostic will have to be issued during template substitution.  */
!   if (!dependent_type_p (TREE_TYPE (var)))
!     {
!       tree inner_type = TREE_TYPE (var);
!
!       /* FIXME: We currently don't get any ARRAY_TYPE because grokdeclarator
!   decays them to POINTER_TYPE too early.  */
!       inner_type = strip_array_and_pointer_to_array_types (inner_type);
!       inner_type = TYPE_MAIN_VARIANT (inner_type);
!
!       if (!COMPLETE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type))
!  incomplete_vars = tree_cons (inner_type, var, incomplete_vars);
!     }
! }

  /* VAR is a VAR_DECL.  If its type is incomplete, remember VAR so that
     we can lay it out later, when and if its type becomes complete.  */
*************** maybe_register_incomplete_var (tree var)
*** 11078,11084 ****
  }

  /* Called when a class type (given by TYPE) is defined.  If there are
!    any existing VAR_DECLs whose type hsa been completed by this
     declaration, update them now.  */

  void
--- 11121,11127 ----
  }

  /* Called when a class type (given by TYPE) is defined.  If there are
!    any existing VAR_DECLs whose type has been completed by this
     declaration, update them now.  */

  void
*************** complete_vars (tree type)
*** 11095,11100 ****
--- 11138,11148 ----
     /* Complete the type of the variable.  The VAR_DECL itself
        will be laid out in expand_expr.  */
     complete_type (TREE_TYPE (var));
+    /* If it is a PARM_DECL, we need to emit an error if the
+       type is abstract, because this is why we registered it
+       here.  */
+    if (TREE_CODE (var) == PARM_DECL)
+      abstract_virtuals_error (var, TREE_TYPE (var));
     /* Remove this entry from the list.  */
     *list = TREE_CHAIN (*list);
   }
Index: typeck2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/typeck2.c,v
retrieving revision 1.154
diff -c -3 -p -r1.154 typeck2.c
*** typeck2.c 22 Jan 2004 00:03:52 -0000 1.154
--- typeck2.c 9 Feb 2004 23:27:46 -0000
*************** abstract_virtuals_error (tree decl, tree
*** 149,182 ****
   return 0;

        if (TREE_CODE (decl) == VAR_DECL)
!  error ("cannot declare variable `%D' to be of type `%T'",
!       decl, type);
        else if (TREE_CODE (decl) == PARM_DECL)
!  error ("cannot declare parameter `%D' to be of type `%T'",
!       decl, type);
        else if (TREE_CODE (decl) == FIELD_DECL)
!  error ("cannot declare field `%D' to be of type `%T'",
!       decl, type);
        else if (TREE_CODE (decl) == FUNCTION_DECL
          && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
!  error ("invalid return type for member function `%#D'", decl);
        else if (TREE_CODE (decl) == FUNCTION_DECL)
!  error ("invalid return type for function `%#D'", decl);
      }
    else
!     error ("cannot allocate an object of type `%T'", type);

    /* Only go through this once.  */
    if (TREE_PURPOSE (u) == NULL_TREE)
      {
        TREE_PURPOSE (u) = error_mark_node;

!       error ("  because the following virtual functions are abstract:");
        for (tu = u; tu; tu = TREE_CHAIN (tu))
!  cp_error_at ("\t%#D", TREE_VALUE (tu));
      }
    else
!     error ("  since type `%T' has abstract virtual functions", type);

    return 1;
  }
--- 149,196 ----
   return 0;

        if (TREE_CODE (decl) == VAR_DECL)
!  error ("%Jcannot declare variable `%D' to be of abstract type `%T'",
!         decl, decl, type);
        else if (TREE_CODE (decl) == PARM_DECL)
!  error ("%Jcannot declare parameter `%D' to be of abstract type `%T'",
!         decl, decl, type);
        else if (TREE_CODE (decl) == FIELD_DECL)
!  error ("%Jcannot declare field `%D' to be of abstract type `%T'",
!         decl, decl, type);
        else if (TREE_CODE (decl) == FUNCTION_DECL
          && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
!  error ("%Jinvalid abstract return type for member function `%#D'",
!         decl, decl);
        else if (TREE_CODE (decl) == FUNCTION_DECL)
!  error ("%Jinvalid abstract return type for function `%#D'",
!         decl, decl);
      }
    else
!     error ("cannot allocate an object of abstract type `%T'", type);

    /* Only go through this once.  */
    if (TREE_PURPOSE (u) == NULL_TREE)
      {
        TREE_PURPOSE (u) = error_mark_node;

!       if (decl)
!  inform ("%J  because the following virtual functions are pure:",
!   decl);
!       else
!  inform ("  because the following virtual functions are pure:");
!
        for (tu = u; tu; tu = TREE_CHAIN (tu))
!  inform ("%J\t%#D", TREE_VALUE (tu), TREE_VALUE (tu));
      }
    else
!   {
!     if (decl)
!       inform ("%J  since type `%T' has pure virtual functions",
!        decl, type);
!     else
!       inform ("  since type `%T' has pure virtual functions",
!        type);
!   }

    return 1;
  }




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

  template <int N>
  struct Y2 {
    void g(X parm3);         // { dg-error "abstract" }
    void g(X parm4[2]);      // { dg-error "abstract" "" { xfail *-*-* } }
  };

  struct X {
    virtual void xfunc(void) = 0;  // { dg-error "note" }
  };
}

namespace N2 {
  struct X1 {
    virtual void xfunc(void) = 0;  // { dg-error "note" }
    void g(X1 parm5);        // { dg-error "abstract" }
    void g(X1 parm6[2]);     // { dg-error "abstract" "" { xfail *-*-* } }
  };

  template <int N>
  struct X2 {
    virtual void xfunc(void) = 0;  // { dg-error "note" "" { xfail *-*-* } }
    void g(X2 parm7);        // { dg-error "abstract" "" { xfail *-*-* } }
    void g(X2 parm8[2]);     // { dg-error "abstract" "" { xfail *-*-* } }
  };
}

namespace N3 {
  struct X {
    virtual void xfunc(void) = 0;  // { dg-error "note" }
  };
  void g(X parm9);           // { dg-error "abstract" }
  void g(X parm10[2]);       // { dg-error "abstract" "" { xfail *-*-* } }
  template <int N>
  void g(X parm11);          // { dg-error "abstract" }
  template <int N>
  void g(X parm12[2]);       // { dg-error "abstract" "" { xfail *-*-* } }
}

// { dg-excess-errors "following virtual functions|has pure virtual
functions" }



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