llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.03k stars 11.58k forks source link

Missing expansion of function parameter pack from early substitution of explicit template arguments #23319

Open hubert-reinterpretcast opened 9 years ago

hubert-reinterpretcast commented 9 years ago
Bugzilla Link 22945
Version trunk
OS All
CC @DougGregor,@zygoloid

Extended Description

N3290 subclause 14.8.2 [temp.deduct] paragraphs 2 to 5 specifies that explicit template arguments are substituted into the type of the function template prior to template argument deduction. In the case below, Clang rejects the call to foo (but adding -DUSE_DEFAULT makes it work, which seems to indicate that it understands that the A<N> *... expands to A<0> *). If substitution is performed on the type of foo such that A<N> *... expands to A<0> *, then the case of foo/bar should behave like zip/zap, but that it not the case with Clang.

SOURCE (<stdin>):

#if USE_DEFAULT
# define DEFAULT_ARG = int
#else
# define DEFAULT_ARG
#endif

template <int> struct A { };

template <typename T> void zip(A<0> *ap, T);
void zap() { zip(0, 0); }

template <int ...N, typename T DEFAULT_ARG> void foo(A<N> *..., T);
void bar() { foo<0>(0, 0); }

COMPILER INVOCATION:

clang -cc1 -std=c++11 -x c++ -

ACTUAL OUTPUT:

<stdin>:13:14: error: no matching function for call to 'foo'
void bar() { foo<0>(0, 0); }
             ^~~~~~
<stdin>:12:50: note: candidate template ignored: couldn't infer template argument 'T'
template <int ...N, typename T DEFAULT_ARG> void foo(A<N> *..., T);
                                                 ^
1 error generated.

EXPECTED OUTPUT:

Clean compile.

COMPILER VERSION INFO:

clang version 3.7.0 (trunk 232493) (llvm/trunk 232485)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Found candidate GCC installation: /usr/local/gcc-4.8.2/lib/gcc/x86_64-unknown-linux-gnu/4.8.2
Selected GCC installation: /usr/local/gcc-4.8.2/lib/gcc/x86_64-unknown-linux-gnu/4.8.2
Candidate multilib: .;@m64
Selected multilib: .;@m64
ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 9 years ago

I don't think it's obvious that this should work. The substitution of <0> into foo doesn't give zip, due to 14.8.1/9; it gives something with a character more like this hypothetical thing:

  template<int ...RestOfN = {}, typename T> void zip2({A<0> *, A<RestOfN>...}, T);

The A<RestOfN>... parameter pack is a non-deduced context. The standard doesn't say how to match up P/A pairs after such a situation.

Suppose we instead have this call:

  foo<>(0);

If we take 14.8.2.1/1 literally, we have two P/A pairs, A<N>* / int and T / (no argument), because the parameter pack special case doesn't apply, so deduction presumably fails.

If we want your example to work, I think 14.8.2.1/1 needs tweaking to say how to form P/A pairs after a function parameter pack that appears before the end of the parameter-declaration-list, or for something (probably 14.8.1) to say how the partial-substitution-of-pack-into-template mechanism is supposed to work.