diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -261,6 +261,8 @@ - Implemented `P2036R3: Change scope of lambda trailing-return-type `_. 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 `_ 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 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1861,6 +1861,8 @@ 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -787,6 +787,23 @@ /// 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 getFunctionScopes() const { return llvm::makeArrayRef(FunctionScopes.begin() + FunctionScopesStart, FunctionScopes.end()); @@ -5270,6 +5287,9 @@ 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 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1054,7 +1054,7 @@ 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 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -685,6 +685,34 @@ 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2698,6 +2698,18 @@ 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 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 @@ LSI = dyn_cast_or_null( FunctionScopes[FunctionScopesIndex]); if (LSI && LSI->BeforeLambdaQualifiersScope) { - if (isa(Var)) + if (isa(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 --- a/clang/test/SemaCXX/lambda-capture-type-deduction.cpp +++ b/clang/test/SemaCXX/lambda-capture-type-deduction.cpp @@ -87,23 +87,23 @@ int y, z; // expected-note 2{{declared here}} auto implicit_tpl = [=]( // expected-note {{variable 'y' is captured here}} decltype( - [&] { return 0; }) y) { //expected-error{{captured variable 'y' cannot appear here}} + [&] { return 0; }) y) { // expected-error{{captured variable 'y' cannot appear here}} return y; }; - auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}} - decltype([&] { return 0; }) y) { // expected-error {{captured variable 'x' cannot appear here}} + auto init_tpl = [x = 1]( // expected-note{{explicitly captured here}} + decltype([&] { 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 @@ 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([&] {}) // expected-error{{captured variable 'z' cannot appear here}} + decltype([=]( // expected-note {{variable 'z' is captured here}} + decltype([&] {}) // expected-error{{captured variable 'z' cannot appear here}} ) {})) {})) {})){}; } @@ -171,3 +171,15 @@ dependent(r); dependent(cr); } + +void test_CWG2569_tpl(auto a) { + (void)[=](decltype(a) b = decltype(a)()){}; +} + +void test_CWG2569() { + int a = 0; + (void)[=](decltype(a) b = decltype(a)()){}; + test_CWG2569_tpl(0); + + (void)[=](decltype(not_a_thing)){}; // expected-error 2{{use of undeclared identifier 'not_a_thing'}} +}