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

>From b9cad98ffd72c3de964d46cbcb16de84227dae7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Tue, 10 Feb 2026 16:06:17 +0100
Subject: [PATCH] Exceptions

---
 clang/include/clang/Basic/Builtins.td         |  11 +
 .../include/clang/Basic/DiagnosticASTKinds.td |   3 +
 clang/lib/AST/ByteCode/ByteCodeEmitter.cpp    |   3 +-
 clang/lib/AST/ByteCode/ByteCodeEmitter.h      |  10 +
 clang/lib/AST/ByteCode/Compiler.cpp           | 185 +++-
 clang/lib/AST/ByteCode/Disasm.cpp             |   2 +
 clang/lib/AST/ByteCode/EvalEmitter.cpp        |   8 +
 clang/lib/AST/ByteCode/EvalEmitter.h          |  11 +
 clang/lib/AST/ByteCode/Function.h             |  25 +-
 clang/lib/AST/ByteCode/Interp.cpp             | 348 ++++++-
 clang/lib/AST/ByteCode/Interp.h               | 125 ++-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      |  32 +
 clang/lib/AST/ByteCode/InterpFrame.h          |   1 +
 clang/lib/AST/ByteCode/InterpStack.cpp        |   6 +
 clang/lib/AST/ByteCode/InterpStack.h          |   2 +
 clang/lib/AST/ByteCode/InterpState.h          |  23 +
 clang/lib/AST/ByteCode/Opcodes.td             |  38 +-
 clang/lib/AST/ByteCode/PrimType.h             |   9 +
 clang/lib/AST/ByteCode/Source.h               |   2 +
 clang/test/AST/ByteCode/cxx20.cpp             |  15 +-
 clang/test/AST/ByteCode/cxx23.cpp             |   4 +-
 clang/test/AST/ByteCode/exceptions.cpp        | 855 ++++++++++++++++++
 clang/test/AST/ByteCode/invalid.cpp           |  30 +-
 clang/utils/TableGen/ClangOpcodesEmitter.cpp  |  12 +-
 24 files changed, 1685 insertions(+), 75 deletions(-)
 create mode 100644 clang/test/AST/ByteCode/exceptions.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 506f47fb597af..dc23df692fbd7 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5919,3 +5919,14 @@ def CountedByRef : Builtin {
   let Attributes = [NoThrow, CustomTypeChecking];
   let Prototype = "int(...)";
 }
+
+def BuiltinCurrentException: LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_current_exception"];
+  let Attributes = [NoThrow, Constexpr];
+  let Prototype = "void*()";
+}
+def BuiltinUncaughtExceptions: LangBuiltin<"CXX_LANG"> {
+  let Spellings = ["__builtin_uncaught_exceptions"];
+  let Attributes = [NoThrow, Constexpr];
+  let Prototype = "int()";
+}
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index bde418695f647..bd3fefd8c54a3 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -406,6 +406,9 @@ def note_constexpr_infer_alloc_token_no_metadata : Note<
   "could not get token metadata for inferred type">;
 def note_constexpr_infer_alloc_token_stateful_mode
     : Note<"stateful alloc token mode not supported in constexpr">;
+def note_constexpr_uncaught_exception : Note<"uncaught exception of type %0: 
'%1'">;
+
+def note_constexpr_exception_in_noexcept_func : Note<"uncaught exception in 
noexcept function">;
 
 def warn_attribute_needs_aggregate : Warning<
   "%0 attribute is ignored in non-aggregate type %1">,
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp 
b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 393b8481fecd1..17cf076c52851 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -85,7 +85,8 @@ void ByteCodeEmitter::compileFunc(const FunctionDecl 
*FuncDecl,
 
   // Set the function's code.
   Func->setCode(FuncDecl, NextLocalOffset, std::move(Code), std::move(SrcMap),
-                std::move(Scopes), FuncDecl->hasBody(), IsValid);
+                std::move(Scopes), std::move(ExceptionTable),
+                FuncDecl->hasBody(), IsValid);
   Func->setIsFullyCompiled(true);
 }
 
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h 
b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 34342e53837b9..dad8d5cc7e275 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -82,6 +82,14 @@ class ByteCodeEmitter {
   llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors;
   std::optional<SourceInfo> LocOverride = std::nullopt;
 
+  unsigned currentCodeSize() const { return Code.size(); }
+
+  void registerExceptionHandler(unsigned From, unsigned To, unsigned Target,
+                                UnsignedOrNone DeclOffset, const Type *T) {
+    ExceptionTable.push_back(
+        ExceptionTableEntry{From, To, Target, DeclOffset, T});
+  }
+
 private:
   /// Current compilation context.
   Context &Ctx;
@@ -97,9 +105,11 @@ class ByteCodeEmitter {
   llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs;
   /// Program code.
   llvm::SmallVector<std::byte> Code;
+  llvm::SmallVector<ExceptionTableEntry> ExceptionTable;
   /// Opcode to expression mapping.
   SourceMap SrcMap;
 
+public:
   /// Returns the offset for a jump or records a relocation.
   int32_t getOffset(LabelTy Label);
 
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 95f9a2f507335..cf89c0fbc6598 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -37,6 +37,17 @@ static std::optional<bool> getBoolValue(const Expr *E) {
   return std::nullopt;
 }
 
+[[maybe_unused]] static bool blockEndsInReturn(const Stmt *S) {
+  if (isa<ReturnStmt>(S))
+    return true;
+
+  if (const auto *CS = dyn_cast<CompoundStmt>(S); CS && !CS->body_empty()) {
+    return isa<ReturnStmt>(CS->body_back());
+  }
+
+  return false;
+}
+
 /// Scope used to handle temporaries in toplevel variable declarations.
 template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
 public:
@@ -3471,10 +3482,141 @@ bool Compiler<Emitter>::VisitPredefinedExpr(const 
PredefinedExpr *E) {
 
 template <class Emitter>
 bool Compiler<Emitter>::VisitCXXThrowExpr(const CXXThrowExpr *E) {
-  if (E->getSubExpr() && !this->discard(E->getSubExpr()))
+  const Expr *SubExpr = E->getSubExpr();
+  if (!Ctx.getLangOpts().CPlusPlus26 || !Ctx.getLangOpts().CXXExceptions) {
+    if (SubExpr && !this->discard(SubExpr))
+      return false;
+    return this->emitInvalid(E);
+  }
+
+  assert(E->getSubExpr()); // XXX
+
+  QualType ExceptionType = SubExpr->getType();
+  OptPrimType ExceptionT = classify(SubExpr);
+
+  const Descriptor *Desc;
+  if (ExceptionT)
+    Desc = P.createDescriptor(SubExpr, *ExceptionT);
+  else
+    Desc = P.createDescriptor(SubExpr, ExceptionType.getTypePtr(), 
std::nullopt,
+                              /*IsConst=*/false);
+
+  if (!this->emitAllocException(Desc, E))
+    return false;
+
+  if (ExceptionT) {
+    if (!this->visit(SubExpr))
+      return false;
+    if (!this->emitInit(*ExceptionT, E))
+      return false;
+  } else {
+    if (!this->visitInitializer(SubExpr))
+      return false;
+  }
+
+  OptPrimType T = classify(E->getSubExpr()->getType());
+  llvm::errs() << "SubExpr type:\n";
+  SubExpr->getType()->dump();
+  llvm::errs() << "T? " << (bool)T << '\n';
+  if (!this->emitSaveException(T.value_or(PT_Ptr), ExceptionType.getTypePtr(), 
(bool)T, E))
+    return false;
+
+  this->VarScope->destroyLocals();
+
+  return this->emitThrow(ExceptionType.getTypePtr(), E);
+}
+
+template <class Emitter>
+bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
+  if (!Ctx.getLangOpts().CPlusPlus26 || !Ctx.getLangOpts().CXXExceptions) {
+    // Ignore all handlers.
+    return this->visitStmt(S->getTryBlock());
+  }
+
+  unsigned NumHandlers = S->getNumHandlers();
+
+  if (!this->emitThrowTrap(S))
+    return false;
+
+  unsigned s = this->currentCodeSize();
+
+  // Emit try block contents.
+  const auto *TryBlock = cast<CompoundStmt>(S->getTryBlock());
+  if (!this->visitStmt(TryBlock))
     return false;
 
-  return this->emitInvalid(E);
+  unsigned e = this->currentCodeSize();
+
+  // Jump after handlers if nothing was thrown.
+  LabelTy EndLabel = this->getLabel();
+  this->jump(EndLabel, S);
+
+  // Register and emit all handlers.
+  for (unsigned I = 0; I != NumHandlers; ++I) {
+    const CXXCatchStmt *Handler = S->getHandler(I);
+    const Stmt *HandlerBlock = Handler->getHandlerBlock();
+    const VarDecl *ExceptionDecl = Handler->getExceptionDecl();
+    QualType CatchType = Handler->getCaughtType();
+    UnsignedOrNone ExceptionDeclOffset = std::nullopt;
+
+    unsigned t = this->currentCodeSize();
+    if (ExceptionDecl) {
+      if (OptPrimType T = classify(CatchType)) {
+        unsigned LocalOffset = allocateLocalPrimitive(ExceptionDecl, *T,
+                                                      /*IsConst=*/true);
+        if (CatchType->isReferenceType()) {
+          if (!this->emitGetPtrExceptionValue(S))
+            return false;
+        } else {
+          if (!this->emitGetExceptionValue(*T, S))
+            return false;
+        }
+        if (!this->emitSetLocal(*T, LocalOffset, S))
+          return false;
+      } else {
+        UnsignedOrNone LocalOffset = allocateLocal(ExceptionDecl, CatchType);
+        if (!LocalOffset)
+          return false;
+
+        if (!this->emitGetPtrLocal(*LocalOffset, Handler))
+          return false;
+        if (!this->emitGetPtrExceptionValue(Handler))
+          return false;
+        // if (!this->emitGetExceptionValuePtr(Handler))
+        // return false;
+        if (!this->emitMemcpy(Handler))
+          return false;
+        if (!this->emitPopPtr(Handler))
+          return false;
+      }
+    } else {
+      // This is a catch-all handler.
+      // if (!this->emitClearExceptionValue(S))
+      // return false;
+
+      // FIXME: MOve down
+      if (!this->emitCatch(S))
+        return false;
+    }
+    const Type *CatchTypePtr = CatchType.getTypePtrOrNull();
+
+    this->registerExceptionHandler(s, e, t, ExceptionDeclOffset, CatchTypePtr);
+    if (!this->visitStmt(HandlerBlock))
+      return false;
+
+    if (!this->emitClearExceptionValue(S))
+      return false;
+
+    // FIXME: Re-enable this.
+    // if (blockEndsInReturn(TryBlock))
+    // continue;
+    this->jump(EndLabel, S);
+  }
+
+  this->fallthrough(EndLabel);
+  this->emitLabel(EndLabel);
+
+  return true;
 }
 
 template <class Emitter>
@@ -6877,12 +7019,6 @@ bool Compiler<Emitter>::visitAttributedStmt(const 
AttributedStmt *S) {
   return true;
 }
 
-template <class Emitter>
-bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
-  // Ignore all handlers.
-  return this->visitStmt(S->getTryBlock());
-}
-
 template <class Emitter>
 bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
   assert(MD->isLambdaStaticInvoker());
@@ -7153,8 +7289,17 @@ bool Compiler<Emitter>::compileConstructor(const 
CXXConstructorDecl *Ctor) {
         return false;
     }
 
-    if (!visitStmt(Body))
-      return false;
+    if (isa<CompoundStmt>(Body)) {
+      if (!visitStmt(Body))
+        return false;
+    } else {
+      // direct try {} body.
+      LocalScope<Emitter> Scope(this);
+      if (!visitStmt(Body))
+        return false;
+      if (!Scope.destroyLocals())
+        return false;
+    }
   }
 
   return this->emitRetVoid(SourceInfo{});
@@ -7250,14 +7395,24 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl 
*F) {
   }
 
   // Regular functions.
-  if (const auto *Body = F->getBody())
-    if (!visitStmt(Body))
-      return false;
+  if (const auto *Body = F->getBody()) {
+    if (isa<CompoundStmt>(Body)) {
+      if (!visitStmt(Body))
+        return false;
+    } else {
+      // direct try {} body.
+      LocalScope<Emitter> Scope(this);
+      if (!visitStmt(Body))
+        return false;
+      if (!Scope.destroyLocals())
+        return false;
+    }
+  }
 
   // Emit a guard return to protect against a code path missing one.
   if (F->getReturnType()->isVoidType())
-    return this->emitRetVoid(SourceInfo{});
-  return this->emitNoRet(SourceInfo{});
+    return this->emitRetVoid(SourceInfo{}) && this->emitAfterRet(SourceInfo{});
+  return this->emitNoRet(SourceInfo{}) && this->emitAfterRet(SourceInfo{});
 }
 
 static uint32_t getBitWidth(const Expr *E) {
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp 
b/clang/lib/AST/ByteCode/Disasm.cpp
index 4caf830a0a1b4..d94775767f6fb 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -188,6 +188,8 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS,
     Text.Addr = Addr;
     Text.IsJump = isJumpOpcode(Op);
     Text.CurrentOp = (PC == OpPC);
+    // llvm::errs() << (void*)(*PC) << " / " << (void*)(*OpPC) << ((*OpPC -
+    // *PC)) << '\n';
     switch (Op) {
 #define GET_DISASM
 #include "Opcodes.inc"
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp 
b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index dbec700c80d44..a458ee217ec9f 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -238,6 +238,10 @@ template <PrimType OpType> bool 
EvalEmitter::emitRet(SourceInfo Info) {
   if (!isActive())
     return true;
 
+  if (S.ThrownValue && !S.ThrownValue->Caught) {
+    return diagnoseUncaughtException(S, OpPC);
+  }
+
   using T = typename PrimConv<OpType>::T;
   EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext()));
   return true;
@@ -247,6 +251,10 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo 
Info) {
   if (!isActive())
     return true;
 
+  if (S.ThrownValue && !S.ThrownValue->Caught) {
+    llvm::errs() << "AAAAAHA!\n";
+  }
+
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   // If we're returning a raw pointer, call our callback.
   if (this->PtrCB)
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h 
b/clang/lib/AST/ByteCode/EvalEmitter.h
index f939ef4839a19..ad8c02e255afb 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -62,6 +62,17 @@ class EvalEmitter : public SourceMapper {
 
   virtual ~EvalEmitter();
 
+  unsigned getOffset(LabelTy L) { return 0; }
+  unsigned currentCodeSize() const {
+    llvm_unreachable("Should never be called on EvalEmitter");
+    return 0;
+  }
+
+  void registerExceptionHandler(unsigned From, unsigned To, unsigned Target,
+                                UnsignedOrNone, const Type *T) {
+    llvm_unreachable("Should never be called on EvalEmitter");
+  }
+
   /// Define a label.
   void emitLabel(LabelTy Label);
   /// Create a label.
diff --git a/clang/lib/AST/ByteCode/Function.h 
b/clang/lib/AST/ByteCode/Function.h
index 289bd64124004..beee1b44944e6 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -63,6 +63,15 @@ class Scope final {
   LocalVectorTy Descriptors;
 };
 
+struct ExceptionTableEntry {
+  unsigned CodeStart;
+  unsigned CodeEnd;
+  unsigned Target;
+  UnsignedOrNone DeclOffset;
+  /// If CatchType is nullptr, this is a catch-all handler.
+  const Type *CatchType;
+};
+
 using FunctionDeclTy =
     llvm::PointerUnion<const FunctionDecl *, const BlockExpr *>;
 
@@ -250,6 +259,16 @@ class Function final {
     return ArgSize - (align(primSize(PT_Ptr)) * (hasThisPointer() + hasRVO()));
   }
 
+  unsigned getParamOffset(unsigned ParamIndex) const {
+    return ParamDescriptors[ParamIndex].Offset;
+  }
+
+  PrimType getParamType(unsigned ParamIndex) const {
+    return ParamDescriptors[ParamIndex].T;
+  }
+
+  llvm::SmallVector<ExceptionTableEntry> ExceptionTable;
+
 private:
   /// Construct a function representing an actual function.
   Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
@@ -259,13 +278,15 @@ class Function final {
   /// Sets the code of a function.
   void setCode(FunctionDeclTy Source, unsigned NewFrameSize,
                llvm::SmallVector<std::byte> &&NewCode, SourceMap &&NewSrcMap,
-               llvm::SmallVector<Scope, 2> &&NewScopes, bool NewHasBody,
-               bool NewIsValid) {
+               llvm::SmallVector<Scope, 2> &&NewScopes,
+               llvm::SmallVector<ExceptionTableEntry> &&ExceptionTable,
+               bool NewHasBody, bool NewIsValid) {
     this->Source = Source;
     FrameSize = NewFrameSize;
     Code = std::move(NewCode);
     SrcMap = std::move(NewSrcMap);
     Scopes = std::move(NewScopes);
+    this->ExceptionTable = std::move(ExceptionTable);
     IsValid = NewIsValid;
     HasBody = NewHasBody;
   }
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 4815828adb613..660971f9226dc 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -24,6 +24,7 @@
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include <variant>
 
 using namespace clang;
 using namespace clang::interp;
@@ -234,6 +235,28 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr) {
 
 namespace clang {
 namespace interp {
+
+bool diagnoseUncaughtException(InterpState &S, CodePtr OpPC) {
+  assert(S.ThrownValue);
+  QualType UncaughtType = QualType(S.ThrownValue->Ty, 0);
+  std::string ValString;
+
+  llvm::errs() << "T? " << (bool)S.ThrownValue->T << '\n';
+  llvm::errs() << S.ThrownValue->Ptr << '\n';
+  if (S.ThrownValue->T) {
+    TYPE_SWITCH(*S.ThrownValue->T, {
+      ValString =
+          S.ThrownValue->Ptr.deref<T>().toDiagnosticString(S.getASTContext());
+    });
+  } else {
+      ValString =
+          S.ThrownValue->Ptr.toDiagnosticString(S.getASTContext());
+  }
+  S.FFDiag(S.ThrownValue->ThrowSite, diag::note_constexpr_uncaught_exception)
+      << UncaughtType << ValString;
+  return false;
+}
+
 PRESERVE_NONE static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset,
                               PrimType PT);
 
@@ -852,9 +875,10 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const 
Block *B) {
 // Similarly, for local loads.
 bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) {
   assert(!B->isExtern());
-  const auto &Desc = *reinterpret_cast<const InlineDescriptor *>(B->rawData());
+  const auto &Desc = B->getBlockDesc<const InlineDescriptor>();
   if (!CheckLifetime(S, OpPC, Desc.LifeState, B, AK_Read))
     return false;
+
   if (!Desc.IsInitialized)
     return DiagnoseUninitialized(S, OpPC, /*Extern=*/false, B, AK_Read);
   if (B->getDescriptor()->IsVolatile) {
@@ -1353,7 +1377,7 @@ static bool runRecordDestructor(InterpState &S, CodePtr 
OpPC,
     return false;
 
   S.Stk.push<Pointer>(BasePtr);
-  return Call(S, OpPC, DtorFunc, 0);
+  return Call(S, OpPC, OpPC, DtorFunc, 0);
 }
 
 static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) {
@@ -1863,16 +1887,16 @@ bool CallVar(InterpState &S, CodePtr OpPC, const 
Function *Func,
   S.Current = FrameBefore;
   return false;
 }
-bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
+bool Call(InterpState &S, CodePtr &PC, CodePtr OpPC, const Function *Func,
           uint32_t VarArgSize) {
-
+  // CodePtr OpPC = PC - align(sizeof(uint32_t)) - align(sizeof(void*));
   // C doesn't have constexpr functions.
   if (!S.getLangOpts().CPlusPlus)
-    return Invalid(S, OpPC);
+    return Invalid(S, PC);
 
   assert(Func);
   auto cleanup = [&]() -> bool {
-    cleanupAfterFunctionCall(S, OpPC, Func);
+    cleanupAfterFunctionCall(S, PC, Func);
     return false;
   };
 
@@ -1941,11 +1965,17 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
 
   auto Memory = new char[InterpFrame::allocSize(Func)];
   auto NewFrame = new (Memory) InterpFrame(S, Func, OpPC, VarArgSize);
+  // Func->dump();
+
+  llvm::errs() << ":::::: CALLING " << Func->getName() << '\n';
+
   InterpFrame *FrameBefore = S.Current;
   S.Current = NewFrame;
 
+  bool NowThrownValueBefore = !S.ThrownValue;
   InterpStateCCOverride CCOverride(S, Func->isImmediate());
   bool Success = Interpret(S);
+
   // Remove initializing  block again.
   if (InstancePtrTracked)
     S.InitializingPtrs.pop_back();
@@ -1958,7 +1988,145 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
     return false;
   }
 
-  assert(S.Current == FrameBefore);
+  if (S.ThrownValue && !S.ThrownValue->Caught && NowThrownValueBefore) {
+    // llvm::errs() << "WE HAVE A THROWN VALUE in CALL\n";
+    // If we have a thrown value in the InterpState, it was thrown in the
+    // function we just called (or deeper down in the stack), but not caught. 
We
+    // now need to check if the current function can call it.
+    const Function *CurrFunction = S.Current->getFunction();
+    if (!CurrFunction) {
+      // llvm::errs() << "BUT NO FUNCTION, SO DIAGNOSING\n";
+      assert(S.Current->isBottomFrame());
+      if (!S.checkingPotentialConstantExpression())
+        return diagnoseUncaughtException(S, OpPC);
+      return false;
+    }
+    auto canCatch = [](const Type *CatchType, const Type *ThrowType) -> bool {
+      if (!CatchType || ASTContext::hasSameType(CatchType, ThrowType))
+        return true;
+
+      assert(CatchType);
+
+      // nullptr_t can be caught by any pointer type.
+      if (ThrowType->isNullPtrType() && CatchType->isPointerType())
+        return true;
+
+      // void* can catch all thown pointer types.
+      if (ThrowType->isPointerType() && CatchType->isVoidPointerType())
+        return true;
+
+      if (CatchType->isPointerOrReferenceType())
+        CatchType = CatchType->getPointeeType().getTypePtr();
+      if (ThrowType->isPointerOrReferenceType())
+        ThrowType = ThrowType->getPointeeType().getTypePtr();
+
+      if (CatchType->isRecordType() && ThrowType->isRecordType()) {
+        const CXXRecordDecl *CatchDecl = CatchType->getAsCXXRecordDecl();
+        const CXXRecordDecl *ThrowDecl = ThrowType->getAsCXXRecordDecl();
+        assert(CatchDecl);
+        assert(ThrowDecl);
+
+        if (CatchDecl == ThrowDecl)
+          return true;
+        if (ThrowDecl->isDerivedFrom(CatchDecl))
+          return true;
+      }
+
+      return false;
+    };
+
+    bool Caught = false;
+    unsigned CodeOffset = PC - CurrFunction->getCodeBegin();
+    for (const auto &E : CurrFunction->ExceptionTable) {
+      if (E.CodeStart <= CodeOffset && E.CodeEnd >= CodeOffset &&
+          (canCatch(E.CatchType, S.ThrownValue->Ty))) {
+
+        const Type *CaughtType = E.CatchType;
+        const Type *It = S.ThrownValue->Ty;
+
+        llvm::errs() << "Resetting stack to " << S.ThrowTrapStackSize << '\n';
+        while (S.Stk.size() != S.ThrowTrapStackSize) {
+          S.Stk.discardSlow();
+        }
+
+        bool NeedsCast =
+            CaughtType &&
+            ((CaughtType->isRecordType() && It->isRecordType()) ||
+             (CaughtType->isPointerOrReferenceType() &&
+              CaughtType->getPointeeType()->isRecordType() &&
+              It->isPointerOrReferenceType() &&
+              It->getPointeeType()->isRecordType())
+
+             || (It->isRecordType() && CaughtType->isReferenceType() &&
+                 CaughtType->getPointeeType()->isRecordType()));
+
+        llvm::errs() << "NeedsCast: " << NeedsCast << '\n';
+
+        if (NeedsCast) {
+          llvm::errs() << "CASTING AFTER CALL\n";
+          Pointer ThrownValue = std::get<Pointer>(S.ThrownValue->Value);
+          Pointer CastedValue =
+              ThrownValue; // std::get<Pointer>(S.ThrownValue->Value);
+          llvm::errs() << "ThrownValue: " << ThrownValue << '\n';
+
+          if (CaughtType->isPointerOrReferenceType())
+            CaughtType = CaughtType->getPointeeType().getTypePtr();
+
+          if (It->isPointerOrReferenceType())
+            It = It->getPointeeType().getTypePtr();
+          const Record *CaughtRecord =
+              S.getContext().getRecord(CaughtType->getAsCXXRecordDecl());
+          const Record *ItRecord =
+              S.getContext().getRecord(It->getAsCXXRecordDecl());
+          while (ItRecord != CaughtRecord) {
+            llvm::errs() << It << " / " << CaughtType << '\n';
+            const Record *R =
+                S.getContext().getRecord(It->getAsCXXRecordDecl());
+            assert(R);
+            llvm::errs() << "It record: " << R->getName() << '\n';
+            for (const Record::Base &B : R->bases()) {
+              if (cast<CXXRecordDecl>(ItRecord->getDecl())
+                      ->isDerivedFrom(cast<CXXRecordDecl>(B.Decl))) {
+                llvm::errs() << "yay!\n";
+                llvm::errs() << "Casted Value: " << CastedValue << '\n';
+                CastedValue = CastedValue.atField(B.Offset);
+                llvm::errs() << "Casted Value: " << CastedValue << '\n';
+                It = CastedValue.getType().getTypePtr();
+                ItRecord = S.getContext().getRecord(It->getAsCXXRecordDecl());
+                // return true;
+                break;
+              }
+            }
+          }
+
+          S.ThrownValue->Value = CastedValue;
+          S.ThrownValue->Ptr = CastedValue;
+        }
+
+        llvm::errs() << "AAAAAAAHA! IN " << CurrFunction->getName() << "\n";
+        PC = S.Current->getFunction()->getCodeBegin() + E.Target;
+        llvm::errs() << "Offset now: "
+                     << (PC - S.Current->getFunction()->getCodeBegin()) << 
'\n';
+        Caught = true;
+        break;
+      }
+    }
+
+    if (!Caught) {
+      bool IsNoExcept = S.Current->getFunction()
+                            ->getDecl()
+                            ->getType()
+                            ->getAs<FunctionProtoType>()
+                            ->hasNoexceptExceptionSpec();
+
+      if (IsNoExcept)
+        S.CCEDiag(S.Current->getSource(OpPC),
+                  diag::note_constexpr_exception_in_noexcept_func);
+      PC = S.Current->getFunction()->getCodeEnd() - align(sizeof(Opcode));
+    }
+  }
+
+  // assert(S.Current == FrameBefore);
   return true;
 }
 
@@ -2259,7 +2427,7 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const 
Function *Func,
     }
   }
 
-  if (!Call(S, OpPC, Func, VarArgSize))
+  if (!Call(S, OpPC, OpPC, Func, VarArgSize))
     return false;
 
   // Covariant return types. The return type of Overrider is a pointer
@@ -2301,7 +2469,7 @@ bool CallBI(InterpState &S, CodePtr OpPC, const CallExpr 
*CE,
   return InterpretBuiltin(S, OpPC, CE, BuiltinID);
 }
 
-bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
+bool CallPtr(InterpState &S, CodePtr &PC, CodePtr OpPC, uint32_t ArgSize,
              const CallExpr *CE) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
 
@@ -2360,7 +2528,7 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t 
ArgSize,
   if (F->isVirtual())
     return CallVirt(S, OpPC, F, VarArgSize);
 
-  return Call(S, OpPC, F, VarArgSize);
+  return Call(S, PC, OpPC, F, VarArgSize);
 }
 
 static void startLifetimeRecurse(PtrView Ptr) {
@@ -3120,7 +3288,7 @@ constexpr bool OpReturns(Opcode Op) {
          Op == OP_RetSint64 || Op == OP_RetUint64 || Op == OP_RetIntAP ||
          Op == OP_RetIntAPS || Op == OP_RetBool || Op == OP_RetFixedPoint ||
          Op == OP_RetPtr || Op == OP_RetMemberPtr || Op == OP_RetFloat ||
-         Op == OP_EndSpeculation;
+         Op == OP_EndSpeculation || Op == OP_AfterRet;
 }
 
 #if USE_TAILCALLS
@@ -3263,5 +3431,163 @@ PRESERVE_NONE static bool BCP(InterpState &S, CodePtr 
&RealPC, int32_t Offset,
   return true;
 }
 
+static bool canCatch(const Type *CatchType, const Type *ThrowType) {
+  if (!CatchType || ASTContext::hasSameType(CatchType, ThrowType))
+    return true;
+
+  assert(CatchType);
+
+  // nullptr_t can be caught by any pointer type.
+  if (ThrowType->isNullPtrType() && CatchType->isPointerType())
+    return true;
+
+  // void* can catch all thown pointer types.
+  if (ThrowType->isPointerType() && CatchType->isVoidPointerType())
+    return true;
+
+  if (CatchType->isPointerType() && !ThrowType->isPointerType())
+    return false;
+
+  if (CatchType->isPointerOrReferenceType())
+    CatchType = CatchType->getPointeeType().getTypePtr();
+  if (ThrowType->isPointerOrReferenceType())
+    ThrowType = ThrowType->getPointeeType().getTypePtr();
+
+  if (CatchType == ThrowType)
+    return true;
+
+  if (CatchType->isRecordType() && ThrowType->isRecordType()) {
+    const CXXRecordDecl *CatchDecl = CatchType->getAsCXXRecordDecl();
+    const CXXRecordDecl *ThrowDecl = ThrowType->getAsCXXRecordDecl();
+    assert(CatchDecl);
+    assert(ThrowDecl);
+
+    if (CatchDecl == ThrowDecl)
+      return true;
+
+    if (ThrowDecl->isDerivedFrom(CatchDecl))
+      return true;
+  }
+
+  return false;
+}
+
+bool catchException(InterpState &S, CodePtr OpPC, CodePtr &PC, const Type *Ty) 
{
+  assert(Ty);
+  assert(S.ThrownValue);
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+
+  // Will later be diagnosed in EvalEmitter.
+  if (S.Current->isBottomFrame())
+    return true;
+
+  const auto *Source = cast<CXXThrowExpr>(S.Current->getExpr(OpPC));
+  const Pointer ThrownValue = S.ThrownValue->Ptr;
+  S.ThrownValue = std::nullopt;
+
+  const Function *F = S.Current->getFunction();
+  unsigned CodeOffset = PC - F->getCodeBegin();
+
+  bool Caught = false;
+  const Type *CaughtType = nullptr;
+
+  for (const auto &E : F->ExceptionTable) {
+    if (E.CodeStart <= CodeOffset && E.CodeEnd >= CodeOffset &&
+        canCatch(E.CatchType, Ty)) {
+      PC = S.Current->getFunction()->getCodeBegin() + E.Target;
+      Caught = true;
+      CaughtType = E.CatchType;
+      break;
+    }
+  }
+
+  llvm::errs() << "Caught: " << Caught << '\n';
+
+  // llvm::errs() << S.Stk.size() <<" / " << S.ThrowTrapStackSize << '\n';
+  // while (S.Stk.size() != S.ThrowTrapStackSize) {
+  // S.Stk.discardSlow();
+  // }
+
+  const Type *It = ThrownValue.getType().getTypePtr();
+  bool IsComposite = !ThrownValue.getFieldDesc()->isPrimitive();
+  // bool IsPrimitive = ThrownValue.T;
+  bool NeedsCast =
+      CaughtType &&
+      ((CaughtType->isRecordType() && It->isRecordType()) ||
+       (CaughtType->isPointerOrReferenceType() &&
+        CaughtType->getPointeeType()->isRecordType() &&
+        It->isPointerOrReferenceType() && It->getPointeeType()->isRecordType())
+
+       || (It->isRecordType() && CaughtType->isReferenceType() &&
+           CaughtType->getPointeeType()->isRecordType()));
+
+  llvm::errs() << "NeedsCast: " << NeedsCast << '\n';
+  llvm::errs() << "IsComposite: " << IsComposite << '\n';
+  if (!NeedsCast) {
+    if (!IsComposite) {
+
+      TYPE_SWITCH(ThrownValue.getFieldDesc()->getPrimType(), {
+        S.ThrownValue =
+            ThrowValue(Ty, Source, ThrownValue.deref<T>(), ThrownValue,
+                       ThrownValue.getFieldDesc()->getPrimType());
+      });
+    } else {
+      S.ThrownValue =
+          ThrowValue(Ty, Source, ThrownValue, ThrownValue, std::nullopt);
+    }
+    if (!Caught) {
+      PC = S.Current->getFunction()->getCodeEnd() - align(sizeof(Opcode));
+    }
+    return true;
+  }
+
+  if (CaughtType && CaughtType->isPointerOrReferenceType())
+    CaughtType = CaughtType->getPointeeType().getTypePtr();
+
+  // const Type *It = CaughtType;
+  assert(NeedsCast);
+
+  Pointer CastedValue = ThrownValue;
+  // llvm::errs() << "Comp: " << IsComp << '\n';
+  if (!IsComposite) {
+    // if (CaughtType && CaughtType->isRecordType() && It->isRecordType()) {
+    CastedValue = CastedValue.deref<Pointer>();
+    // llvm::errs() << "WE ARE CASTING\n";
+    It = It->getPointeeType().getTypePtr();
+  }
+
+  const Record *CaughtRecord =
+      S.getContext().getRecord(CaughtType->getAsCXXRecordDecl());
+  const Record *ItRecord = S.getContext().getRecord(It->getAsCXXRecordDecl());
+  while (ItRecord != CaughtRecord) {
+    // llvm::errs() << It << " / " << CaughtType << '\n';
+    const Record *R = S.getContext().getRecord(It->getAsCXXRecordDecl());
+    assert(R);
+    // llvm::errs() << "It record: " << R->getName() << '\n';
+    for (const Record::Base &B : R->bases()) {
+      if (cast<CXXRecordDecl>(ItRecord->getDecl())
+              ->isDerivedFrom(cast<CXXRecordDecl>(B.Decl))) {
+        // llvm::errs() << "yay!\n";
+        // llvm::errs() << "Casted Value: " << CastedValue << '\n';
+        CastedValue = CastedValue.atField(B.Offset);
+        // llvm::errs() << "Casted Value: " << CastedValue << '\n';
+        It = CastedValue.getType().getTypePtr();
+        ItRecord = S.getContext().getRecord(It->getAsCXXRecordDecl());
+        // return true;
+        break;
+      }
+    }
+  }
+  S.ThrownValue =
+      ThrowValue(Ty, Source, CastedValue, CastedValue, PT_Ptr);
+  if (!Caught) {
+    PC = S.Current->getFunction()->getCodeEnd() - align(sizeof(Opcode));
+  }
+
+  return true;
+
+  return true;
+}
+
 } // namespace interp
 } // namespace clang
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 97d9e7cc14e32..723b69ae792f8 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -27,6 +27,7 @@
 #include "InterpStack.h"
 #include "InterpState.h"
 #include "MemberPointer.h"
+#include "Opcode.h"
 #include "PrimType.h"
 #include "Program.h"
 #include "State.h"
@@ -85,6 +86,8 @@ bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
 bool DiagnoseUninitialized(InterpState &S, CodePtr OpPC, bool Extern,
                            const Block *B, AccessKinds AK);
 
+bool diagnoseUncaughtException(InterpState &S, CodePtr OpPC);
+
 /// Checks a direct load of a primitive value from a global or local variable.
 bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
 bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B);
@@ -118,13 +121,13 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr 
OpPC,
 
 bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
              uint32_t VarArgSize);
-bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
+bool Call(InterpState &S, CodePtr &PC, CodePtr OpPC, const Function *Func,
           uint32_t VarArgSize);
 bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
               uint32_t VarArgSize);
 bool CallBI(InterpState &S, CodePtr OpPC, const CallExpr *CE,
             uint32_t BuiltinID);
-bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
+bool CallPtr(InterpState &S, CodePtr &PC, CodePtr OpPC, uint32_t ArgSize,
              const CallExpr *CE);
 bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
 bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index);
@@ -147,6 +150,8 @@ bool isConstexprUnknown(const Block *B);
 bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestType,
                  bool IsReferenceCast);
 
+bool catchException(InterpState &S, CodePtr OpPC, CodePtr &PC, const Type *Ty);
+
 enum class ShiftDir { Left, Right };
 
 /// Checks if the shift operation is legal.
@@ -305,6 +310,64 @@ PRESERVE_NONE inline bool RetVoid(InterpState &S, CodePtr 
&PC) {
   return true;
 }
 
+inline bool Throw(InterpState &S, CodePtr &PC, const Type *Ty) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  CodePtr OpPC;
+  if (PC)
+    OpPC = PC - align(sizeof(Opcode)) - align(sizeof(bool)); // XXX
+  else
+    OpPC = PC;
+
+  return catchException(S, OpPC, PC, Ty);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool SaveException(InterpState &S, CodePtr OpPC, const Type *Ty, bool 
IsPrimitive) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  llvm::errs() << "Primitive: " << IsPrimitive << '\n';
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+
+  const auto *Source = cast<CXXThrowExpr>(S.Current->getExpr(OpPC));
+  if (IsPrimitive) {
+    S.ThrownValue = ThrowValue(Ty, Source, Ptr, Ptr, Name);
+  } else {
+    llvm::errs() << "NOT primitive\n";
+    S.ThrownValue = ThrowValue(Ty, Source, Ptr, Ptr, std::nullopt);
+  }
+  return true;
+}
+
+inline bool ThrowTrap(InterpState &S, CodePtr OpPC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  S.ThrowTrapStackSize = S.Stk.size();
+  llvm::errs() << "Trap now: " << S.ThrowTrapStackSize << '\n';
+
+  return true;
+}
+
+inline bool Catch(InterpState &S, CodePtr OpPC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  assert(S.ThrownValue);
+  S.ThrownValue->Caught = true;
+
+  return true;
+}
+
+/// Allocate memory to store an exception object. This uses the InterpState
+/// allocator, so has the same lifetime as local variables (minus scoping).
+inline bool AllocException(InterpState &S, CodePtr &PC,
+                           const Descriptor *Desc) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+
+  assert(Desc);
+  char *Memory = (char *)S.allocate(sizeof(Block) + Desc->getAllocSize());
+  Block *B = new (Memory) Block(~0u, Desc);
+  B->invokeCtor();
+  S.Stk.push<Pointer>(B);
+  return true;
+}
+
 
//===----------------------------------------------------------------------===//
 // Add, Sub, Mul
 
//===----------------------------------------------------------------------===//
@@ -2384,7 +2447,8 @@ bool Init(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckInit(S, OpPC, Ptr))
     return false;
-  Ptr.initialize();
+  if (Ptr.canBeInitialized())
+    Ptr.initialize();
   new (&Ptr.deref<T>()) T(Value);
   return true;
 }
@@ -2395,7 +2459,8 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckInit(S, OpPC, Ptr))
     return false;
-  Ptr.initialize();
+  if (Ptr.canBeInitialized())
+    Ptr.initialize();
   new (&Ptr.deref<T>()) T(Value);
   return true;
 }
@@ -3416,6 +3481,30 @@ PRESERVE_NONE inline bool NoRet(InterpState &S, CodePtr 
OpPC) {
   S.FFDiag(EndLoc, diag::note_constexpr_no_return);
   return false;
 }
+PRESERVE_NONE inline bool AfterRet(InterpState &S, CodePtr &PC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+
+  if (S.ThrownValue) {
+    while (S.Stk.size() != S.Current->getFrameOffset())
+      S.Stk.discardSlow();
+  }
+
+  assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+
+  if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
+    cleanupAfterFunctionCall(S, PC, S.Current->getFunction());
+
+  if (InterpFrame *Caller = S.Current->Caller) {
+    PC = S.Current->getRetPC();
+    InterpFrame::free(S.Current);
+    S.Current = Caller;
+  } else {
+    InterpFrame::free(S.Current);
+    S.Current = nullptr;
+  }
+
+  return true;
+}
 
 
//===----------------------------------------------------------------------===//
 // NarrowPtr, ExpandPtr
@@ -4100,6 +4189,34 @@ inline bool CheckDestruction(InterpState &S, CodePtr 
OpPC) {
   return CheckDestructor(S, OpPC, Ptr);
 }
 
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetExceptionValue(InterpState &S, CodePtr OpPC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  assert(S.ThrownValue);
+  llvm::errs() << "-> " << std::get<T>(S.ThrownValue->Value) << '\n';
+  S.Stk.push<T>(std::get<T>(S.ThrownValue->Value));
+  S.ThrownValue = std::nullopt;
+  return true;
+}
+
+inline bool GetPtrExceptionValue(InterpState &S, CodePtr OpPC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  assert(S.ThrownValue);
+  llvm::errs() << "-> " << S.ThrownValue->Ptr << '\n';
+  // llvm::errs() << "   " << std::get<Pointer>(S.ThrownValue->Value) << '\n';;
+  S.Stk.push<Pointer>(
+      S.ThrownValue->Ptr); // std::get<T>(S.ThrownValue->Value));
+  S.ThrownValue = std::nullopt;
+  return true;
+}
+
+inline bool ClearExceptionValue(InterpState &S, CodePtr OpPC) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  // assert(S.ThrownValue);
+  S.ThrownValue = std::nullopt;
+  return true;
+}
+
 
//===----------------------------------------------------------------------===//
 // Read opcode arguments
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 73952e032f1eb..93a8448311561 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -4551,6 +4551,33 @@ static bool interp__builtin_ia32_vpdp(InterpState &S, 
CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_current_exception(InterpState &S, CodePtr OpPC,
+                                              const CallExpr *Call) {
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  if (!S.ThrownValue) {
+    S.Stk.push<Pointer>();
+    return true;
+  }
+
+  S.Stk.push<Pointer>(S.ThrownValue->Ptr);
+  return true;
+}
+
+static bool interp__builtin_uncaught_exceptions(InterpState &S, CodePtr OpPC,
+                                                const CallExpr *Call) {
+  // llvm::errs() << "CC: " << S.inConstantContext() << '\n';
+  if (!S.inConstantContext())
+    return false;
+
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  llvm::errs() << "ThrownValue: " << (bool)S.ThrownValue << '\n';
+  if (S.ThrownValue && !S.ThrownValue->Caught)
+    pushInteger(S, 1, Call->getType());
+  else
+    pushInteger(S, 0, Call->getType());
+  return true;
+}
+
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
                       uint32_t BuiltinID) {
   if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -6633,6 +6660,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, 
const CallExpr *Call,
   case X86::BI__builtin_ia32_vpdpbusds256:
   case X86::BI__builtin_ia32_vpdpbusds512:
     return interp__builtin_ia32_vpdp(S, OpPC, Call, true);
+
+  case Builtin::BI__builtin_current_exception:
+    return interp__builtin_current_exception(S, OpPC, Call);
+  case Builtin::BI__builtin_uncaught_exceptions:
+    return interp__builtin_uncaught_exceptions(S, OpPC, Call);
   default:
     S.FFDiag(S.Current->getLocation(OpPC),
              diag::note_invalid_subexpr_in_const_expr)
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h 
b/clang/lib/AST/ByteCode/InterpFrame.h
index 731ffddc5a68d..2257ff660cba4 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -104,6 +104,7 @@ class InterpFrame final : public Frame {
 
   /// Mutates a local variable.
   template <typename T> void setLocal(unsigned Offset, const T &Value) {
+    // assert(localInlineDesc(Offset)->Desc->isPrimitive());
     localRef<T>(Offset) = Value;
     localInlineDesc(Offset)->IsInitialized = true;
     localInlineDesc(Offset)->LifeState = Lifetime::Started;
diff --git a/clang/lib/AST/ByteCode/InterpStack.cpp 
b/clang/lib/AST/ByteCode/InterpStack.cpp
index 461bc35979247..55cf9f5cb7947 100644
--- a/clang/lib/AST/ByteCode/InterpStack.cpp
+++ b/clang/lib/AST/ByteCode/InterpStack.cpp
@@ -95,6 +95,12 @@ void InterpStack::shrink(size_t Size) {
   StackSize -= Size;
 }
 
+void InterpStack::discardSlow() {
+  assert(!empty());
+
+  TYPE_SWITCH(ItemTypes.back(), { discard<T>(); });
+}
+
 void InterpStack::dump() const {
   llvm::errs() << "Items: " << ItemTypes.size() << ". Size: " << size() << 
'\n';
   if (ItemTypes.empty())
diff --git a/clang/lib/AST/ByteCode/InterpStack.h 
b/clang/lib/AST/ByteCode/InterpStack.h
index 6d58f5a24bd77..a353505ad609b 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -58,6 +58,8 @@ class InterpStack final {
     shrink(aligned_size<T>());
   }
 
+  void discardSlow();
+
   /// Returns a reference to the value on the top of the stack.
   template <typename T> T &peek() const {
     assert(!ItemTypes.empty());
diff --git a/clang/lib/AST/ByteCode/InterpState.h 
b/clang/lib/AST/ByteCode/InterpState.h
index 4e4a053d6bbed..0449ea0af4792 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -13,15 +13,22 @@
 #ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H
 #define LLVM_CLANG_AST_INTERP_INTERPSTATE_H
 
+#include "Boolean.h"
+#include "Char.h"
 #include "Context.h"
 #include "DynamicAllocator.h"
 #include "Floating.h"
 #include "Function.h"
+#include "Integral.h"
+#include "IntegralAP.h"
 #include "InterpFrame.h"
 #include "InterpStack.h"
+#include "MemberPointer.h"
+#include "Pointer.h"
 #include "State.h"
 
 namespace clang {
+class CXXThrowExpr;
 namespace interp {
 class Context;
 class SourceMapper;
@@ -39,6 +46,20 @@ enum class EvaluationKind : uint8_t {
   Dtor, /// We're checking for constant destruction of a global variable.
 };
 
+struct ThrowValue {
+  const Type *Ty;
+  const CXXThrowExpr *ThrowSite;
+  AnyPrimType Value;
+  Pointer Ptr;
+  OptPrimType T;
+  bool Caught;
+  explicit ThrowValue(const Type *Ty, const CXXThrowExpr *ThrowSite,
+                      AnyPrimType Value, Pointer Ptr, OptPrimType T,
+                      bool Caught = false)
+      : Ty(Ty), ThrowSite(ThrowSite), Value(Value), Ptr(Ptr), T(T),
+        Caught(Caught) {}
+};
+
 /// Interpreter context.
 class InterpState final : public State, public SourceMapper {
 public:
@@ -177,6 +198,7 @@ class InterpState final : public State, public SourceMapper 
{
   mutable std::optional<llvm::BumpPtrAllocator> Allocator;
 
 public:
+  size_t ThrowTrapStackSize = 0;
   /// Reference to the module containing all bytecode.
   Program &P;
   /// Temporary stack.
@@ -200,6 +222,7 @@ class InterpState final : public State, public SourceMapper 
{
   const unsigned EvalID;
 
   EvaluationKind EvalKind = EvaluationKind::None;
+  std::optional<ThrowValue> ThrownValue = std::nullopt;
 
   /// Things needed to do speculative execution.
   SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index b6190554178f3..04e28a2e324a5 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -142,6 +142,7 @@ class Opcode {
   string Name = "";
   bit CanReturn = 0;
   bit ChangesPC = 0;
+  bit BothPCs = 0;
   bit HasCustomLink = 0;
   bit HasCustomEval = 0;
   bit HasGroup = 0;
@@ -227,10 +228,43 @@ def RetValue : Opcode {
 def NoRet : Opcode {}
 
 
+/// Exceptions
+def AfterRet : Opcode {
+  let CanReturn = 1;
+}
+
+def Throw : Opcode {
+  let Args = [ArgTypePtr];
+  let ChangesPC = 1;
+}
+
+def SaveException : Opcode {
+  let Types = [AllTypeClass];
+  let Args = [ArgTypePtr, ArgBool];
+  let HasGroup = 1;
+}
+
+def GetExceptionValue : Opcode {
+  let Types = [AllTypeClass];
+  let HasGroup = 1;
+}
+
+def GetPtrExceptionValue : Opcode;
+def ClearExceptionValue : Opcode;
+
+def AllocException : Opcode {
+  let Args = [ArgDesc];
+}
+
+def ThrowTrap : Opcode;
+def Catch : Opcode;
+
+
 def Call : Opcode {
   let Args = [ArgFunction, ArgUint32];
+  let ChangesPC = 1;
+  let BothPCs = 1;
 }
-
 def CallVirt : Opcode {
   let Args = [ArgFunction, ArgUint32];
 }
@@ -239,6 +273,8 @@ def CallBI : Opcode { let Args = [ArgCallExpr, ArgUint32]; }
 
 def CallPtr : Opcode {
   let Args = [ArgUint32, ArgCallExpr];
+  let ChangesPC = 1;
+  let BothPCs = 1;
 }
 
 def CallVar : Opcode {
diff --git a/clang/lib/AST/ByteCode/PrimType.h 
b/clang/lib/AST/ByteCode/PrimType.h
index 8f725942fedb9..a3cd1ffd2d7f3 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -17,6 +17,7 @@
 #include <climits>
 #include <cstddef>
 #include <cstdint>
+#include <variant>
 
 namespace clang {
 namespace interp {
@@ -49,6 +50,14 @@ enum PrimType : uint8_t {
   PT_MemberPtr = 14,
 };
 
+// Alias for using any one of our primitive types.
+using AnyPrimType =
+    std::variant<Char<true>, Char<false>, Integral<16, true>,
+                 Integral<16, false>, Integral<32, true>, Integral<32, false>,
+                 Integral<64, true>, Integral<64, false>, IntegralAP<true>,
+                 IntegralAP<false>, Boolean, FixedPoint, Floating, Pointer,
+                 MemberPointer>;
+
 constexpr bool isIntegerOrBoolType(PrimType T) { return T <= PT_Bool; }
 constexpr bool isIntegerType(PrimType T) { return T <= PT_IntAPS; }
 
diff --git a/clang/lib/AST/ByteCode/Source.h b/clang/lib/AST/ByteCode/Source.h
index 56ca197e66473..464c1c8bc9811 100644
--- a/clang/lib/AST/ByteCode/Source.h
+++ b/clang/lib/AST/ByteCode/Source.h
@@ -36,6 +36,8 @@ class CodePtr final {
     return *this;
   }
 
+  CodePtr operator+(int32_t Offset) { return CodePtr(Ptr + Offset); }
+
   int32_t operator-(const CodePtr &RHS) const {
     assert(Ptr != nullptr && RHS.Ptr != nullptr && "Invalid code pointer");
     return Ptr - RHS.Ptr;
diff --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index b7ca154a0a987..8f9ec92e82cd6 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,expected 
-fcxx-exceptions %s -DNEW_INTERP -fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,ref      
-fcxx-exceptions %s
+// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,expected %s 
-DNEW_INTERP -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fcxx-exceptions -std=c++20 -verify=both,ref      %s
 
 
 int x;
@@ -673,7 +673,7 @@ namespace ConstexprArrayInitLoopExprDestructors
   struct Highlander {
       int *p = 0;
       constexpr Highlander() {}
-      constexpr void set(int *p) { this->p = p; ++*p; if (*p != 1) throw 
"there can be only one"; }
+      constexpr void set(int *p) { this->p = p; ++*p; if (*p != 1) 
__builtin_abort(); }
       constexpr ~Highlander() { --*p; }
   };
 
@@ -776,7 +776,7 @@ namespace FailingDestructor {
 
     constexpr ~D() {
       if (!can_destroy)
-        throw "oh no";
+        __builtin_abort();
     }
   };
   template<D d>
@@ -1062,7 +1062,7 @@ namespace OnePastEndDtor {
 namespace Virtual {
   struct NonZeroOffset { int padding = 123; };
 
-  constexpr void assert(bool b) { if (!b) throw 0; }
+  constexpr void assert(bool b) { if (!b) __builtin_abort(); }
 
   // Ensure that we pick the right final overrider during construction.
   struct A {
@@ -1191,9 +1191,8 @@ namespace DiscardedTrivialCXXConstructExpr {
     int x;
   };
 
-  constexpr int foo(int x) { // ref-error {{never produces a constant 
expression}}
-    throw S(3); // both-note {{not valid in a constant expression}} \
-                // ref-note {{not valid in a constant expression}}
+  constexpr int foo(int x) { // both-error {{never produces a constant 
expression}}
+    __builtin_abort(); // both-note 2{{not valid in a constant expression}}
     return 1;
   }
 
diff --git a/clang/test/AST/ByteCode/cxx23.cpp 
b/clang/test/AST/ByteCode/cxx23.cpp
index ba7db75a589a1..33bc905840e79 100644
--- a/clang/test/AST/ByteCode/cxx23.cpp
+++ b/clang/test/AST/ByteCode/cxx23.cpp
@@ -129,8 +129,8 @@ namespace StaticOperators {
 
   struct S1 {
     constexpr S1() { // all20-error {{never produces a constant expression}}
-      throw; // all-note {{not valid in a constant expression}} \
-             // all20-note {{not valid in a constant expression}}
+      __builtin_abort(); // all-note {{not valid in a constant expression}} \
+                         // all20-note {{not valid in a constant expression}}
     }
     static constexpr int operator()() { return 3; } // ref20-warning {{C++23 
extension}} \
                                                     // expected20-warning 
{{C++23 extension}}
diff --git a/clang/test/AST/ByteCode/exceptions.cpp 
b/clang/test/AST/ByteCode/exceptions.cpp
new file mode 100644
index 0000000000000..2a01e4155baca
--- /dev/null
+++ b/clang/test/AST/ByteCode/exceptions.cpp
@@ -0,0 +1,855 @@
+// RUN: %clang_cc1 -fcxx-exceptions -std=c++26 
-fexperimental-new-constant-interpreter -verify %s
+
+namespace std {
+  class exception {
+  public:
+    constexpr exception() noexcept {};
+    // constexpr exception(const exception&) noexcept;
+    // constexpr exception& operator=(const exception&) noexcept;
+    constexpr virtual ~exception() {};
+    // constexpr virtual const char* what() const noexcept;
+  };
+
+  template <typename T> struct remove_reference { using type = T; };
+  template <typename T> struct remove_reference<T &> { using type = T; };
+  template <typename T> struct remove_reference<T &&> { using type = T; };
+  template <typename T>
+  constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
+    return static_cast<typename std::remove_reference<T>::type &&>(t);
+  }
+};
+
+
+class Bad : std::exception {};
+
+namespace Simple {
+  constexpr int a() {
+    try {
+    } catch(int e){
+      return 12;
+    }
+    return -2;
+  }
+  static_assert(a() == -2);
+
+  constexpr int b() {
+    try {
+      throw 12;
+    } catch(int e){
+      return 12;
+    }
+    return -2;
+  }
+  static_assert(b() == 12);
+
+  constexpr int c() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = 140;
+    }
+    return m;
+  }
+  static_assert(c() == 140);
+
+  constexpr int d() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = 140;
+    } catch (float f) {
+      m = 15;
+    }
+    return m;
+  }
+  static_assert(d() == 140);
+
+  constexpr int e() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(int e){
+      m = e + 2;
+    }
+    return m;
+  }
+  static_assert(e() == 14);
+
+  constexpr int f() {
+    int m = 12;
+    try {
+      throw 12;
+    } catch(...){
+      m = 100;
+    }
+    return m;
+  }
+  static_assert(f() == 100);
+
+  constexpr int g() {
+    int m = 12;
+    try {
+      throw Bad();
+    } catch(Bad &B){
+      m = 100;
+    }
+    return m;
+  }
+  static_assert(g() == 100);
+
+  constexpr int h() {
+    int m = 12;
+    try {
+      throw ++m;
+    } catch(...){
+    }
+    return m;
+  }
+  static_assert(h() == 13);
+
+  constexpr int i(bool b) {
+    try {
+      if (b)
+        throw 12;
+      else
+        throw 14.0f;
+    } catch (int) {
+      return 100;
+    } catch (float) {
+      return 200;
+    }
+    return 0;
+  }
+  static_assert(i(true) == 100);
+  static_assert(i(false) == 200);
+}
+
+namespace Uncaught {
+
+  constexpr int a() {
+    throw 12; // expected-note {{uncaught exception of type 'int': '12'}}
+    return 0;
+  }
+  static_assert(a() == 13); // expected-error {{not an integral constant 
expression}}
+}
+
+namespace NoFrame {
+  static_assert((1, throw 2, 3) == 1); // expected-error {{not an integral 
constant expression}} \
+                                       // expected-note {{uncaught exception 
of type 'int': '2'}} \
+                                       // expected-warning {{left operand of 
comma operator has no effect}}
+
+}
+
+namespace CleanupAfterThrowingCall {
+  constexpr int a() {
+    throw 12;
+    return -12;
+  }
+  constexpr int test() {
+    try {
+      a();
+    } catch (int i) {
+      return 26;
+    }
+
+    return 120;
+  }
+  static_assert(test() == 26);
+
+  constexpr int b2() {
+    throw 1.0;
+    return 1;
+  }
+  constexpr int a2() {
+    b2();
+    throw 12;
+    return -12;
+  }
+  constexpr int test2() {
+
+    try {
+      a2();
+    } catch (int i) {
+      return 26;
+    } catch (double d ){
+      return (int)(d * 2);
+    }
+
+    return 120;
+  }
+  static_assert(test2() == 2);
+}
+
+namespace Dtors {
+  class Inc {
+    public:
+    int &m;
+    constexpr Inc(int &m) : m(m) {}
+    constexpr ~Inc() { ++m; }
+  };
+
+  constexpr int test1() {
+    int m = 10;
+    Inc _(m);
+
+    try {
+      throw 12;
+    } catch (int) {
+      return m;
+    }
+  }
+  static_assert(test1() == 10);
+
+  constexpr int test2() {
+    int m = 10;
+    try {
+      Inc _(m);
+      throw 12;
+    } catch (int) {
+    }
+    return m;
+  }
+  static_assert(test2() == 11);
+
+  struct checker {
+    int & counter;
+    constexpr ~checker() {
+      ++counter;
+    }
+  };
+
+  constexpr int test() {
+    int counter = 0;
+    {
+      try {
+        auto c1 = checker{counter};
+        throw 42;
+      } catch (...) {
+        return counter * 7;
+      }
+    }
+    return counter * 3;
+  }
+
+  constexpr int destruction_counter = test();
+  static_assert(destruction_counter == 7);
+}
+
+namespace CatchArray {
+  template <typename T> consteval T test(T head, auto... tail) {
+    const T array[] = {head, tail...};
+    try {
+      throw array;
+    } catch (const T (&arr)[5]) {
+      return -2;
+    } catch (const T * ptr) {
+      return *ptr;
+    } catch (...) {
+      return -1;
+    }
+  }
+
+  constexpr auto r0 = test(1,2,3,4,5,6);
+  static_assert(r0 == 1);
+
+  constexpr auto r1 = test(1,2,3,4,5);
+  static_assert(r1 == 1);
+
+  constexpr auto r2 = test(1,2,3,4);
+  static_assert(r2 == 1);
+
+  constexpr auto r3 = test(7,1,2,3,4,5,6);
+  static_assert(r3 == 7);
+
+  constexpr auto r4 = test(8,1,2,3,4,5);
+  static_assert(r4 == 8);
+
+  constexpr auto r5 = test(9,1,2,3,4);
+  static_assert(r5 == 9);
+}
+
+namespace CatchVoidPtr {
+  consteval int test() {
+    int p = 3;
+    try {
+      throw &p;
+    } catch (void * ptr) {
+      return *static_cast<int *>(ptr);
+    }
+  }
+
+  static_assert(test() == 3);
+}
+
+namespace Nullptr {
+  consteval int test_nullptr() {
+    try {
+      throw nullptr;
+    } catch (const int * ex) {
+      return true;
+    } catch (...) {
+      return false;
+    }
+  }
+  static_assert(test_nullptr());
+
+  consteval int test_zero() {
+    try {
+      throw 0;
+    } catch (const int * ex) {
+      return false;
+    } catch (...) {
+      return true;
+    }
+  }
+  static_assert(test_zero());
+}
+
+namespace CatchAll {
+  template <typename T, typename... Args> consteval int test(Args && ... args) 
{
+    try {
+      throw T{args...};
+    } catch (unsigned v) {
+      return static_cast<int>(v) * 2;
+    } catch (int v) {
+      return v * 3;
+    } catch (bool v) {
+      return static_cast<int>(v) * 5;
+    } catch (...) {
+      return -1;
+    }
+    return 0;
+  }
+
+  static_assert(test<unsigned>(42u) == 84);
+  static_assert(test<int>(13) == 39);
+  static_assert(test<bool>(true) == 5);
+  static_assert(test<long>(42) == -1);
+}
+
+namespace Copy {
+  class Child {};
+  constexpr int test() {
+
+    try {
+      throw Child{};
+    } catch (Child C) {
+      return 20;
+    }
+    return 30;
+  }
+  static_assert(test() == 20);
+
+  constexpr int a(){
+      throw Child{};
+  }
+  constexpr int test2() {
+
+    try {
+      a();
+    } catch (Child C) {
+      return 20;
+    }
+    return 30;
+  }
+  static_assert(test2() == 20);
+}
+
+namespace Inheritance {
+  class Parent2 {
+  public:
+    int F = 5;
+    constexpr int getFive() { return F; }
+  };
+  class Parent : public Parent2{
+  };
+  class Child : public Parent {};
+
+  constexpr int foo() {
+    try {
+      throw Child{};
+    } catch (Parent2 P) {
+      return P.getFive() + 9;
+    }
+    return 0;
+  }
+  static_assert(foo() == 14);
+
+  constexpr int foo2() {
+    Child C{};
+    try {
+      throw &C;
+    } catch (Parent2 *P) {
+      return P->getFive() + 12;
+    }
+    return 0;
+  }
+  static_assert(foo2() == 17);
+
+  constexpr int a() {
+    throw Child{};
+  };
+  constexpr int foo3() {
+    try {
+      a();
+    } catch (Parent2 P) {
+      return P.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo3() == 5);
+
+  constexpr int b(Child *C) {
+    throw C;
+  };
+
+  constexpr int foo4() {
+    Child C{};
+    try {
+      b(&C);
+    } catch (Parent *P) {
+      return P->getFive();
+    }
+    return 0;
+  }
+  static_assert(foo4() == 5);
+}
+
+namespace Pointer {
+  static constexpr auto via_const_catch = 2;
+  static constexpr auto via_childs_get_value = 3;
+  static constexpr auto via_special_child_catch = 5;
+
+  struct parent {
+    int value;
+    explicit constexpr parent(int v) noexcept: value{v} { }
+    constexpr virtual int get_value() const noexcept {
+      return value;
+    }
+    constexpr virtual ~parent() = default;
+  };
+
+  struct modifying_child: parent {
+    explicit constexpr modifying_child(int v) noexcept: parent{v} { }
+    constexpr int get_value() const noexcept override {
+      return value * via_childs_get_value;
+    }
+  };
+
+  struct ordinary_child: parent {
+    explicit constexpr ordinary_child(int v) noexcept: parent{v} { }
+  };
+
+  struct special_child: parent {
+    explicit constexpr special_child(int v) noexcept: parent{v} { }
+  };
+
+  consteval int test(void (*fnc)()) {
+    int result = 0;
+    try {
+      fnc();
+    } catch (special_child * sch) {
+      result = sch->get_value() * via_special_child_catch;
+      delete sch;
+    } catch (const special_child * sch) {
+      result = sch->get_value() * via_special_child_catch * via_const_catch;
+      delete sch;
+    } catch (parent * exc) {
+      result = exc->get_value();
+      delete exc;
+    } catch (const parent * exc) {
+      result = exc->get_value() * via_const_catch;
+      delete exc;
+    }
+    return result;
+  }
+
+  constexpr auto r1 = test([] { throw new parent{1}; });
+  static_assert(r1 == 1);
+
+  constexpr auto r2 = test([] { throw new modifying_child{3}; });
+  static_assert(r2 == 3 * via_childs_get_value);
+
+  constexpr auto r3 = test([] { throw new ordinary_child{5}; });
+  static_assert(r3 == 5);
+
+  constexpr auto r4 = test([] { throw new special_child{17}; });
+  static_assert(r4 == 17 * via_special_child_catch);
+}
+
+namespace References1 {
+  class Parent {
+  public:
+    int F = 10;
+    constexpr int getTen() { return F; }
+  };
+
+  class Child : public Parent {
+    public:
+    int F = 5;
+    constexpr int getFive() { return F; }
+
+  };
+
+  constexpr int foo() {
+    try {
+      throw Child{};
+    } catch (Child &C) {
+      return C.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo() == 5);
+
+  constexpr int nested() {
+    throw Child{};
+    return 1;
+  };
+  constexpr int foo2() {
+    try {
+      nested();
+    } catch (Child &C) {
+      return C.getFive();
+    }
+    return 0;
+  }
+  static_assert(foo2() == 5);
+
+  constexpr int foo3() {
+    try {
+      throw Child{};
+    } catch (Parent &P) {
+      return P.getTen();
+    }
+    return 0;
+  }
+  static_assert(foo3() == 10);
+
+  constexpr int foo4() {
+    try {
+      nested();
+    } catch (Parent &P) {
+      return P.getTen();
+    }
+    return 0;
+  }
+  static_assert(foo4() == 10);
+
+  constexpr int foo5() {
+    try {
+      throw 13;
+    } catch (const int &a) {
+      return 25;
+    }
+    return -1;
+  }
+  static_assert(foo5() == 25);
+
+  consteval bool reference_test(const int & ref) {
+      try {
+          throw ref;
+      } catch (const int & exc_ref) {
+          if (exc_ref != ref) {
+              return 3;
+          } else if (&exc_ref != &ref) {
+              return 2;
+          }
+          return 1;
+      }
+  }
+  static_assert(reference_test(10) == 1);
+
+  consteval bool copy_test(const int & ref) {
+      try {
+          throw ref;
+      } catch (const int exc_ref) {
+          if (exc_ref != ref) {
+              return 3;
+          } else if (&exc_ref == &ref) {
+              return 2;
+          }
+          return 1;
+      }
+  }
+  static_assert(copy_test(10) == 1);
+
+  consteval bool conversion_test(const int & ref) {
+      try {
+          throw ref;
+      } catch (const long exc_ref) {
+          if (exc_ref != ref) {
+              return 3;
+          } else if (static_cast<const void *>(&exc_ref) == static_cast<const 
void *>(&ref)) {
+              return 2;
+          }
+          return 4;
+      } catch (int) {
+          return 1;
+      }
+  }
+  static_assert(conversion_test(10) == 1);
+
+
+
+
+}
+
+namespace References2 {
+  static constexpr auto via_const_catch = 2;
+  static constexpr auto via_childs_get_value = 3;
+  static constexpr auto via_special_child_catch = 5;
+
+  struct parent {
+    int value;
+    explicit constexpr parent(int v) noexcept: value{v} { }
+    constexpr virtual int get_value() const noexcept {
+      return value;
+    }
+    constexpr virtual ~parent() = default;
+  };
+
+  struct modifying_child: parent {
+    explicit constexpr modifying_child(int v) noexcept: parent{v} { }
+    constexpr int get_value() const noexcept override {
+      return value * via_childs_get_value;
+    }
+  };
+
+  struct ordinary_child: parent {
+    explicit constexpr ordinary_child(int v) noexcept: parent{v} { }
+  };
+
+  struct special_child: parent {
+    explicit constexpr special_child(int v) noexcept: parent{v} { }
+  };
+
+  consteval int test(void (*fnc)()) {
+    int result = 0;
+    try {
+      fnc();
+    } catch (special_child & sch) {
+      result = sch.get_value() * via_special_child_catch;
+    } catch (const special_child & sch) {
+      result = sch.get_value() * via_special_child_catch * via_const_catch;
+    } catch (parent & exc) {
+      result = exc.get_value();
+    } catch (const parent & exc) {
+      result = exc.get_value() * via_const_catch;
+    }
+    return result;
+  }
+
+  constexpr auto r1 = test([] { throw parent{1}; });
+  static_assert(r1 == 1);
+
+  constexpr auto r2 = test([] { throw modifying_child{3}; });
+  static_assert(r2 == 3 * via_childs_get_value);
+
+  constexpr auto r3 = test([] { throw ordinary_child{5}; });
+  static_assert(r3 == 5);
+
+  constexpr auto r4 = test([] { throw special_child{17}; });
+  static_assert(r4 == 17 * via_special_child_catch); 
+}
+
+namespace Comma {
+  constexpr int catch_it() {
+    try {
+      return (1, throw 2, 3); // expected-warning {{left operand of comma 
operator has no effect}}
+    } catch (int v) {
+      return v;
+    }
+  }
+  static_assert(catch_it() == 2);
+}
+
+namespace Lifetime {
+  [[noreturn]] constexpr auto create(int v) -> int {
+    throw v; // expected-note {{uncaught exception of type 'int': '42'}} \
+             // expected-note {{uncaught exception of type 'int': '56'}} \
+             // expected-note {{uncaught exception of type 'int': '32'}} \
+             // expected-note {{uncaught exception of type 'int': '4'}} \
+             // expected-note {{uncaught exception of type 'int': '1'}}
+  }
+
+  constexpr int convert(int x) {
+    return x;
+  }
+
+  constexpr auto value1 = convert(create(42)); // expected-error {{must be 
initialized by a constant expression}}
+
+
+  struct wrapper {
+    int value;
+  };
+
+  constexpr auto value2 = wrapper{create(56)}; // expected-error {{must be 
initialized by a constant expression}}
+  constexpr auto value3 = wrapper(create(32)); // expected-error {{must be 
initialized by a constant expression}}
+  constexpr auto value4 = (1,2,3,create(4)); // expected-error {{must be 
initialized by a constant expression}}
+
+  constexpr int fnc() {
+    const auto v = create(1);
+    return v;
+  }
+
+  constexpr auto value5 = fnc(); // expected-error {{must be initialized by a 
constant expression}}
+}
+
+namespace TheWorst {
+  constexpr int zomg() try {
+    throw 12;
+  } catch (int i) {
+    return 13;
+  }
+
+  constexpr int c() {
+    return zomg();
+  }
+  static_assert(c() == 13);
+
+
+  struct hanaxception {
+    int v;
+  };
+
+  struct checker {
+    int value;
+    constexpr checker(int v) try : value{v} {
+      if (v > 10) {
+        throw hanaxception{v};
+      }
+    } catch (const hanaxception h) {
+    }
+  };
+
+  constexpr int test() {
+    auto c = checker{11};
+    return 42;
+  }
+  constexpr int constructor_test = test();
+  static_assert(constructor_test == 42);
+}
+
+namespace Destructors {
+  struct F {
+    constexpr ~F() noexcept(false){
+      throw 42; // expected-note {{uncaught exception of type 'int': '42'}}
+    }
+  };
+  constexpr int test() {
+    try {
+      F f;
+      return 1337;
+    } catch (int i) {
+      return i;
+    }
+
+    return 12;
+  }
+  static_assert(test() == 42);
+
+  constexpr int test2() {
+    F f;
+    return 1337;
+  }
+  static_assert(test2() == 42); // expected-error {{not an integral constant 
expression}}
+
+}
+
+namespace Noexcept {
+  constexpr void throw_exception_here() noexcept(false) {
+    throw 42;
+  }
+
+  constexpr int test() noexcept {
+    throw_exception_here(); // expected-note {{uncaught exception in noexcept 
function}}
+    return 42;
+  }
+  constexpr int value = test(); // expected-error {{must be initialized by a 
constant expression}} \
+                                // expected-note {{in call to}}
+}
+
+namespace Move {
+  struct foo {
+    int value;
+    constexpr foo(int v): value{v} {}
+    constexpr foo(foo && other): value{other.value + 1} {}
+    constexpr int get() const {
+        return value;
+    }
+  };
+
+  consteval int testMove() {
+      try {
+          throw std::move(foo{1});
+      } catch (const foo & f) {
+          return f.get();
+      }
+      return 8;
+  }
+  static_assert(testMove() == 2);
+}
+
+namespace UncaughtExceptions {
+  constexpr int foo() {
+    try {
+      throw 42;
+    } catch (...) {
+      return __builtin_uncaught_exceptions();
+    }
+    return -1;
+  }
+  static_assert(foo() == 0);
+
+  struct F{
+    constexpr ~F() {
+      if (__builtin_uncaught_exceptions() != 0) {
+        __builtin_abort(); // expected-note {{subexpression not valid in a 
constant expression}}
+      }
+    }
+  };
+  constexpr int foo2() {
+
+    try {
+      F f; // expected-note {{in call to}}
+      throw 42;
+    } catch (...) {
+      return 0;
+    }
+
+    return -1;
+  }
+  static_assert(foo2() == 0); // expected-error {{not an integral constant 
expression}} \
+                              // expected-note {{in call to}}
+
+  constexpr int foo3() {
+    try {
+      throw 42;
+    } catch (int) {
+      return __builtin_uncaught_exceptions();
+    }
+    return -1;
+  }
+  static_assert(foo3() == 0);
+
+
+
+}
+
+namespace Uncaught2 {
+  class E : std::exception {
+    constexpr const char *what() const noexcept {
+      return "SOME EXCEPTION WHADDAYAKNOW";
+    }
+  };
+  constexpr int foo() {
+    // FIXME: Call what() instead(?)
+    throw E(); // expected-note {{uncaught exception of type 'E': '&E()'}}
+    return 1;
+  }
+  static_assert(foo() == 1); // expected-error {{static assertion expression 
is not an integral constant expression}}
+
+
+}
diff --git a/clang/test/AST/ByteCode/invalid.cpp 
b/clang/test/AST/ByteCode/invalid.cpp
index e79b698a32719..f70f843569c03 100644
--- a/clang/test/AST/ByteCode/invalid.cpp
+++ b/clang/test/AST/ByteCode/invalid.cpp
@@ -1,31 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64 -fcxx-exceptions -std=c++20 
-fexperimental-new-constant-interpreter -verify=expected,both %s
-// RUN: %clang_cc1 -triple x86_64 -fcxx-exceptions -std=c++20                  
                       -verify=ref,both %s
-
-namespace Throw {
-
-  constexpr int ConditionalThrow(bool t) {
-    if (t)
-      throw 4; // both-note {{subexpression not valid in a constant 
expression}}
-
-    return 0;
-  }
-
-  static_assert(ConditionalThrow(false) == 0, "");
-  static_assert(ConditionalThrow(true) == 0, ""); // both-error {{not an 
integral constant expression}} \
-                                                  // both-note {{in call to 
'ConditionalThrow(true)'}}
-
-  constexpr int Throw() { // both-error {{never produces a constant 
expression}}
-    throw 5; // both-note {{subexpression not valid in a constant expression}}
-    return 0;
-  }
-
-  constexpr int NoSubExpr() { // both-error {{never produces a constant 
expression}}
-    throw; // both-note 2{{subexpression not valid}}
-    return 0;
-  }
-  static_assert(NoSubExpr() == 0, ""); // both-error {{not an integral 
constant expression}} \
-                                       // both-note {{in call to}}
-}
+// RUN: %clang_cc1 -triple x86_64 -std=c++20 
-fexperimental-new-constant-interpreter -verify=expected,both %s
+// RUN: %clang_cc1 -triple x86_64 -std=c++20                                   
      -verify=ref,both %s
 
 namespace Asm {
   constexpr int ConditionalAsm(bool t) {
diff --git a/clang/utils/TableGen/ClangOpcodesEmitter.cpp 
b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
index 154969cf49b04..c1c5ff067cf6d 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -128,6 +128,7 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
     const auto &Args = R->getValueAsListOfDefs("Args");
     bool ChangesPC = R->getValueAsBit("ChangesPC");
     bool CanFail = R->getValueAsBit("CanFail");
+    bool BothPCs = R->getValueAsBit("BothPCs");
 
     if (Args.empty()) {
       if (CanReturn) {
@@ -162,7 +163,7 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
 
     OS << "  {\n";
 
-    if (!ChangesPC)
+    if (!ChangesPC || BothPCs)
       OS << "    CodePtr OpPC = PC;\n";
 
     // Emit calls to read arguments.
@@ -185,9 +186,11 @@ void 
ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
     OS << N;
     PrintTypes(OS, TS);
     OS << "(S";
-    if (ChangesPC)
+    if (ChangesPC) {
       OS << ", PC";
-    else
+      if (BothPCs)
+        OS << ", OpPC";
+    } else
       OS << ", OpPC";
     for (size_t I = 0, N = Args.size(); I < N; ++I)
       OS << ", V" << I;
@@ -425,6 +428,7 @@ void ClangOpcodesEmitter::EmitEval(raw_ostream &OS, 
StringRef N,
                 OS << (AsRef ? "const " : " ") << Name << " "
                    << (AsRef ? "&" : "") << "A" << I << ", ";
               }
+              bool BothPCs = R->getValueAsBit("BothPCs");
               OS << "SourceInfo L) {\n";
               OS << "  if (!isActive()) return true;\n";
               OS << "  CurrentSource = L;\n";
@@ -432,6 +436,8 @@ void ClangOpcodesEmitter::EmitEval(raw_ostream &OS, 
StringRef N,
               OS << "  return " << N;
               PrintTypes(OS, TS);
               OS << "(S, OpPC";
+              if (BothPCs)
+                OS << ", OpPC";
               for (size_t I = 0, N = Args.size(); I < N; ++I)
                 OS << ", A" << I;
               OS << ");\n";

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to