This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] [PR2204] Check for parameters of abstract types - Take 3
- From: "Giovanni Bajo" <giovannibajo at libero dot it>
- To: "Jason Merrill" <jason at redhat dot com>
- Cc: <gcc-patches at gcc dot gnu dot org>
- Date: Fri, 11 Jun 2004 13:52:40 +0200
- Subject: [C++ PATCH] [PR2204] Check for parameters of abstract types - Take 3
- References: <1c9c01c3ef6c$71c10f10$9cba2997@bagio><m3vfmf3ctv.fsf@uniton.integrable-solutions.net><1d8201c3ef7f$41471b50$9cba2997@bagio><m3bro73bft.fsf@uniton.integrable-solutions.net><1f3901c3effc$90c19a70$9cba2997@bagio> <xyp65bh9lnc.fsf@miranda.boston.redhat.com>
Jason Merrill wrote:
[http://gcc.gnu.org/ml/gcc-patches/2004-02/msg00953.html]
>> 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.
>
> There's a much simpler solution: just check for abstract types in
> create_array_type_for_decl.
I tried this but it occurred to me that if I checked for abstract types in
grokdeclarator immediatly after the big loop where the type for the declaration
is constructed (and where create_array_type_for_decl is called for the
ARRAY_REF case), I could catch both the array and the non-array case at the
same time, unifying the check. In fact, it works beautifully, so I removed the
other check for abstractness from grokparms. The only quirk is that I need to
create a PARM_DECL just for the purpose of storing it in the incomplete_vars
list (it cannot be done with the real PARM_DECL because that's already decayed
from array to pointer), but it doesn't seem too much of a problem to me.
>> 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.
>
> Please.
I added support for simple cases, where the dependent type contains pure
virtual functions and it is used as a type for parameters. In this case, I am
emitting an error before instantiation (as soon as the type is completely
defined).
I still miss cases where the pure virtuals come from base classes or
specializations of the template (that is, non trivial cases). In this
situation, something will have to be done at instantiation time, but I will
leave this as a followup.
The hunk in abstract_virtual_errors is needed so that we emit diagnostic such
as "cannot define parameter 'parm' to be of type 'X (*)[2]' because 'X' is
abstract": basically, in that line of the diagnostic we want to print the real
type of the parameter, not the innest type which is abstract. Also, removing
the check for dependent_type_p does not hurt anything, because there was
already a check in place for CLASSTYPE_PURE_VIRTUALS(t), which is usually empty
for dependent types.
Tested on i686-pc-linux-gnu, OK for mainline?
Giovanni Bajo
cp/
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): Don't check for abstract types here.
(grokdeclarator): Check for abstract types when grokking a
parameter.
(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.
* class.c (finish_struct): Construct CLASSTYPE_PURE_VIRTUALS for
templates, and call complete_vars to check for abstractness.
* typeck2.c (abstract_virtuals_error): Do not skip dependend types.
Print real type of variables in diagnostic.
testsuite/
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.1206
diff -c -3 -p -r1.1206 decl.c
*** decl.c 13 May 2004 06:40:16 -0000 1.1206
--- decl.c 11 Jun 2004 10:45:08 -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
*** 4833,4851 ****
|| 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)
--- 4835,4844 ----
|| 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)
*************** grokdeclarator (tree declarator,
*** 7703,7708 ****
--- 7696,7718 ----
type = error_mark_node;
}
+ /* If it is a parameter, we need to check whether the type is abstract
+ or not, and issue an error if it is. */
+ if ((decl_context == PARM || decl_context == CATCHPARM)
+ && type != error_mark_node)
+ {
+ tree t = strip_array_and_pointer_to_array_types (type);
+ tree decl = cp_build_parm_decl (declarator, type);
+
+ /* If the type is not complete at this point, register the parameter
+ declaration among the incomplete variables, so that it will get
+ checked when the type is completed. */
+ if (!COMPLETE_TYPE_P (t) || currently_open_class (t))
+ maybe_register_incomplete_parm (decl);
+ else
+ abstract_virtuals_error (decl, t);
+ }
+
if ((decl_context == FIELD || decl_context == PARM)
&& !processing_template_decl
&& variably_modified_type_p (type))
*************** grokparms (tree first_parm, tree *parms)
*** 8636,8643 ****
type = build_pointer_type (type);
TREE_TYPE (decl) = type;
}
- else if (abstract_virtuals_error (decl, type))
- any_error = 1; /* Seems like a good idea. */
else if (POINTER_TYPE_P (type))
{
/* [dcl.fct]/6, parameter types cannot contain pointers
--- 8646,8651 ----
*************** finish_method (tree decl)
*** 10966,10972 ****
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. */
--- 10974,11024 ----
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)
! {
! tree inner_type;
!
! my_friendly_assert (TREE_CODE (var) == PARM_DECL, 20040208);
!
! inner_type = TREE_TYPE (var);
! 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)
*** 10981,10991 ****
&& DECL_EXTERNAL (var))
{
tree inner_type = TREE_TYPE (var);
!
while (TREE_CODE (inner_type) == ARRAY_TYPE)
inner_type = TREE_TYPE (inner_type);
inner_type = TYPE_MAIN_VARIANT (inner_type);
!
if ((!COMPLETE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type))
/* RTTI TD entries are created while defining the type_info. */
|| (TYPE_LANG_SPECIFIC (inner_type)
--- 11033,11043 ----
&& DECL_EXTERNAL (var))
{
tree inner_type = TREE_TYPE (var);
!
while (TREE_CODE (inner_type) == ARRAY_TYPE)
inner_type = TREE_TYPE (inner_type);
inner_type = TYPE_MAIN_VARIANT (inner_type);
!
if ((!COMPLETE_TYPE_P (inner_type) && CLASS_TYPE_P (inner_type))
/* RTTI TD entries are created while defining the type_info. */
|| (TYPE_LANG_SPECIFIC (inner_type)
*************** maybe_register_incomplete_var (tree var)
*** 10995,11001 ****
}
/* 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
--- 11047,11053 ----
}
/* 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)
*** 11012,11017 ****
--- 11064,11076 ----
/* 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,
+ strip_array_and_pointer_to_array_types
+ (TREE_TYPE (var)));
/* Remove this entry from the list. */
*list = TREE_CHAIN (*list);
}
Index: class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/class.c,v
retrieving revision 1.611
diff -c -3 -p -r1.611 class.c
*** class.c 13 May 2004 06:40:13 -0000 1.611
--- class.c 11 Jun 2004 10:45:10 -0000
*************** finish_struct (tree t, tree attributes)
*** 5243,5250 ****
--- 5243,5266 ----
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: typeck2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cp/typeck2.c,v
retrieving revision 1.159
diff -c -3 -p -r1.159 typeck2.c
*** typeck2.c 11 Jun 2004 03:11:05 -0000 1.159
--- typeck2.c 11 Jun 2004 03:32:56 -0000
*************** 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)
{
--- 137,142 ----
*************** abstract_virtuals_error (tree decl, tree
*** 150,162 ****
if (TREE_CODE (decl) == VAR_DECL)
cp_error_at ("cannot declare variable `%+D' to be of abstract "
! "type `%T'", decl, type);
else if (TREE_CODE (decl) == PARM_DECL)
cp_error_at ("cannot declare parameter `%+D' to be of abstract "
! "type `%T'", decl, type);
else if (TREE_CODE (decl) == FIELD_DECL)
cp_error_at ("cannot declare field `%+D' to be of abstract "
! "type `%T'", decl, type);
else if (TREE_CODE (decl) == FUNCTION_DECL
&& TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
cp_error_at ("invalid abstract return type for member function `%+#D'",
--- 145,157 ----
if (TREE_CODE (decl) == VAR_DECL)
cp_error_at ("cannot declare variable `%+D' to be of abstract "
! "type `%T'", decl, TREE_TYPE (decl));
else if (TREE_CODE (decl) == PARM_DECL)
cp_error_at ("cannot declare parameter `%+D' to be of abstract "
! "type `%T'", decl, TREE_TYPE (decl));
else if (TREE_CODE (decl) == FIELD_DECL)
cp_error_at ("cannot declare field `%+D' to be of abstract "
! "type `%T'", decl, TREE_TYPE (decl));
else if (TREE_CODE (decl) == FUNCTION_DECL
&& TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
cp_error_at ("invalid abstract return type for member function `%+#D'",
// { 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" }
}