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.
This commit is contained in:
parent
7424323bd5
commit
2decd2cabe
7 changed files with 147 additions and 11 deletions
|
@ -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)),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'" }
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
|
|
37
gcc/testsuite/g++.dg/template/non-dependent16.C
Normal file
37
gcc/testsuite/g++.dg/template/non-dependent16.C
Normal file
|
@ -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>();
|
||||
}
|
36
gcc/testsuite/g++.dg/template/non-dependent16a.C
Normal file
36
gcc/testsuite/g++.dg/template/non-dependent16a.C
Normal file
|
@ -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>();
|
||||
}
|
21
gcc/testsuite/g++.dg/template/non-dependent17.C
Normal file
21
gcc/testsuite/g++.dg/template/non-dependent17.C
Normal file
|
@ -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 ();
|
||||
}
|
Loading…
Add table
Reference in a new issue