r337481 - Fix template argument deduction when a parameter pack has a value
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 19 12:00:37 PDT 2018
Author: rsmith
Date: Thu Jul 19 12:00:37 2018
New Revision: 337481
URL: http://llvm.org/viewvc/llvm-project?rev=337481&view=rev
Log:
Fix template argument deduction when a parameter pack has a value
provided by an outer template.
We made the incorrect assumption in various places that the only way we
can have any arguments already provided for a pack during template
argument deduction was from a partially-specified pack. That's not true;
we can also have arguments from an enclosing already-instantiated
template, and that can even result in the function template's own pack
parameters having a fixed length and not being packs for the purposes of
template argument deduction.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/include/clang/Sema/TemplateDeduction.h
cfe/trunk/lib/Sema/SemaOverload.cpp
cfe/trunk/lib/Sema/SemaTemplate.cpp
cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
cfe/trunk/test/SemaTemplate/pack-deduction.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Jul 19 12:00:37 2018
@@ -3589,6 +3589,9 @@ def note_ovl_candidate_bad_deduction : N
"candidate template ignored: failed template argument deduction">;
def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: "
"couldn't infer template argument %0">;
+def note_ovl_candidate_incomplete_deduction_pack : Note<"candidate template ignored: "
+ "deduced too few arguments for expanded pack %0; no argument for %ordinal1 "
+ "expanded parameter in deduced argument pack %2">;
def note_ovl_candidate_inconsistent_deduction : Note<
"candidate template ignored: deduced conflicting %select{types|values|"
"templates}0 for parameter %1%diff{ ($ vs. $)|}2,3">;
@@ -4462,6 +4465,9 @@ def err_pack_expansion_length_conflict :
def err_pack_expansion_length_conflict_multilevel : Error<
"pack expansion contains parameter pack %0 that has a different "
"length (%1 vs. %2) from outer parameter packs">;
+def err_pack_expansion_length_conflict_partial : Error<
+ "pack expansion contains parameter pack %0 that has a different "
+ "length (at least %1 vs. %2) from outer parameter packs">;
def err_pack_expansion_member_init : Error<
"pack expansion for initialization of member %0">;
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Jul 19 12:00:37 2018
@@ -6911,6 +6911,9 @@ public:
/// Template argument deduction did not deduce a value
/// for every template parameter.
TDK_Incomplete,
+ /// Template argument deduction did not deduce a value for every
+ /// expansion of an expanded template parameter pack.
+ TDK_IncompletePack,
/// Template argument deduction produced inconsistent
/// deduced values for the given template parameter.
TDK_Inconsistent,
Modified: cfe/trunk/include/clang/Sema/TemplateDeduction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/TemplateDeduction.h?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/TemplateDeduction.h (original)
+++ cfe/trunk/include/clang/Sema/TemplateDeduction.h Thu Jul 19 12:00:37 2018
@@ -51,6 +51,10 @@ class TemplateDeductionInfo {
/// The template parameter depth for which we're performing deduction.
unsigned DeducedDepth;
+ /// The number of parameters with explicitly-specified template arguments,
+ /// up to and including the partially-specified pack (if any).
+ unsigned ExplicitArgs = 0;
+
/// Warnings (and follow-on notes) that were suppressed due to
/// SFINAE while performing template argument deduction.
SmallVector<PartialDiagnosticAt, 4> SuppressedDiagnostics;
@@ -73,6 +77,11 @@ public:
return DeducedDepth;
}
+ /// Get the number of explicitly-specified arguments.
+ unsigned getNumExplicitArgs() const {
+ return ExplicitArgs;
+ }
+
/// Take ownership of the deduced template argument list.
TemplateArgumentList *take() {
TemplateArgumentList *Result = Deduced;
@@ -100,6 +109,13 @@ public:
return SuppressedDiagnostics.front();
}
+ /// Provide an initial template argument list that contains the
+ /// explicitly-specified arguments.
+ void setExplicitArgs(TemplateArgumentList *NewDeduced) {
+ Deduced = NewDeduced;
+ ExplicitArgs = Deduced->size();
+ }
+
/// Provide a new template argument list that contains the
/// results of template argument deduction.
void reset(TemplateArgumentList *NewDeduced) {
@@ -149,6 +165,9 @@ public:
/// TDK_Incomplete: this is the first template parameter whose
/// corresponding template argument was not deduced.
///
+ /// TDK_IncompletePack: this is the expanded parameter pack for
+ /// which we deduced too few arguments.
+ ///
/// TDK_Inconsistent: this is the template parameter for which
/// two different template argument values were deduced.
TemplateParameter Param;
@@ -159,6 +178,9 @@ public:
/// Depending on the result of the template argument deduction,
/// this template argument may have different meanings:
///
+ /// TDK_IncompletePack: this is the number of arguments we deduced
+ /// for the pack.
+ ///
/// TDK_Inconsistent: this argument is the first value deduced
/// for the corresponding template parameter.
///
Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Thu Jul 19 12:00:37 2018
@@ -629,6 +629,8 @@ clang::MakeDeductionFailureInfo(ASTConte
break;
}
+ case Sema::TDK_IncompletePack:
+ // FIXME: It's slightly wasteful to allocate two TemplateArguments for this.
case Sema::TDK_Inconsistent:
case Sema::TDK_Underqualified: {
// FIXME: Should allocate from normal heap so that we can free this later.
@@ -671,6 +673,7 @@ void DeductionFailureInfo::Destroy() {
case Sema::TDK_NonDependentConversionFailure:
break;
+ case Sema::TDK_IncompletePack:
case Sema::TDK_Inconsistent:
case Sema::TDK_Underqualified:
case Sema::TDK_DeducedMismatch:
@@ -720,6 +723,7 @@ TemplateParameter DeductionFailureInfo::
case Sema::TDK_InvalidExplicitArguments:
return TemplateParameter::getFromOpaqueValue(Data);
+ case Sema::TDK_IncompletePack:
case Sema::TDK_Inconsistent:
case Sema::TDK_Underqualified:
return static_cast<DFIParamWithArguments*>(Data)->Param;
@@ -740,6 +744,7 @@ TemplateArgumentList *DeductionFailureIn
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
case Sema::TDK_Incomplete:
+ case Sema::TDK_IncompletePack:
case Sema::TDK_InvalidExplicitArguments:
case Sema::TDK_Inconsistent:
case Sema::TDK_Underqualified:
@@ -777,6 +782,7 @@ const TemplateArgument *DeductionFailure
case Sema::TDK_NonDependentConversionFailure:
return nullptr;
+ case Sema::TDK_IncompletePack:
case Sema::TDK_Inconsistent:
case Sema::TDK_Underqualified:
case Sema::TDK_DeducedMismatch:
@@ -798,6 +804,7 @@ const TemplateArgument *DeductionFailure
case Sema::TDK_Invalid:
case Sema::TDK_InstantiationDepth:
case Sema::TDK_Incomplete:
+ case Sema::TDK_IncompletePack:
case Sema::TDK_TooManyArguments:
case Sema::TDK_TooFewArguments:
case Sema::TDK_InvalidExplicitArguments:
@@ -9893,6 +9900,17 @@ static void DiagnoseBadDeduction(Sema &S
return;
}
+ case Sema::TDK_IncompletePack: {
+ assert(ParamD && "no parameter found for incomplete deduction result");
+ S.Diag(Templated->getLocation(),
+ diag::note_ovl_candidate_incomplete_deduction_pack)
+ << ParamD->getDeclName()
+ << (DeductionFailure.getFirstArg()->pack_size() + 1)
+ << *DeductionFailure.getFirstArg();
+ MaybeEmitInheritedConstructorNote(S, Found);
+ return;
+ }
+
case Sema::TDK_Underqualified: {
assert(ParamD && "no parameter found for bad qualifiers deduction result");
TemplateTypeParmDecl *TParam = cast<TemplateTypeParmDecl>(ParamD);
@@ -10372,6 +10390,7 @@ static unsigned RankDeductionFailure(con
case Sema::TDK_Invalid:
case Sema::TDK_Incomplete:
+ case Sema::TDK_IncompletePack:
return 1;
case Sema::TDK_Underqualified:
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Thu Jul 19 12:00:37 2018
@@ -4914,27 +4914,6 @@ bool Sema::CheckTemplateArgument(NamedDe
return false;
}
-/// Diagnose an arity mismatch in the
-static bool diagnoseArityMismatch(Sema &S, TemplateDecl *Template,
- SourceLocation TemplateLoc,
- TemplateArgumentListInfo &TemplateArgs) {
- TemplateParameterList *Params = Template->getTemplateParameters();
- unsigned NumParams = Params->size();
- unsigned NumArgs = TemplateArgs.size();
-
- SourceRange Range;
- if (NumArgs > NumParams)
- Range = SourceRange(TemplateArgs[NumParams].getLocation(),
- TemplateArgs.getRAngleLoc());
- S.Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
- << (NumArgs > NumParams)
- << (int)S.getTemplateNameKindForDiagnostics(TemplateName(Template))
- << Template << Range;
- S.Diag(Template->getLocation(), diag::note_template_decl_here)
- << Params->getSourceRange();
- return true;
-}
-
/// Check whether the template parameter is a pack expansion, and if so,
/// determine the number of parameters produced by that expansion. For instance:
///
@@ -4988,7 +4967,15 @@ static bool diagnoseMissingArgument(Sema
// FIXME: If there's a more recent default argument that *is* visible,
// diagnose that it was declared too late.
- return diagnoseArityMismatch(S, TD, Loc, Args);
+ TemplateParameterList *Params = TD->getTemplateParameters();
+
+ S.Diag(Loc, diag::err_template_arg_list_different_arity)
+ << /*not enough args*/0
+ << (int)S.getTemplateNameKindForDiagnostics(TemplateName(TD))
+ << TD;
+ S.Diag(TD->getLocation(), diag::note_template_decl_here)
+ << Params->getSourceRange();
+ return true;
}
/// Check that the given template argument list is well-formed
@@ -5040,7 +5027,7 @@ bool Sema::CheckTemplateArgumentList(
} else if (ArgIdx == NumArgs && !PartialTemplateArgs) {
// Not enough arguments for this parameter pack.
Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
- << false
+ << /*not enough args*/0
<< (int)getTemplateNameKindForDiagnostics(TemplateName(Template))
<< Template;
Diag(Template->getLocation(), diag::note_template_decl_here)
@@ -5235,8 +5222,16 @@ bool Sema::CheckTemplateArgumentList(
// If we have any leftover arguments, then there were too many arguments.
// Complain and fail.
- if (ArgIdx < NumArgs)
- return diagnoseArityMismatch(*this, Template, TemplateLoc, NewArgs);
+ if (ArgIdx < NumArgs) {
+ Diag(TemplateLoc, diag::err_template_arg_list_different_arity)
+ << /*too many args*/1
+ << (int)getTemplateNameKindForDiagnostics(TemplateName(Template))
+ << Template
+ << SourceRange(NewArgs[ArgIdx].getLocation(), NewArgs.getRAngleLoc());
+ Diag(Template->getLocation(), diag::note_template_decl_here)
+ << Params->getSourceRange();
+ return true;
+ }
// No problems found with the new argument list, propagate changes back
// to caller.
Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Thu Jul 19 12:00:37 2018
@@ -662,6 +662,19 @@ static TemplateParameter makeTemplatePar
return TemplateParameter(cast<TemplateTemplateParmDecl>(D));
}
+/// If \p Param is an expanded parameter pack, get the number of expansions.
+static Optional<unsigned> getExpandedPackSize(NamedDecl *Param) {
+ if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param))
+ if (NTTP->isExpandedParameterPack())
+ return NTTP->getNumExpansionTypes();
+
+ if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Param))
+ if (TTP->isExpandedParameterPack())
+ return TTP->getNumExpansionTemplateParameters();
+
+ return None;
+}
+
/// A pack that we're currently deducing.
struct clang::DeducedPack {
// The index of the pack.
@@ -688,10 +701,83 @@ namespace {
/// A scope in which we're performing pack deduction.
class PackDeductionScope {
public:
+ /// Prepare to deduce the packs named within Pattern.
PackDeductionScope(Sema &S, TemplateParameterList *TemplateParams,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
TemplateDeductionInfo &Info, TemplateArgument Pattern)
: S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) {
+ unsigned NumNamedPacks = addPacks(Pattern);
+ finishConstruction(NumNamedPacks);
+ }
+
+ /// Prepare to directly deduce arguments of the parameter with index \p Index.
+ PackDeductionScope(Sema &S, TemplateParameterList *TemplateParams,
+ SmallVectorImpl<DeducedTemplateArgument> &Deduced,
+ TemplateDeductionInfo &Info, unsigned Index)
+ : S(S), TemplateParams(TemplateParams), Deduced(Deduced), Info(Info) {
+ addPack(Index);
+ finishConstruction(1);
+ }
+
+private:
+ void addPack(unsigned Index) {
+ // 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();
+
+ // FIXME: What if we encounter multiple packs with different numbers of
+ // pre-expanded expansions? (This should already have been diagnosed
+ // during substitution.)
+ if (Optional<unsigned> ExpandedPackExpansions =
+ getExpandedPackSize(TemplateParams->getParam(Index)))
+ FixedNumExpansions = ExpandedPackExpansions;
+
+ Packs.push_back(Pack);
+ }
+
+ unsigned addPacks(TemplateArgument Pattern) {
+ // 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;
+ addPack(Index);
+ };
+
+ // 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())
+ AddPack(Index);
+ }
+ assert(!Packs.empty() && "Pack expansion without unexpanded packs?");
+
+ unsigned NumNamedPacks = Packs.size();
+
+ // 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);
+
+ return NumNamedPacks;
+ }
+
+ void finishConstruction(unsigned NumNamedPacks) {
// Dig out the partially-substituted pack, if there is one.
const TemplateArgument *PartialPackArgs = nullptr;
unsigned NumPartialPackArgs = 0;
@@ -701,60 +787,29 @@ public:
&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())
- 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);
+ // This pack expansion will have been partially or fully expanded if
+ // it only names explicitly-specified parameter packs (including the
+ // partially-substituted one, if any).
+ bool IsExpanded = true;
+ for (unsigned I = 0; I != NumNamedPacks; ++I) {
+ if (Packs[I].Index >= Info.getNumExplicitArgs()) {
+ IsExpanded = false;
+ IsPartiallyExpanded = false;
+ break;
+ }
+ if (PartialPackDepthIndex ==
+ std::make_pair(Info.getDeducedDepth(), Packs[I].Index)) {
+ IsPartiallyExpanded = true;
+ }
}
+ // Skip over the pack elements that were expanded into separate arguments.
+ // If we partially expanded, this is the number of partial arguments.
+ if (IsPartiallyExpanded)
+ PackElements += NumPartialPackArgs;
+ else if (IsExpanded)
+ PackElements += *FixedNumExpansions;
+
for (auto &Pack : Packs) {
if (Info.PendingDeducedPacks.size() > Pack.Index)
Pack.Outer = Info.PendingDeducedPacks[Pack.Index];
@@ -773,12 +828,13 @@ public:
// 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)
+ if (!IsPartiallyExpanded)
Deduced[Pack.Index] = Pack.New[PackElements];
}
}
}
+public:
~PackDeductionScope() {
for (auto &Pack : Packs)
Info.PendingDeducedPacks[Pack.Index] = Pack.Outer;
@@ -788,6 +844,18 @@ public:
/// sequence of (prior) function parameters / template arguments.
bool isPartiallyExpanded() { return IsPartiallyExpanded; }
+ /// Determine whether this pack expansion scope has a known, fixed arity.
+ /// This happens if it involves a pack from an outer template that has
+ /// (notionally) already been expanded.
+ bool hasFixedArity() { return FixedNumExpansions.hasValue(); }
+
+ /// Determine whether the next element of the argument is still part of this
+ /// pack. This is the case unless the pack is already expanded to a fixed
+ /// length.
+ bool hasNextElement() {
+ return !FixedNumExpansions || *FixedNumExpansions > PackElements;
+ }
+
/// 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
@@ -813,13 +881,21 @@ public:
/// Finish template argument deduction for a set of argument packs,
/// producing the argument packs and checking for consistency with prior
/// deductions.
- Sema::TemplateDeductionResult finish() {
+ Sema::TemplateDeductionResult
+ finish(bool TreatNoDeductionsAsNonDeduced = true) {
// Build argument packs for each of the parameter packs expanded by this
// pack expansion.
for (auto &Pack : Packs) {
// Put back the old value for this pack.
Deduced[Pack.Index] = Pack.Saved;
+ // If we are deducing the size of this pack even if we didn't deduce any
+ // values for it, then make sure we build a pack of the right size.
+ // FIXME: Should we always deduce the size, even if the pack appears in
+ // a non-deduced context?
+ if (!TreatNoDeductionsAsNonDeduced)
+ Pack.New.resize(PackElements);
+
// Build or find a new value for this pack.
DeducedTemplateArgument NewPack;
if (PackElements && Pack.New.empty()) {
@@ -875,14 +951,24 @@ public:
Result = checkDeducedTemplateArguments(S.Context, OldPack, NewPack);
}
+ NamedDecl *Param = TemplateParams->getParam(Pack.Index);
if (Result.isNull()) {
- Info.Param =
- makeTemplateParameter(TemplateParams->getParam(Pack.Index));
+ Info.Param = makeTemplateParameter(Param);
Info.FirstArg = OldPack;
Info.SecondArg = NewPack;
return Sema::TDK_Inconsistent;
}
+ // If we have a pre-expanded pack and we didn't deduce enough elements
+ // for it, fail deduction.
+ if (Optional<unsigned> Expansions = getExpandedPackSize(Param)) {
+ if (*Expansions != PackElements) {
+ Info.Param = makeTemplateParameter(Param);
+ Info.FirstArg = Result;
+ return Sema::TDK_IncompletePack;
+ }
+ }
+
*Loc = Result;
}
@@ -896,6 +982,8 @@ private:
TemplateDeductionInfo &Info;
unsigned PackElements = 0;
bool IsPartiallyExpanded = false;
+ /// The number of expansions, if we have a fully-expanded pack in this scope.
+ Optional<unsigned> FixedNumExpansions;
SmallVector<DeducedPack, 2> Packs;
};
@@ -987,8 +1075,10 @@ DeduceTemplateArguments(Sema &S,
QualType Pattern = Expansion->getPattern();
PackDeductionScope PackScope(S, TemplateParams, Deduced, Info, Pattern);
- if (ParamIdx + 1 == NumParams) {
- for (; ArgIdx < NumArgs; ++ArgIdx) {
+ // A pack scope with fixed arity is not really a pack any more, so is not
+ // a non-deduced context.
+ if (ParamIdx + 1 == NumParams || PackScope.hasFixedArity()) {
+ for (; ArgIdx < NumArgs && PackScope.hasNextElement(); ++ArgIdx) {
// Deduce template arguments from the pattern.
if (Sema::TemplateDeductionResult Result
= DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, Pattern,
@@ -2193,6 +2283,8 @@ static bool hasPackExpansionBeforeEnd(Ar
if (A.getKind() == TemplateArgument::Pack)
return hasPackExpansionBeforeEnd(A.pack_elements());
+ // FIXME: If this is a fixed-arity pack expansion from an outer level of
+ // templates, it should not be treated as a pack expansion.
if (A.isPackExpansion())
FoundPackExpansion = true;
}
@@ -2262,7 +2354,9 @@ DeduceTemplateArguments(Sema &S, Templat
// Keep track of the deduced template arguments for each parameter pack
// expanded by this pack expansion (the outer index) and for each
// template argument (the inner SmallVectors).
- for (; hasTemplateArgumentForDeduction(Args, ArgIdx); ++ArgIdx) {
+ for (; hasTemplateArgumentForDeduction(Args, ArgIdx) &&
+ PackScope.hasNextElement();
+ ++ArgIdx) {
// Deduce template arguments from the pattern.
if (Sema::TemplateDeductionResult Result
= DeduceTemplateArguments(S, TemplateParams, Pattern, Args[ArgIdx],
@@ -2534,6 +2628,16 @@ static Sema::TemplateDeductionResult Con
for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) {
NamedDecl *Param = TemplateParams->getParam(I);
+ // C++0x [temp.arg.explicit]p3:
+ // A trailing template parameter pack (14.5.3) not otherwise deduced will
+ // be deduced to an empty sequence of template arguments.
+ // FIXME: Where did the word "trailing" come from?
+ if (Deduced[I].isNull() && Param->isTemplateParameterPack()) {
+ if (auto Result = PackDeductionScope(S, TemplateParams, Deduced, Info, I)
+ .finish(/*TreatNoDeductionsAsNonDeduced*/false))
+ return Result;
+ }
+
if (!Deduced[I].isNull()) {
if (I < NumAlreadyConverted) {
// We may have had explicitly-specified template arguments for a
@@ -2568,40 +2672,6 @@ static Sema::TemplateDeductionResult Con
continue;
}
- // C++0x [temp.arg.explicit]p3:
- // A trailing template parameter pack (14.5.3) not otherwise deduced will
- // be deduced to an empty sequence of template arguments.
- // FIXME: Where did the word "trailing" come from?
- if (Param->isTemplateParameterPack()) {
- // We may have had explicitly-specified template arguments for this
- // template parameter pack. If so, our empty deduction extends the
- // explicitly-specified set (C++0x [temp.arg.explicit]p9).
- const TemplateArgument *ExplicitArgs;
- unsigned NumExplicitArgs;
- if (CurrentInstantiationScope &&
- CurrentInstantiationScope->getPartiallySubstitutedPack(
- &ExplicitArgs, &NumExplicitArgs) == Param) {
- Builder.push_back(TemplateArgument(
- llvm::makeArrayRef(ExplicitArgs, NumExplicitArgs)));
-
- // Forget the partially-substituted pack; its substitution is now
- // complete.
- CurrentInstantiationScope->ResetPartiallySubstitutedPack();
- } else {
- // Go through the motions of checking the empty argument pack against
- // the parameter pack.
- DeducedTemplateArgument DeducedPack(TemplateArgument::getEmptyPack());
- if (ConvertDeducedTemplateArgument(S, Param, DeducedPack, Template,
- Info, IsDeduced, Builder)) {
- Info.Param = makeTemplateParameter(Param);
- // FIXME: These template arguments are temporary. Free them!
- Info.reset(TemplateArgumentList::CreateCopy(S.Context, Builder));
- return Sema::TDK_SubstitutionFailure;
- }
- }
- continue;
- }
-
// Substitute into the default template argument, if available.
bool HasDefaultArg = false;
TemplateDecl *TD = dyn_cast<TemplateDecl>(Template);
@@ -2962,7 +3032,7 @@ Sema::SubstituteExplicitTemplateArgument
Trap.hasErrorOccurred()) {
unsigned Index = Builder.size();
if (Index >= TemplateParams->size())
- Index = TemplateParams->size() - 1;
+ return TDK_SubstitutionFailure;
Info.Param = makeTemplateParameter(TemplateParams->getParam(Index));
return TDK_InvalidExplicitArguments;
}
@@ -2971,7 +3041,7 @@ Sema::SubstituteExplicitTemplateArgument
// template arguments.
TemplateArgumentList *ExplicitArgumentList
= TemplateArgumentList::CreateCopy(Context, Builder);
- Info.reset(ExplicitArgumentList);
+ Info.setExplicitArgs(ExplicitArgumentList);
// Template argument deduction and the final substitution should be
// done in the context of the templated declaration. Explicit
@@ -2983,14 +3053,19 @@ Sema::SubstituteExplicitTemplateArgument
// note that the template argument pack is partially substituted and record
// the explicit template arguments. They'll be used as part of deduction
// for this template parameter pack.
- for (unsigned I = 0, N = Builder.size(); I != N; ++I) {
- const TemplateArgument &Arg = Builder[I];
+ unsigned PartiallySubstitutedPackIndex = -1u;
+ if (!Builder.empty()) {
+ const TemplateArgument &Arg = Builder.back();
if (Arg.getKind() == TemplateArgument::Pack) {
- CurrentInstantiationScope->SetPartiallySubstitutedPack(
- TemplateParams->getParam(I),
- Arg.pack_begin(),
- Arg.pack_size());
- break;
+ auto *Param = TemplateParams->getParam(Builder.size() - 1);
+ // If this is a fully-saturated fixed-size pack, it should be
+ // fully-substituted, not partially-substituted.
+ Optional<unsigned> Expansions = getExpandedPackSize(Param);
+ if (!Expansions || Arg.pack_size() < *Expansions) {
+ PartiallySubstitutedPackIndex = Builder.size() - 1;
+ CurrentInstantiationScope->SetPartiallySubstitutedPack(
+ Param, Arg.pack_begin(), Arg.pack_size());
+ }
}
}
@@ -3080,13 +3155,13 @@ Sema::SubstituteExplicitTemplateArgument
// case, the empty template argument list <> itself may also be omitted.
//
// Take all of the explicitly-specified arguments and put them into
- // the set of deduced template arguments. Explicitly-specified
- // parameter packs, however, will be set to NULL since the deduction
- // mechanisms handle explicitly-specified argument packs directly.
+ // the set of deduced template arguments. The partially-substituted
+ // parameter pack, however, will be set to NULL since the deduction
+ // mechanism handles the partially-substituted argument pack directly.
Deduced.reserve(TemplateParams->size());
for (unsigned I = 0, N = ExplicitArgumentList->size(); I != N; ++I) {
const TemplateArgument &Arg = ExplicitArgumentList->get(I);
- if (Arg.getKind() == TemplateArgument::Pack)
+ if (I == PartiallySubstitutedPackIndex)
Deduced.push_back(DeducedTemplateArgument());
else
Deduced.push_back(Arg);
@@ -3852,8 +3927,9 @@ Sema::TemplateDeductionResult Sema::Dedu
// 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 (ParamIdx + 1 == NumParamTypes || PackScope.hasFixedArity()) {
+ for (; ArgIdx < Args.size() && PackScope.hasNextElement();
+ PackScope.nextPackElement(), ++ArgIdx) {
ParamTypesForArgChecking.push_back(ParamPattern);
if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
return Result;
Modified: cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateVariadic.cpp Thu Jul 19 12:00:37 2018
@@ -646,7 +646,9 @@ bool Sema::CheckParameterPacksForExpansi
RetainExpansion = false;
std::pair<IdentifierInfo *, SourceLocation> FirstPack;
bool HaveFirstPack = false;
-
+ Optional<unsigned> NumPartialExpansions;
+ SourceLocation PartiallySubstitutedPackLoc;
+
for (ArrayRef<UnexpandedParameterPack>::iterator i = Unexpanded.begin(),
end = Unexpanded.end();
i != end; ++i) {
@@ -711,8 +713,13 @@ bool Sema::CheckParameterPacksForExpansi
= CurrentInstantiationScope->getPartiallySubstitutedPack()){
unsigned PartialDepth, PartialIndex;
std::tie(PartialDepth, PartialIndex) = getDepthAndIndex(PartialPack);
- if (PartialDepth == Depth && PartialIndex == Index)
+ if (PartialDepth == Depth && PartialIndex == Index) {
RetainExpansion = true;
+ // We don't actually know the new pack size yet.
+ NumPartialExpansions = NewPackSize;
+ PartiallySubstitutedPackLoc = i->second;
+ continue;
+ }
}
}
@@ -742,6 +749,28 @@ bool Sema::CheckParameterPacksForExpansi
}
}
+ // If we're performing a partial expansion but we also have a full expansion,
+ // expand to the number of common arguments. For example, given:
+ //
+ // template<typename ...T> struct A {
+ // template<typename ...U> void f(pair<T, U>...);
+ // };
+ //
+ // ... a call to 'A<int, int>().f<int>' should expand the pack once and
+ // retain an expansion.
+ if (NumPartialExpansions) {
+ if (NumExpansions && *NumExpansions < *NumPartialExpansions) {
+ NamedDecl *PartialPack =
+ CurrentInstantiationScope->getPartiallySubstitutedPack();
+ Diag(EllipsisLoc, diag::err_pack_expansion_length_conflict_partial)
+ << PartialPack << *NumPartialExpansions << *NumExpansions
+ << SourceRange(PartiallySubstitutedPackLoc);
+ return true;
+ }
+
+ NumExpansions = NumPartialExpansions;
+ }
+
return false;
}
Modified: cfe/trunk/test/SemaTemplate/pack-deduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/pack-deduction.cpp?rev=337481&r1=337480&r2=337481&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/pack-deduction.cpp (original)
+++ cfe/trunk/test/SemaTemplate/pack-deduction.cpp Thu Jul 19 12:00:37 2018
@@ -65,3 +65,104 @@ namespace PR14615 {
void f() { check<char>(1, 2); } // expected-error {{no matching function}}
}
}
+
+namespace fully_expanded_packs {
+ template<typename ...T> struct A {
+ template<T ...X> static constexpr int f() {
+ // expected-note at -1 1+{{deduced too few arguments for expanded pack 'X'}}
+ // expected-note at -2 1+{{too many template arguments}}
+ return (X + ... + 0); // expected-warning {{extension}}
+ }
+
+ template<T ...X, int Y> static constexpr int g() {
+ // expected-note at -1 1+{{deduced too few arguments for expanded pack 'X'}}
+ // expected-note at -2 1+{{couldn't infer template argument 'Y'}}
+ // expected-note at -3 1+{{too many template arguments}}
+ return (X + ... + (1000 * Y)); // expected-warning {{extension}}
+ }
+
+ template<T ...X, int Y, T ...Z> static constexpr int h() {
+ // expected-note at -1 1+{{deduced too few arguments for expanded pack 'X'}}
+ // expected-note at -2 1+{{couldn't infer template argument 'Y'}}
+ // expected-note at -3 1+{{deduced too few arguments for expanded pack 'Z'}}
+ // expected-note at -4 1+{{too many template arguments}}
+ return (X + ... + (1000 * Y)) + 1000000 * (Z + ... + 0); // expected-warning 2{{extension}}
+ }
+
+ template<T ...X, int ...Z> static constexpr int i() {
+ return (X + ... + 0) + 1000 * (Z + ... + 0); // expected-warning 2{{extension}}
+ }
+
+ template<T ...X, int Y, int ...Z> static constexpr int j() {
+ return (X + ... + (1000 * Y)) + 1000000 * (Z + ... + 0); // expected-warning 2{{extension}}
+ }
+ };
+
+ void check_invalid_calls() {
+ A<int, int>::f(); // expected-error {{no matching function}}
+ A<int, int>::f<>(); // expected-error {{no matching function}}
+ A<int, int>::f<0>(); // expected-error {{no matching function}}
+ A<int, int>::g(); // expected-error {{no matching function}}
+ A<int, int>::g<>(); // expected-error {{no matching function}}
+ A<int, int>::g<0>(); // expected-error {{no matching function}}
+ A<int, int>::g<0, 0>(); // expected-error {{no matching function}}
+ A<>::f<0>(); // expected-error {{no matching function}}
+ A<>::g(); // expected-error {{no matching function}}
+ A<>::g<>(); // expected-error {{no matching function}}
+ A<>::g<0, 0>(); // expected-error {{no matching function}}
+ A<>::h<>(); // expected-error {{no matching function}}
+ A<int>::h<>(); // expected-error {{no matching function}}
+ A<int>::h<0, 0>(); // expected-error {{no matching function}}
+ A<>::h<0, 0>(); // expected-error {{no matching function}}
+ }
+
+ static_assert(A<>::f() == 0, "");
+ static_assert(A<int>::f<1>() == 1, "");
+ static_assert(A<>::g<1>() == 1000, "");
+ static_assert(A<int>::g<1, 2>() == 2001, "");
+ static_assert(A<>::h<1>() == 1000, "");
+ static_assert(A<int>::h<1, 2, 3>() == 3002001, "");
+ static_assert(A<int, int>::h<1, 20, 3, 4, 50>() == 54003021, "");
+ static_assert(A<>::i<1>() == 1000, "");
+ static_assert(A<int>::i<1>() == 1, "");
+ static_assert(A<>::j<1, 2, 30>() == 32001000, "");
+ static_assert(A<int>::j<1, 2, 3, 40>() == 43002001, "");
+}
+
+namespace partial_full_mix {
+ template<typename T, typename U> struct pair {};
+ template<typename ...T> struct tuple {};
+ template<typename ...T> struct A {
+ template<typename ...U> static pair<tuple<T...>, tuple<U...>> f(pair<T, U> ...p);
+ // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 3) from outer parameter packs}}
+ // expected-note at -2 {{[with U = <char, double, void>]: pack expansion contains parameter pack 'U' that has a different length (at least 3 vs. 2) from outer parameter packs}}
+
+ template<typename ...U> static pair<tuple<T...>, tuple<U...>> g(pair<T, U> ...p, ...);
+ // expected-note at -1 {{[with U = <char, double, long>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 3) from outer parameter packs}}
+
+ template<typename ...U> static tuple<U...> h(tuple<pair<T, U>..., pair<int, int>>);
+ // expected-note at -1 {{[with U = <int [2]>]: pack expansion contains parameter pack 'U' that has a different length (2 vs. 1) from outer parameter packs}}
+ };
+
+ pair<tuple<int, float>, tuple<char, double>> k1 = A<int, float>().f<char>(pair<int, char>(), pair<float, double>());
+ pair<tuple<int, float>, tuple<char, double>> k2 = A<int, float>().f<char>(pair<int, char>(), pair<float, double>(), pair<void, long>()); // expected-error {{no match}}
+ pair<tuple<int, float>, tuple<char, double>> k3 = A<int, float>().f<char, double, void>(pair<int, char>(), pair<float, double>()); // expected-error {{no match}}
+
+ // FIXME: We should accept this by treating the pack 'p' as having a fixed length of 2 here.
+ pair<tuple<int, float>, tuple<char, double>> k4 = A<int, float>().g<char>(pair<int, char>(), pair<float, double>(), pair<void, long>()); // expected-error {{no match}}
+
+ // FIXME: We should accept this by treating the pack of pairs as having a fixed length of 2 here.
+ tuple<int[2], int[4]> k5 = A<int[1], int[3]>::h<int[2]>(tuple<pair<int[1], int[2]>, pair<int[3], int[4]>, pair<int[5], int[5]>>()); // expected-error {{no match}}
+}
+
+namespace substitution_vs_function_deduction {
+ template <typename... T> struct A {
+ template <typename... U> void f(void(*...)(T, U)); // expected-warning {{ISO C++11 requires a parenthesized pack declaration to have a name}}
+ template <typename... U> void g(void...(T, U)); // expected-note {{could not match 'void (T, U)' against 'void (*)(int, int)'}}
+ };
+ void f(int, int) {
+ A<int>().f(f);
+ // FIXME: We fail to decay the parameter to a pointer type.
+ A<int>().g(f); // expected-error {{no match}}
+ }
+}
More information about the cfe-commits
mailing list