c++: Check abstract type only on object creation. [PR86252]

Abstract checking has been problematic for a while; when I implemented an
earlier issue resolution to do more checking it led to undesirable
instantiations, and so backed some of it out.  During the C++20 process we
decided with P0929R2 that we should go the other way, and only check
abstractness when we're actually creating an object, not when merely forming
an array or function type.  This means that we can remove the machinery for
checking whether a newly complete class makes some earlier declaration
ill-formed.  This change was moved as a DR, so I'm applying it to all
standard levels.  This could be reconsidered if it causes problems, but I
don't expect it to.

The change to the libstdc++ result_of test brings the expected behavior in
line with that for incomplete types, but as in PR97841 I think the libstdc++
handling of incomplete types in this and other type_traits is itself wrong,
so I expect these lines and others to change again before long.

gcc/cp/ChangeLog:

	* decl.c (cp_finish_decl): Only check abstractness on definition.
	(require_complete_types_for_parms): Check abstractness here.
	(create_array_type_for_decl): Not here.
	(grokdeclarator, grokparms, complete_vars): Not here.
	* pt.c (tsubst, tsubst_arg_types, tsubst_function_type): Not here.
	* typeck2.c (struct pending_abstract_type): Remove.
	(struct abstract_type_hasher): Remove.
	(abstract_pending_vars, complete_type_check_abstract): Remove.
	(abstract_virtuals_error_sfinae): Handle arrays.
	* call.c (conv_is_prvalue): Split out from...
	(conv_binds_ref_to_prvalue): ...here.
	(implicit_conversion_1): Rename from implicit_conversion.
	(implicit_conversion): An abstract prvalue is bad.
	(convert_like_internal): Don't complain if expr is already
	error_mark_node.

gcc/testsuite/ChangeLog:

	* g++.dg/other/abstract1.C: Adjust.
	* g++.dg/other/abstract2.C: Adjust.
	* g++.dg/other/abstract4.C: Adjust.
	* g++.dg/other/abstract5.C: Adjust.
	* g++.dg/other/abstract8.C: New test.
	* g++.dg/template/sfinae-dr657.C: Adjust.
	* g++.old-deja/g++.other/decl3.C: Adjust.

libstdc++-v3/ChangeLog:

	* testsuite/20_util/result_of/sfinae_friendly_1.cc: Adjust.
This commit is contained in:
Jason Merrill 2020-11-13 16:33:50 -05:00
parent 82b6d25d28
commit baf38d2e36
12 changed files with 161 additions and 253 deletions

View File

@ -213,6 +213,7 @@ static conversion *merge_conversion_sequences (conversion *, conversion *);
static tree build_temp (tree, tree, int, diagnostic_t *, tsubst_flags_t);
static conversion *build_identity_conv (tree, tree);
static inline bool conv_binds_to_array_of_unknown_bound (conversion *);
static bool conv_is_prvalue (conversion *);
static tree prevent_lifetime_extension (tree);
/* Returns nonzero iff the destructor name specified in NAME matches BASETYPE.
@ -1963,14 +1964,12 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
return conv;
}
/* Returns the implicit conversion sequence (see [over.ics]) from type
FROM to type TO. The optional expression EXPR may affect the
conversion. FLAGS are the usual overloading flags. If C_CAST_P is
true, this conversion is coming from a C-style cast. */
/* Most of the implementation of implicit_conversion, with the same
parameters. */
static conversion *
implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
int flags, tsubst_flags_t complain)
implicit_conversion_1 (tree to, tree from, tree expr, bool c_cast_p,
int flags, tsubst_flags_t complain)
{
conversion *conv;
@ -2096,6 +2095,26 @@ implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
return NULL;
}
/* Returns the implicit conversion sequence (see [over.ics]) from type
FROM to type TO. The optional expression EXPR may affect the
conversion. FLAGS are the usual overloading flags. If C_CAST_P is
true, this conversion is coming from a C-style cast. */
static conversion *
implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
int flags, tsubst_flags_t complain)
{
conversion *conv = implicit_conversion_1 (to, from, expr, c_cast_p,
flags, complain);
if (!conv || conv->bad_p)
return conv;
if (conv_is_prvalue (conv)
&& CLASS_TYPE_P (conv->type)
&& CLASSTYPE_PURE_VIRTUALS (conv->type))
conv->bad_p = true;
return conv;
}
/* Like implicit_conversion, but return NULL if the conversion is bad.
This is not static so that check_non_deducible_conversion can call it within
@ -7407,7 +7426,7 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
else if (t->kind == ck_identity)
break;
}
if (!complained)
if (!complained && expr != error_mark_node)
{
range_label_for_type_mismatch label (TREE_TYPE (expr), totype);
gcc_rich_location richloc (loc, &label);
@ -8438,6 +8457,24 @@ unsafe_copy_elision_p (tree target, tree exp)
&& !AGGR_INIT_VIA_CTOR_P (init));
}
/* True IFF the result of the conversion C is a prvalue. */
static bool
conv_is_prvalue (conversion *c)
{
if (c->kind == ck_rvalue)
return true;
if (c->kind == ck_base && c->need_temporary_p)
return true;
if (c->kind == ck_user && !TYPE_REF_P (c->type))
return true;
if (c->kind == ck_identity && c->u.expr
&& TREE_CODE (c->u.expr) == TARGET_EXPR)
return true;
return false;
}
/* True iff C is a conversion that binds a reference to a prvalue. */
static bool
@ -8448,17 +8485,7 @@ conv_binds_ref_to_prvalue (conversion *c)
if (c->need_temporary_p)
return true;
c = next_conversion (c);
if (c->kind == ck_rvalue)
return true;
if (c->kind == ck_user && !TYPE_REF_P (c->type))
return true;
if (c->kind == ck_identity && c->u.expr
&& TREE_CODE (c->u.expr) == TARGET_EXPR)
return true;
return false;
return conv_is_prvalue (next_conversion (c));
}
/* True iff converting EXPR to a reference type TYPE does not involve

View File

@ -7926,10 +7926,9 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
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. */
abstract_virtuals_error (decl, type);
/* Check for abstractness of the type. */
if (var_definition_p)
abstract_virtuals_error (decl, type);
if (TREE_TYPE (decl) == error_mark_node)
/* No initialization required. */
@ -10701,11 +10700,6 @@ create_array_type_for_decl (tree name, tree type, tree size, location_t loc)
itype = compute_array_index_type_loc (loc, name, size,
tf_warning_or_error);
/* [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);
}
@ -12097,11 +12091,6 @@ grokdeclarator (const cp_declarator *declarator,
/* Declaring a function type. */
{
iloc_sentinel ils (declspecs->locations[ds_type_spec]);
abstract_virtuals_error (ACU_RETURN, type);
}
/* Pick up type qualifiers which should be applied to `this'. */
memfn_quals = declarator->u.function.qualifiers;
/* Pick up virt-specifiers. */
@ -13872,6 +13861,7 @@ require_complete_types_for_parms (tree parms)
relayout_decl (parms);
DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms));
abstract_virtuals_error (parms, TREE_TYPE (parms));
maybe_warn_parm_abi (TREE_TYPE (parms),
DECL_SOURCE_LOCATION (parms));
}
@ -14109,9 +14099,6 @@ grokparms (tree parmlist, tree *parms)
type = build_pointer_type (type);
TREE_TYPE (decl) = type;
}
else if (abstract_virtuals_error (decl, type))
/* Ignore any default argument. */
init = NULL_TREE;
else if (cxx_dialect < cxx17 && INDIRECT_TYPE_P (type))
{
/* Before C++17 DR 393:
@ -17427,9 +17414,6 @@ complete_vars (tree type)
else
ix++;
}
/* 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 and return an

View File

@ -14932,9 +14932,6 @@ tsubst_arg_types (tree arg_types,
}
return error_mark_node;
}
/* DR 657. */
if (abstract_virtuals_error_sfinae (ACU_PARM, type, complain))
return error_mark_node;
/* Do array-to-pointer, function-to-pointer conversion, and ignore
top-level qualifiers as required. */
@ -15052,9 +15049,6 @@ tsubst_function_type (tree t,
}
return error_mark_node;
}
/* And DR 657. */
if (abstract_virtuals_error_sfinae (ACU_RETURN, return_type, complain))
return error_mark_node;
if (!late_return_type_p)
{
@ -15880,9 +15874,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
return error_mark_node;
}
if (abstract_virtuals_error_sfinae (ACU_ARRAY, type, complain))
return error_mark_node;
r = build_cplus_array_type (type, domain);
if (!valid_array_size_p (input_location, r, in_decl,

View File

@ -123,123 +123,6 @@ cxx_readonly_error (location_t loc, tree arg, enum lvalue_use errstring)
readonly_error (loc, arg, errstring);
}
/* Structure that holds information about declarations whose type was
incomplete and we could not check whether it was abstract or not. */
struct GTY((chain_next ("%h.next"), for_user)) pending_abstract_type {
/* 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;
/* Kind of use in an unnamed declarator. */
enum abstract_class_use use;
/* 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;
};
struct abstract_type_hasher : ggc_ptr_hash<pending_abstract_type>
{
typedef tree compare_type;
static hashval_t hash (pending_abstract_type *);
static bool equal (pending_abstract_type *, tree);
};
/* Compute the hash value of the node VAL. This function is used by the
hash table abstract_pending_vars. */
hashval_t
abstract_type_hasher::hash (pending_abstract_type *pat)
{
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. */
bool
abstract_type_hasher::equal (pending_abstract_type *pat1, tree type2)
{
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 (()) hash_table<abstract_type_hasher> *abstract_pending_vars = NULL;
static int abstract_virtuals_error_sfinae (tree, tree, abstract_class_use, tsubst_flags_t);
/* 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)
{
struct pending_abstract_type *pat;
location_t cur_loc = input_location;
gcc_assert (COMPLETE_TYPE_P (type));
if (!abstract_pending_vars)
return;
/* Retrieve the list of pending declarations for this type. */
pending_abstract_type **slot
= abstract_pending_vars->find_slot_with_hash (type, TYPE_UID (type),
NO_INSERT);
if (!slot)
return;
pat = *slot;
gcc_assert (pat);
/* 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 diagnostic if the type is abstract. */
while (pat)
{
gcc_assert (type == pat->type);
/* 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. */
input_location = pat->locus;
abstract_virtuals_error_sfinae (pat->decl, pat->type, pat->use,
tf_warning_or_error);
pat = pat->next;
}
}
abstract_pending_vars->clear_slot (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, in which case USE specifies
@ -252,6 +135,13 @@ abstract_virtuals_error_sfinae (tree decl, tree type, abstract_class_use use,
{
vec<tree, va_gc> *pure;
if (TREE_CODE (type) == ARRAY_TYPE)
{
decl = NULL_TREE;
use = ACU_ARRAY;
type = strip_array_types (type);
}
/* This function applies only to classes. Any other entity can never
be abstract. */
if (!CLASS_TYPE_P (type))
@ -266,38 +156,6 @@ abstract_virtuals_error_sfinae (tree decl, tree type, abstract_class_use use,
complete_type (type);
#endif
/* 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) && (complain & tf_error))
{
struct pending_abstract_type *pat;
gcc_assert (!decl || DECL_P (decl) || identifier_p (decl));
if (!abstract_pending_vars)
abstract_pending_vars
= hash_table<abstract_type_hasher>::create_ggc (31);
pending_abstract_type **slot
= abstract_pending_vars->find_slot_with_hash (type, TYPE_UID (type),
INSERT);
pat = ggc_alloc<pending_abstract_type> ();
pat->type = type;
pat->decl = decl;
pat->use = use;
pat->locus = ((decl && DECL_P (decl))
? DECL_SOURCE_LOCATION (decl)
: input_location);
pat->next = *slot;
*slot = pat;
return 0;
}
if (!TYPE_SIZE (type))
/* TYPE is being defined, and during that time
CLASSTYPE_PURE_VIRTUALS holds the inline friends. */
@ -2594,6 +2452,3 @@ require_complete_eh_spec_types (tree fntype, tree decl)
}
}
}
#include "gt-cp-typeck2.h"

View File

@ -5,34 +5,38 @@
// c++/9256: Make sure that a pointer to an array of abstract elements
// cannot be created, not even during template substitution (DR337).
// Changed massively by P0929R2: now only creating an object of the array type
// is ill-formed, not merely forming the array type.
struct Abstract { virtual void f() = 0; }; // { dg-message "note" }
struct Complete { void f(); };
/*
* TEST 1
* Arrays of abstract elements cannot be declared.
* Arrays of abstract elements cannot be defined.
*/
Abstract a0[2]; // { dg-error "" }
Abstract (*a1)[2]; // { dg-error "" }
Abstract (**a2)[2]; // { dg-error "" }
Abstract (***a3)[2]; // { dg-error "" }
Abstract (*a1)[2];
Abstract (**a2)[2];
Abstract (***a3)[2];
Abstract *a4;
Abstract *a5[2];
Abstract (*a6[2])[2]; // { dg-error "" }
Abstract (*a6[2])[2];
Abstract **a7[2];
Abstract *(*a8[2])[2];
Abstract (**a9[2])[2]; // { dg-error "" }
Abstract *(*a8[2])[2];
Abstract (**a9[2])[2];
/*
* TEST 2
* If a pointer to an array of abstract elements is created during template
* If an array of abstract elements is created during template
* instantiation, an error should occur.
*/
template <class T> struct K {
T (*a)[2]; // { dg-error "abstract class type" }
T (*a1)[2];
T (a2)[2]; // { dg-error "abstract" }
};
template struct K<Abstract>; // { dg-message "required" }
@ -41,8 +45,9 @@ template struct K<Abstract>; // { dg-message "required" }
/*
* TEST 3
* Deducing an array of abstract elements during type deduction is a silent
* failure (rejects overload).
* Deducing an array of abstract elements during type deduction is no longer a
* silent failure.
*/
template <bool> struct StaticAssert;
@ -54,6 +59,6 @@ typedef struct { char x[2]; } No;
template<typename U> No is_abstract(U (*k)[1]);
template<typename U> Yes is_abstract(...);
StaticAssert<sizeof(is_abstract<Abstract>(0)) == sizeof(Yes)> b1;
StaticAssert<sizeof(is_abstract<Abstract>(0)) == sizeof(No)> b1;
StaticAssert<sizeof(is_abstract<Complete>(0)) == sizeof(No)> b2;
StaticAssert<sizeof(is_abstract<int>(0)) == sizeof(No)> b3;

View File

@ -5,54 +5,59 @@
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-message "note" }
virtual void xfunc(void) = 0; // { dg-message "note" }
};
struct Y1 {
void g(X parm1) {} // { dg-error "abstract" }
void g(X parm2[2]) {}
void g(X (*parm3)[2]) {}
};
template <int N>
struct Y2 {
void g(X parm4) {} // { dg-error "abstract" }
void g(X parm5[2]) {}
void g(X (*parm6)[2]) {}
};
template struct Y2<42>;
}
namespace N2 {
struct X1 { // { dg-message "note" }
virtual void xfunc(void) = 0; // { dg-message "note" }
void g(X1 parm7); // { dg-error "abstract" }
void g(X1 parm8[2]); // { dg-error "abstract" }
void g(X1 (*parm9)[2]); // { dg-error "abstract" }
void g(X1 parm7) {} // { dg-error "abstract" }
void g(X1 parm8[2]) {}
void g(X1 (*parm9)[2]) {}
};
template <int N>
struct X2 { // { dg-message "note" }
virtual void xfunc(void) = 0; // { dg-message "note" }
void g(X2 parm10); // { dg-error "abstract" }
void g(X2 parm11[2]); // { dg-error "abstract" }
void g(X2 (*parm12)[2]); // { dg-error "abstract" }
void g(X2 parm10) {} // { dg-error "abstract" }
void g(X2 parm11[2]) {}
void g(X2 (*parm12)[2]) {}
};
template struct X2<42>;
}
namespace N3 {
struct X { // { dg-message "note" }
virtual void xfunc(void) = 0; // { dg-message "note" }
};
void g(X parm13); // { dg-error "abstract" }
void g(X parm14[2]); // { dg-error "abstract" }
void g(X (*parm15)[2]); // { dg-error "abstract" }
void g(X parm13) {} // { dg-error "abstract" }
void g(X parm14[2]) {}
void g(X (*parm15)[2]) {}
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" }
template <int N>
void g(X parm16) {} // { dg-error "abstract" }
template <int N>
void g(X parm17[2]) {}
template <int N>
void g(X (*parm18)[2]) {}
template void g<42>(X);
}

View File

@ -13,6 +13,6 @@ struct Abs
int main()
{
S<Abs(int)> s; // { dg-error "abstract" }
foo<Abs(int)>(); // { dg-error "abstract" }
S<Abs(int)> s;
foo<Abs(int)>();
}

View File

@ -3,4 +3,4 @@ struct A
virtual void f() = 0;
};
typedef A (*fp)(); // { dg-error "abstract" }
typedef A (*fp)();

View File

@ -0,0 +1,40 @@
// P0929R2: Checking for abstract class types.
// { dg-do compile { target c++11 } }
// { dg-additional-options -Wno-return-type }
struct A
{
virtual void f() = 0;
};
struct B
{
A a; // { dg-error "abstract" }
A ar[4]; // { dg-error "abstract" }
};
using Aa = A[4]; // OK
Aa* aap; // OK
extern A a; // OK
extern Aa aa; // OK
A f(); // OK
void g(A); // OK
A a; // { dg-error "abstract" }
Aa aa; // { dg-error "abstract" }
A f() { } // { dg-error "abstract" }
void g(A) { } // { dg-error "abstract" }
int main()
{
(A(a)); // { dg-error "abstract" }
A{}; // { dg-error "abstract" }
static_cast<A>(a); // { dg-error "abstract" }
Aa{}; // { dg-error "abstract" }
f(); // { dg-error "abstract" }
decltype(f())* p; // OK
g(a); // { dg-error "abstract" }
throw a; // { dg-error "abstract" }
}

View File

@ -1,6 +1,7 @@
// DR 657
// Test that a return or parameter type with abstract class type causes a
// deduction failure.
// DR 657 SUPERSEDED BY DR 1646
// Test that a return or parameter type with abstract class type DOES NOT cause
// a deduction failure, but there is no implicit conversion sequence for
// a parameter of abstract class type.
struct A
{
@ -17,6 +18,6 @@ template<class T> int arg(...);
int main()
{
int i = declval<A>();
int i = declval<A>(); // { dg-error "ambiguous" }
i = arg<A>(1);
}

View File

@ -13,7 +13,7 @@ struct cow_t { // { dg-message "pure" }
int main()
{
cow_t cow[2]; // { dg-error "invalid abstract type" }
cow_t cow[2]; // { dg-error "abstract" }
cow[0].f();
return 0;
}

View File

@ -379,19 +379,19 @@ static_assert(is_type<std::result_of<ident_functor(const volatile Abstract&)>,
const volatile Abstract&>(), "Error!");
static_assert(!has_type<std::result_of<ident_functor(int(&&)[1])>>(), "Error!");
static_assert(!has_type<std::result_of<ident_functor(Abstract&&)>>(), "Error!");
static_assert(is_type<std::result_of<ident_functor(Abstract&&)>,Abstract>(), "Error!");
static_assert(!has_type<std::result_of<ident_functor(const int(&&)[1])>>(),
"Error!");
static_assert(!has_type<std::result_of<ident_functor(const Abstract&&)>>(),
static_assert(is_type<std::result_of<ident_functor(const Abstract&&)>,const Abstract>(),
"Error!");
static_assert(!has_type<std::result_of<ident_functor_noref(int(&)[1])>>(),
"Error!");
static_assert(!has_type<std::result_of<ident_functor_noref
(const int(&)[1])>>(), "Error!");
static_assert(!has_type<std::result_of<ident_functor_noref(Abstract&)>>(),
static_assert(is_type<std::result_of<ident_functor_noref(Abstract&)>,Abstract>(),
"Error!");
static_assert(!has_type<std::result_of
<ident_functor_noref(const Abstract&)>>(), "Error!");
static_assert(is_type<std::result_of
<ident_functor_noref(const Abstract&)>,const Abstract>(), "Error!");
static_assert(!has_type<std::result_of<ident_functor_noref(void(&)())>>(),
"Error!");
static_assert(!has_type<std::result_of<ident_functor_noref(void(&&)())>>(),