Author: rsmith Date: Thu Jul 12 16:32:39 2018 New Revision: 336962 URL: http://llvm.org/viewvc/llvm-project?rev=336962&view=rev Log: PR38136: improve handling of template argument deduction of non-trailing function parameter packs.
This makes our handling of non-trailing function parameter packs consistent between the case of deduction at the top level in a function call and other cases where deduction encounters a non-trailing function parameter pack. Instead of treating a non-trailing pack and all later parameters as being non-deduced, we treat a non-trailing pack as exactly matching any explicitly-specified template arguments (or being an empty pack if there are no such arguments). This corresponds to the "never deduced" rule in [temp.deduct.call]p1, but generalized to all deduction contexts. Non-trailing template argument packs still result in the entire template argument list being treated as non-deduced, as specified in [temp.deduct.type]p9. Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=336962&r1=336961&r2=336962&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original) +++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Thu Jul 12 16:32:39 2018 @@ -941,12 +941,6 @@ DeduceTemplateArguments(Sema &S, SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned TDF, bool PartialOrdering = false) { - // Fast-path check to see if we have too many/too few arguments. - if (NumParams != NumArgs && - !(NumParams && isa<PackExpansionType>(Params[NumParams - 1])) && - !(NumArgs && isa<PackExpansionType>(Args[NumArgs - 1]))) - return Sema::TDK_MiscellaneousDeductionFailure; - // C++0x [temp.deduct.type]p10: // Similarly, if P has a form that contains (T), then each parameter type // Pi of the respective parameter-type- list of P is compared with the @@ -983,13 +977,6 @@ DeduceTemplateArguments(Sema &S, continue; } - // C++0x [temp.deduct.type]p5: - // The non-deduced contexts are: - // - A function parameter pack that does not occur at the end of the - // parameter-declaration-clause. - if (ParamIdx + 1 < NumParams) - return Sema::TDK_Success; - // C++0x [temp.deduct.type]p10: // If the parameter-declaration corresponding to Pi is a function // parameter pack, then the type of its declarator- id is compared with @@ -1000,15 +987,41 @@ DeduceTemplateArguments(Sema &S, QualType Pattern = Expansion->getPattern(); PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); - for (; ArgIdx < NumArgs; ++ArgIdx) { - // Deduce template arguments from the pattern. - if (Sema::TemplateDeductionResult Result - = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern, - Args[ArgIdx], Info, Deduced, - TDF, PartialOrdering)) - return Result; + if (ParamIdx + 1 == NumParams) { + for (; ArgIdx < NumArgs; ++ArgIdx) { + // Deduce template arguments from the pattern. + if (Sema::TemplateDeductionResult Result + = DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern, + Args[ArgIdx], Info, Deduced, + TDF, PartialOrdering)) + return Result; - PackScope.nextPackElement(); + PackScope.nextPackElement(); + } + } else { + // C++0x [temp.deduct.type]p5: + // The non-deduced contexts are: + // - A function parameter pack that does not occur at the end of the + // parameter-declaration-clause. + // + // FIXME: There is no wording to say what we should do in this case. We + // choose to resolve this by applying the same rule that is applied for a + // function call: that is, deduce all contained packs to their + // explicitly-specified values (or to <> if there is no such value). + // + // This is seemingly-arbitrarily different from the case of a template-id + // with a non-trailing pack-expansion in its arguments, which renders the + // entire template-argument-list a non-deduced context. + + // 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 = Expansion->getNumExpansions(); + if (NumExpansions && !PackScope.isPartiallyExpanded()) { + for (unsigned I = 0; I != *NumExpansions && ArgIdx < NumArgs; + ++I, ++ArgIdx) + PackScope.nextPackElement(); + } } // Build argument packs for each of the parameter packs expanded by this @@ -2195,10 +2208,6 @@ DeduceTemplateArguments(Sema &S, Templat // template parameter packs expanded by Pi. TemplateArgument Pattern = Params[ParamIdx].getPackExpansionPattern(); - // FIXME: If there are no remaining arguments, we can bail out early - // and set any deduced parameter packs to an empty argument pack. - // The latter part of this is a (minor) correctness issue. - // Prepare to deduce the packs within the pattern. PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern); @@ -5280,9 +5289,24 @@ MarkUsedTemplateParameters(ASTContext &C const FunctionProtoType *Proto = cast<FunctionProtoType>(T); MarkUsedTemplateParameters(Ctx, Proto->getReturnType(), OnlyDeduced, Depth, Used); - for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I) - MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced, - Depth, Used); + for (unsigned I = 0, N = Proto->getNumParams(); I != N; ++I) { + // C++17 [temp.deduct.type]p5: + // The non-deduced contexts are: [...] + // -- A function parameter pack that does not occur at the end of the + // parameter-declaration-list. + if (!OnlyDeduced || I + 1 == N || + !Proto->getParamType(I)->getAs<PackExpansionType>()) { + MarkUsedTemplateParameters(Ctx, Proto->getParamType(I), OnlyDeduced, + Depth, Used); + } else { + // FIXME: C++17 [temp.deduct.call]p1: + // When a function parameter pack appears in a non-deduced context, + // the type of that pack is never deduced. + // + // We should also track a set of "never deduced" parameters, and + // subtract that from the list of deduced parameters after marking. + } + } if (auto *E = Proto->getNoexceptExpr()) MarkUsedTemplateParameters(Ctx, E, OnlyDeduced, Depth, Used); break; Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp?rev=336962&r1=336961&r2=336962&view=diff ============================================================================== --- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp (original) +++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp Thu Jul 12 16:32:39 2018 @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -// expected-no-diagnostics // FIXME: More bullets to go! @@ -16,8 +15,23 @@ struct has_nondeduced_pack_test<R(FirstT // - A function parameter pack that does not occur at the end of the // parameter-declaration-clause. +// +// We interpret [temp.deduct.call]p1's +// +// "When a function parameter pack appears in a non-deduced context +// (12.9.2.5), the type of that pack is never deduced." +// +// as applying in all deduction contexts, not just [temp.deduct.call], +// so we do *not* deduce Types from the second argument here. (More +// precisely, we deduce it as <> when processing the first argument, +// and then fail because 'int' doesn't match 'double, int'.) int check_nondeduced_pack_test0[ has_nondeduced_pack_test<int(float, double, int), - int(float, double)>::value? 1 : -1]; + int(float, double)>::value? -1 : 1]; +template<typename ...T> void has_non_trailing_pack(T ..., int); +void (*ptr_has_non_trailing_pack)(char, int) = has_non_trailing_pack<char>; +template<typename ...T, typename U> void has_non_trailing_pack_and_more(T ..., U); // expected-note {{failed}} +void (*ptr_has_non_trailing_pack_and_more_1)(float, double, int) = &has_non_trailing_pack_and_more<float, double>; +void (*ptr_has_non_trailing_pack_and_more_2)(float, double, int) = &has_non_trailing_pack_and_more<float>; // expected-error {{does not match}} Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp?rev=336962&r1=336961&r2=336962&view=diff ============================================================================== --- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp (original) +++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp Thu Jul 12 16:32:39 2018 @@ -35,6 +35,44 @@ namespace PackExpansionNotAtEnd { struct UselessPartialSpec<Types..., Tail>; // expected-error{{class template partial specialization contains template parameters that cannot be deduced; this partial specialization will never be used}} } +// When a pack expansion occurs within a template argument list, the entire +// list is a non-deduced context. For the corresponding case in a function +// parameter list, only that parameter is non-deduced. +// +// FIXME: It's not clear that this difference is intended, but the wording is +// explicit. +namespace PackExpansionNotAtEndFunctionVersusTemplate { + template<typename ...T> struct X {}; + template<typename ...T, typename U> void f1(void(T..., U)); + // expected-note@+1 {{couldn't infer template argument 'U'}} + template<typename ...T, typename U> void f2(X<T..., U>); // FIXME: ill-formed, U is not deducible + + void g(int, int, int); + X<int, int, int> h; + void test() { + // This is deducible: the T... parameter is a non-deduced context, but + // that's OK because we don't need to deduce it. + f1<int, int>(g); + // This is not deducible: the T... parameter renders the entire + // template-argument-list a non-deduced context, so U is not deducible. + f2<int, int>(h); // expected-error {{no matching function}} + } + + template<typename T> struct Y; + template<typename ...T, // expected-note {{non-deducible template parameter 'T'}} + typename U> + struct Y<void(T..., U)> {}; // expected-error {{cannot be deduced}} + template<typename ...T, // expected-note {{non-deducible template parameter 'T'}} + typename U> // expected-note {{non-deducible template parameter 'U'}} + struct Y<X<T..., U>>; // expected-error {{cannot be deduced}} + // FIXME: T is not deducible here, due to [temp.deduct.call]p1: + // "When a function parameter pack appears in a non-deduced context, + // the type of that pack is never deduced." + template<typename ...T, + typename U> + struct Y<void(T..., U, T...)> {}; +} + namespace DeduceNonTypeTemplateArgsInArray { template<typename ...ArrayTypes> struct split_arrays; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits