Skip to content

Commit daa6d7b

Browse files
committed
[Clang] Use of decltype(capture) in parameter-declaration-clause
Partially implement the proposed resolution to CWG2569. D119136 broke some libstdc++ code, as P2036R3, implemented as a DR to C++11 made ill-formed some previously valid and innocuous code. We resolve this issue to allow decltype(x) - but not decltype((x) to appear in the parameter list of a lambda that capture x by copy. Unlike CWG2569, we do not extend that special treatment to sizeof/noexcept yet, as the resolution has not been approved yet and keeping the review small allows a quicker fix of impacted code. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D123909
1 parent 86cdb29 commit daa6d7b

File tree

7 files changed

+93
-12
lines changed

7 files changed

+93
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ C++2b Feature Support
261261
- Implemented `P2036R3: Change scope of lambda trailing-return-type <https://wg21.link/P2036R3>`_.
262262
This proposal modifies how variables captured in lambdas can appear in trailing return type
263263
expressions and how their types are deduced therein, in all C++ language versions.
264+
`CWG2569 <https://cplusplus.github.io/CWG/issues/2569.html>`_ is also partially implemented so that
265+
`[x](decltype(x)){}` doesn't become ill-formed with the adoption of P2036R3.
264266

265267
CUDA Language Changes in Clang
266268
------------------------------

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,8 @@ class Parser : public CodeCompletionHandler {
18611861
Token &Replacement);
18621862
ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
18631863

1864+
ExprResult ParseCXXMaybeMutableAgnosticExpression();
1865+
18641866
bool areTokensAdjacent(const Token &A, const Token &B);
18651867

18661868
void CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectTypePtr,

clang/include/clang/Sema/Sema.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,23 @@ class Sema final {
787787
/// context.
788788
unsigned FunctionScopesStart = 0;
789789

790+
/// Whether we are currently in the context of a mutable agnostic identifier
791+
/// as described by CWG2569.
792+
/// We are handling the unqualified-id of a decltype or noexcept expression.
793+
bool InMutableAgnosticContext = false;
794+
795+
/// RAII object used to change the value of \c InMutableAgnosticContext
796+
/// within a \c Sema object.
797+
class MutableAgnosticContextRAII {
798+
Sema &SemaRef;
799+
800+
public:
801+
MutableAgnosticContextRAII(Sema &S) : SemaRef(S) {
802+
SemaRef.InMutableAgnosticContext = true;
803+
}
804+
~MutableAgnosticContextRAII() { SemaRef.InMutableAgnosticContext = false; }
805+
};
806+
790807
ArrayRef<sema::FunctionScopeInfo*> getFunctionScopes() const {
791808
return llvm::makeArrayRef(FunctionScopes.begin() + FunctionScopesStart,
792809
FunctionScopes.end());
@@ -5270,6 +5287,9 @@ class Sema final {
52705287
CorrectionCandidateCallback *CCC = nullptr,
52715288
bool IsInlineAsmIdentifier = false, Token *KeywordReplacement = nullptr);
52725289

5290+
ExprResult ActOnMutableAgnosticIdExpression(Scope *S, CXXScopeSpec &SS,
5291+
UnqualifiedId &Id);
5292+
52735293
void DecomposeUnqualifiedId(const UnqualifiedId &Id,
52745294
TemplateArgumentListInfo &Buffer,
52755295
DeclarationNameInfo &NameInfo,

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
10541054
Actions, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
10551055
Sema::ExpressionEvaluationContextRecord::EK_Decltype);
10561056
Result = Actions.CorrectDelayedTyposInExpr(
1057-
ParseExpression(), /*InitDecl=*/nullptr,
1057+
ParseCXXMaybeMutableAgnosticExpression(), /*InitDecl=*/nullptr,
10581058
/*RecoverUncorrectedTypos=*/false,
10591059
[](Expr *E) { return E->hasPlaceholderType() ? ExprError() : E; });
10601060
if (Result.isInvalid()) {

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,34 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
685685
return Result;
686686
}
687687

688+
/// ParseCXXMaybeMutableAgnosticExpression - Handle expressions inside of
689+
/// sizeof, decltype, noexcept
690+
/// - unqualified-id
691+
/// - expression
692+
/// This serves to silence errors about captured variable referred in lambda
693+
/// parameter list, if they are used as the unqualified-id of a decltype,
694+
/// sizeof, or noexcept expression.
695+
ExprResult Parser::ParseCXXMaybeMutableAgnosticExpression() {
696+
697+
if (!getLangOpts().CPlusPlus11)
698+
return ParseExpression();
699+
700+
if (Tok.is(tok::identifier) && NextToken().is(tok::r_paren)) {
701+
UnqualifiedId Name;
702+
CXXScopeSpec SS;
703+
if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
704+
/*ObjectHadErrors=*/false,
705+
/*EnteringContext=*/false,
706+
/*AllowDestructorName=*/false,
707+
/*AllowConstructorName=*/false,
708+
/*AllowDeductionGuide=*/false,
709+
/*TemplateKWLoc=*/nullptr, Name))
710+
return ExprError();
711+
return Actions.ActOnMutableAgnosticIdExpression(getCurScope(), SS, Name);
712+
}
713+
return ParseExpression();
714+
}
715+
688716
/// ParseLambdaExpression - Parse a C++11 lambda expression.
689717
///
690718
/// lambda-expression:

clang/lib/Sema/SemaExpr.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2698,6 +2698,18 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
26982698
return BuildDeclarationNameExpr(SS, R, ADL);
26992699
}
27002700

2701+
ExprResult Sema::ActOnMutableAgnosticIdExpression(Scope *S, CXXScopeSpec &SS,
2702+
UnqualifiedId &Id) {
2703+
MutableAgnosticContextRAII Ctx(*this);
2704+
return ActOnIdExpression(S, SS, /*TemplateKwLoc*/
2705+
SourceLocation(), Id,
2706+
/*HasTrailingLParen*/ false,
2707+
/*IsAddressOfOperand*/ false,
2708+
/*CorrectionCandidateCallback*/ nullptr,
2709+
/*IsInlineAsmIdentifier*/ false,
2710+
/*KeywordReplacement*/ nullptr);
2711+
}
2712+
27012713
/// BuildQualifiedDeclarationNameExpr - Build a C++ qualified
27022714
/// declaration name, generally during template instantiation.
27032715
/// There's a large number of things which don't need to be done along
@@ -18545,6 +18557,11 @@ static void buildLambdaCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI,
1854518557
static bool CheckCaptureUseBeforeLambdaQualifiers(Sema &S, VarDecl *Var,
1854618558
SourceLocation ExprLoc,
1854718559
LambdaScopeInfo *LSI) {
18560+
18561+
// Allow `[a = 1](decltype(a)) {}` as per CWG2569.
18562+
if (S.InMutableAgnosticContext)
18563+
return true;
18564+
1854818565
if (Var->isInvalidDecl())
1854918566
return false;
1855018567

@@ -18627,7 +18644,7 @@ bool Sema::tryCaptureVariable(
1862718644
LSI = dyn_cast_or_null<LambdaScopeInfo>(
1862818645
FunctionScopes[FunctionScopesIndex]);
1862918646
if (LSI && LSI->BeforeLambdaQualifiersScope) {
18630-
if (isa<ParmVarDecl>(Var))
18647+
if (isa<ParmVarDecl>(Var) && !Var->getDeclContext()->isFunctionOrMethod())
1863118648
return true;
1863218649
IsInLambdaBeforeQualifiers = true;
1863318650
if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) {

clang/test/SemaCXX/lambda-capture-type-deduction.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,23 +87,23 @@ void err() {
8787
int y, z; // expected-note 2{{declared here}}
8888
auto implicit_tpl = [=]( // expected-note {{variable 'y' is captured here}}
8989
decltype(
90-
[&]<decltype(y)> { return 0; }) y) { //expected-error{{captured variable 'y' cannot appear here}}
90+
[&]<decltype((y))> { return 0; }) y) { // expected-error{{captured variable 'y' cannot appear here}}
9191
return y;
9292
};
9393

94-
auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}}
95-
decltype([&]<decltype(x)> { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
94+
auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}}
95+
decltype([&]<decltype((x))> { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
9696
return x;
9797
};
9898

9999
auto implicit = [=]( // expected-note {{variable 'z' is captured here}}
100100
decltype(
101-
[&](decltype(z)) { return 0; }) z) { //expected-error{{captured variable 'z' cannot appear here}}
101+
[&](decltype((z))) { return 0; }) z) { // expected-error{{captured variable 'z' cannot appear here}}
102102
return z;
103103
};
104104

105-
auto init = [x = 1]( // expected-note{{explicitly captured here}}
106-
decltype([&](decltype(x)) { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
105+
auto init = [x = 1]( // expected-note{{explicitly captured here}}
106+
decltype([&](decltype((x))) { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
107107
return x;
108108
};
109109

@@ -141,20 +141,20 @@ void nested() {
141141
decltype([&](
142142
decltype([=]( // expected-note {{variable 'x' is captured here}}
143143
decltype([&](
144-
decltype([&](decltype(x)) {}) // expected-error{{captured variable 'x' cannot appear here}}
144+
decltype([&](decltype((x))) {}) // expected-error{{captured variable 'x' cannot appear here}}
145145
) {})) {})) {})){};
146146

147147
(void)[&](
148148
decltype([&](
149149
decltype([&](
150150
decltype([&](
151-
decltype([&](decltype(y)) {})) {})) {})) {})){};
151+
decltype([&](decltype((y))) {})) {})) {})) {})){};
152152

153153
(void)[=](
154154
decltype([=](
155155
decltype([=](
156-
decltype([=]( // expected-note {{variable 'z' is captured here}}
157-
decltype([&]<decltype(z)> {}) // expected-error{{captured variable 'z' cannot appear here}}
156+
decltype([=]( // expected-note {{variable 'z' is captured here}}
157+
decltype([&]<decltype((z))> {}) // expected-error{{captured variable 'z' cannot appear here}}
158158
) {})) {})) {})){};
159159
}
160160

@@ -171,3 +171,15 @@ void test_dependent() {
171171
dependent<int&>(r);
172172
dependent<const int&>(cr);
173173
}
174+
175+
void test_CWG2569_tpl(auto a) {
176+
(void)[=]<typename T = decltype(a)>(decltype(a) b = decltype(a)()){};
177+
}
178+
179+
void test_CWG2569() {
180+
int a = 0;
181+
(void)[=]<typename T = decltype(a)>(decltype(a) b = decltype(a)()){};
182+
test_CWG2569_tpl(0);
183+
184+
(void)[=]<typename T = decltype(not_a_thing)>(decltype(not_a_thing)){}; // expected-error 2{{use of undeclared identifier 'not_a_thing'}}
185+
}

0 commit comments

Comments
 (0)