[clang] [clang][bytecode] Support compile-time exceptions (PR #189410)

Yanzuo Liu via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 4 07:48:31 PDT 2026


================
@@ -2096,6 +2115,142 @@ static void compileFunction(InterpState &S, const Function *Func) {
       .compileFunc(Definition, const_cast<Function *>(Func));
 }
 
+// We have a saved thrown value in InterpState.
+// Now try to catch that value in the current function.
+// If no corresponding catch handler is found, jump to the
+// AfterRet op at the end of the function.
+static bool catchException(InterpState &S, CodePtr OpPC) {
+  assert(S.getContext().ExceptionsEnabled);
+  assert(S.ThrownValue);
+  assert(!S.ThrownValue->Caught);
+
+  // We've reached the bottom frame. We can't go any higher, so diagnose
+  // an uncaught exception.
+  const Function *CurrFunction = S.Current->getFunction();
+  if (!CurrFunction) {
+    assert(S.Current->isBottomFrame());
+    if (!S.checkingPotentialConstantExpression())
+      return diagnoseUncaughtException(S, OpPC);
+    return false;
+  }
+
+  unsigned CodeOffset = S.PC - CurrFunction->getCodeBegin();
+  std::optional<ExceptionTableEntry> CatchEntry =
+      CurrFunction->findCatchHandler(CodeOffset, S.ThrownValue->Ty,
+                                     S.getASTContext());
+
+  if (!CatchEntry) {
+    // We didn't find an appropriate catch handler in the current function.
+    // Skip to the end of the function.
+    bool CanThrow = S.Current->getFunction()
+                        ->getDecl()
+                        ->getType()
+                        ->getAs<FunctionProtoType>()
+                        ->canThrow();
+    if (!CanThrow) {
+      S.CCEDiag(S.Current->getSource(OpPC),
+                diag::note_constexpr_exception_in_noexcept_func);
+      return false;
+    }
+    // Jump to the end of the function. The calling function will handle
+    // catching the exception.
+    S.PC = S.Current->getFunction()->getCodeEnd() - align(sizeof(Opcode));
+#ifndef NDEBUG
+    CodePtr PCCopy = S.PC;
+    Opcode Op = PCCopy.read<Opcode>();
+    assert(Op == OP_AfterRet);
+#endif
+    return true;
+  }
+
+  // We *did* find a catch handler. We now need to cast the thrown value to
+  // the correct type, if necessary.
+
+  // NB: CaughtType may be null (for catch-all handlers).
+  const Type *CaughtType = CatchEntry->CatchType;
+  const Type *ThrownType = S.ThrownValue->Ty;
+  assert(ThrownType);
+
+  // There might be some values left on the stack that have been added in
+  // between entering the try{} block and the throw statement. We need to
+  // remove all of those so the stack is in a proper state after the catch
+  // handler finishes.
+  while (S.Stk.size() != S.ThrowTrapStackSize) {
+    S.Stk.discardSlow();
+  }
+
+  if (CaughtType && CaughtType->isPointerOrReferenceType())
+    CaughtType = CaughtType->getPointeeType().getTypePtr();
+  if (ThrownType->isPointerOrReferenceType())
+    ThrownType = ThrownType->getPointeeType().getTypePtr();
+
+  bool NeedsCast = CaughtType &&
+                   !ASTContext::hasSameType(CaughtType, ThrownType) &&
+                   CaughtType->isRecordType() && ThrownType->isRecordType();
+
----------------
zwuis wrote:

`S.ThrownValue->CastOffset` should be reset after rethrowing exceptions.

<details>
<summary>Reproducer</summary>

```cpp
struct A { int a = 1; };
struct B { int b = 2; };
struct C : A, B { int c = 3;};
constexpr int f() try {
  try {
    throw C{};
  } catch (B &) {
    throw;
  };
} catch (C.c) {
  return c.c;
}
static_assert(f() == 3);
```

</details>

https://github.com/llvm/llvm-project/pull/189410


More information about the cfe-commits mailing list