Implement P2173 for attributes on lambdas

https://wg21.link/P2173 is making its way through WG21 currently and
has not been formally adopted yet. This feature provides very useful
functionality in that you can specify attributes on the various
function *declarations* generated by a lambda expression, where the
current C++ grammar only allows attributes which apply to the various
function *types* so generated.

This patch implements P2173 on the assumption that it will be adopted
by WG21 with this syntax for C++23.
This commit is contained in:
Aaron Ballman 2021-03-03 10:02:51 -05:00
parent 25ad188bfc
commit b2bc0a3254
5 changed files with 81 additions and 24 deletions

View File

@ -269,6 +269,9 @@ def CXXPre17CompatPedantic : DiagGroup<"c++98-c++11-c++14-compat-pedantic",
def CXXPre20Compat : DiagGroup<"c++98-c++11-c++14-c++17-compat">;
def CXXPre20CompatPedantic : DiagGroup<"c++98-c++11-c++14-c++17-compat-pedantic",
[CXXPre20Compat]>;
def CXXPre2bCompat : DiagGroup<"pre-c++2b-compat">;
def CXXPre2bCompatPedantic :
DiagGroup<"pre-c++2b-compat-pedantic", [CXXPre2bCompat]>;
def CXX98CompatBindToTemporaryCopy :
DiagGroup<"c++98-compat-bind-to-temporary-copy">;
@ -282,7 +285,8 @@ def CXX98Compat : DiagGroup<"c++98-compat",
CXX98CompatUnnamedTypeTemplateArgs,
CXXPre14Compat,
CXXPre17Compat,
CXXPre20Compat]>;
CXXPre20Compat,
CXXPre2bCompat]>;
// Warnings for C++11 features which are Extensions in C++98 mode.
def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic",
[CXX98Compat,
@ -290,7 +294,8 @@ def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic",
CXX98CompatExtraSemi,
CXXPre14CompatPedantic,
CXXPre17CompatPedantic,
CXXPre20CompatPedantic]>;
CXXPre20CompatPedantic,
CXXPre2bCompatPedantic]>;
def CXX11Narrowing : DiagGroup<"c++11-narrowing">;
@ -319,33 +324,40 @@ def CXX11Compat : DiagGroup<"c++11-compat",
CXX11CompatDeprecatedWritableStr,
CXXPre14Compat,
CXXPre17Compat,
CXXPre20Compat]>;
CXXPre20Compat,
CXXPre2bCompat]>;
def : DiagGroup<"c++0x-compat", [CXX11Compat]>;
def CXX11CompatPedantic : DiagGroup<"c++11-compat-pedantic",
[CXX11Compat,
CXXPre14CompatPedantic,
CXXPre17CompatPedantic,
CXXPre20CompatPedantic]>;
CXXPre20CompatPedantic,
CXXPre2bCompatPedantic]>;
def CXX14Compat : DiagGroup<"c++14-compat", [CXXPre17Compat,
CXXPre20Compat]>;
CXXPre20Compat,
CXXPre2bCompat]>;
def CXX14CompatPedantic : DiagGroup<"c++14-compat-pedantic",
[CXX14Compat,
CXXPre17CompatPedantic,
CXXPre20CompatPedantic]>;
CXXPre20CompatPedantic,
CXXPre2bCompatPedantic]>;
def CXX17Compat : DiagGroup<"c++17-compat", [DeprecatedRegister,
DeprecatedIncrementBool,
CXX17CompatMangling,
CXXPre20Compat]>;
CXXPre20Compat,
CXXPre2bCompat]>;
def CXX17CompatPedantic : DiagGroup<"c++17-compat-pedantic",
[CXX17Compat,
CXXPre20CompatPedantic]>;
CXXPre20CompatPedantic,
CXXPre2bCompatPedantic]>;
def : DiagGroup<"c++1z-compat", [CXX17Compat]>;
def CXX20Compat : DiagGroup<"c++20-compat">;
def CXX20Compat : DiagGroup<"c++20-compat", [CXXPre2bCompat]>;
def CXX20CompatPedantic : DiagGroup<"c++20-compat-pedantic",
[CXX20Compat]>;
[CXX20Compat,
CXXPre2bCompatPedantic]>;
def : DiagGroup<"c++2a-compat", [CXX20Compat]>;
def : DiagGroup<"c++2a-compat-pedantic", [CXX20CompatPedantic]>;
@ -1007,6 +1019,10 @@ def CXX17 : DiagGroup<"c++17-extensions">;
// earlier C++ versions.
def CXX20 : DiagGroup<"c++20-extensions", [CXX20Designator]>;
// A warning group for warnings about using C++2b features as extensions in
// earlier C++ versions.
def CXX2b : DiagGroup<"c++2b-extensions">;
def : DiagGroup<"c++0x-extensions", [CXX11]>;
def : DiagGroup<"c++1y-extensions", [CXX14]>;
def : DiagGroup<"c++1z-extensions", [CXX17]>;

View File

@ -961,6 +961,13 @@ def err_lambda_capture_multiple_ellipses : Error<
"multiple ellipses in pack capture">;
def err_capture_default_first : Error<
"capture default must be first">;
def ext_decl_attrs_on_lambda : ExtWarn<
"an attribute specifier sequence in this position is a C++2b extension">,
InGroup<CXX2b>;
def warn_cxx20_compat_decl_attrs_on_lambda : Warning<
"an attribute specifier sequence in this position is incompatible with C++ "
"standards before C++2b">, InGroup<CXXPre2bCompat>, DefaultIgnore;
// C++17 lambda expressions
def err_expected_star_this_capture : Error<
"expected 'this' following '*' in lambda capture list">;

View File

@ -1302,6 +1302,17 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
}
}
// Implement WG21 P2173, which allows attributes immediately before the
// lambda declarator and applies them to the corresponding function operator
// or operator template declaration. We accept this as a conforming extension
// in all language modes that support lambdas.
if (isCXX11AttributeSpecifier()) {
Diag(Tok, getLangOpts().CPlusPlus2b
? diag::warn_cxx20_compat_decl_attrs_on_lambda
: diag::ext_decl_attrs_on_lambda);
MaybeParseCXX11Attributes(D);
}
TypeResult TrailingReturnType;
SourceLocation TrailingReturnTypeLoc;
if (Tok.is(tok::l_paren)) {

View File

@ -33,13 +33,14 @@ template <typename... Ts> void test(Ts... a) {
[]() mutable {};
[]() noexcept {};
[]() -> int { return 0; };
[] [[noreturn]] () {};
}
// CHECK:Dumping test:
// CHECK-NEXT:FunctionTemplateDecl {{.*}} <{{.*}}ast-dump-lambda.cpp:15:1, line:36:1> line:15:32{{( imported)?}} test
// CHECK-NEXT:FunctionTemplateDecl {{.*}} <{{.*}}ast-dump-lambda.cpp:15:1, line:37:1> line:15:32{{( imported)?}} test
// CHECK-NEXT:|-TemplateTypeParmDecl {{.*}} <col:11, col:23> col:23{{( imported)?}} referenced typename depth 0 index 0 ... Ts
// CHECK-NEXT:`-FunctionDecl {{.*}} <col:27, line:36:1> line:15:32{{( imported)?}} test 'void (Ts...)'
// CHECK-NEXT:`-FunctionDecl {{.*}} <col:27, line:37:1> line:15:32{{( imported)?}} test 'void (Ts...)'
// CHECK-NEXT: |-ParmVarDecl {{.*}} <col:37, col:43> col:43{{( imported)?}} referenced a 'Ts...' pack
// CHECK-NEXT: `-CompoundStmt {{.*}} <col:46, line:36:1>
// CHECK-NEXT: `-CompoundStmt {{.*}} <col:46, line:37:1>
// CHECK-NEXT: |-DeclStmt {{.*}} <line:16:3, line:21:4>
// CHECK-NEXT: | `-CXXRecordDecl {{.*}} <line:16:3, line:21:3> line:16:10{{( imported)?}}{{( <undeserialized declarations>)?}} struct V definition
// CHECK-NEXT: | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
@ -275,7 +276,25 @@ template <typename... Ts> void test(Ts... a) {
// CHECK-NEXT: | | |-CXXConversionDecl {{.*}} <col:3, col:18> col:3{{( imported)?}} implicit constexpr operator auto (*)() noexcept 'auto (*() const noexcept)() noexcept' inline
// CHECK-NEXT: | | `-CXXMethodDecl {{.*}} <col:3, col:18> col:3{{( imported)?}} implicit __invoke 'auto () noexcept' static inline
// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:17, col:18>
// CHECK-NEXT: `-LambdaExpr {{.*}} <line:35:3, col:27> '(lambda at {{.*}}ast-dump-lambda.cpp:35:3)'
// CHECK-NEXT: |-LambdaExpr {{.*}} <line:35:3, col:27> '(lambda at {{.*}}ast-dump-lambda.cpp:35:3)'
// CHECK-NEXT: | |-CXXRecordDecl {{.*}} <col:3> col:3{{( imported)?}} implicit{{( <undeserialized declarations>)?}} class definition
// CHECK-NEXT: | | |-DefinitionData lambda empty standard_layout trivially_copyable literal can_const_default_init
// CHECK-NEXT: | | | |-DefaultConstructor defaulted_is_constexpr
// CHECK-NEXT: | | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
// CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit
// CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
// CHECK-NEXT: | | | |-MoveAssignment
// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit
// CHECK-NEXT: | | |-CXXMethodDecl {{.*}} <col:11, col:27> col:3{{( imported)?}} operator() 'auto () const -> int' inline
// CHECK-NEXT: | | | `-CompoundStmt {{.*}} <col:15, col:27>
// CHECK-NEXT: | | | `-ReturnStmt {{.*}} <col:17, col:24>
// CHECK-NEXT: | | | `-IntegerLiteral {{.*}} <col:24> 'int' 0
// CHECK-NEXT: | | |-CXXConversionDecl {{.*}} <col:3, col:27> col:3{{( imported)?}} implicit constexpr operator int (*)() 'auto (*() const noexcept)() -> int' inline
// CHECK-NEXT: | | `-CXXMethodDecl {{.*}} <col:3, col:27> col:3{{( imported)?}} implicit __invoke 'auto () -> int' static inline
// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:15, col:27>
// CHECK-NEXT: | `-ReturnStmt {{.*}} <col:17, col:24>
// CHECK-NEXT: | `-IntegerLiteral {{.*}} <col:24> 'int' 0
// CHECK-NEXT: `-LambdaExpr {{.*}} <line:36:3, col:23> '(lambda at {{.*}}ast-dump-lambda.cpp:36:3)'
// CHECK-NEXT: |-CXXRecordDecl {{.*}} <col:3> col:3{{( imported)?}} implicit{{( <undeserialized declarations>)?}} class definition
// CHECK-NEXT: | |-DefinitionData lambda empty standard_layout trivially_copyable literal can_const_default_init
// CHECK-NEXT: | | |-DefaultConstructor defaulted_is_constexpr
@ -284,12 +303,9 @@ template <typename... Ts> void test(Ts... a) {
// CHECK-NEXT: | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param
// CHECK-NEXT: | | |-MoveAssignment
// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit
// CHECK-NEXT: | |-CXXMethodDecl {{.*}} <col:11, col:27> col:3{{( imported)?}} operator() 'auto () const -> int' inline
// CHECK-NEXT: | | `-CompoundStmt {{.*}} <col:15, col:27>
// CHECK-NEXT: | | `-ReturnStmt {{.*}} <col:17, col:24>
// CHECK-NEXT: | | `-IntegerLiteral {{.*}} <col:24> 'int' 0
// CHECK-NEXT: | |-CXXConversionDecl {{.*}} <col:3, col:27> col:3{{( imported)?}} implicit constexpr operator int (*)() 'auto (*() const noexcept)() -> int' inline
// CHECK-NEXT: | `-CXXMethodDecl {{.*}} <col:3, col:27> col:3{{( imported)?}} implicit __invoke 'auto () -> int' static inline
// CHECK-NEXT: `-CompoundStmt {{.*}} <col:15, col:27>
// CHECK-NEXT: `-ReturnStmt {{.*}} <col:17, col:24>
// CHECK-NEXT: `-IntegerLiteral {{.*}} <col:24> 'int' 0
// CHECK-NEXT: | |-CXXMethodDecl {{.*}} <col:20, col:23> col:3{{( imported)?}} operator() 'auto () const' inline
// CHECK-NEXT: | | |-CompoundStmt {{.*}} <col:22, col:23>
// CHECK-NEXT: | | `-CXX11NoReturnAttr {{.*}} <col:8>
// CHECK-NEXT: | |-CXXConversionDecl {{.*}} <col:3, col:23> col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline
// CHECK-NEXT: | `-CXXMethodDecl {{.*}} <col:3, col:23> col:3{{( imported)?}} implicit __invoke 'auto ()' static inline
// CHECK-NEXT: `-CompoundStmt {{.*}} <col:22, col:23>

View File

@ -101,7 +101,6 @@ class C {
}
void attributes() {
[] [[]] {}; // expected-error {{lambda requires '()' before attribute specifier}}
[] __attribute__((noreturn)) {}; // expected-error {{lambda requires '()' before attribute specifier}}
[]() [[]]
mutable {}; // expected-error {{expected body of lambda expression}}
@ -116,6 +115,14 @@ class C {
[]() __attribute__((noreturn)) mutable { while(1); };
[]() mutable
__attribute__((noreturn)) { while(1); }; // expected-error {{expected body of lambda expression}}
// Testing support for P2173 on adding attributes to the declaration
// rather than the type.
[] [[]] () {}; // expected-warning {{an attribute specifier sequence in this position is a C++2b extension}}
#if __cplusplus > 201703L
[] <typename> [[]] () {}; // expected-warning {{an attribute specifier sequence in this position is a C++2b extension}}
#endif
[] [[]] {}; // expected-warning {{an attribute specifier sequence in this position is a C++2b extension}}
}
};