[Clang][NFCI] Cleanup the fix for default function argument substitution (#104911)
(This is one step towards tweaking `getTemplateInstantiationArgs()` as discussed in https://github.com/llvm/llvm-project/pull/102922) We don't always substitute into default arguments while transforming a function parameter. In that case, we would preserve the uninstantiated expression until after, e.g. building up a CXXDefaultArgExpr and instantiate the expression there. For member function instantiation, this algorithm used to cause a problem in that the default argument of an out-of-line member function specialization couldn't get properly instantiated. This is because, in `getTemplateInstantiationArgs()`, we would give up visiting a function's declaration context if the function is a specialization of a member template. For example, ```cpp template <class T> struct S { template <class U> void f(T = sizeof(T)); }; template <> template <class U> void S<int>::f(int) {} ``` The default argument `sizeof(U)` that lexically appears inside the declaration would be copied to the function declaration in the class template specialization `S<int>`, as well as to the function's out-of-line definition. We use template arguments collected from the out-of-line function definition when substituting into the default arguments. We would therefore give up the traversal after the function, resulting in a single-level template argument of the `f` itself. However the default argument here could still reference the template parameters of the primary template, hence the error. In fact, this is similar to constraint checking in some respects: we actually want the "whole" template arguments relative to the primary template, not those relative to the function definition. So this patch adds another flag to indicate `getTemplateInstantiationArgs()` for that. This patch also consolidates the tests for default arguments and removes some unnecessary tests.
This commit is contained in:
@@ -13071,12 +13071,19 @@ public:
|
||||
/// ForConstraintInstantiation indicates we should continue looking when
|
||||
/// encountering a lambda generic call operator, and continue looking for
|
||||
/// arguments on an enclosing class template.
|
||||
///
|
||||
/// \param SkipForSpecialization when specified, any template specializations
|
||||
/// in a traversal would be ignored.
|
||||
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
|
||||
/// when encountering a specialized member function template, rather than
|
||||
/// returning immediately.
|
||||
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
|
||||
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
|
||||
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
|
||||
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
|
||||
bool ForConstraintInstantiation = false,
|
||||
bool SkipForSpecialization = false);
|
||||
bool SkipForSpecialization = false,
|
||||
bool ForDefaultArgumentSubstitution = false);
|
||||
|
||||
/// RAII object to handle the state changes required to synthesize
|
||||
/// a function body.
|
||||
|
||||
@@ -256,7 +256,8 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
|
||||
Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
|
||||
MultiLevelTemplateArgumentList &Result,
|
||||
const FunctionDecl *Pattern, bool RelativeToPrimary,
|
||||
bool ForConstraintInstantiation) {
|
||||
bool ForConstraintInstantiation,
|
||||
bool ForDefaultArgumentSubstitution) {
|
||||
// Add template arguments from a function template specialization.
|
||||
if (!RelativeToPrimary &&
|
||||
Function->getTemplateSpecializationKindForInstantiation() ==
|
||||
@@ -286,7 +287,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
|
||||
// If this function was instantiated from a specialized member that is
|
||||
// a function template, we're done.
|
||||
assert(Function->getPrimaryTemplate() && "No function template?");
|
||||
if (Function->getPrimaryTemplate()->isMemberSpecialization())
|
||||
if (!ForDefaultArgumentSubstitution &&
|
||||
Function->getPrimaryTemplate()->isMemberSpecialization())
|
||||
return Response::Done();
|
||||
|
||||
// If this function is a generic lambda specialization, we are done.
|
||||
@@ -468,7 +470,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
const NamedDecl *ND, const DeclContext *DC, bool Final,
|
||||
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
|
||||
const FunctionDecl *Pattern, bool ForConstraintInstantiation,
|
||||
bool SkipForSpecialization) {
|
||||
bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
|
||||
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
|
||||
// Accumulate the set of template argument lists in this structure.
|
||||
MultiLevelTemplateArgumentList Result;
|
||||
@@ -510,7 +512,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
|
||||
SkipForSpecialization);
|
||||
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
|
||||
R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
|
||||
ForConstraintInstantiation);
|
||||
ForConstraintInstantiation,
|
||||
ForDefaultArgumentSubstitution);
|
||||
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
|
||||
R = HandleRecordDecl(*this, Rec, Result, Context,
|
||||
ForConstraintInstantiation);
|
||||
@@ -3227,7 +3230,6 @@ bool Sema::SubstDefaultArgument(
|
||||
// default argument expression appears.
|
||||
ContextRAII SavedContext(*this, FD);
|
||||
std::unique_ptr<LocalInstantiationScope> LIS;
|
||||
MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;
|
||||
|
||||
if (ForCallExpr) {
|
||||
// When instantiating a default argument due to use in a call expression,
|
||||
@@ -3240,19 +3242,10 @@ bool Sema::SubstDefaultArgument(
|
||||
/*ForDefinition*/ false);
|
||||
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
|
||||
return true;
|
||||
const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
|
||||
if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
|
||||
TemplateArgumentList *CurrentTemplateArgumentList =
|
||||
TemplateArgumentList::CreateCopy(getASTContext(),
|
||||
TemplateArgs.getInnermost());
|
||||
NewTemplateArgs = getTemplateInstantiationArgs(
|
||||
FD, FD->getDeclContext(), /*Final=*/false,
|
||||
CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
runWithSufficientStackSpace(Loc, [&] {
|
||||
Result = SubstInitializer(PatternExpr, NewTemplateArgs,
|
||||
Result = SubstInitializer(PatternExpr, TemplateArgs,
|
||||
/*DirectInit*/ false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4699,10 +4699,12 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
|
||||
//
|
||||
// template<typename T>
|
||||
// A<T> Foo(int a = A<T>::FooImpl());
|
||||
MultiLevelTemplateArgumentList TemplateArgs =
|
||||
getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
|
||||
/*Final=*/false, /*Innermost=*/std::nullopt,
|
||||
/*RelativeToPrimary=*/true);
|
||||
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
|
||||
FD, FD->getLexicalDeclContext(),
|
||||
/*Final=*/false, /*Innermost=*/std::nullopt,
|
||||
/*RelativeToPrimary=*/true, /*Pattern=*/nullptr,
|
||||
/*ForConstraintInstantiation=*/false, /*SkipForSpecialization=*/false,
|
||||
/*ForDefaultArgumentSubstitution=*/true);
|
||||
|
||||
if (SubstDefaultArgument(CallLoc, Param, TemplateArgs, /*ForCallExpr*/ true))
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user