diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp old mode 100755 new mode 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1974,6 +1974,54 @@ TemplateArgumentList::CreateCopy(SemaRef.Context, Innermost), /*InsertPos=*/nullptr); + + // Parameter packs are allowed after parameters with default values. + // If the specialized Function has more parameters than the TemplatedDecl + // then the default values aren't used and the parameter pack is used and we + // need to remove default values from previous parameters to prevent it + // being diagnosed as invalid code due to the expanded parameter lacking + // default values. + FunctionDecl *TemplatedDecl = FunctionTemplate->getTemplatedDecl(); + unsigned NumTemplatedParams = TemplatedDecl->getNumParams(); + + if (Function->getNumParams() >= NumTemplatedParams) { + unsigned FirstDefault = 0; + unsigned LastDefault = 0; + bool FoundDefault = false; + bool RemoveDefaults = false; + + // Find the FirstDefault up to LastDefault that need to be removed. + // We don't remove if the code's invalid, i.e. a default value missing on + // a regular parameter if there's any with a default value before. + for (unsigned p = 0; p < NumTemplatedParams; ++p) { + ParmVarDecl *Param = TemplatedDecl->getParamDecl(p); + + if (FoundDefault) { + if (Param->isParameterPack()) { + RemoveDefaults = true; + LastDefault = p; + break; + } + + // If we encounter a regular parameter with no default value after + // we've encountered any with a default, then this is invalid code and + // we bail out. This is diagnosed later in CheckCXXDefaultArguments(). + if (!Param->hasDefaultArg()) + break; + } else if (Param->hasDefaultArg()) { + FirstDefault = p; + FoundDefault = true; + } + } + + // Remove default arguments if this is valid code. + if (RemoveDefaults) { + for (unsigned p = FirstDefault; p < LastDefault; ++p) { + ParmVarDecl *Param = Function->getParamDecl(p); + Param->setDefaultArg(nullptr); + } + } + } } else if (isFriend && D->isThisDeclarationADefinition()) { // Do not connect the friend to the template unless it's actually a // definition. We don't want non-template functions to be marked as being diff --git a/clang/test/CXX/drs/dr7xx.cpp b/clang/test/CXX/drs/dr7xx.cpp --- a/clang/test/CXX/drs/dr7xx.cpp +++ b/clang/test/CXX/drs/dr7xx.cpp @@ -230,5 +230,12 @@ template void h(int i = 0, T ...args, int j = 1) {} + +// PR23029 +// Ensure passing parameters using the parameter packs actually works. +void use() { + f(0, 1); + h(0, 1); +} #endif }