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

Reply via email to