Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[clang][Sema] Diagnose exceptions only in non-dependent context in discarded try/catch/throw blocks #139859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
Loading
from

Conversation

Rajveer100
Copy link
Contributor

Resolves #138939

When enabling --fno-exceptions flag, discarded statements containing try/catch/throw in an independent context can be avoided from being rejected.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 14, 2025
@llvmbot
Copy link
Member

llvmbot commented May 14, 2025

@llvm/pr-subscribers-clang

Author: Rajveer Singh Bharadwaj (Rajveer100)

Changes

Resolves #138939

When enabling --fno-exceptions flag, discarded statements containing try/catch/throw in an independent context can be avoided from being rejected.


Full diff: https://github.com/llvm/llvm-project/pull/139859.diff

3 Files Affected:

  • (modified) clang/lib/Sema/SemaExprCXX.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaStmt.cpp (+2-1)
  • (modified) clang/test/SemaCXX/no-exceptions.cpp (+26-1)
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b2a982e953012..16a4da40eec17 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -854,7 +854,8 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
   // Don't report an error if 'throw' is used in system headers or in an OpenMP
   // target region compiled for a GPU architecture.
   if (!IsOpenMPGPUTarget && !getLangOpts().CXXExceptions &&
-      !getSourceManager().isInSystemHeader(OpLoc) && !getLangOpts().CUDA) {
+      !getSourceManager().isInSystemHeader(OpLoc) && !getLangOpts().CUDA &&
+      !CurContext->isDependentContext()) {
     // Delay error emission for the OpenMP device code.
     targetDiag(OpLoc, diag::err_exceptions_disabled) << "throw";
   }
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index e8c1f8490342a..2a0c8b017e2e0 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -4305,7 +4305,8 @@ StmtResult Sema::ActOnCXXTryBlock(SourceLocation TryLoc, Stmt *TryBlock,
   // Don't report an error if 'try' is used in system headers or in an OpenMP
   // target region compiled for a GPU architecture.
   if (!IsOpenMPGPUTarget && !getLangOpts().CXXExceptions &&
-      !getSourceManager().isInSystemHeader(TryLoc) && !getLangOpts().CUDA) {
+      !getSourceManager().isInSystemHeader(TryLoc) && !getLangOpts().CUDA &&
+      !CurContext->isDependentContext()) {
     // Delay error emission for the OpenMP device code.
     targetDiag(TryLoc, diag::err_exceptions_disabled) << "try";
   }
diff --git a/clang/test/SemaCXX/no-exceptions.cpp b/clang/test/SemaCXX/no-exceptions.cpp
index 097123d3fe523..665d43e260601 100644
--- a/clang/test/SemaCXX/no-exceptions.cpp
+++ b/clang/test/SemaCXX/no-exceptions.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
 
 // Various tests for -fno-exceptions
 
@@ -32,3 +32,28 @@ void g() {
 }
 
 }
+
+namespace test2 {
+template <auto enable> void foo(auto &&Fnc) {
+  if constexpr (enable)
+    try {
+      Fnc();
+    } catch (...) {
+    }
+  else
+    Fnc();
+}
+
+void bar1() {
+  foo<false>([] {});
+}
+
+template <typename T> void foo() {
+  try {
+  } catch (...) {
+  }
+  throw 1;
+}
+void bar2() { foo<int>(); }
+
+}

@Rajveer100
Copy link
Contributor Author

@erichkeane
As expected the following case is being accepted, which shouldn't happen, let me know what you recommend to diagnose this:

template <typename T> void foo() {
  try {
  } catch (...) {
  }
  throw 1;
}
void bar2() { foo<int>(); }

@cor3ntin
Copy link
Contributor

@Rajveer100 I think instead of looking at the context, we should check if the expression (Ex) is instantiation dependent

@erichkeane
Copy link
Collaborator

@Rajveer100 I think instead of looking at the context, we should check if the expression (Ex) is instantiation dependent

I don't think we can just check the expression, the 'throw' itself could be completely fine. Consider:

template<typename T> void foo() {
  throw 1;

The expression is NOT dependent, but we don't wanna diagnose anyway unless it is instantiated.

@erichkeane As expected the following case is being accepted, which shouldn't happen, let me know what you recommend to diagnose this:

template <typename T> void foo() {
  try {
  } catch (...) {
  }
  throw 1;
}
void bar2() { foo<int>(); }

What we will have to do likely is move the checking for this to some piece of common code that is used by TreeTransform for these two. I haven't looked at a good place, but you'll probably see TransformCXXThrowExpr (and an equiv for try) that will call a rebuild/etc type function, which should then call the build (or some variety of a check function).

Also note, even in THOSE cases we have to properly check the decl context, since it could be a partial specialization/instantiation, so please make sure those are tested as well!

@Rajveer100
Copy link
Contributor Author

Rajveer100 commented May 14, 2025

@Rajveer100 I think instead of looking at the context, we should check if the expression (Ex) is instantiation dependent

Checking the expression causes a crash:

Details

PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/lib/clang/21/include -nostdsysteminc -fsyntax-only -verify -std=c++20 /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp
1.	/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp:24:8: current parser token ';'
2.	/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp:22:1: parsing namespace 'test1'
3.	/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp:23:10: parsing function body 'test1::f'
4.	/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp:23:10: in compound statement ('{}')
 #0 0x000000010954d198 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10538d198)
 #1 0x000000010954d71c PrintStackTraceSignalHandler(void*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10538d71c)
 #2 0x000000010954b584 llvm::sys::RunSignalHandlers() (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10538b584)
 #3 0x000000010954dfe8 SignalHandler(int, __siginfo*, void*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10538dfe8)
 #4 0x00000001876af624 (/usr/lib/system/libsystem_platform.dylib+0x1804ab624)
 #5 0x000000010a1ef9cc clang::Expr::isInstantiationDependent() const (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10602f9cc)
 #6 0x000000010d507f54 clang::Sema::BuildCXXThrow(clang::SourceLocation, clang::Expr*, bool) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x109347f54)
 #7 0x000000010d507df0 clang::Sema::ActOnCXXThrow(clang::Scope*, clang::SourceLocation, clang::Expr*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x109347df0)
 #8 0x000000010cd0e954 clang::Parser::ParseThrowExpression() (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108b4e954)
 #9 0x000000010ccec734 clang::Parser::ParseAssignmentExpression(clang::TypeCastState) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108b2c734)
#10 0x000000010ccec620 clang::Parser::ParseExpression(clang::TypeCastState) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108b2c620)
#11 0x000000010cd88a2c clang::Parser::ParseExprStatement(clang::Parser::ParsedStmtContext) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bc8a2c)
#12 0x000000010cd86d74 clang::Parser::ParseStatementOrDeclarationAfterAttributes(llvm::SmallVector<clang::Stmt*, 24u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*, clang::ParsedAttributes&, clang::ParsedAttributes&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bc6d74)
#13 0x000000010cd865e0 clang::Parser::ParseStatementOrDeclaration(llvm::SmallVector<clang::Stmt*, 24u>&, clang::Parser::ParsedStmtContext, clang::SourceLocation*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bc65e0)
#14 0x000000010cd8f1f8 clang::Parser::ParseCompoundStatementBody(bool) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bcf1f8)
#15 0x000000010cd90914 clang::Parser::ParseFunctionStatementBody(clang::Decl*, clang::Parser::ParseScope&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bd0914)
#16 0x000000010cdb078c clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bf078c)
#17 0x000000010cc8d06c clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::Parser::ParsedTemplateInfo&, clang::SourceLocation*, clang::Parser::ForRangeInit*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108acd06c)
#18 0x000000010cdaf884 clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bef884)
#19 0x000000010cdaee10 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108beee10)
#20 0x000000010cdae00c clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bee00c)
#21 0x000000010ccbee98 clang::Parser::ParseInnerNamespace(llvm::SmallVector<clang::Parser::InnerNamespaceInfo, 4u> const&, unsigned int, clang::SourceLocation&, clang::ParsedAttributes&, clang::BalancedDelimiterTracker&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108afee98)
#22 0x000000010ccbe458 clang::Parser::ParseNamespace(clang::DeclaratorContext, clang::SourceLocation&, clang::SourceLocation) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108afe458)
#23 0x000000010cc8baf0 clang::Parser::ParseDeclaration(clang::DeclaratorContext, clang::SourceLocation&, clang::ParsedAttributes&, clang::ParsedAttributes&, clang::SourceLocation*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108acbaf0)
#24 0x000000010cdadaf8 clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bedaf8)
#25 0x000000010cdac010 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108bec010)
#26 0x000000010cc70f38 clang::ParseAST(clang::Sema&, bool, bool) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x108ab0f38)
#27 0x000000010b0dd6a4 clang::ASTFrontendAction::ExecuteAction() (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x106f1d6a4)
#28 0x000000010b0dcf38 clang::FrontendAction::Execute() (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x106f1cf38)
#29 0x000000010aff394c clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x106e3394c)
#30 0x000000010b21832c clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10705832c)
#31 0x00000001041d1b34 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x100011b34)
#32 0x00000001041c3700 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x100003700)
#33 0x00000001041c2424 clang_main(int, char**, llvm::ToolContext const&) (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x100002424)
#34 0x00000001041fcf0c main (/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang-21+0x10003cf0c)
#35 0x00000001872d6b4c 
/Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/tools/clang/test/SemaCXX/Output/no-exceptions.cpp.script: line 1: 31304 Segmentation fault: 11  /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/build/lib/clang/21/include -nostdsysteminc -fsyntax-only -verify -std=c++20 /Users/rajveersingh/GitHub-OpenSource/llvm-project/llvm-project/clang/test/SemaCXX/no-exceptions.cpp

--

********************
********************
Failed Tests (1):
  Clang :: SemaCXX/no-exceptions.cpp


Testing Time: 7.40s

Total Discovered Tests: 1
  Failed: 1 (100.00%)

What we will have to do likely is move the checking for this to some piece of common code that is used by TreeTransform for these two. I haven't looked at a good place, but you'll probably see TransformCXXThrowExpr (and an equiv for try) that will call a rebuild/etc type function, which should then call the build (or some variety of a check function).

Also note, even in THOSE cases we have to properly check the decl context, since it could be a partial specialization/instantiation, so please make sure those are tested as well!

I will have a look.

…scarded `try/catch/throw` blocks

Resolves llvm#138939

When enabling `--fno-exceptions` flag, discarded statements containing
`try/catch/throw` in an independent context can be avoided from being
rejected.
@Rajveer100 Rajveer100 force-pushed the try-throw-discarded-no-exceptions branch from 552e394 to 46a9b01 Compare May 16, 2025 12:03
@Rajveer100
Copy link
Contributor Author

Rajveer100 commented May 16, 2025

Moving the checks does work for these limited cases, but now that we don't have them there, one of the old tests now doesn't show up the diagnosis:

void f() {
  throw;
}

void g() {
  try {
    f();
  } catch (...) {
  }
}

Since there is a separate call:

return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.get(), Handlers);

and debugging under lldb, breakpoints don't reach (i.e exit early) the transform calls.

@erichkeane
Copy link
Collaborator

Moving the checks does work for these limited cases, but now that we don't have them there, one of the old tests now doesn't show up the diagnosis:

void f() {
  throw;
}

void g() {
  try {
    f();
  } catch (...) {
  }
}

Since there is a separate call:

return Actions.ActOnCXXTryBlock(TryLoc, TryBlock.get(), Handlers);

and debugging under lldb, breakpoints don't reach (i.e exit early) the transform calls.

Right, 'transform' only happens during template instantiation. So we need to find 'some place' (that is a seperate function) to do these diagnostics. I might suggest doing a new one, since there doesn't seem to be a sensible place to do so otherwise... perhaps something like:

void Sema::DiagnoseExceptionUse(bool isTry) {
// do the checking + diagnostic here, but have it check the decl-context for dependence
}

NOTE I had that return void instead of bool. (And is Diagnose instead of Check). I wonder if there is value to continuing (@AaronBallman??) and putting these in the AST anyway? The continued checking is perhaps valuable, and tooling might appreciate it in the AST anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang++ rejects try-catch in discarded if constexpr branch with -fno-exceptions
4 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.