Author: rsmith Date: Mon Jan 9 01:14:40 2017 New Revision: 291425 URL: http://llvm.org/viewvc/llvm-project?rev=291425&view=rev Log: Implement DR1388 (wg21.link/cwg1388).
This issue clarifies how deduction proceeds past a non-trailing function parameter pack. Essentially, the pack itself is skipped and consumes no arguments (except for those implied by an explicitly-specified template arguments), and nothing is deduced from it. As a small fix to the standard's rule, we do not allow subsequent deduction to change the length of the function parameter pack (by preventing extension of the explicitly-specified pack if present, and otherwise deducing all contained packs to empty packs). Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp cfe/trunk/test/CXX/drs/dr13xx.cpp cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp cfe/trunk/test/SemaTemplate/deduction.cpp cfe/trunk/www/cxx_dr_status.html Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=291425&r1=291424&r2=291425&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Mon Jan 9 01:14:40 2017 @@ -669,6 +669,19 @@ public: Info.PendingDeducedPacks[Pack.Index] = Pack.Outer; } + /// Determine whether this pack has already been partially expanded into a + /// sequence of (prior) function parameters / template arguments. + bool isPartiallyExpanded() { + if (Packs.size() != 1 || !S.CurrentInstantiationScope) + return false; + + auto *PartiallySubstitutedPack = + S.CurrentInstantiationScope->getPartiallySubstitutedPack(); + return PartiallySubstitutedPack && + getDepthAndIndex(PartiallySubstitutedPack) == + std::make_pair(Info.getDeducedDepth(), Packs.front().Index); + } + /// Move to deducing the next element in each pack that is being deduced. void nextPackElement() { // Capture the deduced template arguments for each parameter pack expanded @@ -2552,6 +2565,12 @@ static bool isSimpleTemplateIdType(QualT return false; } +static void +MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, + bool OnlyDeduced, + unsigned Level, + llvm::SmallBitVector &Deduced); + /// \brief Substitute the explicitly-provided template arguments into the /// given function template according to C++ [temp.arg.explicit]. /// @@ -2613,7 +2632,7 @@ Sema::SubstituteExplicitTemplateArgument // Enter a new template instantiation context where we check the // explicitly-specified template arguments against this function template, // and then substitute them into the function parameter types. - SmallVector<TemplateArgument, 4> DeducedArgs(Deduced.begin(), Deduced.end()); + SmallVector<TemplateArgument, 4> DeducedArgs; InstantiatingTemplate Inst(*this, Info.getLocation(), FunctionTemplate, DeducedArgs, ActiveTemplateInstantiation::ExplicitTemplateArgumentSubstitution, @@ -3389,7 +3408,6 @@ Sema::TemplateDeductionResult Sema::Dedu // Template argument deduction is done by comparing each function template // parameter type (call it P) with the type of the corresponding argument // of the call (call it A) as described below. - unsigned CheckArgs = Args.size(); if (Args.size() < Function->getMinRequiredArguments() && !PartialOverloading) return TDK_TooFewArguments; else if (TooManyArguments(NumParams, Args.size(), PartialOverloading)) { @@ -3397,9 +3415,7 @@ Sema::TemplateDeductionResult Sema::Dedu = Function->getType()->getAs<FunctionProtoType>(); if (Proto->isTemplateVariadic()) /* Do nothing */; - else if (Proto->isVariadic()) - CheckArgs = NumParams; - else + else if (!Proto->isVariadic()) return TDK_TooManyArguments; } @@ -3456,7 +3472,7 @@ Sema::TemplateDeductionResult Sema::Dedu dyn_cast<PackExpansionType>(ParamType); if (!ParamExpansion) { // Simple case: matching a function parameter to a function argument. - if (ArgIdx >= CheckArgs) + if (ArgIdx >= Args.size()) break; if (auto Result = DeduceCallArgument(ParamType, ArgIdx++)) @@ -3465,36 +3481,47 @@ Sema::TemplateDeductionResult Sema::Dedu continue; } + QualType ParamPattern = ParamExpansion->getPattern(); + PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info, + ParamPattern); + // C++0x [temp.deduct.call]p1: // For a function parameter pack that occurs at the end of the // parameter-declaration-list, the type A of each remaining argument of // the call is compared with the type P of the declarator-id of the // function parameter pack. Each comparison deduces template arguments // for subsequent positions in the template parameter packs expanded by - // the function parameter pack. For a function parameter pack that does - // not occur at the end of the parameter-declaration-list, the type of - // the parameter pack is a non-deduced context. - // FIXME: This does not say that subsequent parameters are also non-deduced. - // See also DR1388 / DR1399, which effectively says we should keep deducing - // after the pack. - if (ParamIdx + 1 < NumParamTypes) - break; - - QualType ParamPattern = ParamExpansion->getPattern(); - PackDeductionScope PackScope(*this, TemplateParams, Deduced, Info, - ParamPattern); - - for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx) - if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx)) - return Result; + // the function parameter pack. When a function parameter pack appears + // in a non-deduced context [not at the end of the list], the type of + // that parameter pack is never deduced. + // + // FIXME: The above rule allows the size of the parameter pack to change + // after we skip it (in the non-deduced case). That makes no sense, so + // we instead notionally deduce the pack against N arguments, where N is + // the length of the explicitly-specified pack if it's expanded by the + // parameter pack and 0 otherwise, and we treat each deduction as a + // non-deduced context. + if (ParamIdx + 1 == NumParamTypes) { + for (; ArgIdx < Args.size(); PackScope.nextPackElement(), ++ArgIdx) + if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx)) + return Result; + } else { + // If the parameter type contains an explicitly-specified pack that we + // could not expand, skip the number of parameters notionally created + // by the expansion. + Optional<unsigned> NumExpansions = ParamExpansion->getNumExpansions(); + if (NumExpansions && !PackScope.isPartiallyExpanded()) + for (unsigned I = 0; I != *NumExpansions && ArgIdx < Args.size(); + ++I, ++ArgIdx) + // FIXME: Should we add OriginalCallArgs for these? What if the + // corresponding argument is a list? + PackScope.nextPackElement(); + } // Build argument packs for each of the parameter packs expanded by this // pack expansion. if (auto Result = PackScope.finish()) return Result; - - // After we've matching against a parameter pack, we're done. - break; } return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, @@ -4230,12 +4257,6 @@ bool Sema::DeduceReturnType(FunctionDecl return StillUndeduced; } -static void -MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, - bool OnlyDeduced, - unsigned Level, - llvm::SmallBitVector &Deduced); - /// \brief If this is a non-static member function, static void AddImplicitObjectParameterType(ASTContext &Context, Modified: cfe/trunk/test/CXX/drs/dr13xx.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr13xx.cpp?rev=291425&r1=291424&r2=291425&view=diff ============================================================================== --- cfe/trunk/test/CXX/drs/dr13xx.cpp (original) +++ cfe/trunk/test/CXX/drs/dr13xx.cpp Mon Jan 9 01:14:40 2017 @@ -174,3 +174,55 @@ namespace dr1359 { // dr1359: 3.5 constexpr Y y = Y(); // expected-error {{no matching}} #endif } + +namespace dr1388 { // dr1388: 4.0 + template<typename A, typename ...T> void f(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}} + template<typename ...T> void g(T..., int); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}} + template<typename ...T, typename A> void h(T..., A); // expected-note 1+{{candidate}} expected-error 0-1{{C++11}} + + void test_f() { + f(0); // ok, trailing parameter pack deduced to empty + f(0, 0); // expected-error {{no matching}} + f<int>(0); + f<int>(0, 0); // expected-error {{no matching}} + f<int, int>(0, 0); + f<int, int, int>(0, 0); // expected-error {{no matching}} + + g(0); + g(0, 0); // expected-error {{no matching}} + g<>(0); + g<int>(0); // expected-error {{no matching}} + g<int>(0, 0); + + h(0); + h(0, 0); // expected-error {{no matching}} + h<int>(0, 0); + h<int, int>(0, 0); // expected-error {{no matching}} + } + + // A non-trailing parameter pack is still a non-deduced context, even though + // we know exactly how many arguments correspond to it. + template<typename T, typename U> struct pair {}; + template<typename ...T> struct tuple { typedef char type; }; // expected-error 0-2{{C++11}} + template<typename ...T, typename ...U> void f_pair_1(pair<T, U>..., int); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 0)}} + template<typename ...T, typename U> void f_pair_2(pair<T, char>..., U); // expected-error 0-2{{C++11}} + template<typename ...T, typename ...U> void f_pair_3(pair<T, U>..., tuple<U...>); // expected-error 0-2{{C++11}} expected-note {{different lengths (2 vs. 1)}} + template<typename ...T> void f_pair_4(pair<T, char>..., T...); // expected-error 0-2{{C++11}} expected-note {{<int, long> vs. <int, long, const char *>}} + void g(pair<int, char> a, pair<long, char> b, tuple<char, char> c) { + f_pair_1<int, long>(a, b, 0); // expected-error {{no match}} + f_pair_2<int, long>(a, b, 0); + f_pair_3<int, long>(a, b, c); + f_pair_3<int, long>(a, b, tuple<char>()); // expected-error {{no match}} + f_pair_4<int, long>(a, b, 0, 0L); + f_pair_4<int, long>(a, b, 0, 0L, "foo"); // expected-error {{no match}} + } +} + +namespace dr1399 { // dr1399: dup 1388 + template<typename ...T> void f(T..., int, T...) {} // expected-note {{candidate}} expected-error 0-1{{C++11}} + void g() { + f(0); + f<int>(0, 0, 0); + f(0, 0, 0); // expected-error {{no match}} + } +} Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp?rev=291425&r1=291424&r2=291425&view=diff ============================================================================== --- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp (original) +++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p1-0x.cpp Mon Jan 9 01:14:40 2017 @@ -76,14 +76,17 @@ void test_pair_deduction(int *ip, float first_arg_pair(make_pair(ip, 17), 16); // expected-error{{no matching function for call to 'first_arg_pair'}} } -// For a function parameter pack that does not occur at the end of the -// parameter-declaration-list, the type of the parameter pack is a -// non-deduced context. +// A function parameter pack not at the end of the parameter list is never +// deduced. We interpret this as meaning the types within it are never +// deduced, and thus must match explicitly-specified values. template<typename ...Types> struct tuple { }; template<typename ...Types> -void pack_not_at_end(tuple<Types...>, Types... values, int); +void pack_not_at_end(tuple<Types...>, Types... values, int); // expected-note {{<int *, double *> vs. <>}} void test_pack_not_at_end(tuple<int*, double*> t2) { - pack_not_at_end(t2, 0, 0, 0); + pack_not_at_end(t2, 0, 0, 0); // expected-error {{no match}} + // FIXME: Should the "original argument type must match deduced parameter + // type" rule apply here? + pack_not_at_end<int*, double*>(t2, 0, 0, 0); // ok } Modified: cfe/trunk/test/SemaTemplate/deduction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/deduction.cpp?rev=291425&r1=291424&r2=291425&view=diff ============================================================================== --- cfe/trunk/test/SemaTemplate/deduction.cpp (original) +++ cfe/trunk/test/SemaTemplate/deduction.cpp Mon Jan 9 01:14:40 2017 @@ -350,17 +350,35 @@ namespace deduction_substitution_failure } namespace deduction_after_explicit_pack { - template<typename ...T, typename U> int *f(T ...t, int &r, U *u) { // expected-note {{couldn't infer template argument 'U'}} + template<typename ...T, typename U> int *f(T ...t, int &r, U *u) { return u; } template<typename U, typename ...T> int *g(T ...t, int &r, U *u) { return u; } void h(float a, double b, int c) { - // FIXME: Under DR1388, this appears to be valid. - f<float&, double&>(a, b, c, &c); // expected-error {{no matching}} + f<float&, double&>(a, b, c, &c); // ok g<int, float&, double&>(a, b, c, &c); // ok } + + template <typename... T> void i(T..., int, T..., ...); // expected-note 5{{deduced conflicting}} + void j() { + i(0); + i(0, 1); // expected-error {{no match}} + i(0, 1, 2); // expected-error {{no match}} + i<>(0); + i<>(0, 1); // expected-error {{no match}} + i<>(0, 1, 2); // expected-error {{no match}} + i<int, int>(0, 1, 2, 3, 4); + i<int, int>(0, 1, 2, 3, 4, 5); // expected-error {{no match}} + } + + // GCC alarmingly accepts this by deducing T={int} by matching the second + // parameter against the first argument, then passing the first argument + // through the first parameter. + template<typename... T> struct X { X(int); operator int(); }; + template<typename... T> void p(T..., X<T...>, ...); // expected-note {{deduced conflicting}} + void q() { p(X<int>(0), 0); } // expected-error {{no match}} } namespace overload_vs_pack { Modified: cfe/trunk/www/cxx_dr_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=291425&r1=291424&r2=291425&view=diff ============================================================================== --- cfe/trunk/www/cxx_dr_status.html (original) +++ cfe/trunk/www/cxx_dr_status.html Mon Jan 9 01:14:40 2017 @@ -8143,7 +8143,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1388">1388</a></td> <td>CD3</td> <td>Missing non-deduced context following a function parameter pack</td> - <td class="none" align="center">Unknown</td> + <td class="svn" align="center">SVN</td> </tr> <tr id="1389"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1389">1389</a></td> @@ -8209,7 +8209,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1399">1399</a></td> <td>CD3</td> <td>Deduction with multiple function parameter packs</td> - <td class="none" align="center">Unknown</td> + <td class="svn" align="center">Duplicate of <a href="#1388">1388</a></td> </tr> <tr id="1400"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1400">1400</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits