https://github.com/vangthao95 updated 
https://github.com/llvm/llvm-project/pull/214247

>From 758b051aebc6fc7a565ba81f99fb9fa1d44bb1a2 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Tue, 4 Aug 2026 23:24:28 -0400
Subject: [PATCH 1/4] [clang][HIP][CUDA] Disambiguate lambdas from Microsoft
 attributes

With -fms-extensions, a CUDA/HIP lambda capture list can be consumed as a
Microsoft attribute list. This causes attributed lambdas used in direct
initialization to be misparsed as function declarators.

Tentatively inspect the tokens following the capture list and any trailing
attributes to identify CUDA/HIP lambdas before parsing Microsoft attributes.

Add host and device regression coverage for capture forms and preserve valid
Microsoft attribute parsing.

Assisted by: Cursor / Claude Opus 4.8
---
 clang/include/clang/Parse/Parser.h      | 10 +++-
 clang/lib/Parse/ParseExprCXX.cpp        | 17 +++++++
 clang/test/Parser/ms-lambda-capture.hip | 61 +++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Parser/ms-lambda-capture.hip

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 6913c42884a36..c09d7d7c8ba03 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2339,8 +2339,10 @@ class Parser : public CodeCompletionHandler {
 
   bool MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
     bool AttrsParsed = false;
+    // A '[' may begin a Microsoft attribute or a C++ lambda, so only parse it
+    // as an attribute after confirming it does not start a lambda.
     if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
-        Tok.is(tok::l_square)) {
+        Tok.is(tok::l_square) && !startsLambdaNotMicrosoftAttribute()) {
       ParsedAttributes AttrsWithRange(AttrFactory);
       ParseMicrosoftAttributes(AttrsWithRange);
       AttrsParsed = !AttrsWithRange.empty();
@@ -4723,6 +4725,12 @@ class Parser : public CodeCompletionHandler {
   /// If we are not looking at a lambda expression, returns ExprError().
   ExprResult TryParseLambdaExpression();
 
+  /// Returns true if the current '[' begins a CUDA/HIP lambda rather than a
+  /// Microsoft '[]' attribute (enabled under -fms-extensions or HLSL).
+  /// Restricted to CUDA/HIP, the only mode that allows attributes immediately
+  /// after a lambda's capture list.
+  bool startsLambdaNotMicrosoftAttribute();
+
   /// Parse a lambda introducer.
   /// \param Intro A LambdaIntroducer filled in with information about the
   ///        contents of the lambda-introducer.
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 860c069e18fca..f4870df806228 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -759,6 +759,23 @@ ExprResult Parser::TryParseLambdaExpression() {
   return ParseLambdaExpressionAfterIntroducer(Intro);
 }
 
+bool Parser::startsLambdaNotMicrosoftAttribute() {
+  // Restricted to CUDA/HIP, the only mode that allows attributes immediately
+  // after a lambda's capture list.
+  if (!getLangOpts().CUDA || Tok.isNot(tok::l_square))
+    return false;
+
+  // Skip the '[...]' and any trailing attributes (e.g. CUDA/HIP's
+  // '__device__'). A lambda then continues with '(', '{' or '<', while an
+  // attribute is followed by the declaration it applies to (e.g. '[propget]
+  // int get()').
+  RevertingTentativeParsingAction TPA(*this);
+  ConsumeBracket();
+  if (!SkipUntil(tok::r_square, StopAtSemi) || !TrySkipAttributes())
+    return false;
+  return Tok.isOneOf(tok::l_paren, tok::l_brace, tok::less);
+}
+
 bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
                                    LambdaIntroducerTentativeParse *Tentative) {
   if (Tentative)
diff --git a/clang/test/Parser/ms-lambda-capture.hip 
b/clang/test/Parser/ms-lambda-capture.hip
new file mode 100644
index 0000000000000..414913308c27a
--- /dev/null
+++ b/clang/test/Parser/ms-lambda-capture.hip
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fms-extensions -DMS_EXTENSIONS -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcuda-is-device -fms-extensions -DMS_EXTENSIONS 
-fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcuda-is-device -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// A '[' can begin a Microsoft attribute list under -fms-extensions, but it can
+// also begin a C++ lambda-introducer. A lambda passed to a constructor via 
CTAD
+// must not have its capture list consumed as a (possibly empty) Microsoft
+// attribute, which previously misparsed the initializer as a function
+// declarator under -fms-extensions.
+//
+// A lambda and a Microsoft attribute are told apart by what follows the
+// (possibly attributed) '[...]' introducer. A lambda continues with a 
parameter
+// list or body, whereas a Microsoft attribute is followed by the declaration
+// it appertains to.
+
+#include "Inputs/cuda.h"
+
+template <typename F> struct Wrapper {
+  F f;
+  __host__ Wrapper(F fn) : f(fn) {}
+};
+template <typename F> Wrapper(F) -> Wrapper<F>;
+
+void empty_and_defaults() {
+  Wrapper a([] __device__ () {});
+  Wrapper b([=] __device__ () {});
+  Wrapper c([&] __device__ () {});
+  Wrapper d([] __device__ {});
+  (void)a; (void)b; (void)c; (void)d;
+}
+
+void named_captures() {
+  int values[] = {1};
+  int x = 0;
+  Wrapper a([x] __device__ () { return x; });
+  Wrapper b([&x] __device__ () { x++; });
+  Wrapper c([y = x] __device__ () { return y; });
+  Wrapper d([x, &x2 = x] __device__ () { return x + x2; });
+  Wrapper e([a = values[0], b = [] { return 2; }()] __device__ () {
+    return a + b;
+  });
+  (void)a; (void)b; (void)c; (void)d; (void)e;
+}
+
+struct S {
+  int m = 0;
+  void g() {
+    Wrapper a([this] __device__ () { return m; });
+    Wrapper b([*this] __device__ () { return m; });
+    (void)a; (void)b;
+  }
+};
+
+#ifdef MS_EXTENSIONS
+struct MicrosoftAttributeControls {
+  [] static int empty;
+  [propget] int get();
+};
+#endif

>From ba0657194e4a4aceabd013aef34ea9865d02d793 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Thu, 6 Aug 2026 20:38:03 -0400
Subject: [PATCH 2/4] Recognize c++23 lambda forms

---
 clang/include/clang/Parse/Parser.h      |  3 +--
 clang/lib/Parse/ParseExprCXX.cpp        | 24 +++++++++++++------
 clang/test/Parser/ms-lambda-capture.hip | 32 +++++++++++++++++++++++--
 3 files changed, 48 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index c09d7d7c8ba03..f7249dbb96480 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -4727,8 +4727,7 @@ class Parser : public CodeCompletionHandler {
 
   /// Returns true if the current '[' begins a CUDA/HIP lambda rather than a
   /// Microsoft '[]' attribute (enabled under -fms-extensions or HLSL).
-  /// Restricted to CUDA/HIP, the only mode that allows attributes immediately
-  /// after a lambda's capture list.
+  /// Restricted to CUDA/HIP.
   bool startsLambdaNotMicrosoftAttribute();
 
   /// Parse a lambda introducer.
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f4870df806228..a9c8509ed9abd 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -760,20 +760,30 @@ ExprResult Parser::TryParseLambdaExpression() {
 }
 
 bool Parser::startsLambdaNotMicrosoftAttribute() {
-  // Restricted to CUDA/HIP, the only mode that allows attributes immediately
-  // after a lambda's capture list.
+  // Restricted to CUDA/HIP.
   if (!getLangOpts().CUDA || Tok.isNot(tok::l_square))
     return false;
 
   // Skip the '[...]' and any trailing attributes (e.g. CUDA/HIP's
-  // '__device__'). A lambda then continues with '(', '{' or '<', while an
-  // attribute is followed by the declaration it applies to (e.g. '[propget]
-  // int get()').
+  // '__device__'). A lambda then continues with a parameter list, body,
+  // explicit template parameter list, or lambda declarator. An attribute is
+  // followed by the declaration it applies to (e.g. '[propget] int get()').
   RevertingTentativeParsingAction TPA(*this);
   ConsumeBracket();
-  if (!SkipUntil(tok::r_square, StopAtSemi) || !TrySkipAttributes())
+  if (!SkipUntil(tok::r_square, StopAtSemi | StopAtCodeCompletion) ||
+      !TrySkipAttributes())
     return false;
-  return Tok.isOneOf(tok::l_paren, tok::l_brace, tok::less);
+
+  // C++23 allows omitting '()' before 'mutable', 'constexpr', 'consteval', and
+  // 'static'.
+  while (Tok.isOneOf(tok::kw_mutable, tok::kw_constexpr, tok::kw_consteval,
+                     tok::kw_static))
+    ConsumeToken();
+  if (!TrySkipAttributes())
+    return false;
+
+  return Tok.isOneOf(tok::l_paren, tok::l_brace, tok::less, tok::arrow,
+                     tok::kw_noexcept);
 }
 
 bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
diff --git a/clang/test/Parser/ms-lambda-capture.hip 
b/clang/test/Parser/ms-lambda-capture.hip
index 414913308c27a..7644412fd80fa 100644
--- a/clang/test/Parser/ms-lambda-capture.hip
+++ b/clang/test/Parser/ms-lambda-capture.hip
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -fms-extensions -DMS_EXTENSIONS -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fms-extensions -DMS_EXTENSIONS -fsyntax-only 
-verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fcuda-is-device -fms-extensions -DMS_EXTENSIONS 
-fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fcuda-is-device -fms-extensions -DMS_EXTENSIONS 
-fsyntax-only -verify %s
 // RUN: %clang_cc1 -fcuda-is-device -fsyntax-only -verify %s
 // expected-no-diagnostics
 
@@ -12,8 +14,9 @@
 //
 // A lambda and a Microsoft attribute are told apart by what follows the
 // (possibly attributed) '[...]' introducer. A lambda continues with a 
parameter
-// list or body, whereas a Microsoft attribute is followed by the declaration
-// it appertains to.
+// list, body, template parameter list, or lambda declarator ending in an
+// unambiguous continuation. A Microsoft attribute is followed by the
+// declaration it appertains to.
 
 #include "Inputs/cuda.h"
 
@@ -31,6 +34,26 @@ void empty_and_defaults() {
   (void)a; (void)b; (void)c; (void)d;
 }
 
+#if __cplusplus >= 202302L
+void omitted_parameter_list() {
+  Wrapper a([] __device__ noexcept {});
+  Wrapper b([] __device__ -> int { return 1; });
+  Wrapper c([] __device__ mutable {});
+  Wrapper d([] __device__ constexpr {});
+  Wrapper e([] __device__ consteval {});
+  Wrapper f([] __device__ static {});
+  Wrapper g([] __device__ mutable constexpr noexcept -> int { return 1; });
+  Wrapper h([] __device__ mutable [[]] {});
+  (void)a; (void)b; (void)c; (void)d;
+  (void)e; (void)f; (void)g; (void)h;
+}
+
+void explicit_template_parameter_list() {
+  Wrapper a([] __device__ <typename T>(T x) { return x; });
+  (void)a;
+}
+#endif
+
 void named_captures() {
   int values[] = {1};
   int x = 0;
@@ -57,5 +80,10 @@ struct S {
 struct MicrosoftAttributeControls {
   [] static int empty;
   [propget] int get();
+#if __cplusplus >= 202302L
+  [] mutable int member;
+  [] static constexpr int constant = 1;
+  [] consteval int immediate();
+#endif
 };
 #endif

>From 887c2acab5641cfc2393333dd5f7e56d950352bd Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Thu, 3 Sep 2026 17:54:30 -0400
Subject: [PATCH 3/4] Move check to tentative parsing path

---
 clang/include/clang/Parse/Parser.h           | 16 +++----
 clang/lib/Parse/ParseExprCXX.cpp             | 46 +++++---------------
 clang/lib/Parse/ParseTentative.cpp           | 32 ++++++++++++++
 clang/test/Parser/ms-lambda-capture.clcpp    | 20 +++++++++
 clang/test/Parser/ms-lambda-capture.cpp      | 24 ++++++++++
 clang/test/Parser/ms-lambda-capture.hip      | 13 +++++-
 clang/test/ParserHLSL/ms-lambda-capture.hlsl |  9 ++++
 7 files changed, 116 insertions(+), 44 deletions(-)
 create mode 100644 clang/test/Parser/ms-lambda-capture.clcpp
 create mode 100644 clang/test/Parser/ms-lambda-capture.cpp
 create mode 100644 clang/test/ParserHLSL/ms-lambda-capture.hlsl

diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index f7249dbb96480..ccc576d4789fa 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2339,10 +2339,8 @@ class Parser : public CodeCompletionHandler {
 
   bool MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
     bool AttrsParsed = false;
-    // A '[' may begin a Microsoft attribute or a C++ lambda, so only parse it
-    // as an attribute after confirming it does not start a lambda.
     if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
-        Tok.is(tok::l_square) && !startsLambdaNotMicrosoftAttribute()) {
+        Tok.is(tok::l_square)) {
       ParsedAttributes AttrsWithRange(AttrFactory);
       ParseMicrosoftAttributes(AttrsWithRange);
       AttrsParsed = !AttrsWithRange.empty();
@@ -4725,11 +4723,6 @@ class Parser : public CodeCompletionHandler {
   /// If we are not looking at a lambda expression, returns ExprError().
   ExprResult TryParseLambdaExpression();
 
-  /// Returns true if the current '[' begins a CUDA/HIP lambda rather than a
-  /// Microsoft '[]' attribute (enabled under -fms-extensions or HLSL).
-  /// Restricted to CUDA/HIP.
-  bool startsLambdaNotMicrosoftAttribute();
-
   /// Parse a lambda introducer.
   /// \param Intro A LambdaIntroducer filled in with information about the
   ///        contents of the lambda-introducer.
@@ -4747,6 +4740,9 @@ class Parser : public CodeCompletionHandler {
   /// expression.
   ExprResult ParseLambdaExpressionAfterIntroducer(LambdaIntroducer &Intro);
 
+  /// Whether the current token can begin a lambda specifier sequence.
+  bool isLambdaSpecifier();
+
   
//===--------------------------------------------------------------------===//
   // C++ 5.2p1: C++ Casts
 
@@ -9061,6 +9057,10 @@ class Parser : public CodeCompletionHandler {
   /// full validation of the syntactic structure of attributes.
   bool TrySkipAttributes();
 
+  /// Whether tentative lookahead from the current '[' finds a lambda-like
+  /// continuation. This does not parse or validate a lambda.
+  bool hasLambdaLikeContinuation();
+
   
//===--------------------------------------------------------------------===//
   // C++ 7: Declarations [dcl.dcl]
 
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index a9c8509ed9abd..381291f5e7607 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -759,33 +759,6 @@ ExprResult Parser::TryParseLambdaExpression() {
   return ParseLambdaExpressionAfterIntroducer(Intro);
 }
 
-bool Parser::startsLambdaNotMicrosoftAttribute() {
-  // Restricted to CUDA/HIP.
-  if (!getLangOpts().CUDA || Tok.isNot(tok::l_square))
-    return false;
-
-  // Skip the '[...]' and any trailing attributes (e.g. CUDA/HIP's
-  // '__device__'). A lambda then continues with a parameter list, body,
-  // explicit template parameter list, or lambda declarator. An attribute is
-  // followed by the declaration it applies to (e.g. '[propget] int get()').
-  RevertingTentativeParsingAction TPA(*this);
-  ConsumeBracket();
-  if (!SkipUntil(tok::r_square, StopAtSemi | StopAtCodeCompletion) ||
-      !TrySkipAttributes())
-    return false;
-
-  // C++23 allows omitting '()' before 'mutable', 'constexpr', 'consteval', and
-  // 'static'.
-  while (Tok.isOneOf(tok::kw_mutable, tok::kw_constexpr, tok::kw_consteval,
-                     tok::kw_static))
-    ConsumeToken();
-  if (!TrySkipAttributes())
-    return false;
-
-  return Tok.isOneOf(tok::l_paren, tok::l_brace, tok::less, tok::arrow,
-                     tok::kw_noexcept);
-}
-
 bool Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
                                    LambdaIntroducerTentativeParse *Tentative) {
   if (Tentative)
@@ -1225,6 +1198,16 @@ static void DiagnoseStaticSpecifierRestrictions(Parser 
&P,
   }
 }
 
+bool Parser::isLambdaSpecifier() {
+  return Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
+                     tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
+                     tok::kw___private, tok::kw___global, tok::kw___local,
+                     tok::kw___constant, tok::kw___generic, 
tok::kw_groupshared,
+                     tok::kw_requires, tok::kw_noexcept) ||
+         Tok.isRegularKeywordAttribute() ||
+         (Tok.is(tok::l_square) && NextToken().is(tok::l_square));
+}
+
 ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
                      LambdaIntroducer &Intro) {
   SourceLocation LambdaBeginLoc = Intro.Range.getBegin();
@@ -1370,14 +1353,7 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer(
     HasParentheses = true;
   }
 
-  HasSpecifiers =
-      Tok.isOneOf(tok::kw_mutable, tok::arrow, tok::kw___attribute,
-                  tok::kw_constexpr, tok::kw_consteval, tok::kw_static,
-                  tok::kw___private, tok::kw___global, tok::kw___local,
-                  tok::kw___constant, tok::kw___generic, tok::kw_groupshared,
-                  tok::kw_requires, tok::kw_noexcept) ||
-      Tok.isRegularKeywordAttribute() ||
-      (Tok.is(tok::l_square) && NextToken().is(tok::l_square));
+  HasSpecifiers = isLambdaSpecifier();
 
   if (HasSpecifiers && !HasParentheses && !getLangOpts().CPlusPlus23) {
     // It's common to forget that one needs '()' before 'mutable', an
diff --git a/clang/lib/Parse/ParseTentative.cpp 
b/clang/lib/Parse/ParseTentative.cpp
index c71ce09267f8a..6491baf0af92c 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -796,6 +796,34 @@ bool Parser::TrySkipAttributes() {
   return true;
 }
 
+bool Parser::hasLambdaLikeContinuation() {
+  RevertingTentativeParsingAction TPA(*this);
+  ConsumeBracket();
+  if (!SkipUntil(tok::r_square, StopAtSemi | StopAtCodeCompletion))
+    return false;
+
+  // Consume tokens that could also begin the declaration following a
+  // Microsoft attribute. Require a lambda-like continuation after them.
+  while (true) {
+    if (!TrySkipAttributes())
+      return false;
+
+    if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::less, tok::arrow,
+                    tok::kw_requires, tok::kw_noexcept))
+      return true;
+
+    // CUDA and HIP permit the __noinline__ keyword among attributes after the
+    // capture list. TrySkipAttributes does not recognize this keyword form.
+    if (getLangOpts().CUDA && TryConsumeToken(tok::kw___noinline__))
+      continue;
+
+    if (!isLambdaSpecifier())
+      return false;
+
+    ConsumeToken();
+  }
+}
+
 Parser::TPResult Parser::TryParsePtrOperatorSeq() {
   while (true) {
     if (TryAnnotateOptionalCXXScopeToken(true))
@@ -1782,6 +1810,10 @@ Parser::TPResult 
Parser::TryParseParameterDeclarationClause(
         CXX11AttributeKind::NotAttributeSpecifier)
       return TPResult::True;
 
+    if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
+        Tok.is(tok::l_square) && hasLambdaLikeContinuation())
+      return TPResult::False;
+
     ParsedAttributes attrs(AttrFactory);
     MaybeParseMicrosoftAttributes(attrs);
 
diff --git a/clang/test/Parser/ms-lambda-capture.clcpp 
b/clang/test/Parser/ms-lambda-capture.clcpp
new file mode 100644
index 0000000000000..21eaa96341565
--- /dev/null
+++ b/clang/test/Parser/ms-lambda-capture.clcpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spir64-unknown-unknown -cl-std=clc++2021 \
+// RUN:   -fms-extensions -pedantic -fsyntax-only -verify %s
+
+kernel void address_space_qualifiers() {
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto private_lambda([] __private {});
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto global_lambda([] __global {});
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto local_lambda([] __local {});
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto constant_lambda([] __constant {});
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto generic_lambda([] __generic {});
+  // expected-warning@+2 {{an attribute specifier sequence in this position is 
a C++23 extension}}
+  // expected-warning@+1 {{lambda without a parameter clause is a C++23 
extension}}
+  auto attributed_lambda([] [[]] __private {});
+}
+
+int microsoft_attribute_control([] __global int *);
diff --git a/clang/test/Parser/ms-lambda-capture.cpp 
b/clang/test/Parser/ms-lambda-capture.cpp
new file mode 100644
index 0000000000000..dd65345d4a211
--- /dev/null
+++ b/clang/test/Parser/ms-lambda-capture.cpp
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -std=c++23 -fms-extensions -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+struct Wrapper {
+  template <typename F> Wrapper(F) {}
+};
+
+void omitted_parameter_list() {
+  Wrapper a([] mutable {});
+  Wrapper b([] constexpr {});
+  Wrapper c([] consteval {});
+  Wrapper d([] static {});
+  Wrapper e([] mutable constexpr noexcept -> int { return 1; });
+  Wrapper f([] mutable [[]] {});
+}
+
+void named_capture() {
+  int x = 0;
+  Wrapper a([x] mutable { return x; });
+}
+
+void microsoft_attribute_control() {
+  Wrapper declaration([propget] int);
+}
diff --git a/clang/test/Parser/ms-lambda-capture.hip 
b/clang/test/Parser/ms-lambda-capture.hip
index 7644412fd80fa..f7ed2c238cbcb 100644
--- a/clang/test/Parser/ms-lambda-capture.hip
+++ b/clang/test/Parser/ms-lambda-capture.hip
@@ -34,6 +34,14 @@ void empty_and_defaults() {
   (void)a; (void)b; (void)c; (void)d;
 }
 
+void interleaved_noinline() {
+  Wrapper a([] __device__ __noinline__ () {});
+  Wrapper b([] __attribute__((device)) __noinline__
+                __attribute__((host)) () {});
+  Wrapper c([] __device__ __noinline__ {});
+  (void)a; (void)b; (void)c;
+}
+
 #if __cplusplus >= 202302L
 void omitted_parameter_list() {
   Wrapper a([] __device__ noexcept {});
@@ -44,8 +52,11 @@ void omitted_parameter_list() {
   Wrapper f([] __device__ static {});
   Wrapper g([] __device__ mutable constexpr noexcept -> int { return 1; });
   Wrapper h([] __device__ mutable [[]] {});
+  Wrapper i([] __device__ __noinline__ mutable constexpr noexcept -> int {
+    return 1;
+  });
   (void)a; (void)b; (void)c; (void)d;
-  (void)e; (void)f; (void)g; (void)h;
+  (void)e; (void)f; (void)g; (void)h; (void)i;
 }
 
 void explicit_template_parameter_list() {
diff --git a/clang/test/ParserHLSL/ms-lambda-capture.hlsl 
b/clang/test/ParserHLSL/ms-lambda-capture.hlsl
new file mode 100644
index 0000000000000..5fb7906de021a
--- /dev/null
+++ b/clang/test/ParserHLSL/ms-lambda-capture.hlsl
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -o - \
+// RUN:   -fsyntax-only %s -verify
+
+void lambda_direct_initializer() {
+  // expected-warning@#lambda {{lambdas are a clang HLSL extension}}
+  // expected-warning@#lambda {{lambda without a parameter clause is a C++23 
extension}}
+  // expected-warning@#lambda {{static lambdas are a C++23 extension}}
+  int value([] static { return 1; }()); // #lambda
+}

>From 5d7473318687148379f48056043d6b9a7457d618 Mon Sep 17 00:00:00 2001
From: Vang Thao <[email protected]>
Date: Tue, 15 Sep 2026 15:35:40 -0400
Subject: [PATCH 4/4] Add release note, improve tests

---
 clang/docs/ReleaseNotes.md              |  4 ++++
 clang/test/Parser/ms-lambda-capture.cpp |  5 +++--
 clang/test/Parser/ms-lambda-capture.hip | 21 ++++-----------------
 3 files changed, 11 insertions(+), 19 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d719114b5cff6..7687ffc2b462b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -558,6 +558,10 @@ features cannot lower the translation-unit ABI level;
 
 #### Bug Fixes to C++ Support
 
+- Fixed lambdas with specifiers or attributes after the capture list being
+  misparsed as function declarations in direct-initialization contexts under
+  `-fms-extensions` or in HLSL mode.
+
 - Fixed false-positive module ODR diagnostics when a type is found through a
   using-declaration in one definition and directly in another. ODR hashing also
   now distinguishes differently qualified uses of types found through
diff --git a/clang/test/Parser/ms-lambda-capture.cpp 
b/clang/test/Parser/ms-lambda-capture.cpp
index dd65345d4a211..58e535df75f02 100644
--- a/clang/test/Parser/ms-lambda-capture.cpp
+++ b/clang/test/Parser/ms-lambda-capture.cpp
@@ -19,6 +19,7 @@ void named_capture() {
   Wrapper a([x] mutable { return x; });
 }
 
-void microsoft_attribute_control() {
-  Wrapper declaration([propget] int);
+void microsoft_attribute_controls() {
+  Wrapper helpstring([helpstring("h")] int);
+  Wrapper adjacent_lists([library_block][idl_quote("x")] int);
 }
diff --git a/clang/test/Parser/ms-lambda-capture.hip 
b/clang/test/Parser/ms-lambda-capture.hip
index f7ed2c238cbcb..ec89256956e57 100644
--- a/clang/test/Parser/ms-lambda-capture.hip
+++ b/clang/test/Parser/ms-lambda-capture.hip
@@ -1,9 +1,8 @@
-// RUN: %clang_cc1 -fms-extensions -DMS_EXTENSIONS -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++23 -fms-extensions -DMS_EXTENSIONS -fsyntax-only 
-verify %s
+// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fcuda-is-device -fms-extensions -DMS_EXTENSIONS 
-fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++23 -fcuda-is-device -fms-extensions -DMS_EXTENSIONS 
-fsyntax-only -verify %s
-// RUN: %clang_cc1 -fcuda-is-device -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fms-extensions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcuda-is-device -fms-extensions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fcuda-is-device -fms-extensions -fsyntax-only 
-verify %s
 // expected-no-diagnostics
 
 // A '[' can begin a Microsoft attribute list under -fms-extensions, but it can
@@ -86,15 +85,3 @@ struct S {
     (void)a; (void)b;
   }
 };
-
-#ifdef MS_EXTENSIONS
-struct MicrosoftAttributeControls {
-  [] static int empty;
-  [propget] int get();
-#if __cplusplus >= 202302L
-  [] mutable int member;
-  [] static constexpr int constant = 1;
-  [] consteval int immediate();
-#endif
-};
-#endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to