diff mbox series

c++: deleting explicitly-defaulted functions [PR116162]

Message ID 20240911165411.459519-1-polacek@redhat.com
State New
Headers show
Series c++: deleting explicitly-defaulted functions [PR116162] | expand

Commit Message

Marek Polacek Sept. 11, 2024, 4:54 p.m. UTC
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This PR points out the we're not implementing [dcl.fct.def.default]
properly.  Consider e.g.

  struct C {
     C(const C&&) = default;
  };

where we wrongly emit an error, but the move ctor should be just =deleted.
According to [dcl.fct.def.default], if the type of the special member
function differs from the type of the corresponding special member function
that would have been implicitly declared in a way other than as allowed
by 2.1-4, the function is defined as deleted.  There's an exception for
assignment operators in which case the program is ill-formed.

clang++ has a warning for when we delete an explicitly-defaulted function
so this patch adds it too.  I'm also downgrading an error to a pedwarn
in C++17 since the code compiles in C++20.

	PR c++/116162

gcc/c-family/ChangeLog:

	* c.opt (Wdefaulted-function-deleted): New.

gcc/cp/ChangeLog:

	* class.cc (check_bases_and_members): Call delete_defaulted_fn to set
	DECL_DELETED_FN.
	* cp-tree.h (delete_defaulted_fn): Declare.
	* method.cc (delete_defaulted_fn): New.
	(defaulted_late_check): Call delete_defaulted_fn instead of giving
	an error, unless the code is ill-formed.  Change error to pedwarn.

gcc/ChangeLog:

	* doc/invoke.texi: Document -Wdefaulted-function-deleted.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/defaulted15.C: Add dg-warning.
	* g++.dg/cpp0x/defaulted51.C: Likewise.
	* g++.dg/cpp0x/defaulted52.C: Likewise.
	* g++.dg/cpp0x/defaulted53.C: Likewise.
	* g++.dg/cpp0x/defaulted54.C: Likewise.
	* g++.dg/cpp0x/defaulted56.C: Likewise.
	* g++.dg/cpp0x/defaulted57.C: Likewise.
	* g++.dg/cpp0x/defaulted58.C: Likewise.
	* g++.dg/cpp0x/defaulted59.C: Likewise.
	* g++.dg/cpp0x/defaulted63.C: New test.
	* g++.dg/cpp0x/defaulted64.C: New test.
	* g++.dg/cpp0x/defaulted65.C: New test.
	* g++.dg/cpp23/defaulted1.C: New test.
---
 gcc/c-family/c.opt                       |  4 ++
 gcc/cp/class.cc                          |  2 +-
 gcc/cp/cp-tree.h                         |  1 +
 gcc/cp/method.cc                         | 90 +++++++++++++++++++++---
 gcc/doc/invoke.texi                      |  9 +++
 gcc/testsuite/g++.dg/cpp0x/defaulted15.C |  2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted51.C |  3 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted52.C |  2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted53.C |  2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted54.C |  1 +
 gcc/testsuite/g++.dg/cpp0x/defaulted56.C |  6 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted57.C |  6 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted58.C |  1 +
 gcc/testsuite/g++.dg/cpp0x/defaulted59.C |  2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted63.C | 39 ++++++++++
 gcc/testsuite/g++.dg/cpp0x/defaulted64.C | 27 +++++++
 gcc/testsuite/g++.dg/cpp0x/defaulted65.C | 25 +++++++
 gcc/testsuite/g++.dg/cpp23/defaulted1.C  | 23 ++++++
 18 files changed, 226 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted63.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted64.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted65.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/defaulted1.C


base-commit: 670cfd5fe6433ee8f2e86eedb197d2523dbb033b

Comments

Jason Merrill Sept. 11, 2024, 5:19 p.m. UTC | #1
On 9/11/24 12:54 PM, Marek Polacek wrote:
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> -- >8 --
> This PR points out the we're not implementing [dcl.fct.def.default]
> properly.  Consider e.g.
> 
>    struct C {
>       C(const C&&) = default;
>    };
> 
> where we wrongly emit an error, but the move ctor should be just =deleted.
> According to [dcl.fct.def.default], if the type of the special member
> function differs from the type of the corresponding special member function
> that would have been implicitly declared in a way other than as allowed
> by 2.1-4, the function is defined as deleted.  There's an exception for
> assignment operators in which case the program is ill-formed.
> 
> clang++ has a warning for when we delete an explicitly-defaulted function
> so this patch adds it too.  I'm also downgrading an error to a pedwarn
> in C++17 since the code compiles in C++20.
> 
> 	PR c++/116162
> 
> gcc/c-family/ChangeLog:
> 
> 	* c.opt (Wdefaulted-function-deleted): New.
> 
> gcc/cp/ChangeLog:
> 
> 	* class.cc (check_bases_and_members): Call delete_defaulted_fn to set
> 	DECL_DELETED_FN.
> 	* cp-tree.h (delete_defaulted_fn): Declare.
> 	* method.cc (delete_defaulted_fn): New.
> 	(defaulted_late_check): Call delete_defaulted_fn instead of giving
> 	an error, unless the code is ill-formed.  Change error to pedwarn.
> 
> gcc/ChangeLog:
> 
> 	* doc/invoke.texi: Document -Wdefaulted-function-deleted.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/defaulted15.C: Add dg-warning.
> 	* g++.dg/cpp0x/defaulted51.C: Likewise.
> 	* g++.dg/cpp0x/defaulted52.C: Likewise.
> 	* g++.dg/cpp0x/defaulted53.C: Likewise.
> 	* g++.dg/cpp0x/defaulted54.C: Likewise.
> 	* g++.dg/cpp0x/defaulted56.C: Likewise.
> 	* g++.dg/cpp0x/defaulted57.C: Likewise.
> 	* g++.dg/cpp0x/defaulted58.C: Likewise.
> 	* g++.dg/cpp0x/defaulted59.C: Likewise.
> 	* g++.dg/cpp0x/defaulted63.C: New test.
> 	* g++.dg/cpp0x/defaulted64.C: New test.
> 	* g++.dg/cpp0x/defaulted65.C: New test.
> 	* g++.dg/cpp23/defaulted1.C: New test.
> ---
>   gcc/c-family/c.opt                       |  4 ++
>   gcc/cp/class.cc                          |  2 +-
>   gcc/cp/cp-tree.h                         |  1 +
>   gcc/cp/method.cc                         | 90 +++++++++++++++++++++---
>   gcc/doc/invoke.texi                      |  9 +++
>   gcc/testsuite/g++.dg/cpp0x/defaulted15.C |  2 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted51.C |  3 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted52.C |  2 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted53.C |  2 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted54.C |  1 +
>   gcc/testsuite/g++.dg/cpp0x/defaulted56.C |  6 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted57.C |  6 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted58.C |  1 +
>   gcc/testsuite/g++.dg/cpp0x/defaulted59.C |  2 +-
>   gcc/testsuite/g++.dg/cpp0x/defaulted63.C | 39 ++++++++++
>   gcc/testsuite/g++.dg/cpp0x/defaulted64.C | 27 +++++++
>   gcc/testsuite/g++.dg/cpp0x/defaulted65.C | 25 +++++++
>   gcc/testsuite/g++.dg/cpp23/defaulted1.C  | 23 ++++++
>   18 files changed, 226 insertions(+), 19 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted63.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted64.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted65.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/defaulted1.C
> 
> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
> index 491aa02e1a3..f5136fd2341 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -619,6 +619,10 @@ Wdeclaration-missing-parameter-type
>   C ObjC Var(warn_declaration_missing_parameter) Warning Init(1)
>   Warn for missing parameter types in function declarations.
>   
> +Wdefaulted-function-deleted
> +C++ ObjC++ Var(warn_defaulted_fn_deleted) Init(1) Warning
> +Warn when an explicitly defaulted function is deleted.
> +
>   Wdelete-incomplete
>   C++ ObjC++ Var(warn_delete_incomplete) Init(1) Warning
>   Warn when deleting a pointer to incomplete type.
> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> index 950d83b0ea4..a4fdf7f9d11 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -6506,7 +6506,7 @@ check_bases_and_members (tree t)
>   	      /* If the function is defaulted outside the class, we just
>   		 give the synthesis error.  Core Issue #1331 says this is
>   		 no longer ill-formed, it is defined as deleted instead.  */
> -	      DECL_DELETED_FN (fn) = true;
> +	      delete_defaulted_fn (fn);
>   	  }
>   	defaulted_late_check (fn);
>         }
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 7baa2ccbe1e..65295b3326d 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6929,6 +6929,7 @@ extern bool type_build_ctor_call		(tree);
>   extern bool type_build_dtor_call		(tree);
>   extern void explain_non_literal_class		(tree);
>   extern void inherit_targ_abi_tags		(tree);
> +extern void delete_defaulted_fn			(tree);
>   extern void defaulted_late_check		(tree);
>   extern bool defaultable_fn_check		(tree);
>   extern void check_abi_tags			(tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 68a776d2c5a..92fb1a0b238 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -3503,6 +3503,53 @@ implicitly_declare_fn (special_function_kind kind, tree type,
>     return fn;
>   }
>   
> +/* Mark an explicitly defaulted function FN as =deleted and warn.  */
> +
> +void
> +delete_defaulted_fn (tree fn)
> +{
> +  DECL_DELETED_FN (fn) = true;
> +  if (warn_defaulted_fn_deleted)
> +    {
> +      auto_diagnostic_group d;
> +      const char *wmsg, *imsg;
> +      switch (special_function_p (fn))
> +	{
> +	case sfk_copy_constructor:
> +	  wmsg = G_("explicitly defaulted copy constructor is "
> +		    "implicitly deleted");
> +	  imsg = G_("function is implicitly deleted because its declared type "
> +		    "does not match the type of an implicit copy constructor");
> +	  break;
> +	case sfk_move_constructor:
> +	  wmsg = G_("explicitly defaulted move constructor is "
> +		    "implicitly deleted");
> +	  imsg = G_("function is implicitly deleted because its declared type "
> +		    "does not match the type of an implicit move constructor");
> +	  break;
> +	case sfk_copy_assignment:
> +	  wmsg = G_("explicitly defaulted copy assignment operator is "
> +		    "implicitly deleted");
> +	  imsg = G_("function is implicitly deleted because its declared type "
> +		    "does not match the type of an implicit copy assignment "
> +		    "operator");
> +	  break;
> +	case sfk_move_assignment:
> +	  wmsg = G_("explicitly defaulted move assignment operator is "
> +		    "implicitly deleted");
> +	  imsg = G_("function is implicitly deleted because its declared type "
> +		    "does not match the type of an implicit move assignment "
> +		    "operator");
> +	  break;
> +	default:
> +	  gcc_unreachable ();
> +	}
> +      if (warning_at (DECL_SOURCE_LOCATION (fn),
> +		      OPT_Wdefaulted_function_deleted, wmsg))
> +	inform (DECL_SOURCE_LOCATION (fn), imsg);
> +    }
> +}
> +
>   /* Gives any errors about defaulted functions which need to be deferred
>      until the containing class is complete.  */
>   
> @@ -3555,15 +3602,40 @@ defaulted_late_check (tree fn)
>       return compparms (fn_parms, implicit_fn_parms);
>     };
>   
> -  if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
> -		    TREE_TYPE (TREE_TYPE (implicit_fn)))
> -      || !compare_fn_params (fn, implicit_fn))
> -    {
> -      auto_diagnostic_group d;
> -      error ("defaulted declaration %q+D does not match the "
> -	     "expected signature", fn);
> -      inform (DECL_SOURCE_LOCATION (fn),
> -	      "expected signature: %qD", implicit_fn);
> +  const bool rettypes_same_p
> +    = same_type_p (TREE_TYPE (TREE_TYPE (fn)),
> +		   TREE_TYPE (TREE_TYPE (implicit_fn)));
> +  if (!rettypes_same_p || !compare_fn_params (fn, implicit_fn))
> +    {
> +      tree parmtype
> +	= TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
> +		      ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
> +		      : FUNCTION_FIRST_USER_PARMTYPE (fn));
> +      const bool illformed_p
> +	/* [dcl.fct.def.default] "if F1 is an assignment operator"...  */
> +	= (SFK_ASSIGN_P (kind)
> +	   /* "and the return type of F1 differs from the return type of F2"  */
> +	   && (!rettypes_same_p
> +	       /* "or F1's non-object parameter type is not a reference,
> +		  the program is ill-formed"  */
> +	      || !TYPE_REF_P (parmtype)));
> +
> +      if (!illformed_p
> +	  && cxx_dialect >= cxx20
> +	  && !DECL_ARTIFICIAL (fn)
> +	  && DECL_DEFAULTED_IN_CLASS_P (fn))
> +	delete_defaulted_fn (fn);
> +      else
> +	{
> +	  auto_diagnostic_group d;
> +	  /* We used to emit a hard error, so this uses 0 rather than
> +	     OPT_Wpedantic.  */
> +	  if (pedwarn (DECL_SOURCE_LOCATION (fn), 0,
> +		       "defaulted declaration %q+D does not match the "
> +		       "expected signature", fn))
> +	    inform (DECL_SOURCE_LOCATION (fn),
> +		    "expected signature: %qD", implicit_fn);

This should also depend on -Wdefaulted-function-deleted, and set 
DECL_DELETED_FN.  And the C++20 case should show the expected signature. 
  Really, the two cases should share the same code, only the diagnostic 
kind should change.

Jason
diff mbox series

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 491aa02e1a3..f5136fd2341 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -619,6 +619,10 @@  Wdeclaration-missing-parameter-type
 C ObjC Var(warn_declaration_missing_parameter) Warning Init(1)
 Warn for missing parameter types in function declarations.
 
+Wdefaulted-function-deleted
+C++ ObjC++ Var(warn_defaulted_fn_deleted) Init(1) Warning
+Warn when an explicitly defaulted function is deleted.
+
 Wdelete-incomplete
 C++ ObjC++ Var(warn_delete_incomplete) Init(1) Warning
 Warn when deleting a pointer to incomplete type.
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 950d83b0ea4..a4fdf7f9d11 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -6506,7 +6506,7 @@  check_bases_and_members (tree t)
 	      /* If the function is defaulted outside the class, we just
 		 give the synthesis error.  Core Issue #1331 says this is
 		 no longer ill-formed, it is defined as deleted instead.  */
-	      DECL_DELETED_FN (fn) = true;
+	      delete_defaulted_fn (fn);
 	  }
 	defaulted_late_check (fn);
       }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7baa2ccbe1e..65295b3326d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6929,6 +6929,7 @@  extern bool type_build_ctor_call		(tree);
 extern bool type_build_dtor_call		(tree);
 extern void explain_non_literal_class		(tree);
 extern void inherit_targ_abi_tags		(tree);
+extern void delete_defaulted_fn			(tree);
 extern void defaulted_late_check		(tree);
 extern bool defaultable_fn_check		(tree);
 extern void check_abi_tags			(tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 68a776d2c5a..92fb1a0b238 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3503,6 +3503,53 @@  implicitly_declare_fn (special_function_kind kind, tree type,
   return fn;
 }
 
+/* Mark an explicitly defaulted function FN as =deleted and warn.  */
+
+void
+delete_defaulted_fn (tree fn)
+{
+  DECL_DELETED_FN (fn) = true;
+  if (warn_defaulted_fn_deleted)
+    {
+      auto_diagnostic_group d;
+      const char *wmsg, *imsg;
+      switch (special_function_p (fn))
+	{
+	case sfk_copy_constructor:
+	  wmsg = G_("explicitly defaulted copy constructor is "
+		    "implicitly deleted");
+	  imsg = G_("function is implicitly deleted because its declared type "
+		    "does not match the type of an implicit copy constructor");
+	  break;
+	case sfk_move_constructor:
+	  wmsg = G_("explicitly defaulted move constructor is "
+		    "implicitly deleted");
+	  imsg = G_("function is implicitly deleted because its declared type "
+		    "does not match the type of an implicit move constructor");
+	  break;
+	case sfk_copy_assignment:
+	  wmsg = G_("explicitly defaulted copy assignment operator is "
+		    "implicitly deleted");
+	  imsg = G_("function is implicitly deleted because its declared type "
+		    "does not match the type of an implicit copy assignment "
+		    "operator");
+	  break;
+	case sfk_move_assignment:
+	  wmsg = G_("explicitly defaulted move assignment operator is "
+		    "implicitly deleted");
+	  imsg = G_("function is implicitly deleted because its declared type "
+		    "does not match the type of an implicit move assignment "
+		    "operator");
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      if (warning_at (DECL_SOURCE_LOCATION (fn),
+		      OPT_Wdefaulted_function_deleted, wmsg))
+	inform (DECL_SOURCE_LOCATION (fn), imsg);
+    }
+}
+
 /* Gives any errors about defaulted functions which need to be deferred
    until the containing class is complete.  */
 
@@ -3555,15 +3602,40 @@  defaulted_late_check (tree fn)
     return compparms (fn_parms, implicit_fn_parms);
   };
 
-  if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
-		    TREE_TYPE (TREE_TYPE (implicit_fn)))
-      || !compare_fn_params (fn, implicit_fn))
-    {
-      auto_diagnostic_group d;
-      error ("defaulted declaration %q+D does not match the "
-	     "expected signature", fn);
-      inform (DECL_SOURCE_LOCATION (fn),
-	      "expected signature: %qD", implicit_fn);
+  const bool rettypes_same_p
+    = same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+		   TREE_TYPE (TREE_TYPE (implicit_fn)));
+  if (!rettypes_same_p || !compare_fn_params (fn, implicit_fn))
+    {
+      tree parmtype
+	= TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+		      ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
+		      : FUNCTION_FIRST_USER_PARMTYPE (fn));
+      const bool illformed_p
+	/* [dcl.fct.def.default] "if F1 is an assignment operator"...  */
+	= (SFK_ASSIGN_P (kind)
+	   /* "and the return type of F1 differs from the return type of F2"  */
+	   && (!rettypes_same_p
+	       /* "or F1's non-object parameter type is not a reference,
+		  the program is ill-formed"  */
+	      || !TYPE_REF_P (parmtype)));
+
+      if (!illformed_p
+	  && cxx_dialect >= cxx20
+	  && !DECL_ARTIFICIAL (fn)
+	  && DECL_DEFAULTED_IN_CLASS_P (fn))
+	delete_defaulted_fn (fn);
+      else
+	{
+	  auto_diagnostic_group d;
+	  /* We used to emit a hard error, so this uses 0 rather than
+	     OPT_Wpedantic.  */
+	  if (pedwarn (DECL_SOURCE_LOCATION (fn), 0,
+		       "defaulted declaration %q+D does not match the "
+		       "expected signature", fn))
+	    inform (DECL_SOURCE_LOCATION (fn),
+		    "expected signature: %qD", implicit_fn);
+	}
     }
 
   if (DECL_DELETED_FN (implicit_fn))
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9a86a9a181..ca9e745f62e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -251,6 +251,7 @@  in the following sections.
 -Wcomma-subscript  -Wconditionally-supported
 -Wno-conversion-null  -Wctad-maybe-unsupported
 -Wctor-dtor-privacy  -Wdangling-reference
+-Wno-defaulted-function-deleted
 -Wno-delete-incomplete
 -Wdelete-non-virtual-dtor  -Wno-deprecated-array-compare
 -Wdeprecated-copy -Wdeprecated-copy-dtor
@@ -4797,6 +4798,14 @@  not caught by reference. @option{-Wcatch-value} is enabled by @option{-Wall}.
 @item -Wconditionally-supported @r{(C++ and Objective-C++ only)}
 Warn for conditionally-supported (C++11 [intro.defs]) constructs.
 
+@opindex Wdefaulted-function-deleted
+@opindex Wno-defaulted-function-deleted
+@item -Wno-defaulted-function-deleted @r{(C++ and Objective-C++ only)}
+Warn when an explicitly defaulted function is deleted by the compiler.
+That can occur when the function's declared type does not match the type
+of the function that would have been implicitly declared.  This warning
+is enabled by default.
+
 @opindex Wdelete-incomplete
 @opindex Wno-delete-incomplete
 @item -Wno-delete-incomplete @r{(C++ and Objective-C++ only)}
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
index 1e0b3545840..a2f7c438b95 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
@@ -48,7 +48,7 @@  struct F
 
 struct G: public F
 {
-  G(const G&) = default;
+  G(const G&) = default;  // { dg-warning "implicitly deleted" }
 };
 
 struct H
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
index 0a7d308707c..561f8998177 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
@@ -4,7 +4,8 @@ 
 template<int> struct A
 {
   A();
-  A(volatile A&) = default;  // { dg-error "defaulted" }
+  A(volatile A&) = default;  // { dg-error "defaulted" "" { target c++17_down } }
+			     // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
 };
 
 struct B
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted52.C b/gcc/testsuite/g++.dg/cpp0x/defaulted52.C
index c617230b493..8a8a1cfb09e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted52.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted52.C
@@ -13,7 +13,7 @@  template<typename T> struct W
 {
   W();
   // This should now compile and be =deleted.
-  W(const W&) = default;
+  W(const W&) = default; // { dg-warning "implicitly deleted" }
   T t;
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
index 8147e7e2ad1..786051adae0 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
@@ -14,7 +14,7 @@  struct R
 
 struct S
 {
-  S& operator=(const S&) = default;
+  S& operator=(const S&) = default; // { dg-warning "implicitly deleted" }
   M m;
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted54.C b/gcc/testsuite/g++.dg/cpp0x/defaulted54.C
index f8ddc4e47ce..35ab94ff1f6 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted54.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted54.C
@@ -11,6 +11,7 @@  template<typename T> struct W
 {
   W();
   W(const W&) = default; // { dg-error "binding" }
+// { dg-warning "implicitly deleted" "" { target *-*-* } .-1 }
   T t;
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
index e7ce12c5566..0e36fd293f6 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
@@ -11,12 +11,14 @@  struct S
 
 struct T
 {
-  constexpr T(volatile T &) = default; // { dg-error "defaulted" }
+  constexpr T(volatile T &) = default; // { dg-error "defaulted" "" { target c++17_down } }
+				       // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
 };
 
 struct U
 {
-  constexpr U(const volatile U &) = default; // { dg-error "defaulted" }
+  constexpr U(const volatile U &) = default; // { dg-error "defaulted" "" { target c++17_down } }
+					     // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
 };
 
 struct V
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
index 37fb7dd6e1d..feca9662b4a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
@@ -11,12 +11,14 @@  struct S
 
 struct T
 {
-  T& operator=(volatile T &) = default; // { dg-error "defaulted" }
+  T& operator=(volatile T &) = default; // { dg-error "defaulted" "" { target c++17_down } }
+					// { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
 };
 
 struct U
 {
-  U& operator=(const volatile U &) = default; // { dg-error "defaulted" }
+  U& operator=(const volatile U &) = default; // { dg-error "defaulted" "" { target c++17_down } }
+					      // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
 };
 
 struct V
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted58.C b/gcc/testsuite/g++.dg/cpp0x/defaulted58.C
index 920a4ad0c6d..e499414cd62 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted58.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted58.C
@@ -11,6 +11,7 @@  template<typename T> struct W
 {
   W() = default;
   W& operator=(const W&) = default; // { dg-error "binding" }
+// { dg-warning "implicitly deleted" "" { target *-*-* } .-1 }
   T t;
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
index 4f871d7f5b1..1c13f4066dd 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
@@ -8,5 +8,5 @@  struct M
 
 struct W : public M
 {
-  W(const W&) = default;
+  W(const W&) = default; // { dg-warning "implicitly deleted" }
 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted63.C b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C
new file mode 100644
index 00000000000..99f92ff69c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C
@@ -0,0 +1,39 @@ 
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+
+struct C0 {
+  C0(C0&) = default;
+};
+
+struct C1 {
+  C1(volatile C1&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } }
+			      // { dg-error "does not match" "" { target c++17_down } .-1 }
+};
+
+struct C2 {
+  C2(const C2&) = default;
+};
+
+struct C3 {
+  C3(const volatile C3&) = default;  // { dg-warning "implicitly deleted" "" { target c++20 } }
+				      // { dg-error "does not match" "" { target c++17_down } .-1 }
+};
+
+struct M0 {
+  M0(M0&&) = default;
+};
+
+struct M1 {
+  M1(const M1&&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } }
+			    // { dg-error "does not match" "" { target c++17_down } .-1 }
+};
+
+struct M2 {
+  M2(volatile M2&&) = default;	// { dg-warning "implicitly deleted" "" { target c++20 } }
+				// { dg-error "does not match" "" { target c++17_down } .-1 }
+};
+
+struct M3 {
+  M3(const volatile M3&&) = default;  // { dg-warning "implicitly deleted" "" { target c++20 } }
+				      // { dg-error "does not match" "" { target c++17_down } .-1 }
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted64.C b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C
new file mode 100644
index 00000000000..f20030192c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C
@@ -0,0 +1,27 @@ 
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+
+struct M
+{
+  M& operator=(M&&);
+};
+
+struct R
+{
+  R& operator=(R&&) = default;
+  M m;
+};
+
+struct S
+{
+  S& operator=(const S&&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } }
+				     // { dg-error "does not match" "" { target c++17_down } .-1 }
+
+  M m;
+};
+
+struct T
+{
+  T operator=(T&&) = default; // { dg-error "defaulted" }
+  M m;
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted65.C b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C
new file mode 100644
index 00000000000..88ca1d96084
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C
@@ -0,0 +1,25 @@ 
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+
+struct S
+{
+  S& operator=(S &&) = default;
+};
+
+struct T
+{
+  T& operator=(volatile T &&) = default; // { dg-error "defaulted" "" { target c++17_down } }
+					 // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+};
+
+struct U
+{
+  U& operator=(const volatile U &&) = default; // { dg-error "defaulted" "" { target c++17_down } }
+					       // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+};
+
+struct V
+{
+  V& operator=(const V &&) = default; // { dg-error "defaulted" "" { target c++17_down } }
+				      // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/defaulted1.C b/gcc/testsuite/g++.dg/cpp23/defaulted1.C
new file mode 100644
index 00000000000..00cf894fa1d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/defaulted1.C
@@ -0,0 +1,23 @@ 
+// PR c++/116162
+// { dg-do compile { target c++23 } }
+
+struct M
+{
+  M& operator=(M&);
+};
+
+struct T
+{
+  // if F1 is an assignment operator, and the return type of F1 differs
+  // from the return type,  the program is ill-formed.
+  T operator=(this T&, T&) = default; // { dg-error "defaulted" }
+  M m;
+};
+
+struct U
+{
+  // if F1's non-object parameter type is not a reference, the program
+  // is ill-formed.
+  U& operator=(this U&, U) = default; // { dg-error "defaulted" }
+  M m;
+};