public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r12-6075] c++: ahead-of-time overload set pruning for non-dep calls
@ 2021-12-20 14:29 Patrick Palka
  0 siblings, 0 replies; only message in thread
From: Patrick Palka @ 2021-12-20 14:29 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2decd2cabe5a4f9f403cadc5470e5ed2095b0c45

commit r12-6075-g2decd2cabe5a4f9f403cadc5470e5ed2095b0c45
Author: Patrick Palka <ppalka@redhat.com>
Date:   Mon Dec 20 09:28:20 2021 -0500

    c++: ahead-of-time overload set pruning for non-dep calls
    
    This patch makes us remember the function selected by overload resolution
    during ahead of time processing of a non-dependent call expression, so
    that at instantiation time we avoid repeating some of the work of overload
    resolution for the call.  Note that we already do this for non-dependent
    operator expressions via build_min_non_dep_op_overload.
    
    Some caveats:
    
     * When processing ahead of time a non-dependent call to a member
       function template of a currently open class template (as in
       g++.dg/template/deduce4.C), we end up generating an "inside-out"
       partial instantiation such as S<T>::foo<int, int>(), the likes of
       which we're apparently not prepared to fully instantiate.  So in this
       situation, we instead prune to the selected template instead of the
       specialization in this situation.
    
     * This change triggered a latent FUNCTION_DECL pretty printing issue
       in cpp0x/error2.C -- since we now resolve the call to foo<0> ahead
       of time, the error now looks like:
    
         error: expansion pattern ‘foo()()=0’ contains no parameter pack
    
       where the FUNCTION_DECL for foo<0> is clearly misprinted.  But this
       pretty-printing issue could be reproduced without this patch if
       we define foo as a non-template function.  Since this testcase was
       added to verify pretty printing of TEMPLATE_ID_EXPR, I work around
       this test failure by making the call to foo type-dependent and thus
       immune to this ahead of time pruning.
    
     * We now reject parts of cpp0x/fntmp-equiv1.C because we notice that
       the non-dependent call d(f, b) in
    
         int d(int, int);
         template <unsigned long f, unsigned b, typename> e<d(f, b)> d();
    
       is non-constexpr.  Since this testcase is about equivalency of
       dependent names in the context of declaration matching, it seems the
       best fix here is to make the calls to d, d2 and d3 within the
       function signatures dependent.
    
    gcc/cp/ChangeLog:
    
            * call.c (build_new_method_call): For a non-dependent call
            expression inside a template, returning a templated tree
            whose overload set contains just the selected function.
            * semantics.c (finish_call_expr): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/error2.C: Make the call to foo type-dependent in
            order to avoid latent pretty-printing issue for FUNCTION_DECL
            inside MODOP_EXPR.
            * g++.dg/cpp0x/fntmp-equiv1.C: Make the calls to d, d2 and d3
            within the function signatures dependent.
            * g++.dg/template/non-dependent16.C: New test.
            * g++.dg/template/non-dependent16a.C: New test.
            * g++.dg/template/non-dependent17.C: New test.

Diff:
---
 gcc/cp/call.c                                    | 27 +++++++++++++++++
 gcc/cp/semantics.c                               | 15 ++++++++++
 gcc/testsuite/g++.dg/cpp0x/error2.C              |  4 +--
 gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C        | 18 ++++++------
 gcc/testsuite/g++.dg/template/non-dependent16.C  | 37 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/non-dependent16a.C | 36 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/template/non-dependent17.C  | 21 ++++++++++++++
 7 files changed, 147 insertions(+), 11 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 495dcdd77b3..1fbfc580a1e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -11163,6 +11163,33 @@ build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args,
 	}
       if (INDIRECT_REF_P (call))
 	call = TREE_OPERAND (call, 0);
+
+      /* Prune all but the selected function from the original overload
+	 set so that we can avoid some duplicate work at instantiation time.  */
+      if (really_overloaded_fn (fns))
+	{
+	  if (DECL_TEMPLATE_INFO (fn)
+	      && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (fn))
+	      && dependent_type_p (DECL_CONTEXT (fn)))
+	    {
+	      /* FIXME: We're not prepared to fully instantiate "inside-out"
+		 partial instantiations such as A<T>::f<int>().  So instead
+		 use the selected template, not the specialization.  */
+
+	      if (OVL_SINGLE_P (fns))
+		/* If the original overload set consists of a single function
+		   template, this isn't beneficial.  */
+		goto skip_prune;
+
+	      fn = ovl_make (DECL_TI_TEMPLATE (fn));
+	      if (template_only)
+		fn = lookup_template_function (fn, explicit_targs);
+	    }
+	  orig_fns = copy_node (orig_fns);
+	  BASELINK_FUNCTIONS (orig_fns) = fn;
+	}
+
+skip_prune:
       call = (build_min_non_dep_call_vec
 	      (call,
 	       build_min (COMPONENT_REF, TREE_TYPE (CALL_EXPR_FN (call)),
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 6603066c620..6ffd82cb0ec 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2893,6 +2893,21 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
     {
       if (INDIRECT_REF_P (result))
 	result = TREE_OPERAND (result, 0);
+
+      /* Prune all but the selected function from the original overload
+	 set so that we can avoid some duplicate work at instantiation time.  */
+      if (TREE_CODE (result) == CALL_EXPR
+	  && really_overloaded_fn (orig_fn))
+	{
+	  orig_fn = CALL_EXPR_FN (result);
+	  if (TREE_CODE (orig_fn) == COMPONENT_REF)
+	    {
+	      /* The non-dependent result of build_new_method_call.  */
+	      orig_fn = TREE_OPERAND (orig_fn, 1);
+	      gcc_assert (BASELINK_P (orig_fn));
+	    }
+	}
+
       result = build_call_vec (TREE_TYPE (result), orig_fn, orig_args);
       SET_EXPR_LOCATION (result, input_location);
       KOENIG_LOOKUP_P (result) = koenig_p;
diff --git a/gcc/testsuite/g++.dg/cpp0x/error2.C b/gcc/testsuite/g++.dg/cpp0x/error2.C
index e6af294c180..eb966362ccb 100644
--- a/gcc/testsuite/g++.dg/cpp0x/error2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/error2.C
@@ -3,7 +3,7 @@
 
 template<int> int foo();
 
-template<typename F> void bar(F f)
+template<typename F, int N> void bar(F f)
 {
-  f((foo<0>()=0)...); // { dg-error "pattern '\\(foo\\<0\\>\\)\\(\\)=0'" }
+  f((foo<N>()=0)...); // { dg-error "pattern '\\(foo\\<N\\>\\)\\(\\)=0'" }
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C
index 833ae6fc85c..c7d7d60481e 100644
--- a/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/fntmp-equiv1.C
@@ -3,21 +3,21 @@
 
 int d(int, int);
 template <long> class e {};
-template <unsigned long f, unsigned b, typename> e<sizeof(d(f, b))> d();
-template <unsigned long f, unsigned b, typename> e<d(f, b)> d();
+template <class T> e<sizeof(d(T{}, T{}))> d(...);
+template <class T> e<d(T{}, T{})> d(...);
 
 template <class T, class U> constexpr T d2(T, U) { return 42; }
-template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2();
-template <unsigned long f, unsigned b, typename> e<d2(f, b)> d2();
+template <class T> e<d2(T{}, T{})> d2(...);
+template <class T> e<d2(T{}, T{})> d2(...);
 
 template <typename a, typename c> a d3(a, c);
-template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3();
-template <unsigned long f, unsigned b, typename> e<sizeof(d3(f, b))> d3();
+template <class T> e<sizeof(d3(T{}, T{}))> d3(...);
+template <class T> e<sizeof(d3(T{}, T{}))> d3(...);
 
 
 int main()
 {
-  d<1,2,int>();
-  d2<1,2,int>();
-  d3<1,2,int>();
+  d<int>();
+  d2<int>();
+  d3<int>();
 }
diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C
new file mode 100644
index 00000000000..ee8ef902529
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/non-dependent16.C
@@ -0,0 +1,37 @@
+// This test verifies that after resolving a non-dependent call expression
+// ahead of time, we prune all but the selected candidate from the overload
+// set.  Without this optimization, overload resolution for the final call to
+// f<void>() would be exponential in the size of the overload set.
+
+// { dg-do compile { target c++11 } }
+
+template<class T> void f();
+template<class T> auto f() -> decltype(f<void>(), 1, *T());
+template<class T> auto f() -> decltype(f<void>(), 2, *T());
+template<class T> auto f() -> decltype(f<void>(), 3, *T());
+template<class T> auto f() -> decltype(f<void>(), 4, *T());
+template<class T> auto f() -> decltype(f<void>(), 5, *T());
+template<class T> auto f() -> decltype(f<void>(), 6, *T());
+template<class T> auto f() -> decltype(f<void>(), 7, *T());
+template<class T> auto f() -> decltype(f<void>(), 8, *T());
+template<class T> auto f() -> decltype(f<void>(), 9, *T());
+template<class T> auto f() -> decltype(f<void>(), 10, *T());
+template<class T> auto f() -> decltype(f<void>(), 11, *T());
+template<class T> auto f() -> decltype(f<void>(), 12, *T());
+template<class T> auto f() -> decltype(f<void>(), 13, *T());
+template<class T> auto f() -> decltype(f<void>(), 14, *T());
+template<class T> auto f() -> decltype(f<void>(), 15, *T());
+template<class T> auto f() -> decltype(f<void>(), 16, *T());
+template<class T> auto f() -> decltype(f<void>(), 17, *T());
+template<class T> auto f() -> decltype(f<void>(), 18, *T());
+template<class T> auto f() -> decltype(f<void>(), 19, *T());
+template<class T> auto f() -> decltype(f<void>(), 20, *T());
+template<class T> auto f() -> decltype(f<void>(), 21, *T());
+template<class T> auto f() -> decltype(f<void>(), 22, *T());
+template<class T> auto f() -> decltype(f<void>(), 23, *T());
+template<class T> auto f() -> decltype(f<void>(), 24, *T());
+template<class T> auto f() -> decltype(f<void>(), 25, *T());
+
+int main() {
+  f<void>();
+}
diff --git a/gcc/testsuite/g++.dg/template/non-dependent16a.C b/gcc/testsuite/g++.dg/template/non-dependent16a.C
new file mode 100644
index 00000000000..0e04d646c0b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/non-dependent16a.C
@@ -0,0 +1,36 @@
+// Like non-dependent16.C, but using member functions.
+
+// { dg-do compile { target c++11 } }
+
+struct A {
+  template<class T> static void f();
+  template<class T> static auto f() -> decltype(f<void>(), 1, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 2, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 3, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 4, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 5, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 6, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 7, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 8, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 9, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 10, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 11, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 12, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 13, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 14, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 15, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 16, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 17, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 18, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 19, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 20, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 21, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 22, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 23, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 24, *T());
+  template<class T> static auto f() -> decltype(f<void>(), 25, *T());
+};
+
+int main() {
+  A::f<void>();
+}
diff --git a/gcc/testsuite/g++.dg/template/non-dependent17.C b/gcc/testsuite/g++.dg/template/non-dependent17.C
new file mode 100644
index 00000000000..6b62dd2a60d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/non-dependent17.C
@@ -0,0 +1,21 @@
+// A variant of deduce4.C with multiple overloads of foo.  Verify we don't
+// crash after ahead-of-time pruning of the overload set for the non-dependent
+// call to foo.
+// { dg-do compile }
+
+template <typename T>
+struct S {
+  template <typename U, typename V>
+  static void foo(V) { }
+  template <typename U>
+  static void foo(...) { }
+
+  void bar () { foo<int>(10); }
+};
+
+void
+test ()
+{
+  S<int> s;
+  s.bar ();
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-12-20 14:29 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-20 14:29 [gcc r12-6075] c++: ahead-of-time overload set pruning for non-dep calls Patrick Palka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).