From fb6fe237ce9a38b43c0eb39bbd6afa63c01ac70f Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 12 Jul 2018 23:32:39 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336962 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaTemplateDeduction.cpp | 80 ++++++++++++------- .../temp.deduct/temp.deduct.type/p5-0x.cpp | 18 ++++- .../temp.deduct/temp.deduct.type/p9-0x.cpp | 38 +++++++++ 3 files changed, 106 insertions(+), 30 deletions(-) diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 8ae22136e1..3a480628a7 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -941,12 +941,6 @@ DeduceTemplateArguments(Sema &S, SmallVectorImpl &Deduced, unsigned TDF, bool PartialOrdering = false) { - // Fast-path check to see if we have too many/too few arguments. - if (NumParams != NumArgs && - !(NumParams && isa(Params[NumParams - 1])) && - !(NumArgs && isa(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 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, TemplateParameterList *TemplateParams, // 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 &Ctx, QualType T, const FunctionProtoType *Proto = cast(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()) { + 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; diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp index d239a5e172..697412995b 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p5-0x.cpp @@ -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 when processing the first argument, +// and then fail because 'int' doesn't match 'double, int'.) int check_nondeduced_pack_test0[ has_nondeduced_pack_test::value? 1 : -1]; + int(float, double)>::value? -1 : 1]; +template void has_non_trailing_pack(T ..., int); +void (*ptr_has_non_trailing_pack)(char, int) = has_non_trailing_pack; +template 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; +void (*ptr_has_non_trailing_pack_and_more_2)(float, double, int) = &has_non_trailing_pack_and_more; // expected-error {{does not match}} diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp index 54a54b0c48..fccac8f1e5 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.type/p9-0x.cpp @@ -35,6 +35,44 @@ namespace PackExpansionNotAtEnd { struct UselessPartialSpec; // 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 struct X {}; + template void f1(void(T..., U)); + // expected-note@+1 {{couldn't infer template argument 'U'}} + template void f2(X); // FIXME: ill-formed, U is not deducible + + void g(int, int, int); + X 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(g); + // This is not deducible: the T... parameter renders the entire + // template-argument-list a non-deduced context, so U is not deducible. + f2(h); // expected-error {{no matching function}} + } + + template struct Y; + template + struct Y {}; // expected-error {{cannot be deduced}} + template // expected-note {{non-deducible template parameter 'U'}} + struct Y>; // 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 + struct Y {}; +} + namespace DeduceNonTypeTemplateArgsInArray { template struct split_arrays; -- 2.50.1