The limitations of the initial patch (checking specializiation template usage), 
have been discussed.

> I realized that for the case where we have a member function template
> of a class template, and a specialization of the enclosing class only
> (like below),
>
> template <>
> template <typename U>
> void S<int>::f() {
>   // some constrained auto
> }
>
> When using S<int>::f<double>, DECL_TEMPLATE_INFO(fn) is non-zero, and
> DECL_TEMPLATE_SPECIALIZATION(fn) is zero, while
> DECL_TEMPLATE_SPECIALIZATION(DECL_TI_TEMPLATE(fn)) is non-zero.
> So it means that the patch will extract DECL_TI_ARGS(fn) as
> outer_targs, and it would be <int> <double> while the type of the
> constrained auto will be as template <typename U> ... and will not be
> dependent on the parameters of the enclosing class.
> This means that again (outer_targs + targs) will have more depth than
> auto_node levels.
> This means that for the case where the function is not an explicit
> specialization, but it is defined in an explicitly specialized scope,
> the patch will not work.

As described in more detail below, this patch attempts to resolve this issue by 
trimming full_targs.

> > Another more context-unaware approach to fix this might be to only
> > use the innermost level from 'full_targs' for satisfaction if
> > TEMPLATE_TYPE_ORIG_LEVEL is 1 (which means this constrained auto
> > appeared in a context that doesn't have template parameters such as an
> > explicit specialization or ordinary non-template function, and so
> > only the level corresponding to the deduced type is needed for
> > satisfaction.)
> >
> > Generalizing on that, another approach might be to handle missing_levels < 0
> > by removing -missing_levels from full_targs via get_innermost_template_args.
> > But AFAICT it should suffice to handle the TEMPLATE_TYPE_ORIG_LEVEL == 1
> > case specifically.
>
> I was unable to understand why you think that it might not handle
> TEMPLATE_TYPE_ORIG_LEVEL > 1 cases, so I tried to formulate my
> reasoning as follows.
>
> Assuming contexts adc_variable_type, adc_return_type, adc_decomp_type:
> For any case where missing_level < 0, it means that the type depends
> on fewer levels than the template arguments used to materialize it.
> This can only happen when the type is defined in an explicit
> specialization scope. This explicit specialization might not occur in
> its immediate scope.
> Note that partial specialization (while changing the set of
> parameters) cannot reduce the number of levels for the type.
> Because of the fact that the enclosing scope of any explicit
> specialization is explicitly specialized
> (https://eel.is/c++draft/temp.expl.spec#16), the type will not depend
> on template parameters outside of its innermost explicit specialized
> scope.
> Assuming that there are no real missing levels, by removing those
> levels, missing_level should be = 0. As a result, by roughly doing
>
> if (missing_levels < 0) {
>   tree trimmed_full_args = get_innermost_template_args(full_targs,
> TEMPLATE_TYPE_ORIG_LEVEL(auto_node));
>   full_targs = trimmed_full_args;
> }
> in pt.cc:31262, where we calculate and check missing_levels, we would
> be able to fix the errors.
> Note that, for the case where there are real missing levels, we are
> putting irrelevant template arguments for the missing levels instead
> of make_tree_vec(0). By this change:
> - If the type is independent of those missing levels: it works fine either 
> way.
> - If the type is dependent on those missing levels: Instead of raising
> an ICE, the compiler exhibits undefined behavior.
---
 gcc/cp/pt.cc                                  | 14 ++++++--
 .../g++.dg/cpp2a/concepts-placeholder14.C     | 19 +++++++++++
 .../g++.dg/cpp2a/concepts-placeholder15.C     | 15 +++++++++
 .../g++.dg/cpp2a/concepts-placeholder16.C     | 33 +++++++++++++++++++
 4 files changed, 78 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 3b2106dd3..bdf03a1a7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -31044,7 +31044,8 @@ unparenthesized_id_or_class_member_access_p (tree init)
    OUTER_TARGS is used during template argument deduction (context == 
adc_unify)
    to properly substitute the result.  It's also used in the adc_unify and
    adc_requirement contexts to communicate the necessary template arguments
-   to satisfaction.  OUTER_TARGS is ignored in other contexts.
+   to satisfaction.  OUTER_TARGS will be used for other contexts if it is a
+   function scope deduction. Otherwise it is ignored.
 
    Additionally for adc_unify contexts TMPL is the template for which TYPE
    is a template parameter type.
@@ -31260,8 +31261,15 @@ do_auto_deduction (tree type, tree init, tree 
auto_node,
         these missing levels, but this hack otherwise allows us to handle a
         large subset of possible constraints (including all non-dependent
         constraints).  */
-      if (int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
-                               - TMPL_ARGS_DEPTH (full_targs)))
+      int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
+                               - TMPL_ARGS_DEPTH (full_targs));
+      if (missing_levels < 0)
+       {
+         tree trimmed_full_args = get_innermost_template_args(full_targs,
+                               TEMPLATE_TYPE_ORIG_LEVEL (auto_node));
+         full_targs = trimmed_full_args;
+       }
+      if (missing_levels > 0)
        {
          tree dummy_levels = make_tree_vec (missing_levels);
          for (int i = 0; i < missing_levels; ++i)
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
new file mode 100644
index 000000000..fcdbd7608
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder14.C
@@ -0,0 +1,19 @@
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T>
+concept C = __is_same(T, int);
+
+template<typename T>
+void f() {
+}
+
+template<>
+void f<int>() {
+  C auto x = 1;
+}
+
+int main() {
+  f<int>();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
new file mode 100644
index 000000000..b4f73f407
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C
@@ -0,0 +1,15 @@
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+int x = 0;
+
+template<>
+C<double> auto x<double> = 1.0;
+
+int main() {
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
new file mode 100644
index 000000000..f808ef1b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
@@ -0,0 +1,33 @@
+// PR c++/114915
+// { dg-do compile { target c++20 } }
+
+template<typename T, typename U>
+concept C = __is_same(T, U);
+
+template<typename T>
+struct A
+{ 
+    template<typename U>
+    void f() {
+    }
+};
+ 
+template<>
+template<>
+void A<int>::f<int>() {
+  C<int> auto x = 1;
+}
+
+template<>
+template<typename U>
+void A<bool>::f() {
+  C<int> auto x = 1;
+}
+
+int main() {
+  A<bool> a;
+  a.f<char>();
+  A<int> b;
+  b.f<int>();
+  return 0;
+}
-- 
2.45.0

Reply via email to