c++: merge default targs for function templates [PR65396]

Message ID 20220303200653.3352485-1-ppalka@redhat.com
State New
Headers
Series c++: merge default targs for function templates [PR65396] |

Commit Message

Patrick Palka March 3, 2022, 8:06 p.m. UTC
  We currently merge default template arguments for class templates, but
not for function templates.  This patch fixes this by splitting out the
argument merging logic in redeclare_class_template into a separate
function and using it in duplicate_decls as well.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

	PR c++/65396

gcc/cp/ChangeLog:

	* cp-tree.h (merge_default_template_args): Declare.
	* decl.cc (merge_default_template_args): Define, split out from
	redeclare_class_template.
	(duplicate_decls): Use it when merging member function template
	and free function declarations.
	* pt.cc (redeclare_class_template): Split out default argument
	merging logic into merge_default_template_args.  Improve location
	of a note when there's a template parameter kind mismatch.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/vt-34314.C: Adjust expected location of
	"redeclared here" note.
	* g++.dg/template/pr92440.C: Likewise.
	* g++.old-deja/g++.pt/redecl1.C: Adjust expected location of
	"redefinition of default argument" error.
	* g++.dg/template/defarg23.C: New test.
	* g++.dg/template/defarg23a.C: New test.
---
 gcc/cp/cp-tree.h                            |  1 +
 gcc/cp/decl.cc                              | 60 ++++++++++++++++++++-
 gcc/cp/pt.cc                                | 31 ++---------
 gcc/testsuite/g++.dg/cpp0x/vt-34314.C       | 12 ++---
 gcc/testsuite/g++.dg/template/defarg23.C    | 21 ++++++++
 gcc/testsuite/g++.dg/template/defarg23a.C   | 24 +++++++++
 gcc/testsuite/g++.dg/template/pr92440.C     |  4 +-
 gcc/testsuite/g++.old-deja/g++.pt/redecl1.C | 12 ++---
 8 files changed, 123 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/defarg23.C
 create mode 100644 gcc/testsuite/g++.dg/template/defarg23a.C
  

Comments

Jason Merrill March 8, 2022, 10:18 p.m. UTC | #1
On 3/3/22 16:06, Patrick Palka wrote:
> We currently merge default template arguments for class templates, but
> not for function templates.  This patch fixes this by splitting out the
> argument merging logic in redeclare_class_template into a separate
> function and using it in duplicate_decls as well.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?
> 
> 	PR c++/65396
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (merge_default_template_args): Declare.
> 	* decl.cc (merge_default_template_args): Define, split out from
> 	redeclare_class_template.
> 	(duplicate_decls): Use it when merging member function template
> 	and free function declarations.
> 	* pt.cc (redeclare_class_template): Split out default argument
> 	merging logic into merge_default_template_args.  Improve location
> 	of a note when there's a template parameter kind mismatch.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/vt-34314.C: Adjust expected location of
> 	"redeclared here" note.
> 	* g++.dg/template/pr92440.C: Likewise.
> 	* g++.old-deja/g++.pt/redecl1.C: Adjust expected location of
> 	"redefinition of default argument" error.
> 	* g++.dg/template/defarg23.C: New test.
> 	* g++.dg/template/defarg23a.C: New test.
> ---
>   gcc/cp/cp-tree.h                            |  1 +
>   gcc/cp/decl.cc                              | 60 ++++++++++++++++++++-
>   gcc/cp/pt.cc                                | 31 ++---------
>   gcc/testsuite/g++.dg/cpp0x/vt-34314.C       | 12 ++---
>   gcc/testsuite/g++.dg/template/defarg23.C    | 21 ++++++++
>   gcc/testsuite/g++.dg/template/defarg23a.C   | 24 +++++++++
>   gcc/testsuite/g++.dg/template/pr92440.C     |  4 +-
>   gcc/testsuite/g++.old-deja/g++.pt/redecl1.C | 12 ++---
>   8 files changed, 123 insertions(+), 42 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/template/defarg23.C
>   create mode 100644 gcc/testsuite/g++.dg/template/defarg23a.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 8a44218611f..ea53e2d0ef2 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6783,6 +6783,7 @@ extern void note_iteration_stmt_body_end	(bool);
>   extern void determine_local_discriminator	(tree);
>   extern int decls_match				(tree, tree, bool = true);
>   extern bool maybe_version_functions		(tree, tree, bool);
> +extern bool merge_default_template_args		(tree, tree, bool);
>   extern tree duplicate_decls			(tree, tree,
>   						 bool hiding = false,
>   						 bool was_hidden = false);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 23c06655bde..a0bce56c121 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1470,6 +1470,43 @@ duplicate_function_template_decls (tree newdecl, tree olddecl)
>     return false;
>   }
>   
> +/* OLD_PARMS is the innermost set of template parameters for some template
> +   declaration, and NEW_PARMS is the corresponding set of template parameters
> +   for a redeclaration of that template.  Merge the default arguments within
> +   these two sets of parameters.  CLASS_P is true iff the template in
> +   question is a class template.  */
> +
> +bool
> +merge_default_template_args (tree new_parms, tree old_parms, bool class_p)
> +{
> +  gcc_checking_assert (TREE_VEC_LENGTH (new_parms)
> +		       == TREE_VEC_LENGTH (old_parms));
> +  for (int i = 0; i < TREE_VEC_LENGTH (new_parms); i++)
> +    {
> +      tree new_parm = TREE_VALUE (TREE_VEC_ELT (new_parms, i));
> +      tree old_parm = TREE_VALUE (TREE_VEC_ELT (old_parms, i));
> +      tree& new_default = TREE_PURPOSE (TREE_VEC_ELT (new_parms, i));
> +      tree& old_default = TREE_PURPOSE (TREE_VEC_ELT (old_parms, i));
> +      if (new_default != NULL_TREE && old_default != NULL_TREE)
> +	{
> +	  auto_diagnostic_group d;
> +	  error ("redefinition of default argument for %q+#D", new_parm);
> +	  inform (DECL_SOURCE_LOCATION (old_parm),
> +		  "original definition appeared here");
> +	  return false;
> +	}
> +      else if (new_default != NULL_TREE)
> +	/* Update the previous template parameters (which are the ones
> +	   that will really count) with the new default value.  */
> +	old_default = new_default;
> +      else if (class_p && old_default != NULL_TREE)
> +	/* Update the new parameters, too; they'll be used as the
> +	   parameters for any members.  */
> +	new_default = old_default;
> +    }
> +  return true;
> +}
> +
>   /* If NEWDECL is a redeclaration of OLDDECL, merge the declarations.
>      If the redeclaration is invalid, a diagnostic is issued, and the
>      error_mark_node is returned.  Otherwise, OLDDECL is returned.
> @@ -1990,7 +2027,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
>   		     template shall be specified on the initial declaration
>   		     of the member function within the class template.  */
>   		  || CLASSTYPE_TEMPLATE_INFO (CP_DECL_CONTEXT (olddecl))))
> -	    check_redeclaration_no_default_args (newdecl);
> +	    {
> +	      check_redeclaration_no_default_args (newdecl);
> +
> +	      if (DECL_TEMPLATE_INFO (olddecl)
> +		  && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (olddecl)))
> +		{
> +		  /* NEWDECL doesn't have its own TEMPLATE_INFO, so we need to
> +		     use current_template_parms.  */
> +		  gcc_checking_assert (!DECL_TEMPLATE_INFO (newdecl));

I think let's try to use DECL_TEMPLATE_INFO if it's set, and only fall 
back to current_template_parms if it isn't.  OK with that change.

> +		  tree new_parms
> +		    = INNERMOST_TEMPLATE_PARMS (current_template_parms);
> +		  tree old_parms
> +		    = DECL_INNERMOST_TEMPLATE_PARMS (DECL_TI_TEMPLATE (olddecl));
> +		  merge_default_template_args (new_parms, old_parms,
> +					       /*class_p=*/false);
> +		}
> +	    }
>   	  else
>   	    {
>   	      tree t1 = FUNCTION_FIRST_USER_PARMTYPE (olddecl);
> @@ -2235,6 +2288,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
>   		 translation unit."  */
>   	      check_no_redeclaration_friend_default_args
>   		(old_result, new_result);
> +
> +	      tree new_parms = DECL_INNERMOST_TEMPLATE_PARMS (newdecl);
> +	      tree old_parms = DECL_INNERMOST_TEMPLATE_PARMS (olddecl);
> +	      merge_default_template_args (new_parms, old_parms,
> +					   /*class_p=*/false);
>   	    }
>   	  if (!DECL_UNIQUE_FRIEND_P (old_result))
>   	    DECL_UNIQUE_FRIEND_P (new_result) = false;
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 18a21572ce3..d94d4538faa 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -6274,8 +6274,6 @@ redeclare_class_template (tree type, tree parms, tree cons)
>       {
>         tree tmpl_parm;
>         tree parm;
> -      tree tmpl_default;
> -      tree parm_default;
>   
>         if (TREE_VEC_ELT (tmpl_parms, i) == error_mark_node
>             || TREE_VEC_ELT (parms, i) == error_mark_node)
> @@ -6286,8 +6284,6 @@ redeclare_class_template (tree type, tree parms, tree cons)
>   	return false;
>   
>         parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
> -      tmpl_default = TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i));
> -      parm_default = TREE_PURPOSE (TREE_VEC_ELT (parms, i));
>   
>         /* TMPL_PARM and PARM can be either TYPE_DECL, PARM_DECL, or
>   	 TEMPLATE_DECL.  */
> @@ -6303,7 +6299,7 @@ redeclare_class_template (tree type, tree parms, tree cons)
>   	{
>   	  auto_diagnostic_group d;
>   	  error ("template parameter %q+#D", tmpl_parm);
> -	  inform (input_location, "redeclared here as %q#D", parm);
> +	  inform (DECL_SOURCE_LOCATION (parm), "redeclared here as %q#D", parm);
>   	  return false;
>   	}
>   
> @@ -6321,28 +6317,6 @@ redeclare_class_template (tree type, tree parms, tree cons)
>   	  return false;
>   	}
>   
> -      if (tmpl_default != NULL_TREE && parm_default != NULL_TREE)
> -	{
> -	  /* We have in [temp.param]:
> -
> -	     A template-parameter may not be given default arguments
> -	     by two different declarations in the same scope.  */
> -	  auto_diagnostic_group d;
> -	  error_at (input_location, "redefinition of default argument for %q#D", parm);
> -	  inform (DECL_SOURCE_LOCATION (tmpl_parm),
> -		  "original definition appeared here");
> -	  return false;
> -	}
> -
> -      if (parm_default != NULL_TREE)
> -	/* Update the previous template parameters (which are the ones
> -	   that will really count) with the new default value.  */
> -	TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i)) = parm_default;
> -      else if (tmpl_default != NULL_TREE)
> -	/* Update the new parameters, too; they'll be used as the
> -	   parameters for any members.  */
> -	TREE_PURPOSE (TREE_VEC_ELT (parms, i)) = tmpl_default;
> -
>         /* Give each template template parm in this redeclaration a
>   	 DECL_CONTEXT of the template for which they are a parameter.  */
>         if (TREE_CODE (parm) == TEMPLATE_DECL)
> @@ -6352,6 +6326,9 @@ redeclare_class_template (tree type, tree parms, tree cons)
>   	}
>       }
>   
> +  if (!merge_default_template_args (parms, tmpl_parms, /*class_p=*/true))
> +    return false;
> +
>     tree ci = get_constraints (tmpl);
>     tree req1 = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE;
>     tree req2 = cons ? CI_TEMPLATE_REQS (cons) : NULL_TREE;
> diff --git a/gcc/testsuite/g++.dg/cpp0x/vt-34314.C b/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
> index b37cac53223..704a975cc95 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
> @@ -3,8 +3,8 @@
>   template<typename Fun, typename... Args> // { dg-error "template parameter" }
>   struct call;
>   
> -template<typename Fun, typename Arg0>
> -struct call // { dg-message "note: redeclared here" }
> +template<typename Fun, typename Arg0> // { dg-message "note: redeclared here" }
> +struct call
>   {
>       template<typename Sig>
>       struct result;
> @@ -20,8 +20,8 @@ struct call // { dg-message "note: redeclared here" }
>   template<typename Fun, int... N> // { dg-error "template parameter" }
>   struct call2;
>   
> -template<typename Fun, int N>
> -struct call2 // { dg-message "note: redeclared here" }
> +template<typename Fun, int N> // { dg-message "note: redeclared here" }
> +struct call2
>   {
>       template<typename Sig>
>       struct result;
> @@ -36,8 +36,8 @@ struct call2 // { dg-message "note: redeclared here" }
>   template<typename Fun, template<typename> class... TT> // { dg-error "template parameter" }
>   struct call3;
>   
> -template<typename Fun, template<typename> class TT>
> -struct call3 // { dg-message "note: redeclared here" }
> +template<typename Fun, template<typename> class TT> // { dg-message "note: redeclared here" }
> +struct call3
>   {
>       template<typename Sig>
>       struct result;
> diff --git a/gcc/testsuite/g++.dg/template/defarg23.C b/gcc/testsuite/g++.dg/template/defarg23.C
> new file mode 100644
> index 00000000000..443d02656cc
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/defarg23.C
> @@ -0,0 +1,21 @@
> +// PR c++/65396
> +// { dg-do compile { target c++11 } }
> +
> +template<class T>     void f();
> +template<class T=int> void f();
> +
> +template<class T=int> void g(); // { dg-message "original definition" }
> +template<class T=int> void g(); // { dg-error "redefinition of default" }
> +
> +template<class T, class U=bool> void h();
> +template<class T=char, class U>
> +void h() {
> +  static_assert(__is_same(T, char), "");
> +  static_assert(__is_same(U, bool), "");
> +}
> +
> +int main() {
> +  f();
> +  g();
> +  h();
> +}
> diff --git a/gcc/testsuite/g++.dg/template/defarg23a.C b/gcc/testsuite/g++.dg/template/defarg23a.C
> new file mode 100644
> index 00000000000..3de0306112e
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/template/defarg23a.C
> @@ -0,0 +1,24 @@
> +// PR c++/65396
> +// { dg-do compile { target c++11 } }
> +// Like defarg23.C, but for member functions.
> +
> +struct A {
> +  template<class T> void f();
> +  template<class T=int> void g(); // { dg-message "original definition" }
> +  template<class T=char, class U> void h();
> +};
> +
> +template<class T=int> void A::f() { }
> +template<class T=int> void A::g() { } // { dg-error "redefinition of default" }
> +template<class T, class U=bool>
> +void A::h() {
> +  static_assert(__is_same(T, char), "");
> +  static_assert(__is_same(U, bool), "");
> +}
> +
> +int main() {
> +  A a;
> +  a.f();
> +  a.g();
> +  a.h();
> +}
> diff --git a/gcc/testsuite/g++.dg/template/pr92440.C b/gcc/testsuite/g++.dg/template/pr92440.C
> index 20db5f10586..f1e9281ab86 100644
> --- a/gcc/testsuite/g++.dg/template/pr92440.C
> +++ b/gcc/testsuite/g++.dg/template/pr92440.C
> @@ -3,8 +3,8 @@
>   
>   template <int T> // { dg-error "template parameter" }
>   struct S {
> -    template <class U>
> -    friend struct S;  // { dg-message "note: redeclared here as" }
> +    template <class U> // { dg-message "note: redeclared here as" }
> +    friend struct S;
>   };
>   
>   S<0> s;
> diff --git a/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C b/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
> index 48517f5d1d3..7596513acc0 100644
> --- a/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
> +++ b/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
> @@ -9,14 +9,14 @@ struct S1 {}; // { dg-error "redeclared" } used 1 template parameter
>   template <class T = int> // { dg-message "original definition" }
>   struct S2;
>   
> -template <class T = int>
> -struct S2; // { dg-error "redefinition of default" }
> +template <class T = int> // { dg-error "redefinition of default" }
> +struct S2;
>   
>   template <class T> // { dg-error "template parameter" }
>   struct S3;
>   
> -template <int I>
> -struct S3; // { dg-message "note: redeclared here" }
> +template <int I> // { dg-message "note: redeclared here" }
> +struct S3;
>   
> -template <template <class T> class C>
> -struct S3; // { dg-message "note: redeclared here" }
> +template <template <class T> class C> // { dg-message "note: redeclared here" }
> +struct S3;
  

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8a44218611f..ea53e2d0ef2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6783,6 +6783,7 @@  extern void note_iteration_stmt_body_end	(bool);
 extern void determine_local_discriminator	(tree);
 extern int decls_match				(tree, tree, bool = true);
 extern bool maybe_version_functions		(tree, tree, bool);
+extern bool merge_default_template_args		(tree, tree, bool);
 extern tree duplicate_decls			(tree, tree,
 						 bool hiding = false,
 						 bool was_hidden = false);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 23c06655bde..a0bce56c121 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -1470,6 +1470,43 @@  duplicate_function_template_decls (tree newdecl, tree olddecl)
   return false;
 }
 
+/* OLD_PARMS is the innermost set of template parameters for some template
+   declaration, and NEW_PARMS is the corresponding set of template parameters
+   for a redeclaration of that template.  Merge the default arguments within
+   these two sets of parameters.  CLASS_P is true iff the template in
+   question is a class template.  */
+
+bool
+merge_default_template_args (tree new_parms, tree old_parms, bool class_p)
+{
+  gcc_checking_assert (TREE_VEC_LENGTH (new_parms)
+		       == TREE_VEC_LENGTH (old_parms));
+  for (int i = 0; i < TREE_VEC_LENGTH (new_parms); i++)
+    {
+      tree new_parm = TREE_VALUE (TREE_VEC_ELT (new_parms, i));
+      tree old_parm = TREE_VALUE (TREE_VEC_ELT (old_parms, i));
+      tree& new_default = TREE_PURPOSE (TREE_VEC_ELT (new_parms, i));
+      tree& old_default = TREE_PURPOSE (TREE_VEC_ELT (old_parms, i));
+      if (new_default != NULL_TREE && old_default != NULL_TREE)
+	{
+	  auto_diagnostic_group d;
+	  error ("redefinition of default argument for %q+#D", new_parm);
+	  inform (DECL_SOURCE_LOCATION (old_parm),
+		  "original definition appeared here");
+	  return false;
+	}
+      else if (new_default != NULL_TREE)
+	/* Update the previous template parameters (which are the ones
+	   that will really count) with the new default value.  */
+	old_default = new_default;
+      else if (class_p && old_default != NULL_TREE)
+	/* Update the new parameters, too; they'll be used as the
+	   parameters for any members.  */
+	new_default = old_default;
+    }
+  return true;
+}
+
 /* If NEWDECL is a redeclaration of OLDDECL, merge the declarations.
    If the redeclaration is invalid, a diagnostic is issued, and the
    error_mark_node is returned.  Otherwise, OLDDECL is returned.
@@ -1990,7 +2027,23 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 		     template shall be specified on the initial declaration
 		     of the member function within the class template.  */
 		  || CLASSTYPE_TEMPLATE_INFO (CP_DECL_CONTEXT (olddecl))))
-	    check_redeclaration_no_default_args (newdecl);
+	    {
+	      check_redeclaration_no_default_args (newdecl);
+
+	      if (DECL_TEMPLATE_INFO (olddecl)
+		  && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (olddecl)))
+		{
+		  /* NEWDECL doesn't have its own TEMPLATE_INFO, so we need to
+		     use current_template_parms.  */
+		  gcc_checking_assert (!DECL_TEMPLATE_INFO (newdecl));
+		  tree new_parms
+		    = INNERMOST_TEMPLATE_PARMS (current_template_parms);
+		  tree old_parms
+		    = DECL_INNERMOST_TEMPLATE_PARMS (DECL_TI_TEMPLATE (olddecl));
+		  merge_default_template_args (new_parms, old_parms,
+					       /*class_p=*/false);
+		}
+	    }
 	  else
 	    {
 	      tree t1 = FUNCTION_FIRST_USER_PARMTYPE (olddecl);
@@ -2235,6 +2288,11 @@  duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
 		 translation unit."  */
 	      check_no_redeclaration_friend_default_args
 		(old_result, new_result);
+
+	      tree new_parms = DECL_INNERMOST_TEMPLATE_PARMS (newdecl);
+	      tree old_parms = DECL_INNERMOST_TEMPLATE_PARMS (olddecl);
+	      merge_default_template_args (new_parms, old_parms,
+					   /*class_p=*/false);
 	    }
 	  if (!DECL_UNIQUE_FRIEND_P (old_result))
 	    DECL_UNIQUE_FRIEND_P (new_result) = false;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 18a21572ce3..d94d4538faa 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -6274,8 +6274,6 @@  redeclare_class_template (tree type, tree parms, tree cons)
     {
       tree tmpl_parm;
       tree parm;
-      tree tmpl_default;
-      tree parm_default;
 
       if (TREE_VEC_ELT (tmpl_parms, i) == error_mark_node
           || TREE_VEC_ELT (parms, i) == error_mark_node)
@@ -6286,8 +6284,6 @@  redeclare_class_template (tree type, tree parms, tree cons)
 	return false;
 
       parm = TREE_VALUE (TREE_VEC_ELT (parms, i));
-      tmpl_default = TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i));
-      parm_default = TREE_PURPOSE (TREE_VEC_ELT (parms, i));
 
       /* TMPL_PARM and PARM can be either TYPE_DECL, PARM_DECL, or
 	 TEMPLATE_DECL.  */
@@ -6303,7 +6299,7 @@  redeclare_class_template (tree type, tree parms, tree cons)
 	{
 	  auto_diagnostic_group d;
 	  error ("template parameter %q+#D", tmpl_parm);
-	  inform (input_location, "redeclared here as %q#D", parm);
+	  inform (DECL_SOURCE_LOCATION (parm), "redeclared here as %q#D", parm);
 	  return false;
 	}
 
@@ -6321,28 +6317,6 @@  redeclare_class_template (tree type, tree parms, tree cons)
 	  return false;
 	}
 
-      if (tmpl_default != NULL_TREE && parm_default != NULL_TREE)
-	{
-	  /* We have in [temp.param]:
-
-	     A template-parameter may not be given default arguments
-	     by two different declarations in the same scope.  */
-	  auto_diagnostic_group d;
-	  error_at (input_location, "redefinition of default argument for %q#D", parm);
-	  inform (DECL_SOURCE_LOCATION (tmpl_parm),
-		  "original definition appeared here");
-	  return false;
-	}
-
-      if (parm_default != NULL_TREE)
-	/* Update the previous template parameters (which are the ones
-	   that will really count) with the new default value.  */
-	TREE_PURPOSE (TREE_VEC_ELT (tmpl_parms, i)) = parm_default;
-      else if (tmpl_default != NULL_TREE)
-	/* Update the new parameters, too; they'll be used as the
-	   parameters for any members.  */
-	TREE_PURPOSE (TREE_VEC_ELT (parms, i)) = tmpl_default;
-
       /* Give each template template parm in this redeclaration a
 	 DECL_CONTEXT of the template for which they are a parameter.  */
       if (TREE_CODE (parm) == TEMPLATE_DECL)
@@ -6352,6 +6326,9 @@  redeclare_class_template (tree type, tree parms, tree cons)
 	}
     }
 
+  if (!merge_default_template_args (parms, tmpl_parms, /*class_p=*/true))
+    return false;
+
   tree ci = get_constraints (tmpl);
   tree req1 = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE;
   tree req2 = cons ? CI_TEMPLATE_REQS (cons) : NULL_TREE;
diff --git a/gcc/testsuite/g++.dg/cpp0x/vt-34314.C b/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
index b37cac53223..704a975cc95 100644
--- a/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
+++ b/gcc/testsuite/g++.dg/cpp0x/vt-34314.C
@@ -3,8 +3,8 @@ 
 template<typename Fun, typename... Args> // { dg-error "template parameter" }
 struct call;
 
-template<typename Fun, typename Arg0>
-struct call // { dg-message "note: redeclared here" }
+template<typename Fun, typename Arg0> // { dg-message "note: redeclared here" }
+struct call
 {
     template<typename Sig>
     struct result;
@@ -20,8 +20,8 @@  struct call // { dg-message "note: redeclared here" }
 template<typename Fun, int... N> // { dg-error "template parameter" }
 struct call2;
 
-template<typename Fun, int N>
-struct call2 // { dg-message "note: redeclared here" }
+template<typename Fun, int N> // { dg-message "note: redeclared here" }
+struct call2
 {
     template<typename Sig>
     struct result;
@@ -36,8 +36,8 @@  struct call2 // { dg-message "note: redeclared here" }
 template<typename Fun, template<typename> class... TT> // { dg-error "template parameter" }
 struct call3;
 
-template<typename Fun, template<typename> class TT>
-struct call3 // { dg-message "note: redeclared here" }
+template<typename Fun, template<typename> class TT> // { dg-message "note: redeclared here" }
+struct call3
 {
     template<typename Sig>
     struct result;
diff --git a/gcc/testsuite/g++.dg/template/defarg23.C b/gcc/testsuite/g++.dg/template/defarg23.C
new file mode 100644
index 00000000000..443d02656cc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/defarg23.C
@@ -0,0 +1,21 @@ 
+// PR c++/65396
+// { dg-do compile { target c++11 } }
+
+template<class T>     void f();
+template<class T=int> void f();
+
+template<class T=int> void g(); // { dg-message "original definition" }
+template<class T=int> void g(); // { dg-error "redefinition of default" }
+
+template<class T, class U=bool> void h();
+template<class T=char, class U>
+void h() {
+  static_assert(__is_same(T, char), "");
+  static_assert(__is_same(U, bool), "");
+}
+
+int main() {
+  f();
+  g();
+  h();
+}
diff --git a/gcc/testsuite/g++.dg/template/defarg23a.C b/gcc/testsuite/g++.dg/template/defarg23a.C
new file mode 100644
index 00000000000..3de0306112e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/defarg23a.C
@@ -0,0 +1,24 @@ 
+// PR c++/65396
+// { dg-do compile { target c++11 } }
+// Like defarg23.C, but for member functions.
+
+struct A {
+  template<class T> void f();
+  template<class T=int> void g(); // { dg-message "original definition" }
+  template<class T=char, class U> void h();
+};
+
+template<class T=int> void A::f() { }
+template<class T=int> void A::g() { } // { dg-error "redefinition of default" }
+template<class T, class U=bool>
+void A::h() {
+  static_assert(__is_same(T, char), "");
+  static_assert(__is_same(U, bool), "");
+}
+
+int main() {
+  A a;
+  a.f();
+  a.g();
+  a.h();
+}
diff --git a/gcc/testsuite/g++.dg/template/pr92440.C b/gcc/testsuite/g++.dg/template/pr92440.C
index 20db5f10586..f1e9281ab86 100644
--- a/gcc/testsuite/g++.dg/template/pr92440.C
+++ b/gcc/testsuite/g++.dg/template/pr92440.C
@@ -3,8 +3,8 @@ 
 
 template <int T> // { dg-error "template parameter" }
 struct S {
-    template <class U>
-    friend struct S;  // { dg-message "note: redeclared here as" }
+    template <class U> // { dg-message "note: redeclared here as" }
+    friend struct S;
 };
 
 S<0> s;
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C b/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
index 48517f5d1d3..7596513acc0 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/redecl1.C
@@ -9,14 +9,14 @@  struct S1 {}; // { dg-error "redeclared" } used 1 template parameter
 template <class T = int> // { dg-message "original definition" }
 struct S2; 
 
-template <class T = int>
-struct S2; // { dg-error "redefinition of default" } 
+template <class T = int> // { dg-error "redefinition of default" } 
+struct S2;
 
 template <class T> // { dg-error "template parameter" } 
 struct S3;
 
-template <int I>
-struct S3; // { dg-message "note: redeclared here" } 
+template <int I> // { dg-message "note: redeclared here" } 
+struct S3;
 
-template <template <class T> class C>
-struct S3; // { dg-message "note: redeclared here" } 
+template <template <class T> class C> // { dg-message "note: redeclared here" } 
+struct S3;