Skip to content

Commit f721e05

Browse files
committed
PR46648: Do not eagerly instantiate default arguments for a generic
lambda when instantiating a call operator specialization. We previously incorrectly thought that such substitution was happening in the context of substitution into a local scope, which is a context where we should perform eager default argument instantiation.
1 parent a5569f0 commit f721e05

File tree

6 files changed

+45
-11
lines changed

6 files changed

+45
-11
lines changed

clang/include/clang/AST/DeclBase.h

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -875,15 +875,19 @@ class alignas(8) Decl {
875875
return getParentFunctionOrMethod() == nullptr;
876876
}
877877

878-
/// Returns true if this declaration is lexically inside a function or inside
879-
/// a variable initializer. It recognizes non-defining declarations as well
880-
/// as members of local classes and lambdas:
878+
/// Determine whether a substitution into this declaration would occur as
879+
/// part of a substitution into a dependent local scope. Such a substitution
880+
/// transitively substitutes into all constructs nested within this
881+
/// declaration.
882+
///
883+
/// This recognizes non-defining declarations as well as members of local
884+
/// classes and lambdas:
881885
/// \code
882-
/// void foo() { void bar(); }
883-
/// void foo2() { class ABC { void bar(); }; }
884-
/// inline int x = [](){ return 0; }();
886+
/// template<typename T> void foo() { void bar(); }
887+
/// template<typename T> void foo2() { class ABC { void bar(); }; }
888+
/// template<typename T> inline int x = [](){ return 0; }();
885889
/// \endcode
886-
bool isInLocalScope() const;
890+
bool isInLocalScopeForInstantiation() const;
887891

888892
/// If this decl is defined inside a function/method/block it returns
889893
/// the corresponding DeclContext, otherwise it returns null.

clang/lib/AST/DeclBase.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,10 @@ void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
364364
}
365365
}
366366

367-
bool Decl::isInLocalScope() const {
367+
bool Decl::isInLocalScopeForInstantiation() const {
368368
const DeclContext *LDC = getLexicalDeclContext();
369+
if (!LDC->isDependentContext())
370+
return false;
369371
while (true) {
370372
if (LDC->isFunctionOrMethod())
371373
return true;

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2426,7 +2426,7 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
24262426
UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm);
24272427
} else if (Expr *Arg = OldParm->getDefaultArg()) {
24282428
FunctionDecl *OwningFunc = cast<FunctionDecl>(OldParm->getDeclContext());
2429-
if (OwningFunc->isInLocalScope()) {
2429+
if (OwningFunc->isInLocalScopeForInstantiation()) {
24302430
// Instantiate default arguments for methods of local classes (DR1484)
24312431
// and non-defining declarations.
24322432
Sema::ContextRAII SavedContext(*this, OwningFunc);

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4458,7 +4458,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
44584458
EPI.ExceptionSpec.Type != EST_None &&
44594459
EPI.ExceptionSpec.Type != EST_DynamicNone &&
44604460
EPI.ExceptionSpec.Type != EST_BasicNoexcept &&
4461-
!Tmpl->isInLocalScope()) {
4461+
!Tmpl->isInLocalScopeForInstantiation()) {
44624462
FunctionDecl *ExceptionSpecTemplate = Tmpl;
44634463
if (EPI.ExceptionSpec.Type == EST_Uninstantiated)
44644464
ExceptionSpecTemplate = EPI.ExceptionSpec.SourceTemplate;

clang/test/SemaTemplate/default-arguments-cxx0x.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
22
// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s
3+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
34
// expected-no-diagnostics
45

56
// Test default template arguments for function templates.
@@ -132,5 +133,32 @@ namespace lambda {
132133
void foo() {
133134
bar<int>();
134135
}
136+
137+
#if __cplusplus >= 202002L
138+
// PR46648: ensure we don't reject this by triggering default argument
139+
// instantiation spuriously.
140+
auto x = []<typename T>(T x = 123) {};
141+
void y() { x(nullptr); }
142+
143+
template<int A> struct X {
144+
template<int B> constexpr int f() {
145+
auto l = []<int C>(int n = A + B + C) { return n; };
146+
return l.template operator()<3>();
147+
}
148+
};
149+
static_assert(X<100>().f<20>() == 123);
150+
151+
template<> template<int B> constexpr int X<200>::f() {
152+
auto l = []<int C>(int n = 300 + B + C) { return n; };
153+
return l.template operator()<1>();
154+
}
155+
static_assert(X<200>().f<20>() == 321);
156+
157+
template<> template<> constexpr int X<300>::f<20>() {
158+
auto l = []<int C>(int n = 450 + C) { return n; };
159+
return l.template operator()<6>();
160+
}
161+
static_assert(X<300>().f<20>() == 456);
162+
#endif
135163
} // namespace lambda
136164
#endif

clang/test/SemaTemplate/dependent-expr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ namespace PR45083 {
144144
void h(auto a, void*) {} // expected-error {{redefinition}}
145145

146146
void i(auto a) {
147-
[](auto a, int = ({decltype(a) i; i * 2;})){}(a); // expected-error {{no matching function}} expected-note {{substitution failure}}
147+
[](auto a, int = ({decltype(a) i; i * 2;})){}(a); // expected-error {{invalid operands to binary expression ('decltype(a)' (aka 'void *') and 'int')}} expected-note {{in instantiation of}}
148148
}
149149
void use_i() {
150150
i(0);

0 commit comments

Comments
 (0)