C++ PATCH for c++/84489, dependent default template argument

Message ID CADzB+2m=LGcK+OOCKMXu3MSU8zbq0-15xiJ91TsNqhx=pUTcrQ@mail.gmail.com
State New
Headers show
Series
  • C++ PATCH for c++/84489, dependent default template argument
Related show

Commit Message

Jason Merrill Feb. 27, 2018, 5:26 p.m.
The logic in type_unification_real for handling template parms that
depend on earlier template parms is a bit complicated.  It already
recognizes when the type of the parm depends on something not
available yet, and it dealt with the case where substituting partial
args left some template parm uses behind, but it didn't handle the
case where substituting partial args just failed.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit dade5ae09222e696b2aaa05d730e2414b3568bd4
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Feb 26 23:38:10 2018 -0500

            PR c++/84489 - dependent default template argument
    
            * pt.c (type_unification_real): Handle early substitution failure.

Comments

Jason Merrill March 23, 2018, 10:04 p.m. | #1
On Tue, Feb 27, 2018 at 12:26 PM, Jason Merrill <jason@redhat.com> wrote:
> The logic in type_unification_real for handling template parms that

> depend on earlier template parms is a bit complicated.  It already

> recognizes when the type of the parm depends on something not

> available yet, and it dealt with the case where substituting partial

> args left some template parm uses behind, but it didn't handle the

> case where substituting partial args just failed.


And that was still wrong, leading to the regression on the later
testcase in 78489.  When substitution fails, that still needs to cause
deduction to fail immediately.  The important part of the 84489 patch
was setting processing_template_decl so unresolved template parms
don't cause substitution failure.

And to handle the first testcase in 78489, we also need to substitute
into the type of non-type parameters even if we aren't going to use
their default arguments yet.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 02864b90c69f3af56054a627e834dd66a0aa0c80
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Mar 23 11:15:05 2018 -0400

            PR c++/78489 - wrong SFINAE behavior.
    
            PR c++/84489
            * pt.c (type_unification_real): Don't defer substitution failure.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 5293c2b5491..8af29cef8d3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -19996,41 +19996,49 @@ type_unification_real (tree tparms,
 	  if (targ || tparm == error_mark_node)
 	    continue;
 	  tree parm = TREE_VALUE (tparm);
-
-	  tsubst_flags_t fcomplain = complain;
-	  if (saw_undeduced == 1)
-	    {
-	      /* When saw_undeduced == 1, substitution into parm and arg might
-		 fail or not replace all template parameters, and that's
-		 fine.  */
-	      fcomplain = tf_none;
-	      if (TREE_CODE (parm) == PARM_DECL
-		  && uses_template_parms (TREE_TYPE (parm)))
-		continue;
-	    }
-
 	  tree arg = TREE_PURPOSE (tparm);
 	  reopen_deferring_access_checks (*checks);
 	  location_t save_loc = input_location;
 	  if (DECL_P (parm))
 	    input_location = DECL_SOURCE_LOCATION (parm);
-
 	  if (saw_undeduced == 1)
 	    ++processing_template_decl;
-	  arg = tsubst_template_arg (arg, full_targs, fcomplain, NULL_TREE);
+
+	  if (saw_undeduced == 1
+	      && TREE_CODE (parm) == PARM_DECL
+	      && uses_template_parms (TREE_TYPE (parm)))
+	    {
+	      /* The type of this non-type parameter depends on undeduced
+		 parameters.  Don't try to use its default argument yet,
+		 but do check whether the arguments we already have cause
+		 substitution failure, so that that happens before we try
+		 later default arguments (78489).  */
+	      tree type = tsubst (TREE_TYPE (parm), full_targs, complain,
+				  NULL_TREE);
+	      if (type == error_mark_node)
+		arg = error_mark_node;
+	      else
+		arg = NULL_TREE;
+	    }
+	  else
+	    {
+	      arg = tsubst_template_arg (arg, full_targs, complain, NULL_TREE);
+
+	      if (!uses_template_parms (arg))
+		arg = convert_template_argument (parm, arg, full_targs,
+						 complain, i, NULL_TREE);
+	      else if (saw_undeduced == 1)
+		arg = NULL_TREE;
+	      else
+		arg = error_mark_node;
+	    }
+
 	  if (saw_undeduced == 1)
 	    --processing_template_decl;
-
-	  if (arg != error_mark_node && !uses_template_parms (arg))
-	    arg = convert_template_argument (parm, arg, full_targs, complain,
-					     i, NULL_TREE);
-	  else if (saw_undeduced == 1)
-	    arg = NULL_TREE;
-	  else
-	    arg = error_mark_node;
 	  input_location = save_loc;
 	  *checks = get_deferred_access_checks ();
 	  pop_deferring_access_checks ();
+
 	  if (arg == error_mark_node)
 	    return 1;
 	  else if (arg)
diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg4a.C b/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg4a.C
new file mode 100644
index 00000000000..023d9afdf70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg4a.C
@@ -0,0 +1,6 @@
+// PR c++/55724
+// { dg-do compile { target c++11 } }
+
+template<int N> struct S {};
+template<typename T = int, T N = 42> void f(S<N>) {}
+int main() { S<1> s; f(s); }
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae60.C b/gcc/testsuite/g++.dg/cpp0x/sfinae60.C
new file mode 100644
index 00000000000..cfb4dc0b9a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae60.C
@@ -0,0 +1,25 @@
+// PR c++/78489
+// { dg-do compile { target c++11 } }
+
+template <bool P, class T = void> struct enable_if { using type = T; };
+template <class T> struct enable_if<false, T> {};
+
+template <class Dummy> struct use_type { using type = int; };
+
+template <bool Pred>
+struct get_type {
+    static_assert(Pred, "");
+    using type = int;
+};
+
+template <bool Val,
+              class      = typename enable_if<Val>::type, // Evaluation/Substitution should end here
+              class ValT = typename get_type<Val>::type,  // This should not be instantiated
+              typename use_type<ValT>::type = 0           // This NTTP causes ValT to be required
+            >
+constexpr bool test(int) { return false; }
+
+template <bool>
+constexpr bool test(long) { return true; }
+
+static_assert(test<false>(0), ""); // should call test(long)
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae61.C b/gcc/testsuite/g++.dg/cpp0x/sfinae61.C
new file mode 100644
index 00000000000..9e7145305e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae61.C
@@ -0,0 +1,21 @@
+// PR c++/78489
+// { dg-do compile { target c++11 } }
+
+template <bool Pred, class T> struct enable_if { typedef T type; };
+template <class T> struct enable_if<false, T> {};
+
+template <int Idx> struct blows_up { static_assert(Idx != Idx, ""); };
+
+template <int Idx,
+           // substitution should fail here
+          typename enable_if<Idx != Idx, int>::type = 0,
+          // GCC evaluates this statement
+          class = typename blows_up<Idx>::type 
+>
+void Foo() {}
+
+// Check the constructor in as SFINAE context
+template <int I> constexpr auto test(int) -> decltype((Foo<I>(), true)) { return true; }
+template <int>   constexpr bool test(long) { return false; }
+
+static_assert(!test<3>(0), ""); // Blows up

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 40c897aadc9..2a64fa6d9ad 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -19831,21 +19831,28 @@  type_unification_real (tree tparms,
 	    continue;
 	  tree parm = TREE_VALUE (tparm);
 
-	  if (TREE_CODE (parm) == PARM_DECL
-	      && uses_template_parms (TREE_TYPE (parm))
-	      && saw_undeduced < 2)
-	    continue;
+	  tsubst_flags_t fcomplain = complain;
+	  if (saw_undeduced == 1)
+	    {
+	      /* When saw_undeduced == 1, substitution into parm and arg might
+		 fail or not replace all template parameters, and that's
+		 fine.  */
+	      fcomplain = tf_none;
+	      if (TREE_CODE (parm) == PARM_DECL
+		  && uses_template_parms (TREE_TYPE (parm)))
+		continue;
+	    }
 
 	  tree arg = TREE_PURPOSE (tparm);
 	  reopen_deferring_access_checks (*checks);
 	  location_t save_loc = input_location;
 	  if (DECL_P (parm))
 	    input_location = DECL_SOURCE_LOCATION (parm);
-	  arg = tsubst_template_arg (arg, full_targs, complain, NULL_TREE);
-	  if (!uses_template_parms (arg))
+	  arg = tsubst_template_arg (arg, full_targs, fcomplain, NULL_TREE);
+	  if (arg != error_mark_node && !uses_template_parms (arg))
 	    arg = convert_template_argument (parm, arg, full_targs, complain,
 					     i, NULL_TREE);
-	  else if (saw_undeduced < 2)
+	  else if (saw_undeduced == 1)
 	    arg = NULL_TREE;
 	  else
 	    arg = error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg7.C b/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg7.C
new file mode 100644
index 00000000000..636bf1afd88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/fntmpdefarg7.C
@@ -0,0 +1,10 @@ 
+// PR c++/84489
+// { dg-do compile { target c++11 } }
+
+template <class T = int, T N = T(), bool B = (N >> 1)>
+T f1() {return 0;}
+
+int main()
+{
+  f1(); // Bug here
+}