Author: Yassine Missoum
Date: 2026-09-17T08:52:12-07:00
New Revision: 36763294ef639d8a6a4379d81da653dda1c20686

URL: 
https://github.com/llvm/llvm-project/commit/36763294ef639d8a6a4379d81da653dda1c20686
DIFF: 
https://github.com/llvm/llvm-project/commit/36763294ef639d8a6a4379d81da653dda1c20686.diff

LOG: [Clang][Parser] Accept contextual _except in MS compatibility mode 
(#223551)

Clang already accepts _try, _finally, and _leave under
-fms-compatibility, but rejects the corresponding _except spelling
because __except is recognized contextually by the parser rather than as
a reserved token.

This patch extends the contextual handler check to accept _except in
Microsoft compatibility mode and uses that check consistently in SEH and
C++ try-handler parsing. _except remains an ordinary identifier outside
the handler position, and -fms-extensions alone does not enable the
alias.

The change is limited to parsing; it does not modify SEH lowering, LLVM
IR, optimization, or runtime behavior.

Assisted-by: Claude Opus 5 (via VS Code)

---------

Co-authored-by: Adam Glass <[email protected]>

Added: 
    clang/test/Parser/ms-seh-single-underscore-strict.c
    clang/test/Parser/ms-seh-single-underscore.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/include/clang/Parse/Parser.h
    clang/lib/Parse/ParseStmt.cpp
    clang/lib/Parse/Parser.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 2be9f3bd7eb20..5128254bdea91 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -802,6 +802,11 @@ features cannot lower the translation-unit ABI level;
 
 #### Windows Support
 
+- Clang now accepts ``_except`` as an alias for ``__except`` in SEH handler
+  position when ``-fms-compatibility`` is enabled, matching the existing
+  ``_try``, ``_finally``, and ``_leave`` aliases. ``_except`` remains an 
ordinary
+  identifier outside that context.
+
 - Fixed ``setjmp`` on 32-bit Arm passing the frame pointer, rather than the
   stack pointer as it was on entry to the function, as the frame value the CRT
   stores in the ``jmp_buf``. Clang now uses ``llvm.sponentry`` there, as it

diff  --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 960b3c2485db0..87e8dc9808e5c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -661,6 +661,7 @@ class Parser : public CodeCompletionHandler {
 
   /// Contextual keywords for Microsoft extensions.
   IdentifierInfo *Ident__except;
+  IdentifierInfo *Ident_except;
 
   std::unique_ptr<CommentHandler> CommentSemaHandler;
 
@@ -669,7 +670,7 @@ class Parser : public CodeCompletionHandler {
   /// function call.
   bool CalledSignatureHelp = false;
 
-  IdentifierInfo *getSEHExceptKeyword();
+  bool isTokenSEHExcept();
 
   /// Whether to skip parsing of function bodies.
   ///

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 5e67cd551bff8..14bea6a1a7948 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -611,8 +611,7 @@ StmtResult Parser::ParseSEHTryBlock() {
     return TryBlock;
 
   StmtResult Handler;
-  if (Tok.is(tok::identifier) &&
-      Tok.getIdentifierInfo() == getSEHExceptKeyword()) {
+  if (isTokenSEHExcept()) {
     SourceLocation Loc = ConsumeToken();
     Handler = ParseSEHExceptBlock(Loc);
   } else if (Tok.is(tok::kw___finally)) {
@@ -2680,16 +2679,13 @@ StmtResult 
Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry) {
 
   // Borland allows SEH-handlers with 'try'
 
-  if ((Tok.is(tok::identifier) &&
-       Tok.getIdentifierInfo() == getSEHExceptKeyword()) ||
-      Tok.is(tok::kw___finally)) {
+  if (isTokenSEHExcept() || Tok.is(tok::kw___finally)) {
     // TODO: Factor into common return ParseSEHHandlerCommon(...)
     StmtResult Handler;
-    if(Tok.getIdentifierInfo() == getSEHExceptKeyword()) {
+    if (isTokenSEHExcept()) {
       SourceLocation Loc = ConsumeToken();
       Handler = ParseSEHExceptBlock(Loc);
-    }
-    else {
+    } else {
       SourceLocation Loc = ConsumeToken();
       Handler = ParseSEHFinallyBlock(Loc);
     }
@@ -2700,8 +2696,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation 
TryLoc, bool FnTry) {
                                     TryLoc,
                                     TryBlock.get(),
                                     Handler.get());
-  }
-  else {
+  } else {
     StmtVector Handlers;
 
     // C++11 attributes can't appear here, despite this context seeming

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 60279d5984e14..c86ed6b2ea3f9 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -48,12 +48,25 @@ class ActionCommentHandler : public CommentHandler {
 };
 } // end anonymous namespace
 
-IdentifierInfo *Parser::getSEHExceptKeyword() {
-  // __except is accepted as a (contextual) keyword
+bool Parser::isTokenSEHExcept() {
+  if (!Tok.is(tok::identifier))
+    return false;
+
   if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
     Ident__except = PP.getIdentifierInfo("__except");
 
-  return Ident__except;
+  const IdentifierInfo *Identifier = Tok.getIdentifierInfo();
+  if (Identifier == Ident__except)
+    return true;
+
+  if (getLangOpts().MSVCCompat) {
+    if (!Ident_except)
+      Ident_except = PP.getIdentifierInfo("_except");
+    if (Identifier == Ident_except)
+      return true;
+  }
+
+  return false;
 }
 
 Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
@@ -548,6 +561,7 @@ void Parser::Initialize() {
       nullptr;
 
   Ident__except = nullptr;
+  Ident_except = nullptr;
 
   Ident__exception_code = Ident__exception_info = nullptr;
   Ident__abnormal_termination = Ident___exception_code = nullptr;

diff  --git a/clang/test/Parser/ms-seh-single-underscore-strict.c 
b/clang/test/Parser/ms-seh-single-underscore-strict.c
new file mode 100644
index 0000000000000..c00ede66e9cd7
--- /dev/null
+++ b/clang/test/Parser/ms-seh-single-underscore-strict.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -triple x86_64-pc-windows-msvc -fsyntax-only 
-fms-extensions -verify
+// RUN: %clang_cc1 %s -triple x86_64-pc-windows-msvc -x c++ -fsyntax-only 
-fms-extensions -verify
+
+int _except(int);
+
+int use_except_identifier(int value) {
+  return _except(value);
+}
+
+void double_except(void) {
+  __try {
+  } __except(1) {
+  }
+}
+
+void single_except(void) {
+  __try {
+  } _except(1) { // expected-error {{expected '__except' or '__finally' 
block}} expected-error {{expected ';' after expression}}
+  }
+}
\ No newline at end of file

diff  --git a/clang/test/Parser/ms-seh-single-underscore.c 
b/clang/test/Parser/ms-seh-single-underscore.c
new file mode 100644
index 0000000000000..c3d7067e5a1c9
--- /dev/null
+++ b/clang/test/Parser/ms-seh-single-underscore.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 %s -triple x86_64-pc-windows-msvc -fsyntax-only 
-fms-compatibility -Wmicrosoft -verify
+// RUN: %clang_cc1 %s -triple x86_64-pc-windows-msvc -x c++ -fsyntax-only 
-fms-compatibility -Wmicrosoft -verify
+
+int _except(int);
+
+int use_except_identifier(int value) {
+  int (*handler)(int) = _except;
+  return handler(value) + _except(value);
+}
+
+void single_except(void) {
+  _try {
+    _leave;
+  } _except(1) {
+  }
+}
+
+void single_finally(void) {
+  _try {
+  } _finally {
+  }
+}
+
+void mixed_spellings(void) {
+  _try {
+    __leave;
+  } __except(1) {
+  }
+
+  __try {
+  } _except(1) {
+  }
+
+  __try {
+  } _finally {
+  }
+}
+
+void bad_except(void) {
+  int value;
+
+  _try {
+  } _except(1) value; // expected-error {{expected '{'}} expected-warning 
{{expression result unused}}
+}
\ No newline at end of file


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

Reply via email to