[clang] [Clang][Parser] Accept contextual _except in MS compatibility mode (PR #223551)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 14 15:49:34 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Yassine Missoum (yasster)
<details>
<summary>Changes</summary>
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.
Tests
Adds C and C++ regression coverage for:
Single-underscore and mixed SEH spellings.
Ordinary uses of _except as an identifier.
Malformed-handler diagnostics.
Rejection without -fms-compatibility, while preserving __except support.
During initial validation, the patched Clang was built on Windows with assertions enabled. Both new tests and three neighboring SEH/Microsoft-extension parser tests passed: 5/5 lit tests. The full Clang test suite has not been run, and subsequent test edits have not been revalidated.
Assisted-by: Claude Opus 5 (via VS Code)
---
Full diff: https://github.com/llvm/llvm-project/pull/223551.diff
5 Files Affected:
- (modified) clang/include/clang/Parse/Parser.h (+3)
- (modified) clang/lib/Parse/ParseStmt.cpp (+3-6)
- (modified) clang/lib/Parse/Parser.cpp (+19)
- (added) clang/test/Parser/ms-seh-single-underscore-strict.c (+20)
- (added) clang/test/Parser/ms-seh-single-underscore.c (+44)
``````````diff
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6913c42884a367..b3f6be017523c1 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 5e67cd551bff8b..4e88710eac19c5 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 da5f23c4ca30e9..a9454b19b167b3 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 00000000000000..c00ede66e9cd7d
--- /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 00000000000000..c3d7067e5a1c9f
--- /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
``````````
</details>
https://github.com/llvm/llvm-project/pull/223551
More information about the cfe-commits
mailing list