[clang] 3676329 - [Clang][Parser] Accept contextual _except in MS compatibility mode (#223551)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 17 08:52:19 PDT 2026
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 <adamglass at microsoft.com>
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
More information about the cfe-commits
mailing list