[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