aaron.ballman created this revision. aaron.ballman added reviewers: rsmith, rjmccall, jyknight. aaron.ballman requested review of this revision.
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 <https://reviews.llvm.org/P2173> on the assumption that it will be adopted by WG21 with this syntax because there are no other syntactic locations to put an attribute on a lambda that will unambiguously appertain to the declaration rather than the type. Given the large number of function declaration attributes compared to function type attributes, such a construct is needed. The proposal has been approved in principle by EWG and is scheduled to go through Core wording review, so there is a strong likelihood it will be adopted, at least with this syntax. When WG21 adopts the proposal, the diagnostic can be changed as appropriate. In the unlikely event that WG21 decides not to adopt the proposal, this syntax is still a conforming extension. I need this functionality in a downstream fork of Clang that is currently sliding some attributes written in the type position to instead apply to the declaration. https://reviews.llvm.org/D95691 Files: clang/include/clang/Basic/DiagnosticParseKinds.td clang/lib/Parse/ParseExprCXX.cpp clang/test/AST/ast-dump-lambda.cpp clang/test/Parser/cxx0x-lambda-expressions.cpp
Index: clang/test/Parser/cxx0x-lambda-expressions.cpp =================================================================== --- clang/test/Parser/cxx0x-lambda-expressions.cpp +++ clang/test/Parser/cxx0x-lambda-expressions.cpp @@ -101,7 +101,6 @@ } 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 @@ []() __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 Clang extension}} +#if __cplusplus > 201703L + [] <typename> [[]] () {}; // expected-warning {{an attribute specifier sequence in this position is a Clang extension}} +#endif + [] [[]] {}; // expected-warning {{an attribute specifier sequence in this position is a Clang extension}} } }; Index: clang/test/AST/ast-dump-lambda.cpp =================================================================== --- clang/test/AST/ast-dump-lambda.cpp +++ clang/test/AST/ast-dump-lambda.cpp @@ -33,13 +33,14 @@ []() 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 @@ // 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 @@ // 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> Index: clang/lib/Parse/ParseExprCXX.cpp =================================================================== --- clang/lib/Parse/ParseExprCXX.cpp +++ clang/lib/Parse/ParseExprCXX.cpp @@ -1302,6 +1302,17 @@ } } + // 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()) { + // FIXME: if the paper is adopted by WG21, this diagnostic should be + // replaced with one about it being an extension in older standard modes. + Diag(Tok.getLocation(), diag::ext_decl_attrs_on_lambda); + MaybeParseCXX11Attributes(D); + } + TypeResult TrailingReturnType; SourceLocation TrailingReturnTypeLoc; if (Tok.is(tok::l_paren)) { Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -952,6 +952,10 @@ "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 Clang extension">, + InGroup<DiagGroup<"attributes-preceding-lambda-declarator">>; + // C++17 lambda expressions def err_expected_star_this_capture : Error< "expected 'this' following '*' in lambda capture list">;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits