summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp145
-rw-r--r--clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp14
-rw-r--r--clang/test/SemaTemplate/deduction.cpp10
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
}
}
OpenPOWER on IntegriCloud