From 2e34318cd3fbffedbd231239d9f99a0907442970 Mon Sep 17 00:00:00 2001 From: GkvJwa Date: Thu, 18 Dec 2025 00:30:02 +0800 Subject: [PATCH 1/3] Add test and skip Borland --- clang/lib/Sema/SemaStmt.cpp | 70 +++++++++++++++++++++++++++ clang/test/SemaCXX/exceptions-seh.cpp | 30 ++++++++++++ 2 files changed, 100 insertions(+) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1b1643250d05e..c562d15b0338c 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -4515,6 +4515,57 @@ void Sema::DiagnoseExceptionUse(SourceLocation Loc, bool IsTry) { targetDiag(Loc, diag::err_exceptions_disabled) << (IsTry ? "try" : "throw"); } +// Walk the statement subtree and return the first statement that +// contains a non-trivial C++ object that would require destruction at +// scope exit, or nullptr if none was found. +static const Stmt *findNonTrivialObject(Sema &S, const Stmt *Node) { + (void)S; + if (!Node) + return nullptr; + + // Check for declarations of local variables with non-trivial destructors. + if (const DeclStmt *DS = dyn_cast(Node)) { + for (const Decl *D : DS->decls()) { + if (const VarDecl *VD = dyn_cast(D)) { + QualType T = VD->getType(); + if (const RecordType *RT = T->getAs()) { + if (const CXXRecordDecl *RD = RT->getAsCXXRecordDecl()) { + if (RD->hasDefinition() && !RD->hasTrivialDestructor()) + return DS; + } + } + } + } + } + + // Check for expressions that materialize temporaries or otherwise + // produce prvalue/xvalue C++ objects that will require destruction. + if (const Expr *E = dyn_cast(Node)) { + // A throw-expression creates an exception object as part of throwing and + // doesn't create a temporary that lives until the end of the handler + // scope; ignore it when checking for non-trivial temporaries. + if (isa(E)) + return nullptr; + + QualType T = E->getType(); + if (T->isRecordType() && E->getValueKind() != VK_LValue) { + if (const RecordType *RT = T->getAs()) { + if (const CXXRecordDecl *RD = RT->getAsCXXRecordDecl()) { + if (RD->hasDefinition() && !RD->hasTrivialDestructor()) + return E; + } + } + } + } + + // Recurse into children. + for (const Stmt *Child : Node->children()) + if (const Stmt *SWith = findNonTrivialObject(S, Child)) + return SWith; + + return nullptr; +} + StmtResult Sema::ActOnSEHTryBlock(bool IsCXXTry, SourceLocation TryLoc, Stmt *TryBlock, Stmt *Handler) { assert(TryBlock && Handler); @@ -4535,6 +4586,17 @@ StmtResult Sema::ActOnSEHTryBlock(bool IsCXXTry, SourceLocation TryLoc, FSI->setHasSEHTry(TryLoc); + // Disallow non-trivial C++ objects in an SEH __try block as well. If the + // try block contains temporaries or local objects with non-trivial + // destructors, emit the same diagnostic and fail parsing the try. Skip + // this diagnostic when Borland extensions are enabled. + if (!getLangOpts().Borland) { + if (const Stmt *Offending = findNonTrivialObject(*this, TryBlock)) { + return StmtError( + Diag(Offending->getBeginLoc(), diag::err_seh_try_outside_functions)); + } + } + // Reject __try in Obj-C methods, blocks, and captured decls, since we don't // track if they use SEH. DeclContext *DC = CurContext; @@ -4562,6 +4624,14 @@ StmtResult Sema::ActOnSEHExceptBlock(SourceLocation Loc, Expr *FilterExpr, Diag(FilterExpr->getExprLoc(), diag::err_filter_expression_integral) << FTy); } + // Disallow non-trivial C++ objects in an SEH __except handler. Skip + // this diagnostic when Borland extensions are enabled. + if (!getLangOpts().Borland) { + if (const Stmt *Offending = findNonTrivialObject(*this, Block)) { + return StmtError( + Diag(Offending->getBeginLoc(), diag::err_seh_try_outside_functions)); + } + } return SEHExceptStmt::Create(Context, Loc, FilterExpr, Block); } diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp index 02bb786160dcf..0a008aeb5721e 100644 --- a/clang/test/SemaCXX/exceptions-seh.cpp +++ b/clang/test/SemaCXX/exceptions-seh.cpp @@ -126,3 +126,33 @@ void instantiate_dependent_filter() { dependent_filter(); dependent_filter(); // expected-note {{requested here}} } + +int puts(const char *); +class CheckError { +public: + static CheckError Check(const char* msg); + + ~CheckError(); +}; + +int foo__try(const int* f) { + int e; + __try { + CheckError::Check("null pointer"); // expected-error{{cannot use SEH '__try' in blocks}} + e = *f; + } __except (1) { + puts("Caught a C-based exception."); + } + return e; +} + +int foo__except(const int* f) { + int e; + __try { + puts("null pointer"); + e = *f; + } __except (1) { + CheckError::Check("Caught a C-based exception."); // expected-error{{cannot use SEH '__try' in blocks}} + } + return e; +} \ No newline at end of file From 7c12341708516032754e2f5a68518e1efaeb1654 Mon Sep 17 00:00:00 2001 From: GkvJwa Date: Thu, 18 Dec 2025 00:33:23 +0800 Subject: [PATCH 2/3] Add throw --- clang/test/SemaCXX/exceptions-seh.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp index 0a008aeb5721e..5c6222ab63551 100644 --- a/clang/test/SemaCXX/exceptions-seh.cpp +++ b/clang/test/SemaCXX/exceptions-seh.cpp @@ -155,4 +155,16 @@ int foo__except(const int* f) { CheckError::Check("Caught a C-based exception."); // expected-error{{cannot use SEH '__try' in blocks}} } return e; +} + +// has throw should work +int foo__except_with_throw(const int* f) { + int e; + __try { + puts("null pointer"); + e = *f; + } __except (1) { + throw(CheckError::Check("Caught a C-based exception.")); + } + return e; } \ No newline at end of file From ac93f64132b69e974983c9d5a5a741807d02afca Mon Sep 17 00:00:00 2001 From: GkvJwa Date: Thu, 18 Dec 2025 00:34:19 +0800 Subject: [PATCH 3/3] Add space --- clang/test/SemaCXX/exceptions-seh.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp index 5c6222ab63551..bdc369ff767da 100644 --- a/clang/test/SemaCXX/exceptions-seh.cpp +++ b/clang/test/SemaCXX/exceptions-seh.cpp @@ -167,4 +167,4 @@ int foo__except_with_throw(const int* f) { throw(CheckError::Check("Caught a C-based exception.")); } return e; -} \ No newline at end of file +}