c++: Check satisfaction before non-dep convs. [CWG2369]
authorJason Merrill <jason@redhat.com>
Mon, 17 Aug 2020 20:36:33 +0000 (16:36 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 19 Aug 2020 20:08:17 +0000 (16:08 -0400)
It's very hard to use concepts to protect a template from hard errors due to
unwanted instantiation if constraints aren't checked until after doing all
substitution and checking of non-dependent conversions.

It was pretty straightforward to insert the satisfaction check into the
logic, but I needed to make the 3-parameter version of
satisfy_declaration_constraints call push_tinst_level like the 2-parameter
version already does.  For simplicity, I also made it add any needed outer
template arguments from the TEMPLATE_DECL to the args.

The testsuite changes are mostly because this change causes unsatisfaction
to cause deduction to fail rather than reject the candidate later in
overload resolution.

gcc/cp/ChangeLog:

DR 2369
* cp-tree.h (push_tinst_level, push_tinst_level_loc): Declare.
* constraint.cc (satisfy_declaration_constraints):
Use add_outermost_template_args and push_tinst_level.
* pt.c (add_outermost_template_args): Handle getting
a TEMPLATE_DECL as the first argument.
(push_tinst_level, push_tinst_level_loc): No longer static.
(fn_type_unification): Check satisfaction before non-dependent
conversions.

gcc/testsuite/ChangeLog:

DR 2369
* g++.dg/concepts/diagnostic10.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic13.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic2.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic3.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic4.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic5.C: Adjust expexcted errors.
* g++.dg/concepts/diagnostic9.C: Adjust expexcted errors.
* g++.dg/concepts/expression2.C: Adjust expexcted errors.
* g++.dg/concepts/fn5.C: Adjust expexcted errors.
* g++.dg/concepts/placeholder5.C: Adjust expexcted errors.
* g++.dg/concepts/pr67595.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-pr78752-2.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-pr84140.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-recursive-sat3.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-requires18.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-requires19.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts3.C: Adjust expexcted errors.
* g++.dg/cpp2a/concepts-nondep1.C: New test.
* g++.dg/cpp2a/concepts-nondep1a.C: New test.

22 files changed:
gcc/cp/constraint.cc
gcc/cp/cp-tree.h
gcc/cp/pt.c
gcc/testsuite/g++.dg/concepts/diagnostic10.C
gcc/testsuite/g++.dg/concepts/diagnostic13.C
gcc/testsuite/g++.dg/concepts/diagnostic2.C
gcc/testsuite/g++.dg/concepts/diagnostic3.C
gcc/testsuite/g++.dg/concepts/diagnostic4.C
gcc/testsuite/g++.dg/concepts/diagnostic5.C
gcc/testsuite/g++.dg/concepts/diagnostic9.C
gcc/testsuite/g++.dg/concepts/expression2.C
gcc/testsuite/g++.dg/concepts/fn5.C
gcc/testsuite/g++.dg/concepts/placeholder5.C
gcc/testsuite/g++.dg/concepts/pr67595.C
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-pr78752-2.C
gcc/testsuite/g++.dg/cpp2a/concepts-pr84140.C
gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat3.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C
gcc/testsuite/g++.dg/cpp2a/concepts-requires19.C
gcc/testsuite/g++.dg/cpp2a/concepts3.C

index 48d52ec5b7acb0265100ff63f0b9964e20b81efb..7a2f3b9fde02f5c90602ab201615e7336c3e8e92 100644 (file)
@@ -2814,16 +2814,22 @@ satisfy_declaration_constraints (tree t, tree args, subst_info info)
   info.in_decl = t;
 
   gcc_assert (TREE_CODE (t) == TEMPLATE_DECL);
+
+  args = add_outermost_template_args (t, args);
+
+  tree result = boolean_true_node;
   if (tree norm = normalize_template_requirements (t, info.noisy ()))
     {
+      if (!push_tinst_level (t, args))
+       return result;
       tree pattern = DECL_TEMPLATE_RESULT (t);
       push_access_scope (pattern);
-      tree result = satisfy_associated_constraints (norm, args, info);
+      result = satisfy_associated_constraints (norm, args, info);
       pop_access_scope (pattern);
-      return result;
+      pop_tinst_level ();
     }
 
-  return boolean_true_node;
+  return result;
 }
 
 static tree
index 44531cd86dc74179232747062700cec86bc71816..3f3717a6bb51696a4d5d1078f7557d1d9f9626d1 100644 (file)
@@ -6981,7 +6981,9 @@ extern bool template_parm_object_p                (const_tree);
 extern tree tparm_object_argument              (tree);
 extern bool explicit_class_specialization_p     (tree);
 extern bool push_tinst_level                    (tree);
+extern bool push_tinst_level                    (tree, tree);
 extern bool push_tinst_level_loc                (tree, location_t);
+extern bool push_tinst_level_loc                (tree, tree, location_t);
 extern void pop_tinst_level                     (void);
 extern struct tinst_level *outermost_tinst_level(void);
 extern void init_template_processing           (void);
index edaefcf505f12c9cfcc7684b625207de84c6fc64..5dbdd37f6e3ed6b34e8457125fb3bbe22e1d00dd 100644 (file)
@@ -586,13 +586,23 @@ add_to_template_args (tree args, tree extra_args)
    (EXTRA_ARGS) levels are added.  This function is used to combine
    the template arguments from a partial instantiation with the
    template arguments used to attain the full instantiation from the
-   partial instantiation.  */
+   partial instantiation.
+
+   If ARGS is a TEMPLATE_DECL, use its parameters as args.  */
 
 tree
 add_outermost_template_args (tree args, tree extra_args)
 {
   tree new_args;
 
+  if (!args)
+    return extra_args;
+  if (TREE_CODE (args) == TEMPLATE_DECL)
+    {
+      tree ti = get_template_info (DECL_TEMPLATE_RESULT (args));
+      args = TI_ARGS (ti);
+    }
+
   /* If there are more levels of EXTRA_ARGS than there are ARGS,
      something very fishy is going on.  */
   gcc_assert (TMPL_ARGS_DEPTH (args) >= TMPL_ARGS_DEPTH (extra_args));
@@ -10772,7 +10782,7 @@ static GTY(()) struct tinst_level *last_error_tinst_level;
 /* We're starting to instantiate D; record the template instantiation context
    at LOC for diagnostics and to restore it later.  */
 
-static bool
+bool
 push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
 {
   struct tinst_level *new_level;
@@ -10826,7 +10836,7 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc)
 /* We're starting substitution of TMPL<ARGS>; record the template
    substitution context for diagnostics and to restore it later.  */
 
-static bool
+bool
 push_tinst_level (tree tmpl, tree args)
 {
   return push_tinst_level_loc (tmpl, args, input_location);
@@ -21297,13 +21307,24 @@ fn_type_unification (tree fn,
       goto fail;
     }
 
+ deduced:
+
+  /* CWG2369: Check satisfaction before non-deducible conversions.  */
+  if (!constraints_satisfied_p (fn, targs))
+    {
+      if (explain_p)
+       diagnose_constraints (DECL_SOURCE_LOCATION (fn), fn, targs);
+      goto fail;
+    }
+
   /* DR 1391: All parameters have args, now check non-dependent parms for
-     convertibility.  */
-  if (check_non_deducible_conversions (parms, args, nargs, fn, strict, flags,
-                                      convs, explain_p))
+     convertibility.  We don't do this if all args were explicitly specified,
+     as the standard says that we substitute explicit args immediately.  */
+  if (incomplete
+      && check_non_deducible_conversions (parms, args, nargs, fn, strict, flags,
+                                         convs, explain_p))
     goto fail;
 
- deduced:
   /* All is well so far.  Now, check:
 
      [temp.deduct]
index fcc6043ca933f29fbdd44674d98fcfa6618d021a..7a90b836143bde71936f6afe4953cf358fb20512 100644 (file)
@@ -14,5 +14,5 @@ struct S
 void
 bar()
 {
-  foo<S>(); // { dg-error "unsatisfied constraints" }
+  foo<S>(); // { dg-error "no match" }
 }
index accd8a6d2bdbd779be61e4383ae1973e8d6c92ee..1aff859f0bbe251d200327d5980f4de93bcc3700 100644 (file)
@@ -10,5 +10,5 @@ void foo() { }
 
 void bar()
 {
-  foo<int, char>(); // { dg-error "unsatisfied constraints" }
+  foo<int, char>(); // { dg-error "no match" }
 }
index 47accb8366eca1538e500d1fe7bc54ed5474046e..004c7a378e7173f6bc0f4b0e094ef4b794ef65ce 100644 (file)
@@ -22,7 +22,7 @@ template<typename T>
 void
 baz()
 {
-  bar<int>(); // { dg-error "unsatisfied constraints" }
+  bar<int>(); // { dg-error "no match" }
 /* { dg-begin-multiline-output "" }
    bar<int>();
             ^
index b4c75409b94f46229be12262d757f5eb4e09a8cd..7796e2642519f6d634460e4d170d6d027977d513 100644 (file)
@@ -24,6 +24,6 @@ baz() // { dg-message "with Is = .2, 3, 4... evaluated to .false." }
 void
 baz()
 {
-  bar<int, char>(); // { dg-error "unsatisfied constraints" }
-  baz<2,3,4>(); // { dg-error "unsatisfied constraints" }
+  bar<int, char>(); // { dg-error "no match" }
+  baz<2,3,4>(); // { dg-error "no match" }
 }
index 677bc867634784c69c778c9fdc69bf4d3dc639ff..e38e35a4d009c5e6584a5919024d48377fcdb7a5 100644 (file)
@@ -15,4 +15,4 @@ template<typename T>
   // { dg-message "typename remove_reference<T>::type" "" { target *-*-* } .-1 }
   void foo() { }
 
-void bar() { foo<int> (); } // { dg-error "use of" }
+void bar() { foo<int> (); } // { dg-error "no match" }
index 81705f6a0c6d3891983ac91e63d4c4e8d2bbea3f..70426ea30994957b470630e47ed067094de871e6 100644 (file)
@@ -39,5 +39,5 @@ template<typename T>
 void
 bar()
 {
-  foo<char>(); // { dg-error "use of" }
+  foo<char>(); // { dg-error "no match" }
 }
index 414b924f11571ce19d62a114d6ec82753ddb02d3..8eecfe6f81dd2b28d85a133247d2f4de32031fe6 100644 (file)
@@ -1,11 +1,12 @@
 // PR c++/85278
 // { dg-do compile { target concepts } }
 
+// { dg-message "candidate: .*const decltype\\(f2::a\\)&&" "" { target *-*-* } .+2 }
 template<typename T>
 void f2(T a)
   requires requires (const decltype(a) &&x) { -x; }
 { }
 
 int main() {
-  f2<void*>(nullptr); // { dg-error "use of function .*const decltype\\(f2::a\\)&&" }
+  f2<void*>(nullptr); // { dg-error "no match" }
 }
index 60dd5414461a2e5555b8d4f481437c79d875353d..4bb5bc71462c9c0c67b478d4b86d1a53445d22fd 100644 (file)
@@ -31,7 +31,7 @@ class S
 
 int main()
 {
-  f1(s); // { dg-error "unsatisfied|private" }
+  f1(s); // { dg-error "no match" }
   f2(s); // { dg-error "" }
 
   // When used in non-SFINAE contexts, make sure that we fail
index 6d86ac5d0af188be2a4171e0e400b258ed4546d7..bf277135e791a5bfe91d98d45aefb71b03046e58 100644 (file)
@@ -19,6 +19,6 @@ int main() {
   S1<char> s1;      // { dg-error "constraint|invalid" }
   S2<int, char> s2; // { dg-error "constraint|invalid" }
 
-  f('a');    // { dg-error "unsatisfied" }
-  g(0, 'a'); // { dg-error "unsatisfied" }
+  f('a');    // { dg-error "no match" }
+  g(0, 'a'); // { dg-error "no match" }
 }
index eccad65a30175dc944c9af5db36c3eb1e394cee7..3f29c9339739e062c311df85cec4e9219902f7e4 100644 (file)
@@ -14,5 +14,5 @@ concept bool C =
 template <C c>
 constexpr bool f() { return true; }
 
-static_assert(f<double>(), "");        // { dg-error "unsatisfied|as type" }
-static_assert(f<int>(), ""); // { dg-error "unsatisfied|as type" }
+static_assert(f<double>(), "");        // { dg-error "no match" }
+static_assert(f<int>(), ""); // { dg-error "no match" }
index 029ec7a1186b48bd3e4ef97a81e692909b71295c..37adf931fb5d33a7cea7b048d3571c8f4a61fbdd 100644 (file)
@@ -5,7 +5,7 @@ template <class X> concept bool allocatable = requires{{new X}->X *; };
 template <class X> concept bool semiregular = allocatable<X>;
 template <class X> concept bool readable = requires{requires semiregular<X>;};
 template <class> int weak_input_iterator = requires{{0}->readable;};
-template <class X> bool input_iterator{weak_input_iterator<X>}; // { dg-warning "narrowing conversion" }
+template <class X> bool input_iterator{weak_input_iterator<X>}; // { dg-prune-output "narrowing conversion" }
 template <class X> bool forward_iterator{input_iterator<X>};
 template <class X> bool bidirectional_iterator{forward_iterator<X>};
 template <class X>
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1.C
new file mode 100644 (file)
index 0000000..5304b66
--- /dev/null
@@ -0,0 +1,19 @@
+// DR 2369
+// { dg-do compile { target c++20 } }
+
+template <class T> struct Z {
+  typedef typename T::x xx;
+};
+template <class T> concept C = requires { typename T::A; };
+template <C T> typename Z<T>::xx f(void *, T); // #1
+template <class T> void f(int, T); // #2
+struct A {} a;
+struct ZZ {
+  template <class T, class = typename Z<T>::xx> operator T *();
+  operator int();
+};
+int main() {
+  ZZ zz;
+  f(1, a); // OK, deduction fails for #1 because there is no conversion from int to void*
+  f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-nondep1a.C
new file mode 100644 (file)
index 0000000..2ba4dbc
--- /dev/null
@@ -0,0 +1,20 @@
+// DR 2369
+// { dg-do compile { target c++20 } }
+
+template <class T> struct Z {
+  typedef typename T::x xx;
+};
+template <class T> concept C = requires { typename T::A; }; // { dg-message "T::A" }
+template <C T> typename Z<T>::xx f(void *, T); // { dg-message "not satisfied" }
+
+struct A {} a;
+struct ZZ {
+  template <class T, class = typename Z<T>::xx> operator T *();
+  operator int();
+};
+int main() {
+  ZZ zz;
+  f(1, a); // { dg-error "no match" } no conversion from int to void*
+  // { dg-message "cannot convert" "" { target *-*-* } .-1 }
+  f(zz, 42); // { dg-error "no match" } C<int> is not satisfied
+}
index 6777054285d827578052947871af279241ac0d5a..7d7c716cfecf705882e8b7caba9f722ea9983dd0 100644 (file)
@@ -17,5 +17,5 @@ int main()
   func(1, 2, 3);
 
   t.func(1, 2, ""); // { dg-error "no match" }
-  func(1, 2, ""); // { dg-error "unsatisfied constraints" }
+  func(1, 2, ""); // { dg-error "no match" }
 }
index b604f7e00bf18f703fd1fa65680ac95112504742..7eff65bee9e5a8b7d199340fde5d5d9c6df83d21 100644 (file)
@@ -34,4 +34,3 @@ int main()
 {
   return distance(I<int>{}, I<void>{});
 }
-
index 679f5cfbf696383954b0ba5b79b20a990d69fc54..b2a6a796ea7cfbd4a9645dd443aa6ac1b1836701 100644 (file)
@@ -8,5 +8,5 @@ void foo(T t) { }
 
 void test()
 {
-  foo(0); // { dg-error "unsatisfied constraints" }
+  foo(0); // { dg-error "no match" }
 }
index 9d8ec94f696cd1ead5cd80f3f3b159e36fc31901..a9b7720cc6c1a97aae4d3da148f7f529745bd81a 100644 (file)
@@ -67,11 +67,11 @@ void test()
 {
   f1<int>();
   f1<bool>();
-  f1<void>(); // { dg-error "unsatisfied" }
+  f1<void>(); // { dg-error "no match" }
 
   f2<int>();
   f2<bool>();
-  f2<void>(); // { dg-error "unsatisfied" }
+  f2<void>(); // { dg-error "no match" }
 
   data<char> x;
   x.f1<int>();
index b020250da50d01a25f8c0725734b789dcf5cf941..68aeccf3197eaa9d664146b1e6cee50ca83db46b 100644 (file)
@@ -47,12 +47,12 @@ void f6(T x) requires requires { requires check_v<decltype(x)>; } { }
 
 void test()
 {
-  f1<int>(); // { dg-error "unsatisfied" }
-  f2(0); // { dg-error "unsatisfied" }
+  f1<int>(); // { dg-error "no match" }
+  f2(0); // { dg-error "no match" }
 
-  f3<int>(); // { dg-error "unsatisfied" }
-  f4(0); // { dg-error "unsatisfied" }
+  f3<int>(); // { dg-error "no match" }
+  f4(0); // { dg-error "no match" }
 
-  f5<int>(); // { dg-error "unsatisfied" }
-  f6(0); // { dg-error "unsatisfied" }
+  f5<int>(); // { dg-error "no match" }
+  f6(0); // { dg-error "no match" }
 }
index 4ccfd0805b7b7f7aed1f7a2ec240c669f533ee02..1e28e31ad88a1ca91a68297261f67270d5576c31 100644 (file)
@@ -42,7 +42,7 @@ template<typename T>
 void f3() { }
 
 void driver2() {
-  f1<S1>(); // { dg-error "unsatisfied|is private" }
-  f2<S1>(); // { dg-error "unsatisfied|is private" }
-  f3<S1>(); // { dg-error "unsatisfied|is private" }
+  f1<S1>(); // { dg-error "no match" }
+  f2<S1>(); // { dg-error "no match" }
+  f3<S1>(); // { dg-error "no match" }
 }