This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for c++/38796 (checking defaulted declarations)
- From: Jason Merrill <jason at redhat dot com>
- To: gcc-patches List <gcc-patches at gcc dot gnu dot org>
- Date: Mon, 26 Oct 2009 15:20:00 -0400
- Subject: C++ PATCH for c++/38796 (checking defaulted declarations)
This patch checks that a defaulted special member function is actually
one that can be defaulted, according to the resolution of core issue
906. A defaulted member function must have the same signature as the
corresponding implicit declaration, no exception-specification, and if
it is declared in the class body, must have public access and not be
explicit.
I've adjusted the library headers as needed to conform to this, by
removing throw() and moving protected definitions out of the class body.
Tested x86_64-pc-linux-gnu, applied to trunk.
commit def51528aa22f9963c85e479ecdc7d10cf01f2a6
Author: Jason Merrill <jason@redhat.com>
Date: Fri Oct 16 02:46:51 2009 -0400
PR c++/38796, Core issue 906
gcc/cp
* cp-tree.h (DECL_DEFAULTED_OUTSIDE_CLASS_P): New.
(DECL_DEFAULTED_IN_CLASS_P): New.
* class.c (user_provided_p): Non-static.
(check_methods): Use it.
(check_bases_and_members): Check defaulted fns.
(defaultable_fn_p): Move and rename to...
* method.c (defaultable_fn_check): ...this.
(defaulted_late_check): New.
* pt.c (tsubst_decl): Call it.
* decl2.c (grokfield): Adjust.
* decl.c (cp_finish_decl): Adjust.
(grok_special_member_properties): Use user_provided_p.
libstdc++-v3
* include/std/future (~Future_result_base): Default outside class
body.
* include/std/system_error (error_category()): Likewise.
* libsupc++/nested_exception.h (nested_exception): Remove
exception specifications from defaulted methods.
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index d29d661..d737bdf 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -3843,7 +3843,7 @@ check_methods (tree t)
VEC_safe_push (tree, gc, CLASSTYPE_PURE_VIRTUALS (t), x);
}
/* All user-provided destructors are non-trivial. */
- if (DECL_DESTRUCTOR_P (x) && !DECL_DEFAULTED_FN (x))
+ if (DECL_DESTRUCTOR_P (x) && user_provided_p (x))
TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = 1;
}
}
@@ -4174,17 +4174,17 @@ type_has_user_nondefault_constructor (tree t)
}
/* Returns true iff FN is a user-provided function, i.e. user-declared
- and not defaulted at its first declaration. */
+ and not defaulted at its first declaration; or explicit, private,
+ protected, or non-const. */
-static bool
+bool
user_provided_p (tree fn)
{
if (TREE_CODE (fn) == TEMPLATE_DECL)
return true;
else
return (!DECL_ARTIFICIAL (fn)
- && !(DECL_DEFAULTED_FN (fn)
- && DECL_INITIALIZED_IN_CLASS_P (fn)));
+ && !DECL_DEFAULTED_IN_CLASS_P (fn));
}
/* Returns true iff class T has a user-provided constructor. */
@@ -4238,31 +4238,6 @@ type_has_user_provided_default_constructor (tree t)
return false;
}
-/* Returns true if FN can be explicitly defaulted. */
-
-bool
-defaultable_fn_p (tree fn)
-{
- if (DECL_CONSTRUCTOR_P (fn))
- {
- if (FUNCTION_FIRST_USER_PARMTYPE (fn) == void_list_node)
- return true;
- else if (copy_fn_p (fn) > 0
- && (TREE_CHAIN (FUNCTION_FIRST_USER_PARMTYPE (fn))
- == void_list_node))
- return true;
- else
- return false;
- }
- else if (DECL_DESTRUCTOR_P (fn))
- return true;
- else if (DECL_ASSIGNMENT_OPERATOR_P (fn)
- && DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR)
- return copy_fn_p (fn);
- else
- return false;
-}
-
/* Remove all zero-width bit-fields from T. */
static void
@@ -4356,6 +4331,7 @@ check_bases_and_members (tree t)
tree access_decls;
bool saved_complex_asn_ref;
bool saved_nontrivial_dtor;
+ tree fn;
/* By default, we use const reference arguments and generate default
constructors. */
@@ -4453,6 +4429,31 @@ check_bases_and_members (tree t)
cant_have_const_ctor,
no_const_asn_ref);
+ /* Check defaulted declarations here so we have cant_have_const_ctor
+ and don't need to worry about clones. */
+ for (fn = TYPE_METHODS (t); fn; fn = TREE_CHAIN (fn))
+ if (DECL_DEFAULTED_IN_CLASS_P (fn))
+ {
+ int copy = copy_fn_p (fn);
+ if (copy > 0)
+ {
+ bool imp_const_p
+ = (DECL_CONSTRUCTOR_P (fn) ? !cant_have_const_ctor
+ : !no_const_asn_ref);
+ bool fn_const_p = (copy == 2);
+
+ if (fn_const_p && !imp_const_p)
+ /* If the function is defaulted outside the class, we just
+ give the synthesis error. */
+ error ("%q+D declared to take const reference, but implicit "
+ "declaration would take non-const", fn);
+ else if (imp_const_p && !fn_const_p)
+ error ("%q+D declared to take non-const reference cannot be "
+ "defaulted in the class body", fn);
+ }
+ defaulted_late_check (fn);
+ }
+
if (LAMBDA_TYPE_P (t))
{
/* "The closure type associated with a lambda-expression has a deleted
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3d826b9..ea28e9f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2815,10 +2815,18 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define DECL_DELETED_FN(DECL) \
(DECL_LANG_SPECIFIC (FUNCTION_DECL_CHECK (DECL))->u.base.threadprivate_or_deleted_p)
-/* Nonzero if DECL was declared with '= default'. */
+/* Nonzero if DECL was declared with '= default' (maybe implicitly). */
#define DECL_DEFAULTED_FN(DECL) \
(LANG_DECL_FN_CHECK (DECL)->defaulted_p)
+/* Nonzero if DECL is explicitly defaulted in the class body. */
+#define DECL_DEFAULTED_IN_CLASS_P(DECL) \
+ (DECL_DEFAULTED_FN (DECL) && DECL_INITIALIZED_IN_CLASS_P (DECL))
+/* Nonzero if DECL was defaulted outside the class body. */
+#define DECL_DEFAULTED_OUTSIDE_CLASS_P(DECL) \
+ (DECL_DEFAULTED_FN (DECL) \
+ && !(DECL_ARTIFICIAL (DECL) || DECL_INITIALIZED_IN_CLASS_P (DECL)))
+
/* Record whether a typedef for type `int' was actually `signed int'. */
#define C_TYPEDEF_EXPLICITLY_SIGNED(EXP) DECL_LANG_FLAG_1 (EXP)
@@ -4483,9 +4491,11 @@ extern void check_for_override (tree, tree);
extern void push_class_stack (void);
extern void pop_class_stack (void);
extern bool type_has_user_nondefault_constructor (tree);
+extern bool user_provided_p (tree);
extern bool type_has_user_provided_constructor (tree);
extern bool type_has_user_provided_default_constructor (tree);
-extern bool defaultable_fn_p (tree);
+extern void defaulted_late_check (tree);
+extern bool defaultable_fn_check (tree);
extern void fixup_type_variants (tree);
extern tree* decl_cloned_function_p (const_tree, bool);
extern void clone_function_decl (tree, int);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c772ca5..ead3f33 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -5603,17 +5603,10 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
}
else if (init == ridpointers[(int)RID_DEFAULT])
{
- if (!defaultable_fn_p (decl))
- {
- error ("%qD cannot be defaulted", decl);
- DECL_INITIAL (decl) = NULL_TREE;
- }
+ if (defaultable_fn_check (decl))
+ DECL_DEFAULTED_FN (decl) = 1;
else
- {
- DECL_DEFAULTED_FN (decl) = 1;
- FOR_EACH_CLONE (clone, decl)
- DECL_DEFAULTED_FN (clone) = 1;
- }
+ DECL_INITIAL (decl) = NULL_TREE;
}
}
@@ -9866,9 +9859,9 @@ grokparms (tree parmlist, tree *parms)
0 if D is not a copy constructor or copy assignment
operator.
1 if D is a copy constructor or copy assignment operator whose
- first parameter is a reference to const qualified T.
- 2 if D is a copy constructor or copy assignment operator whose
first parameter is a reference to non-const qualified T.
+ 2 if D is a copy constructor or copy assignment operator whose
+ first parameter is a reference to const qualified T.
This function can be used as a predicate. Positive values indicate
a copy constructor and nonzero values indicate a copy assignment
@@ -9977,10 +9970,6 @@ move_fn_p (const_tree d)
/* Remember any special properties of member function DECL. */
-#define DECL_DEFAULTED_IN_CLASS_P(DECL) \
- (DECL_DEFAULTED_FN (DECL) \
- && (DECL_ARTIFICIAL (DECL) || DECL_INITIALIZED_IN_CLASS_P (DECL)))
-
void
grok_special_member_properties (tree decl)
{
@@ -10007,7 +9996,7 @@ grok_special_member_properties (tree decl)
are no other parameters or else all other parameters have
default arguments. */
TYPE_HAS_INIT_REF (class_type) = 1;
- if (!DECL_DEFAULTED_IN_CLASS_P (decl))
+ if (user_provided_p (decl))
TYPE_HAS_COMPLEX_INIT_REF (class_type) = 1;
if (ctor > 1)
TYPE_HAS_CONST_INIT_REF (class_type) = 1;
@@ -10015,8 +10004,7 @@ grok_special_member_properties (tree decl)
else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl)))
{
TYPE_HAS_DEFAULT_CONSTRUCTOR (class_type) = 1;
- if (TREE_CODE (decl) == TEMPLATE_DECL
- || !DECL_DEFAULTED_IN_CLASS_P (decl))
+ if (user_provided_p (decl))
TYPE_HAS_COMPLEX_DFLT (class_type) = 1;
}
else if (is_list_ctor (decl))
@@ -10035,7 +10023,7 @@ grok_special_member_properties (tree decl)
if (assop)
{
TYPE_HAS_ASSIGN_REF (class_type) = 1;
- if (!DECL_DEFAULTED_IN_CLASS_P (decl))
+ if (user_provided_p (decl))
TYPE_HAS_COMPLEX_ASSIGN_REF (class_type) = 1;
if (assop != 1)
TYPE_HAS_CONST_ASSIGN_REF (class_type) = 1;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 3e8c0d7..592ee08 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -862,9 +862,7 @@ grokfield (const cp_declarator *declarator,
}
else if (init == ridpointers[(int)RID_DEFAULT])
{
- if (!defaultable_fn_p (value))
- error ("%qD cannot be defaulted", value);
- else
+ if (defaultable_fn_check (value))
{
DECL_DEFAULTED_FN (value) = 1;
DECL_INITIALIZED_IN_CLASS_P (value) = 1;
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index e8b28d8..266406c 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1130,6 +1130,88 @@ implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
return fn;
}
+/* Gives any errors about defaulted functions which need to be deferred
+ until the containing class is complete. */
+
+void
+defaulted_late_check (tree fn)
+{
+ /* Complain about invalid signature for defaulted fn. */
+ tree ctx = DECL_CONTEXT (fn);
+ special_function_kind kind = special_function_p (fn);
+ bool fn_const_p = (copy_fn_p (fn) == 2);
+ tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p);
+
+ if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+ TREE_TYPE (TREE_TYPE (implicit_fn)))
+ || !compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)),
+ TYPE_ARG_TYPES (TREE_TYPE (implicit_fn))))
+ {
+ error ("defaulted declaration %q+D", fn);
+ error_at (DECL_SOURCE_LOCATION (fn),
+ "does not match expected signature %qD", implicit_fn);
+ }
+}
+
+/* Returns true iff FN can be explicitly defaulted, and gives any
+ errors if defaulting FN is ill-formed. */
+
+bool
+defaultable_fn_check (tree fn)
+{
+ special_function_kind kind = sfk_none;
+
+ if (DECL_CONSTRUCTOR_P (fn))
+ {
+ if (FUNCTION_FIRST_USER_PARMTYPE (fn) == void_list_node)
+ kind = sfk_constructor;
+ else if (copy_fn_p (fn) > 0
+ && (TREE_CHAIN (FUNCTION_FIRST_USER_PARMTYPE (fn))
+ == void_list_node))
+ kind = sfk_copy_constructor;
+ else if (move_fn_p (fn))
+ kind = sfk_move_constructor;
+ }
+ else if (DECL_DESTRUCTOR_P (fn))
+ kind = sfk_destructor;
+ else if (DECL_ASSIGNMENT_OPERATOR_P (fn)
+ && DECL_OVERLOADED_OPERATOR_P (fn) == NOP_EXPR
+ && copy_fn_p (fn))
+ kind = sfk_assignment_operator;
+
+ if (kind == sfk_none)
+ {
+ error ("%qD cannot be defaulted", fn);
+ return false;
+ }
+ else
+ {
+ tree t = FUNCTION_FIRST_USER_PARMTYPE (fn);
+ for (; t && t != void_list_node; t = TREE_CHAIN (t))
+ if (TREE_PURPOSE (t))
+ {
+ error ("defaulted function %q+D with default argument", fn);
+ break;
+ }
+ if (TYPE_BEING_DEFINED (DECL_CONTEXT (fn)))
+ {
+ if (DECL_NONCONVERTING_P (fn))
+ error ("%qD declared explicit cannot be defaulted in the class "
+ "body", fn);
+ if (current_access_specifier != access_public_node)
+ error ("%qD declared with non-public access cannot be defaulted "
+ "in the class body", fn);
+ if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn)))
+ error ("function %q+D defaulted on its first declaration "
+ "must not have an exception-specification", fn);
+ }
+ else if (!processing_template_decl)
+ defaulted_late_check (fn);
+
+ return true;
+ }
+}
+
/* Add an implicit declaration to TYPE for the kind of function
indicated by SFK. Return the FUNCTION_DECL for the new implicit
declaration. */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index e80bc30..f480682 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8848,6 +8848,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
= remove_attribute ("visibility", DECL_ATTRIBUTES (r));
}
determine_visibility (r);
+ if (DECL_DEFAULTED_OUTSIDE_CLASS_P (r)
+ && !processing_template_decl)
+ defaulted_late_check (r);
apply_late_template_attributes (&r, DECL_ATTRIBUTES (r), 0,
args, complain, in_decl);
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
new file mode 100644
index 0000000..092b560
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
@@ -0,0 +1,43 @@
+// PR c++/38796
+// { dg-options -std=c++0x }
+
+struct A
+{
+ A (int);
+ A (const A& = 1) = default; // { dg-error "default argument" }
+ void operator= (const A&) = default; // { dg-error "defaulted|match" }
+};
+
+struct B
+{
+private:
+ B() = default; // { dg-error "access" }
+};
+
+struct C
+{
+protected:
+ ~C() = default; // { dg-error "access" }
+};
+
+struct D
+{
+private:
+ D& operator= (const D&) = default; // { dg-error "access" }
+};
+
+struct E
+{
+ explicit E (const E&) = default; // { dg-error "explicit" }
+};
+
+struct F
+{
+ F(F&) = default; // { dg-error "non-const" }
+};
+
+struct G: public F
+{
+ // Can't be const because F copy ctor isn't.
+ G(const G&) = default; // { dg-error "const" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted16.C b/gcc/testsuite/g++.dg/cpp0x/defaulted16.C
new file mode 100644
index 0000000..741b43d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted16.C
@@ -0,0 +1,13 @@
+// Test that non-inline default causes the function to be defined even if
+// it isn't used.
+
+// { dg-options -std=c++0x }
+// { dg-final { scan-assembler "_ZN1AC1Ev" } }
+
+struct A
+{
+ A();
+};
+
+A::A() = default;
+
diff --git a/libstdc++-v3/include/std/future b/libstdc++-v3/include/std/future
index f922dcd..00f5c48 100644
--- a/libstdc++-v3/include/std/future
+++ b/libstdc++-v3/include/std/future
@@ -130,9 +130,11 @@ namespace std
};
protected:
- ~_Future_result_base() = default;
+ ~_Future_result_base();
};
+ inline _Future_result_base::~_Future_result_base() = default;
+
// TODO: use template alias when available
/*
template<typename _Res>
diff --git a/libstdc++-v3/include/std/system_error b/libstdc++-v3/include/std/system_error
index 7f462a2..8647411 100644
--- a/libstdc++-v3/include/std/system_error
+++ b/libstdc++-v3/include/std/system_error
@@ -64,7 +64,7 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
class error_category
{
protected:
- error_category() = default;
+ error_category();
public:
virtual ~error_category() { }
@@ -100,6 +100,8 @@ _GLIBCXX_BEGIN_NAMESPACE(std)
{ return this != &__other; }
};
+ inline error_category::error_category() = default;
+
// DR 890.
_GLIBCXX_CONST const error_category& system_category() throw ();
_GLIBCXX_CONST const error_category& generic_category() throw ();
diff --git a/libstdc++-v3/libsupc++/nested_exception.h b/libstdc++-v3/libsupc++/nested_exception.h
index 752c595..d4e1347 100644
--- a/libstdc++-v3/libsupc++/nested_exception.h
+++ b/libstdc++-v3/libsupc++/nested_exception.h
@@ -57,9 +57,9 @@ namespace std
public:
nested_exception() throw() : _M_ptr(current_exception()) { }
- nested_exception(const nested_exception&) throw() = default;
+ nested_exception(const nested_exception&) = default;
- nested_exception& operator=(const nested_exception&) throw() = default;
+ nested_exception& operator=(const nested_exception&) = default;
virtual ~nested_exception() = default;
diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/assign_neg.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/assign_neg.cc
index 0bad6ba..588a27e 100644
--- a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/assign_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/assign_neg.cc
@@ -33,4 +33,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 32 }
-// { dg-error "deleted function" "" { target *-*-* } 862 }
+// { dg-error "deleted function" "" { target *-*-* } 864 }
diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/copy_neg.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/copy_neg.cc
index 655ca8c..d0d0622 100644
--- a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/copy_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/copy_neg.cc
@@ -32,4 +32,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 31 }
-// { dg-error "deleted function" "" { target *-*-* } 861 }
+// { dg-error "deleted function" "" { target *-*-* } 863 }
diff --git a/libstdc++-v3/testsuite/30_threads/promise/cons/assign_neg.cc b/libstdc++-v3/testsuite/30_threads/promise/cons/assign_neg.cc
index f2fbf17..b97d3ba 100644
--- a/libstdc++-v3/testsuite/30_threads/promise/cons/assign_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/promise/cons/assign_neg.cc
@@ -33,4 +33,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 32 }
-// { dg-error "deleted function" "" { target *-*-* } 588 }
+// { dg-error "deleted function" "" { target *-*-* } 590 }
diff --git a/libstdc++-v3/testsuite/30_threads/promise/cons/copy_neg.cc b/libstdc++-v3/testsuite/30_threads/promise/cons/copy_neg.cc
index 17757ae..f94cffb 100644
--- a/libstdc++-v3/testsuite/30_threads/promise/cons/copy_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/promise/cons/copy_neg.cc
@@ -32,4 +32,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 31 }
-// { dg-error "deleted function" "" { target *-*-* } 572 }
+// { dg-error "deleted function" "" { target *-*-* } 574 }
diff --git a/libstdc++-v3/testsuite/30_threads/shared_future/cons/assign_neg.cc b/libstdc++-v3/testsuite/30_threads/shared_future/cons/assign_neg.cc
index 868e0b8..61563e3 100644
--- a/libstdc++-v3/testsuite/30_threads/shared_future/cons/assign_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/shared_future/cons/assign_neg.cc
@@ -35,4 +35,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 34 }
-// { dg-error "deleted function" "" { target *-*-* } 481 }
+// { dg-error "deleted function" "" { target *-*-* } 483 }
diff --git a/libstdc++-v3/testsuite/30_threads/unique_future/cons/assign_neg.cc b/libstdc++-v3/testsuite/30_threads/unique_future/cons/assign_neg.cc
index f8f5a69..9783124 100644
--- a/libstdc++-v3/testsuite/30_threads/unique_future/cons/assign_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/unique_future/cons/assign_neg.cc
@@ -35,4 +35,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 34 }
-// { dg-error "deleted function" "" { target *-*-* } 401 }
+// { dg-error "deleted function" "" { target *-*-* } 403 }
diff --git a/libstdc++-v3/testsuite/30_threads/unique_future/cons/copy_neg.cc b/libstdc++-v3/testsuite/30_threads/unique_future/cons/copy_neg.cc
index 95b5a1d..4b59899 100644
--- a/libstdc++-v3/testsuite/30_threads/unique_future/cons/copy_neg.cc
+++ b/libstdc++-v3/testsuite/30_threads/unique_future/cons/copy_neg.cc
@@ -34,4 +34,4 @@ void test01()
}
// { dg-error "used here" "" { target *-*-* } 33 }
-// { dg-error "deleted function" "" { target *-*-* } 400 }
+// { dg-error "deleted function" "" { target *-*-* } 402 }