Skip to content

Commit 9c80516

Browse files
aaronpucherttstellar
authored andcommitted
PR45000: Let Sema::SubstParmVarDecl handle default args of lambdas in initializers
Summary: We extend the behavior for local functions and methods of local classes to lambdas in variable initializers. The initializer is not a separate scope, but we treat it as such. We also remove the (faulty) instantiation of default arguments in TreeTransform::TransformLambdaExpr, because it doesn't do proper initialization, and if it did, we would do it twice (and thus also emit eventual errors twice). Reviewed By: rsmith Differential Revision: https://reviews.llvm.org/D76038 (cherry picked from commit f43859a)
1 parent 1d1469a commit 9c80516

File tree

7 files changed

+31
-21
lines changed

7 files changed

+31
-21
lines changed

clang/include/clang/AST/DeclBase.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -856,14 +856,15 @@ class alignas(8) Decl {
856856
return getParentFunctionOrMethod() == nullptr;
857857
}
858858

859-
/// Returns true if this declaration lexically is inside a function.
860-
/// It recognizes non-defining declarations as well as members of local
861-
/// classes:
859+
/// Returns true if this declaration is lexically inside a function or inside
860+
/// a variable initializer. It recognizes non-defining declarations as well
861+
/// as members of local classes:
862862
/// \code
863863
/// void foo() { void bar(); }
864864
/// void foo2() { class ABC { void bar(); }; }
865+
/// inline int x = [](){ return 0; };
865866
/// \endcode
866-
bool isLexicallyWithinFunctionOrMethod() const;
867+
bool isInLocalScope() const;
867868

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

clang/lib/AST/DeclBase.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,16 @@ void Decl::setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC,
332332
}
333333
}
334334

335-
bool Decl::isLexicallyWithinFunctionOrMethod() const {
335+
bool Decl::isInLocalScope() const {
336336
const DeclContext *LDC = getLexicalDeclContext();
337337
while (true) {
338338
if (LDC->isFunctionOrMethod())
339339
return true;
340340
if (!isa<TagDecl>(LDC))
341341
return false;
342+
if (const auto *CRD = dyn_cast<CXXRecordDecl>(LDC))
343+
if (CRD->isLambda())
344+
return true;
342345
LDC = LDC->getLexicalParent();
343346
}
344347
return false;

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2343,7 +2343,7 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
23432343
UnparsedDefaultArgInstantiations[OldParm].push_back(NewParm);
23442344
} else if (Expr *Arg = OldParm->getDefaultArg()) {
23452345
FunctionDecl *OwningFunc = cast<FunctionDecl>(OldParm->getDeclContext());
2346-
if (OwningFunc->isLexicallyWithinFunctionOrMethod()) {
2346+
if (OwningFunc->isInLocalScope()) {
23472347
// Instantiate default arguments for methods of local classes (DR1484)
23482348
// and non-defining declarations.
23492349
Sema::ContextRAII SavedContext(*this, OwningFunc);

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4367,7 +4367,7 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New,
43674367
EPI.ExceptionSpec.Type != EST_None &&
43684368
EPI.ExceptionSpec.Type != EST_DynamicNone &&
43694369
EPI.ExceptionSpec.Type != EST_BasicNoexcept &&
4370-
!Tmpl->isLexicallyWithinFunctionOrMethod()) {
4370+
!Tmpl->isInLocalScope()) {
43714371
FunctionDecl *ExceptionSpecTemplate = Tmpl;
43724372
if (EPI.ExceptionSpec.Type == EST_Uninstantiated)
43734373
ExceptionSpecTemplate = EPI.ExceptionSpec.SourceTemplate;

clang/lib/Sema/TreeTransform.h

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11828,19 +11828,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1182811828

1182911829
LSI->CallOperator = NewCallOperator;
1183011830

11831-
for (unsigned I = 0, NumParams = NewCallOperator->getNumParams();
11832-
I != NumParams; ++I) {
11833-
auto *P = NewCallOperator->getParamDecl(I);
11834-
if (P->hasUninstantiatedDefaultArg()) {
11835-
EnterExpressionEvaluationContext Eval(
11836-
getSema(),
11837-
Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed, P);
11838-
ExprResult R = getDerived().TransformExpr(
11839-
E->getCallOperator()->getParamDecl(I)->getDefaultArg());
11840-
P->setDefaultArg(R.get());
11841-
}
11842-
}
11843-
1184411831
getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
1184511832
getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator});
1184611833

clang/test/SemaCXX/vartemplate-lambda.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ template <class> auto fn0 = [] {};
44
template <typename> void foo0() { fn0<char>(); }
55

66
template<typename T> auto fn1 = [](auto a) { return a + T(1); };
7-
template<typename T> auto v1 = [](int a = T(1)) { return a; }();
7+
template<typename T> auto v1 = [](int a = T()) { return a; }();
8+
// expected-error@-1{{cannot initialize a parameter of type 'int' with an rvalue of type 'int *'}}
9+
// expected-error@-2{{no matching function for call}}
10+
// expected-note@-3{{passing argument to parameter 'a' here}}
11+
// expected-note@-4{{candidate function not viable}}
12+
// expected-note@-5{{conversion candidate of type 'int (*)(int)'}}
813

914
struct S {
1015
template<class T>
@@ -16,6 +21,7 @@ int foo2() {
1621
X a = 0x61;
1722
fn1<char>(a);
1823
(void)v1<int>;
24+
(void)v1<int *>; // expected-note{{in instantiation of variable template specialization 'v1' requested here}}
1925
(void)S::t<int>; // expected-note{{in instantiation of static data member 'S::t<int>' requested here}}
2026
return 0;
2127
}

clang/test/SemaTemplate/instantiate-local-class.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,16 @@ namespace anon_union_default_member_init {
486486
}
487487
void g() { f<int>(); }
488488
}
489+
490+
namespace PR45000 {
491+
template <typename T>
492+
void f(int x = [](T x = nullptr) -> int { return x; }());
493+
// expected-error@-1 {{cannot initialize a parameter of type 'int' with an rvalue of type 'nullptr_t'}}
494+
// expected-note@-2 {{passing argument to parameter 'x' here}}
495+
// expected-error@-3 {{no matching function for call}}
496+
// expected-note@-4 {{candidate function not viable: requires single argument 'x', but no arguments were provided}}
497+
// expected-note@-5 {{conversion candidate of type 'auto (*)(int) -> int'}}
498+
499+
void g() { f<int>(); }
500+
// expected-note@-1 {{in instantiation of default function argument expression for 'f<int>' required here}}
501+
}

0 commit comments

Comments
 (0)