Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit f4b7de1

Browse files
committed
Improve our handling of lambda expressions that occur within default
arguments. There are two aspects to this: - Make sure that when marking the declarations referenced in a default argument, we don't try to mark local variables, both because it's a waste of time and because the semantics are wrong: we're not in a place where we could capture these variables again even if it did make sense. - When a lambda expression occurs in a default argument of a function template, make sure that the corresponding closure type is considered dependent, so that it will get properly instantiated. The second bit is a bit of a hack; to fix it properly, we may have to rearchitect our handling of default arguments, parsing them only after creating the function definition. However, I'd like to separate that work from the lambdas work. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151076 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent f18a87b commit f4b7de1

File tree

9 files changed

+85
-19
lines changed

9 files changed

+85
-19
lines changed

Diff for: include/clang/AST/DeclCXX.h

+30-5
Original file line numberDiff line numberDiff line change
@@ -560,18 +560,28 @@ class CXXRecordDecl : public RecordDecl {
560560
struct LambdaDefinitionData : public DefinitionData {
561561
typedef LambdaExpr::Capture Capture;
562562

563-
LambdaDefinitionData(CXXRecordDecl *D)
564-
: DefinitionData(D), NumCaptures(0), NumExplicitCaptures(0),
565-
ContextDecl(0), Captures(0)
563+
LambdaDefinitionData(CXXRecordDecl *D, bool Dependent)
564+
: DefinitionData(D), Dependent(Dependent), NumCaptures(0),
565+
NumExplicitCaptures(0), ManglingNumber(0), ContextDecl(0), Captures(0)
566566
{
567567
IsLambda = true;
568568
}
569569

570+
/// \brief Whether this lambda is known to be dependent, even if its
571+
/// context isn't dependent.
572+
///
573+
/// A lambda with a non-dependent context can be dependent if it occurs
574+
/// within the default argument of a function template, because the
575+
/// lambda will have been created with the enclosing context as its
576+
/// declaration context, rather than function. This is an unfortunate
577+
/// artifact of having to parse the default arguments before
578+
unsigned Dependent : 1;
579+
570580
/// \brief The number of captures in this lambda.
571581
unsigned NumCaptures : 16;
572582

573583
/// \brief The number of explicit captures in this lambda.
574-
unsigned NumExplicitCaptures : 16;
584+
unsigned NumExplicitCaptures : 15;
575585

576586
/// \brief The number used to indicate this lambda expression for name
577587
/// mangling in the Itanium C++ ABI.
@@ -689,7 +699,7 @@ class CXXRecordDecl : public RecordDecl {
689699
IdentifierInfo *Id, CXXRecordDecl* PrevDecl=0,
690700
bool DelayTypeCreation = false);
691701
static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC,
692-
SourceLocation Loc);
702+
SourceLocation Loc, bool DependentLambda);
693703
static CXXRecordDecl *CreateDeserialized(const ASTContext &C, unsigned ID);
694704

695705
bool isDynamicClass() const {
@@ -1478,6 +1488,21 @@ class CXXRecordDecl : public RecordDecl {
14781488
return getLambdaData().ContextDecl;
14791489
}
14801490

1491+
/// \brief Determine whether this lambda expression was known to be dependent
1492+
/// at the time it was created, even if its context does not appear to be
1493+
/// dependent.
1494+
///
1495+
/// This flag is a workaround for an issue with parsing, where default
1496+
/// arguments are parsed before their enclosing function declarations have
1497+
/// been created. This means that any lambda expressions within those
1498+
/// default arguments will have as their DeclContext the context enclosing
1499+
/// the function declaration, which may be non-dependent even when the
1500+
/// function declaration itself is dependent. This flag indicates when we
1501+
/// know that the lambda is dependent despite that.
1502+
bool isDependentLambda() const {
1503+
return isLambda() && getLambdaData().Dependent;
1504+
}
1505+
14811506
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
14821507
static bool classofKind(Kind K) {
14831508
return K >= firstCXXRecord && K <= lastCXXRecord;

Diff for: include/clang/Sema/Sema.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -2386,7 +2386,8 @@ class Sema {
23862386
QualType getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc);
23872387

23882388
void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
2389-
void MarkDeclarationsReferencedInExpr(Expr *E);
2389+
void MarkDeclarationsReferencedInExpr(Expr *E,
2390+
bool SkipLocalVariables = false);
23902391

23912392
/// \brief Try to recover by turning the given expression into a
23922393
/// call. Returns true if recovery was attempted or an error was
@@ -3543,7 +3544,8 @@ class Sema {
35433544
void ActOnCXXExitDeclInitializer(Scope *S, Decl *Dcl);
35443545

35453546
/// \brief Create a new lambda closure type.
3546-
CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange);
3547+
CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange,
3548+
bool KnownDependent = false);
35473549

35483550
/// \brief Start the definition of a lambda expression.
35493551
CXXMethodDecl *startLambdaDefinition(CXXRecordDecl *Class,

Diff for: lib/AST/DeclBase.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -741,10 +741,14 @@ bool DeclContext::isDependentContext() const {
741741
if (isa<ClassTemplatePartialSpecializationDecl>(this))
742742
return true;
743743

744-
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(this))
744+
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(this)) {
745745
if (Record->getDescribedClassTemplate())
746746
return true;
747-
747+
748+
if (Record->isDependentLambda())
749+
return true;
750+
}
751+
748752
if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(this)) {
749753
if (Function->getDescribedFunctionTemplate())
750754
return true;

Diff for: lib/AST/DeclCXX.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ CXXRecordDecl *CXXRecordDecl::Create(const ASTContext &C, TagKind TK,
8383
}
8484

8585
CXXRecordDecl *CXXRecordDecl::CreateLambda(const ASTContext &C, DeclContext *DC,
86-
SourceLocation Loc) {
86+
SourceLocation Loc, bool Dependent) {
8787
CXXRecordDecl* R = new (C) CXXRecordDecl(CXXRecord, TTK_Class, DC, Loc, Loc,
8888
0, 0);
8989
R->IsBeingDefined = true;
90-
R->DefinitionData = new (C) struct LambdaDefinitionData(R);
90+
R->DefinitionData = new (C) struct LambdaDefinitionData(R, Dependent);
9191
C.getTypeDeclType(R, /*PrevDecl=*/0);
9292
return R;
9393
}

Diff for: lib/Sema/SemaExpr.cpp

+22-4
Original file line numberDiff line numberDiff line change
@@ -3146,7 +3146,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
31463146
// We already type-checked the argument, so we know it works.
31473147
// Just mark all of the declarations in this potentially-evaluated expression
31483148
// as being "referenced".
3149-
MarkDeclarationsReferencedInExpr(Param->getDefaultArg());
3149+
MarkDeclarationsReferencedInExpr(Param->getDefaultArg(),
3150+
/*SkipLocalVariables=*/true);
31503151
return Owned(CXXDefaultArgExpr::Create(Context, CallLoc, Param));
31513152
}
31523153

@@ -10145,13 +10146,22 @@ namespace {
1014510146
/// potentially-evaluated subexpressions as "referenced".
1014610147
class EvaluatedExprMarker : public EvaluatedExprVisitor<EvaluatedExprMarker> {
1014710148
Sema &S;
10149+
bool SkipLocalVariables;
1014810150

1014910151
public:
1015010152
typedef EvaluatedExprVisitor<EvaluatedExprMarker> Inherited;
1015110153

10152-
explicit EvaluatedExprMarker(Sema &S) : Inherited(S.Context), S(S) { }
10154+
EvaluatedExprMarker(Sema &S, bool SkipLocalVariables)
10155+
: Inherited(S.Context), S(S), SkipLocalVariables(SkipLocalVariables) { }
1015310156

1015410157
void VisitDeclRefExpr(DeclRefExpr *E) {
10158+
// If we were asked not to visit local variables, don't.
10159+
if (SkipLocalVariables) {
10160+
if (VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
10161+
if (VD->hasLocalStorage())
10162+
return;
10163+
}
10164+
1015510165
S.MarkDeclRefReferenced(E);
1015610166
}
1015710167

@@ -10193,6 +10203,10 @@ namespace {
1019310203
}
1019410204

1019510205
void VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {
10206+
// If we were asked not to visit local variables, don't.
10207+
if (SkipLocalVariables && E->getDecl()->hasLocalStorage())
10208+
return;
10209+
1019610210
S.MarkBlockDeclRefReferenced(E);
1019710211
}
1019810212

@@ -10211,8 +10225,12 @@ namespace {
1021110225

1021210226
/// \brief Mark any declarations that appear within this expression or any
1021310227
/// potentially-evaluated subexpressions as "referenced".
10214-
void Sema::MarkDeclarationsReferencedInExpr(Expr *E) {
10215-
EvaluatedExprMarker(*this).Visit(E);
10228+
///
10229+
/// \param SkipLocalVariables If true, don't mark local variables as
10230+
/// 'referenced'.
10231+
void Sema::MarkDeclarationsReferencedInExpr(Expr *E,
10232+
bool SkipLocalVariables) {
10233+
EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E);
1021610234
}
1021710235

1021810236
/// \brief Emit a diagnostic that describes an effect on the run-time behavior

Diff for: lib/Sema/SemaLambda.cpp

+12-3
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@
2121
using namespace clang;
2222
using namespace sema;
2323

24-
CXXRecordDecl *Sema::createLambdaClosureType(SourceRange IntroducerRange) {
24+
CXXRecordDecl *Sema::createLambdaClosureType(SourceRange IntroducerRange,
25+
bool KnownDependent) {
2526
DeclContext *DC = CurContext;
2627
while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext()))
2728
DC = DC->getParent();
2829

2930
// Start constructing the lambda class.
3031
CXXRecordDecl *Class = CXXRecordDecl::CreateLambda(Context, DC,
31-
IntroducerRange.getBegin());
32+
IntroducerRange.getBegin(),
33+
KnownDependent);
3234
DC->addDecl(Class);
3335

3436
return Class;
@@ -142,7 +144,14 @@ void Sema::addLambdaParameters(CXXMethodDecl *CallOperator, Scope *CurScope) {
142144
void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
143145
Declarator &ParamInfo,
144146
Scope *CurScope) {
145-
CXXRecordDecl *Class = createLambdaClosureType(Intro.Range);
147+
// Determine if we're within a context where we know that the lambda will
148+
// be dependent, because there are template parameters in scope.
149+
bool KnownDependent = false;
150+
if (Scope *TmplScope = CurScope->getTemplateParamParent())
151+
if (!TmplScope->decl_empty())
152+
KnownDependent = true;
153+
154+
CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, KnownDependent);
146155

147156
// Determine the signature of the call operator.
148157
TypeSourceInfo *MethodTyInfo;

Diff for: lib/Serialization/ASTReaderDecl.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
11071107
typedef LambdaExpr::Capture Capture;
11081108
CXXRecordDecl::LambdaDefinitionData &Lambda
11091109
= static_cast<CXXRecordDecl::LambdaDefinitionData &>(Data);
1110+
Lambda.Dependent = Record[Idx++];
11101111
Lambda.NumCaptures = Record[Idx++];
11111112
Lambda.NumExplicitCaptures = Record[Idx++];
11121113
Lambda.ManglingNumber = Record[Idx++];
@@ -1134,7 +1135,7 @@ void ASTDeclReader::VisitCXXRecordDecl(CXXRecordDecl *D) {
11341135
// allocate the appropriate DefinitionData structure.
11351136
bool IsLambda = Record[Idx++];
11361137
if (IsLambda)
1137-
D->DefinitionData = new (C) CXXRecordDecl::LambdaDefinitionData(D);
1138+
D->DefinitionData = new (C) CXXRecordDecl::LambdaDefinitionData(D, false);
11381139
else
11391140
D->DefinitionData = new (C) struct CXXRecordDecl::DefinitionData(D);
11401141

Diff for: lib/Serialization/ASTWriter.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -4330,6 +4330,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
43304330
// Add lambda-specific data.
43314331
if (Data.IsLambda) {
43324332
CXXRecordDecl::LambdaDefinitionData &Lambda = D->getLambdaData();
4333+
Record.push_back(Lambda.Dependent);
43334334
Record.push_back(Lambda.NumCaptures);
43344335
Record.push_back(Lambda.NumExplicitCaptures);
43354336
Record.push_back(Lambda.ManglingNumber);

Diff for: test/CXX/expr/expr.prim/expr.prim.lambda/p13.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ void f2() {
88
void g4(int = ([=]{ return 0; })());
99
void g5(int = ([]{ return sizeof i; })());
1010
}
11+
12+
namespace lambda_in_default_args {
13+
int f(int = [] () -> int { int n; return ++n; } ());
14+
template<typename T> T g(T = [] () -> T { T n; return ++n; } ());
15+
int k = f() + g<int>();
16+
}

0 commit comments

Comments
 (0)