[clang] daa6d7b - [Clang] Use of decltype(capture) in parameter-declaration-clause
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 18 06:58:31 PDT 2022
Author: Corentin Jabot
Date: 2022-04-18T15:58:25+02:00
New Revision: daa6d7b250edb81ffef7770a71049d7af211698b
URL: https://github.com/llvm/llvm-project/commit/daa6d7b250edb81ffef7770a71049d7af211698b
DIFF: https://github.com/llvm/llvm-project/commit/daa6d7b250edb81ffef7770a71049d7af211698b.diff
LOG: [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
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/Sema.h
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/SemaCXX/lambda-capture-type-deduction.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2aabeaf6d679f..fd4eac5458c0a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -261,6 +261,8 @@ C++2b Feature Support
- Implemented `P2036R3: Change scope of lambda trailing-return-type <https://wg21.link/P2036R3>`_.
This proposal modifies how variables captured in lambdas can appear in trailing return type
expressions and how their types are deduced therein, in all C++ language versions.
+ `CWG2569 <https://cplusplus.github.io/CWG/issues/2569.html>`_ is also partially implemented so that
+ `[x](decltype(x)){}` doesn't become ill-formed with the adoption of P2036R3.
CUDA Language Changes in Clang
------------------------------
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 3046962dddec4..e9b1fa0f27451 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1861,6 +1861,8 @@ class Parser : public CodeCompletionHandler {
Token &Replacement);
ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
+ ExprResult ParseCXXMaybeMutableAgnosticExpression();
+
bool areTokensAdjacent(const Token &A, const Token &B);
void CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectTypePtr,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 95ce074fdccd3..e2ae02ea9f76f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -787,6 +787,23 @@ class Sema final {
/// context.
unsigned FunctionScopesStart = 0;
+ /// Whether we are currently in the context of a mutable agnostic identifier
+ /// as described by CWG2569.
+ /// We are handling the unqualified-id of a decltype or noexcept expression.
+ bool InMutableAgnosticContext = false;
+
+ /// RAII object used to change the value of \c InMutableAgnosticContext
+ /// within a \c Sema object.
+ class MutableAgnosticContextRAII {
+ Sema &SemaRef;
+
+ public:
+ MutableAgnosticContextRAII(Sema &S) : SemaRef(S) {
+ SemaRef.InMutableAgnosticContext = true;
+ }
+ ~MutableAgnosticContextRAII() { SemaRef.InMutableAgnosticContext = false; }
+ };
+
ArrayRef<sema::FunctionScopeInfo*> getFunctionScopes() const {
return llvm::makeArrayRef(FunctionScopes.begin() + FunctionScopesStart,
FunctionScopes.end());
@@ -5270,6 +5287,9 @@ class Sema final {
CorrectionCandidateCallback *CCC = nullptr,
bool IsInlineAsmIdentifier = false, Token *KeywordReplacement = nullptr);
+ ExprResult ActOnMutableAgnosticIdExpression(Scope *S, CXXScopeSpec &SS,
+ UnqualifiedId &Id);
+
void DecomposeUnqualifiedId(const UnqualifiedId &Id,
TemplateArgumentListInfo &Buffer,
DeclarationNameInfo &NameInfo,
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 9cf9f7cc4c371..e367417410c86 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1054,7 +1054,7 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
Actions, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
Sema::ExpressionEvaluationContextRecord::EK_Decltype);
Result = Actions.CorrectDelayedTyposInExpr(
- ParseExpression(), /*InitDecl=*/nullptr,
+ ParseCXXMaybeMutableAgnosticExpression(), /*InitDecl=*/nullptr,
/*RecoverUncorrectedTypos=*/false,
[](Expr *E) { return E->hasPlaceholderType() ? ExprError() : E; });
if (Result.isInvalid()) {
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index b03d4e3a90acb..336b24942e07d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -685,6 +685,34 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
return Result;
}
+/// ParseCXXMaybeMutableAgnosticExpression - Handle expressions inside of
+/// sizeof, decltype, noexcept
+/// - unqualified-id
+/// - expression
+/// This serves to silence errors about captured variable referred in lambda
+/// parameter list, if they are used as the unqualified-id of a decltype,
+/// sizeof, or noexcept expression.
+ExprResult Parser::ParseCXXMaybeMutableAgnosticExpression() {
+
+ if (!getLangOpts().CPlusPlus11)
+ return ParseExpression();
+
+ if (Tok.is(tok::identifier) && NextToken().is(tok::r_paren)) {
+ UnqualifiedId Name;
+ CXXScopeSpec SS;
+ if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr,
+ /*ObjectHadErrors=*/false,
+ /*EnteringContext=*/false,
+ /*AllowDestructorName=*/false,
+ /*AllowConstructorName=*/false,
+ /*AllowDeductionGuide=*/false,
+ /*TemplateKWLoc=*/nullptr, Name))
+ return ExprError();
+ return Actions.ActOnMutableAgnosticIdExpression(getCurScope(), SS, Name);
+ }
+ return ParseExpression();
+}
+
/// ParseLambdaExpression - Parse a C++11 lambda expression.
///
/// lambda-expression:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cfebd3cb928cf..3e84961a5079f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2698,6 +2698,18 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
return BuildDeclarationNameExpr(SS, R, ADL);
}
+ExprResult Sema::ActOnMutableAgnosticIdExpression(Scope *S, CXXScopeSpec &SS,
+ UnqualifiedId &Id) {
+ MutableAgnosticContextRAII Ctx(*this);
+ return ActOnIdExpression(S, SS, /*TemplateKwLoc*/
+ SourceLocation(), Id,
+ /*HasTrailingLParen*/ false,
+ /*IsAddressOfOperand*/ false,
+ /*CorrectionCandidateCallback*/ nullptr,
+ /*IsInlineAsmIdentifier*/ false,
+ /*KeywordReplacement*/ nullptr);
+}
+
/// BuildQualifiedDeclarationNameExpr - Build a C++ qualified
/// declaration name, generally during template instantiation.
/// 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,
static bool CheckCaptureUseBeforeLambdaQualifiers(Sema &S, VarDecl *Var,
SourceLocation ExprLoc,
LambdaScopeInfo *LSI) {
+
+ // Allow `[a = 1](decltype(a)) {}` as per CWG2569.
+ if (S.InMutableAgnosticContext)
+ return true;
+
if (Var->isInvalidDecl())
return false;
@@ -18627,7 +18644,7 @@ bool Sema::tryCaptureVariable(
LSI = dyn_cast_or_null<LambdaScopeInfo>(
FunctionScopes[FunctionScopesIndex]);
if (LSI && LSI->BeforeLambdaQualifiersScope) {
- if (isa<ParmVarDecl>(Var))
+ if (isa<ParmVarDecl>(Var) && !Var->getDeclContext()->isFunctionOrMethod())
return true;
IsInLambdaBeforeQualifiers = true;
if (!CheckCaptureUseBeforeLambdaQualifiers(*this, Var, ExprLoc, LSI)) {
diff --git a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
index 88976571dca32..f54f2796c73fa 100644
--- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
+++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp
@@ -87,23 +87,23 @@ void err() {
int y, z; // expected-note 2{{declared here}}
auto implicit_tpl = [=]( // expected-note {{variable 'y' is captured here}}
decltype(
- [&]<decltype(y)> { return 0; }) y) { //expected-error{{captured variable 'y' cannot appear here}}
+ [&]<decltype((y))> { return 0; }) y) { // expected-error{{captured variable 'y' cannot appear here}}
return y;
};
- auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}}
- decltype([&]<decltype(x)> { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
+ auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}}
+ decltype([&]<decltype((x))> { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
return x;
};
auto implicit = [=]( // expected-note {{variable 'z' is captured here}}
decltype(
- [&](decltype(z)) { return 0; }) z) { //expected-error{{captured variable 'z' cannot appear here}}
+ [&](decltype((z))) { return 0; }) z) { // expected-error{{captured variable 'z' cannot appear here}}
return z;
};
- auto init = [x = 1]( // expected-note{{explicitly captured here}}
- decltype([&](decltype(x)) { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
+ auto init = [x = 1]( // expected-note{{explicitly captured here}}
+ decltype([&](decltype((x))) { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}}
return x;
};
@@ -141,20 +141,20 @@ void nested() {
decltype([&](
decltype([=]( // expected-note {{variable 'x' is captured here}}
decltype([&](
- decltype([&](decltype(x)) {}) // expected-error{{captured variable 'x' cannot appear here}}
+ decltype([&](decltype((x))) {}) // expected-error{{captured variable 'x' cannot appear here}}
) {})) {})) {})){};
(void)[&](
decltype([&](
decltype([&](
decltype([&](
- decltype([&](decltype(y)) {})) {})) {})) {})){};
+ decltype([&](decltype((y))) {})) {})) {})) {})){};
(void)[=](
decltype([=](
decltype([=](
- decltype([=]( // expected-note {{variable 'z' is captured here}}
- decltype([&]<decltype(z)> {}) // expected-error{{captured variable 'z' cannot appear here}}
+ decltype([=]( // expected-note {{variable 'z' is captured here}}
+ decltype([&]<decltype((z))> {}) // expected-error{{captured variable 'z' cannot appear here}}
) {})) {})) {})){};
}
@@ -171,3 +171,15 @@ void test_dependent() {
dependent<int&>(r);
dependent<const int&>(cr);
}
+
+void test_CWG2569_tpl(auto a) {
+ (void)[=]<typename T = decltype(a)>(decltype(a) b = decltype(a)()){};
+}
+
+void test_CWG2569() {
+ int a = 0;
+ (void)[=]<typename T = decltype(a)>(decltype(a) b = decltype(a)()){};
+ test_CWG2569_tpl(0);
+
+ (void)[=]<typename T = decltype(not_a_thing)>(decltype(not_a_thing)){}; // expected-error 2{{use of undeclared identifier 'not_a_thing'}}
+}
More information about the cfe-commits
mailing list