[clang] [Clang][Parser] Accept contextual _except in MS compatibility mode (PR #223551)
Yassine Missoum via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 15 11:21:54 PDT 2026
https://github.com/yasster updated https://github.com/llvm/llvm-project/pull/223551
>From 02789db5e1b8f41fb3421df3ffdeb563b5444e19 Mon Sep 17 00:00:00 2001
From: Yassine Missoum <mmissoum at microsoft.com>
Date: Mon, 14 Sep 2026 11:55:01 -0700
Subject: [PATCH 1/3] [Clang][Parser] Accept contextual _except in MS
compatibility mode
Recognize _except as an SEH handler when Microsoft compatibility is enabled, matching the existing _try, _finally and _leave aliases. Keep _except usable as an ordinary identifier outside the handler position.
Add C and C++ parser coverage for mixed spellings, malformed handlers, ordinary identifier uses and rejection without Microsoft compatibility.
Co-authored-by: Adam Glass <adamglass at microsoft.com>
---
clang/include/clang/Parse/Parser.h | 3 ++
clang/lib/Parse/ParseStmt.cpp | 9 ++--
clang/lib/Parse/Parser.cpp | 19 ++++++++
.../Parser/ms-seh-single-underscore-strict.c | 20 +++++++++
clang/test/Parser/ms-seh-single-underscore.c | 44 +++++++++++++++++++
5 files changed, 89 insertions(+), 6 deletions(-)
create mode 100644 clang/test/Parser/ms-seh-single-underscore-strict.c
create mode 100644 clang/test/Parser/ms-seh-single-underscore.c
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6913c42884a36..b3f6be017523c 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_single;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -671,6 +672,8 @@ class Parser : public CodeCompletionHandler {
IdentifierInfo *getSEHExceptKeyword();
+ bool isTokenSEHExcept();
+
/// Whether to skip parsing of function bodies.
///
/// This option can be used, for example, to speed up searches for
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 5e67cd551bff8..4e88710eac19c 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,12 +2679,10 @@ 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);
}
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index da5f23c4ca30e..a9454b19b167b 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -56,6 +56,24 @@ IdentifierInfo *Parser::getSEHExceptKeyword() {
return Ident__except;
}
+bool Parser::isTokenSEHExcept() {
+ if (!Tok.is(tok::identifier))
+ return false;
+
+ const IdentifierInfo *Identifier = Tok.getIdentifierInfo();
+ if (Identifier == getSEHExceptKeyword())
+ return true;
+
+ if (getLangOpts().MSVCCompat) {
+ if (!Ident__except_single)
+ Ident__except_single = PP.getIdentifierInfo("_except");
+ if (Identifier == Ident__except_single)
+ return true;
+ }
+
+ return false;
+}
+
Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
: PP(pp),
PreferredType(&actions.getASTContext(), pp.isCodeCompletionEnabled()),
@@ -548,6 +566,7 @@ void Parser::Initialize() {
nullptr;
Ident__except = nullptr;
+ Ident__except_single = 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
>From c891334299cf89a80bf561719fab32c85eb197a0 Mon Sep 17 00:00:00 2001
From: Yassine Missoum <mmissoum at microsoft.com>
Date: Mon, 14 Sep 2026 17:27:13 -0700
Subject: [PATCH 2/3] [Clang][Parser] Simplify contextual SEH keyword checks
Inline the single-use __except lookup into isTokenSEHExcept, use Ident_except for the single-underscore spelling, and fix formatting in the C++ try-handler parsing path.
---
clang/include/clang/Parse/Parser.h | 4 +---
clang/lib/Parse/ParseStmt.cpp | 6 ++----
clang/lib/Parse/Parser.cpp | 21 ++++++++-------------
3 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index b3f6be017523c..af05afe374a6d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -661,7 +661,7 @@ class Parser : public CodeCompletionHandler {
/// Contextual keywords for Microsoft extensions.
IdentifierInfo *Ident__except;
- IdentifierInfo *Ident__except_single;
+ IdentifierInfo *Ident_except;
std::unique_ptr<CommentHandler> CommentSemaHandler;
@@ -670,8 +670,6 @@ 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 4e88710eac19c..14bea6a1a7948 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2685,8 +2685,7 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc, bool FnTry) {
if (isTokenSEHExcept()) {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHExceptBlock(Loc);
- }
- else {
+ } else {
SourceLocation Loc = ConsumeToken();
Handler = ParseSEHFinallyBlock(Loc);
}
@@ -2697,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 a9454b19b167b..3fed2dead6362 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -48,26 +48,21 @@ class ActionCommentHandler : public CommentHandler {
};
} // end anonymous namespace
-IdentifierInfo *Parser::getSEHExceptKeyword() {
- // __except is accepted as a (contextual) keyword
- if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
- Ident__except = PP.getIdentifierInfo("__except");
-
- return Ident__except;
-}
-
bool Parser::isTokenSEHExcept() {
if (!Tok.is(tok::identifier))
return false;
+ if (!Ident__except && (getLangOpts().MicrosoftExt || getLangOpts().Borland))
+ Ident__except = PP.getIdentifierInfo("__except");
+
const IdentifierInfo *Identifier = Tok.getIdentifierInfo();
- if (Identifier == getSEHExceptKeyword())
+ if (Identifier == Ident__except)
return true;
if (getLangOpts().MSVCCompat) {
- if (!Ident__except_single)
- Ident__except_single = PP.getIdentifierInfo("_except");
- if (Identifier == Ident__except_single)
+ if (!Ident_except)
+ Ident_except = PP.getIdentifierInfo("_except");
+ if (Identifier == Ident_except)
return true;
}
@@ -566,7 +561,7 @@ void Parser::Initialize() {
nullptr;
Ident__except = nullptr;
- Ident__except_single = nullptr;
+ Ident_except = nullptr;
Ident__exception_code = Ident__exception_info = nullptr;
Ident__abnormal_termination = Ident___exception_code = nullptr;
>From 68e6fb575b32d0099237eb2230daa50288ce0b01 Mon Sep 17 00:00:00 2001
From: Yassine Missoum <mmissoum at microsoft.com>
Date: Tue, 15 Sep 2026 11:20:58 -0700
Subject: [PATCH 3/3] [Clang] Document contextual _except support in release
notes
---
clang/docs/ReleaseNotes.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 043a0ddae2a6c..9fbe62eeb9bf1 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -756,6 +756,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
More information about the cfe-commits
mailing list