diff options
-rw-r--r-- | clang/lib/Sema/SemaTemplateDeduction.cpp | 145 | ||||
-rw-r--r-- | clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp | 14 | ||||
-rw-r--r-- | clang/test/SemaTemplate/deduction.cpp | 10 |
3 files changed, 112 insertions, 57 deletions
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 32e195b5b97..50d7c23c26c 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -112,6 +112,15 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams, SmallVectorImpl<DeducedTemplateArgument> &Deduced, bool NumberOfArgumentsMustMatch); +static void MarkUsedTemplateParameters(ASTContext &Ctx, + const TemplateArgument &TemplateArg, + bool OnlyDeduced, unsigned Depth, + llvm::SmallBitVector &Used); + +static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, + bool OnlyDeduced, unsigned Level, + llvm::SmallBitVector &Deduced); + /// \brief If the given expression is of a form that permits the deduction /// of a non-type template parameter, return the declaration of that /// non-type template parameter. @@ -334,12 +343,24 @@ static Sema::TemplateDeductionResult DeduceNonTypeTemplateArgument( if (!S.getLangOpts().CPlusPlus1z) return Sema::TDK_Success; + if (NTTP->isExpandedParameterPack()) + // FIXME: We may still need to deduce parts of the type here! But we + // don't have any way to find which slice of the type to use, and the + // type stored on the NTTP itself is nonsense. Perhaps the type of an + // expanded NTTP should be a pack expansion type? + return Sema::TDK_Success; + + // Get the type of the parameter for deduction. + QualType ParamType = NTTP->getType(); + if (auto *Expansion = dyn_cast<PackExpansionType>(ParamType)) + ParamType = Expansion->getPattern(); + // FIXME: It's not clear how deduction of a parameter of reference // type from an argument (of non-reference type) should be performed. // For now, we just remove reference types from both sides and let // the final check for matching types sort out the mess. return DeduceTemplateArgumentsByTypeMatch( - S, TemplateParams, NTTP->getType().getNonReferenceType(), + S, TemplateParams, ParamType.getNonReferenceType(), ValueType.getNonReferenceType(), Info, Deduced, TDF_SkipNonDependent, /*PartialOrdering=*/false, /*ArrayBound=*/NewDeduced.wasDeducedFromArrayBound()); @@ -617,29 +638,68 @@ public: SmallVectorImpl<DeducedTemplateArgument> &Deduced, TemplateDeductionInfo &Info, TemplateArgument Pattern) : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) { + // Dig out the partially-substituted pack, if there is one. + const TemplateArgument *PartialPackArgs = nullptr; + unsigned NumPartialPackArgs = 0; + std::pair<unsigned, unsigned> PartialPackDepthIndex(-1u, -1u); + if (auto *Scope = S.CurrentInstantiationScope) + if (auto *Partial = Scope->getPartiallySubstitutedPack( + &PartialPackArgs, &NumPartialPackArgs)) + PartialPackDepthIndex = getDepthAndIndex(Partial); + // Compute the set of template parameter indices that correspond to // parameter packs expanded by the pack expansion. { llvm::SmallBitVector SawIndices(TemplateParams->size()); + + auto AddPack = [&](unsigned Index) { + if (SawIndices[Index]) + return; + SawIndices[Index] = true; + + // Save the deduced template argument for the parameter pack expanded + // by this pack expansion, then clear out the deduction. + DeducedPack Pack(Index); + Pack.Saved = Deduced[Index]; + Deduced[Index] = TemplateArgument(); + + Packs.push_back(Pack); + }; + + // First look for unexpanded packs in the pattern. SmallVector<UnexpandedParameterPack, 2> Unexpanded; S.collectUnexpandedParameterPacks(Pattern, Unexpanded); for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { unsigned Depth, Index; std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]); - if (Depth == Info.getDeducedDepth() && !SawIndices[Index]) { - SawIndices[Index] = true; - - // Save the deduced template argument for the parameter pack expanded - // by this pack expansion, then clear out the deduction. - DeducedPack Pack(Index); - Pack.Saved = Deduced[Index]; - Deduced[Index] = TemplateArgument(); - - Packs.push_back(Pack); - } + if (Depth == Info.getDeducedDepth()) + AddPack(Index); } + assert(!Packs.empty() && "Pack expansion without unexpanded packs?"); + + // This pack expansion will have been partially expanded iff the only + // unexpanded parameter pack within it is the partially-substituted pack. + IsPartiallyExpanded = + Packs.size() == 1 && + PartialPackDepthIndex == + std::make_pair(Info.getDeducedDepth(), Packs.front().Index); + + // Skip over the pack elements that were expanded into separate arguments. + if (IsPartiallyExpanded) + PackElements += NumPartialPackArgs; + + // We can also have deduced template parameters that do not actually + // appear in the pattern, but can be deduced by it (the type of a non-type + // template parameter pack, in particular). These won't have prevented us + // from partially expanding the pack. + llvm::SmallBitVector Used(TemplateParams->size()); + MarkUsedTemplateParameters(S.Context, Pattern, /*OnlyDeduced*/true, + Info.getDeducedDepth(), Used); + for (int Index = Used.find_first(); Index != -1; + Index = Used.find_next(Index)) + if (TemplateParams->getParam(Index)->isParameterPack()) + AddPack(Index); } - assert(!Packs.empty() && "Pack expansion without unexpanded packs?"); for (auto &Pack : Packs) { if (Info.PendingDeducedPacks.size() > Pack.Index) @@ -648,18 +708,19 @@ public: Info.PendingDeducedPacks.resize(Pack.Index + 1); Info.PendingDeducedPacks[Pack.Index] = &Pack; - if (S.CurrentInstantiationScope) { - // If the template argument pack was explicitly specified, add that to - // the set of deduced arguments. - const TemplateArgument *ExplicitArgs; - unsigned NumExplicitArgs; - NamedDecl *PartiallySubstitutedPack = - S.CurrentInstantiationScope->getPartiallySubstitutedPack( - &ExplicitArgs, &NumExplicitArgs); - if (PartiallySubstitutedPack && - getDepthAndIndex(PartiallySubstitutedPack) == - std::make_pair(Info.getDeducedDepth(), Pack.Index)) - Pack.New.append(ExplicitArgs, ExplicitArgs + NumExplicitArgs); + if (PartialPackDepthIndex == + std::make_pair(Info.getDeducedDepth(), Pack.Index)) { + Pack.New.append(PartialPackArgs, PartialPackArgs + NumPartialPackArgs); + // We pre-populate the deduced value of the partially-substituted + // pack with the specified value. This is not entirely correct: the + // value is supposed to have been substituted, not deduced, but the + // cases where this is observable require an exact type match anyway. + // + // FIXME: If we could represent a "depth i, index j, pack elem k" + // parameter, we could substitute the partially-substituted pack + // everywhere and avoid this. + if (Pack.New.size() > PackElements) + Deduced[Pack.Index] = Pack.New[PackElements]; } } } @@ -671,16 +732,7 @@ public: /// 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); - } + bool isPartiallyExpanded() { return IsPartiallyExpanded; } /// Move to deducing the next element in each pack that is being deduced. void nextPackElement() { @@ -692,8 +744,13 @@ public: if (!Pack.New.empty() || !DeducedArg.isNull()) { while (Pack.New.size() < PackElements) Pack.New.push_back(DeducedTemplateArgument()); - Pack.New.push_back(DeducedArg); - DeducedArg = DeducedTemplateArgument(); + if (Pack.New.size() == PackElements) + Pack.New.push_back(DeducedArg); + else + Pack.New[PackElements] = DeducedArg; + DeducedArg = Pack.New.size() > PackElements + 1 + ? Pack.New[PackElements + 1] + : DeducedTemplateArgument(); } } ++PackElements; @@ -784,6 +841,7 @@ private: SmallVectorImpl<DeducedTemplateArgument> &Deduced; TemplateDeductionInfo &Info; unsigned PackElements = 0; + bool IsPartiallyExpanded = false; SmallVector<DeducedPack, 2> Packs; }; @@ -2591,12 +2649,6 @@ static bool isSimpleTemplateIdType(QualType T) { 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]. /// @@ -4894,13 +4946,6 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info); } -static void -MarkUsedTemplateParameters(ASTContext &Ctx, - const TemplateArgument &TemplateArg, - bool OnlyDeduced, - unsigned Depth, - llvm::SmallBitVector &Used); - /// \brief Mark the template parameters that are used by the given /// expression. static void diff --git a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp index 750a0892917..5eee34e8097 100644 --- a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp +++ b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp @@ -203,13 +203,19 @@ namespace transform_params { }; B b({1, 2, 3}, "foo", {'x', 'y', 'z', 'w'}); // ok + // This should be accepted once -std=c++1z implies + // -frelaxed-template-template-args. Without that, a template template + // parameter 'template<int, int, int> typename' cannot bind to a template + // template argument 'template<int...> typename'. template<typename ...T> struct C { // expected-note {{candidate}} template<T ...V, template<T...> typename X> - C(X<V...>); // expected-note {{substitution failure [with T = <>, V = <0, 1, 2>]}} + C(X<V...>); // expected-note {{substitution failure [with T = <int, int, int>, V = <0, 1, 2>]}} }; template<int...> struct Y {}; - // FIXME: This incorrectly deduces T = <>, rather than deducing - // T = <int, int, int> from the types of the elements of V. - // (This failure is not related to class template argument deduction.) C c(Y<0, 1, 2>{}); // expected-error {{no viable constructor or deduction guide}} + + template<typename ...T> struct D { + template<T ...V> D(Y<V...>); + }; + D d(Y<0, 1, 2>{}); } diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp index 216b8347be7..8ac65aacd5b 100644 --- a/clang/test/SemaTemplate/deduction.cpp +++ b/clang/test/SemaTemplate/deduction.cpp @@ -526,13 +526,17 @@ namespace dependent_list_deduction { static_assert(is_same<X<T...>, X<int, char, char>>::value, ""); static_assert(is_same<Y<V...>, Y<3, 2, 4>>::value, ""); } - template<typename ...T, T ...V> void g(const T (&...p)[V]) { // expected-note {{deduced incomplete pack}} + template<typename ...T, T ...V> void g(const T (&...p)[V]) { static_assert(is_same<X<T...>, X<int, decltype(sizeof(0))>>::value, ""); static_assert(is_same<Y<V...>, Y<2, 3>>::value, ""); } void h() { f({1, 2, 3}, {'a', 'b'}, "foo"); - // FIXME: Deduction in this case should succeed. - g({1, 2}, {{}, {}, {}}); // expected-error {{no match}} + g({1, 2}, {{}, {}, {}}); +#if __cplusplus <= 201402 + // expected-error@-2 {{no match}} + // expected-note@-9 {{deduced incomplete pack}} + // We deduce V$1 = (size_t)3, which in C++1z also deduces T$1 = size_t. +#endif } } |