Skip to content

Commit 8ac140f

Browse files
authored
[Clang][NFCI] Cleanup the fix for default function argument substitution (#104911)
(This is one step towards tweaking `getTemplateInstantiationArgs()` as discussed in #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.
1 parent 5f91de9 commit 8ac140f

File tree

3 files changed

+22
-20
lines changed

3 files changed

+22
-20
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13071,12 +13071,19 @@ class Sema final : public SemaBase {
1307113071
/// ForConstraintInstantiation indicates we should continue looking when
1307213072
/// encountering a lambda generic call operator, and continue looking for
1307313073
/// arguments on an enclosing class template.
13074+
///
13075+
/// \param SkipForSpecialization when specified, any template specializations
13076+
/// in a traversal would be ignored.
13077+
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
13078+
/// when encountering a specialized member function template, rather than
13079+
/// returning immediately.
1307413080
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
1307513081
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
1307613082
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
1307713083
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
1307813084
bool ForConstraintInstantiation = false,
13079-
bool SkipForSpecialization = false);
13085+
bool SkipForSpecialization = false,
13086+
bool ForDefaultArgumentSubstitution = false);
1308013087

1308113088
/// RAII object to handle the state changes required to synthesize
1308213089
/// a function body.

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
256256
Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
257257
MultiLevelTemplateArgumentList &Result,
258258
const FunctionDecl *Pattern, bool RelativeToPrimary,
259-
bool ForConstraintInstantiation) {
259+
bool ForConstraintInstantiation,
260+
bool ForDefaultArgumentSubstitution) {
260261
// Add template arguments from a function template specialization.
261262
if (!RelativeToPrimary &&
262263
Function->getTemplateSpecializationKindForInstantiation() ==
@@ -286,7 +287,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
286287
// If this function was instantiated from a specialized member that is
287288
// a function template, we're done.
288289
assert(Function->getPrimaryTemplate() && "No function template?");
289-
if (Function->getPrimaryTemplate()->isMemberSpecialization())
290+
if (!ForDefaultArgumentSubstitution &&
291+
Function->getPrimaryTemplate()->isMemberSpecialization())
290292
return Response::Done();
291293

292294
// If this function is a generic lambda specialization, we are done.
@@ -468,7 +470,7 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
468470
const NamedDecl *ND, const DeclContext *DC, bool Final,
469471
std::optional<ArrayRef<TemplateArgument>> Innermost, bool RelativeToPrimary,
470472
const FunctionDecl *Pattern, bool ForConstraintInstantiation,
471-
bool SkipForSpecialization) {
473+
bool SkipForSpecialization, bool ForDefaultArgumentSubstitution) {
472474
assert((ND || DC) && "Can't find arguments for a decl if one isn't provided");
473475
// Accumulate the set of template argument lists in this structure.
474476
MultiLevelTemplateArgumentList Result;
@@ -510,7 +512,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
510512
SkipForSpecialization);
511513
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
512514
R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
513-
ForConstraintInstantiation);
515+
ForConstraintInstantiation,
516+
ForDefaultArgumentSubstitution);
514517
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
515518
R = HandleRecordDecl(*this, Rec, Result, Context,
516519
ForConstraintInstantiation);
@@ -3227,7 +3230,6 @@ bool Sema::SubstDefaultArgument(
32273230
// default argument expression appears.
32283231
ContextRAII SavedContext(*this, FD);
32293232
std::unique_ptr<LocalInstantiationScope> LIS;
3230-
MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;
32313233

32323234
if (ForCallExpr) {
32333235
// When instantiating a default argument due to use in a call expression,
@@ -3240,19 +3242,10 @@ bool Sema::SubstDefaultArgument(
32403242
/*ForDefinition*/ false);
32413243
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
32423244
return true;
3243-
const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
3244-
if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
3245-
TemplateArgumentList *CurrentTemplateArgumentList =
3246-
TemplateArgumentList::CreateCopy(getASTContext(),
3247-
TemplateArgs.getInnermost());
3248-
NewTemplateArgs = getTemplateInstantiationArgs(
3249-
FD, FD->getDeclContext(), /*Final=*/false,
3250-
CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true);
3251-
}
32523245
}
32533246

32543247
runWithSufficientStackSpace(Loc, [&] {
3255-
Result = SubstInitializer(PatternExpr, NewTemplateArgs,
3248+
Result = SubstInitializer(PatternExpr, TemplateArgs,
32563249
/*DirectInit*/ false);
32573250
});
32583251
}

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,10 +4699,12 @@ bool Sema::InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
46994699
//
47004700
// template<typename T>
47014701
// A<T> Foo(int a = A<T>::FooImpl());
4702-
MultiLevelTemplateArgumentList TemplateArgs =
4703-
getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(),
4704-
/*Final=*/false, /*Innermost=*/std::nullopt,
4705-
/*RelativeToPrimary=*/true);
4702+
MultiLevelTemplateArgumentList TemplateArgs = getTemplateInstantiationArgs(
4703+
FD, FD->getLexicalDeclContext(),
4704+
/*Final=*/false, /*Innermost=*/std::nullopt,
4705+
/*RelativeToPrimary=*/true, /*Pattern=*/nullptr,
4706+
/*ForConstraintInstantiation=*/false, /*SkipForSpecialization=*/false,
4707+
/*ForDefaultArgumentSubstitution=*/true);
47064708

47074709
if (SubstDefaultArgument(CallLoc, Param, TemplateArgs, /*ForCallExpr*/ true))
47084710
return true;

0 commit comments

Comments
 (0)