tambre created this revision. tambre added reviewers: rsmith, rjmccall. Herald added a project: clang. Herald added a subscriber: cfe-commits.
When a parameter pack is expanded the expanded parameters don't have default values. Thus if a parameter pack is expanded after any arguments with a default value, the function is incorrectly rejected in CheckCXXDefaultArguments(). Fix this by removing default arguments for prior parameters if a parameter pack is expanded. Fixes PR23029. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D79800 Files: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/CXX/drs/dr7xx.cpp Index: clang/test/CXX/drs/dr7xx.cpp =================================================================== --- clang/test/CXX/drs/dr7xx.cpp +++ clang/test/CXX/drs/dr7xx.cpp @@ -230,5 +230,12 @@ template <typename... T> 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 } Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ 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
Index: clang/test/CXX/drs/dr7xx.cpp =================================================================== --- clang/test/CXX/drs/dr7xx.cpp +++ clang/test/CXX/drs/dr7xx.cpp @@ -230,5 +230,12 @@ template <typename... T> 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 } Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ 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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits