Timm =?utf-8?q?Bäder?= <[email protected]>,
Timm =?utf-8?q?Bäder?= <[email protected]>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>


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

>From 6ef06558d1b2d6c27252ab5f9785b55a800d06c0 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 1/3] Exceptions

---
 .../include/clang/Basic/DiagnosticASTKinds.td |    5 +
 clang/lib/AST/ByteCode/ByteCodeEmitter.cpp    |    3 +-
 clang/lib/AST/ByteCode/ByteCodeEmitter.h      |    9 +
 clang/lib/AST/ByteCode/Compiler.cpp           |  221 +++-
 clang/lib/AST/ByteCode/Compiler.h             |    3 +
 clang/lib/AST/ByteCode/Context.cpp            |    3 +
 clang/lib/AST/ByteCode/Context.h              |    2 +
 clang/lib/AST/ByteCode/Disasm.cpp             |    8 +
 clang/lib/AST/ByteCode/EvalEmitter.h          |   10 +
 clang/lib/AST/ByteCode/Exceptions.cpp         |   56 +
 clang/lib/AST/ByteCode/Exceptions.h           |   51 +
 clang/lib/AST/ByteCode/Function.cpp           |    9 +
 clang/lib/AST/ByteCode/Function.h             |   11 +-
 clang/lib/AST/ByteCode/Interp.cpp             |  163 ++-
 clang/lib/AST/ByteCode/Interp.h               |   77 +-
 clang/lib/AST/ByteCode/InterpFrame.h          |    4 -
 clang/lib/AST/ByteCode/InterpStack.h          |    2 +
 clang/lib/AST/ByteCode/InterpState.cpp        |    8 +
 clang/lib/AST/ByteCode/InterpState.h          |    3 +
 clang/lib/AST/ByteCode/Opcodes.td             |   26 +
 clang/lib/AST/ByteCode/PrimType.h             |   10 +
 clang/lib/AST/CMakeLists.txt                  |    1 +
 clang/test/AST/ByteCode/cxx20.cpp             |   15 +-
 clang/test/AST/ByteCode/cxx23.cpp             |    4 +-
 clang/test/AST/ByteCode/exceptions.cpp        | 1162 +++++++++++++++++
 clang/test/AST/ByteCode/invalid.cpp           |   30 +-
 26 files changed, 1826 insertions(+), 70 deletions(-)
 create mode 100644 clang/lib/AST/ByteCode/Exceptions.cpp
 create mode 100644 clang/lib/AST/ByteCode/Exceptions.h
 create mode 100644 clang/test/AST/ByteCode/exceptions.cpp

diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td 
b/clang/include/clang/Basic/DiagnosticASTKinds.td
index c4582a3a9d641..1e7f22ceb095b 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -409,6 +409,11 @@ 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 note_constexpr_no_active_exception : Note<"rethrow with no active 
exception">;
+def note_constexpr_throw_with_active_exception : Note<"throw while another 
exception is already active">;
+def note_previous_throw : Note<"currently active exception thrown here">;
 
 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..0cdd5d2d1dfa5 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,6 +105,7 @@ 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;
 
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 316b2a4f092f9..9abb1edda3ecb 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -50,6 +50,17 @@ static bool isSideEffectFree(const Expr *E) {
   return false;
 }
 
+[[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 chain managing the variable lifetimes.
 template <class Emitter> class VariableScope {
 public:
@@ -3742,10 +3753,134 @@ 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.ExceptionsEnabled) {
+    if (SubExpr && !this->discard(SubExpr))
+      return false;
+    return this->emitInvalid(E);
+  }
+
+  if (!SubExpr)
+    return this->emitReThrow(E);
+
+  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());
+  if (!this->emitSaveException(ExceptionType.getTypePtr(), T, E))
     return false;
 
-  return this->emitInvalid(E);
+  this->VarScope->destroyLocals();
+
+  return this->emitThrow(E);
+}
+
+template <class Emitter>
+bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
+  if (!Ctx.ExceptionsEnabled) {
+    // Ignore all handlers.
+    return this->visitStmt(S->getTryBlock());
+  }
+
+  unsigned NumHandlers = S->getNumHandlers();
+
+  // When an exception is thrown in the middle of a try{} block, we use this
+  // throw trap to pop all values from the stack that have been added during
+  // the try block before the throw.
+  if (!this->emitThrowTrap(S))
+    return false;
+
+  // For the try block, we record the bytecode offset before and
+  // after it. When an exception is thrown, we check if the offset
+  // at that point is between the start/end of the appropriate catch
+  // handler for this try block. If we find such a handler, we jump to it.
+  unsigned TryBlockStart = this->currentCodeSize();
+  {
+    const auto *TryBlock = cast<CompoundStmt>(S->getTryBlock());
+    if (!this->visitStmt(TryBlock))
+      return false;
+  }
+  unsigned TryBlockEnd = 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 HandlerOffset = 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->emitMemcpy(Handler))
+          return false;
+        if (!this->emitPopPtr(Handler))
+          return false;
+      }
+    } else {
+      // This is a catch-all handler.
+    }
+    const Type *CatchTypePtr = CatchType.getTypePtrOrNull();
+
+    this->registerExceptionHandler(TryBlockStart, TryBlockEnd, HandlerOffset,
+                                   ExceptionDeclOffset, CatchTypePtr);
+    if (!this->visitStmt(HandlerBlock))
+      return false;
+
+    if (blockEndsInReturn(HandlerBlock))
+      continue;
+    this->jump(EndLabel, S);
+  }
+
+  this->fallthrough(EndLabel);
+  this->emitLabel(EndLabel);
+
+  return true;
 }
 
 template <class Emitter>
@@ -7211,12 +7346,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 for (auto x : {1, 2}) {}
 ///
 /// This is not a loop from an AST perspective at all since it has already
@@ -7309,11 +7438,9 @@ bool 
Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
     return false;
 
   this->emitCleanup();
-  if (ReturnType)
-    return this->emitRet(*ReturnType, MD);
 
-  // Nothing to do, since we emitted the RVO pointer above.
-  return this->emitRetVoid(MD);
+  bool CanThrow = MD->getType()->getAs<FunctionProtoType>()->canThrow();
+  return this->emitFunctionReturn(MD, ReturnType, CanThrow);
 }
 
 template <class Emitter>
@@ -7549,11 +7676,21 @@ 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{});
+  bool CanThrow = Ctor->getType()->getAs<FunctionProtoType>()->canThrow();
+  return this->emitFunctionReturn(Ctor, ReturnType, CanThrow);
 }
 
 template <class Emitter>
@@ -7624,7 +7761,11 @@ bool Compiler<Emitter>::compileDestructor(const 
CXXDestructorDecl *Dtor) {
   if (!this->emitMarkDestroyed(Dtor))
     return false;
 
-  return this->emitPopPtr(Dtor) && this->emitRetVoid(Dtor);
+  if (!this->emitPopPtr(Dtor))
+    return false;
+
+  bool CanThrow = Dtor->getType()->getAs<FunctionProtoType>()->canThrow();
+  return this->emitFunctionReturn(Dtor, ReturnType, CanThrow);
 }
 
 template <class Emitter>
@@ -7667,14 +7808,32 @@ 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{});
+  if (F->getReturnType()->isVoidType()) {
+    if (!this->emitRetVoid(SourceInfo{}))
+      return false;
+  } else if (!this->emitNoRet(SourceInfo{})) {
+    return false;
+  }
+
+  bool CanThrow = F->getType()->getAs<FunctionProtoType>()->canThrow();
+  if (Ctx.ExceptionsEnabled && CanThrow)
+    return this->emitAfterRet(SourceInfo{});
+  return true;
 }
 
 static uint32_t getBitWidth(const Expr *E) {
@@ -8808,6 +8967,24 @@ bool Compiler<Emitter>::emitBuiltinBitCast(const 
CastExpr *E) {
   return true;
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::emitFunctionReturn(const FunctionDecl *FD,
+                                           OptPrimType ReturnType,
+                                           bool CanThrow) {
+  bool NeedsAfterRet = Ctx.ExceptionsEnabled && CanThrow;
+
+  if (!NeedsAfterRet) {
+    if (ReturnType)
+      return this->emitRet(*ReturnType, FD);
+
+    return this->emitRetVoid(FD);
+  }
+
+  if (ReturnType)
+    return this->emitRet(*ReturnType, FD) && this->emitAfterRet(FD);
+  return this->emitRetVoid(FD) && this->emitAfterRet(FD);
+}
+
 /// Replicate a scalar value into every scalar element of an aggregate.
 /// The scalar is stored in a local at \p SrcOffset and a pointer to the
 /// destination must be on top of the interpreter stack. Each element receives
diff --git a/clang/lib/AST/ByteCode/Compiler.h 
b/clang/lib/AST/ByteCode/Compiler.h
index 46d6b1a49c001..0854c32d295f4 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -467,6 +467,9 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, 
bool>,
 
   bool refersToUnion(const Expr *E);
 
+  bool emitFunctionReturn(const FunctionDecl *FD, OptPrimType ReturnType,
+                          bool CanThrow = false);
+
 protected:
   /// Variable to storage mapping.
   llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
diff --git a/clang/lib/AST/ByteCode/Context.cpp 
b/clang/lib/AST/ByteCode/Context.cpp
index 9f1d9b899052d..6981af5c0ed71 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -33,6 +33,9 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new 
Program(*this)) {
   this->LongLongWidth = Ctx.getTargetInfo().getLongLongWidth();
   assert(Ctx.getTargetInfo().getCharWidth() == 8 &&
          "We're assuming 8 bit chars");
+
+  this->ExceptionsEnabled =
+      Ctx.getLangOpts().CPlusPlus26 && Ctx.getLangOpts().CXXExceptions;
 }
 
 Context::~Context() = default;
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 47821a3e3a7f3..9b0a6976894cf 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -46,6 +46,8 @@ class EvalIDScope;
 /// Holds all information required to evaluate constexpr code in a module.
 class Context final {
 public:
+  unsigned ExceptionsEnabled : 1;
+
   /// Initialises the constexpr VM.
   explicit Context(ASTContext &Ctx);
 
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp 
b/clang/lib/AST/ByteCode/Disasm.cpp
index 4caf830a0a1b4..419b5a4ef5ada 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -165,6 +165,14 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS,
   OS << "rvo:        " << hasRVO() << "\n";
   OS << "this arg:   " << hasThisPointer() << "\n";
 
+  // Print exception table.
+  if (!ExceptionTable.empty()) {
+    for (auto &EE : ExceptionTable) {
+      OS << EE.CodeStart << ".." << EE.CodeEnd << " -> " << EE.Target << " ("
+         << EE.CatchType << ")\n";
+    }
+  }
+
   struct OpText {
     size_t Addr;
     std::string Op;
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h 
b/clang/lib/AST/ByteCode/EvalEmitter.h
index 6ad4f833f9dad..f462758d9b06b 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -62,6 +62,16 @@ class EvalEmitter : public SourceMapper {
 
   virtual ~EvalEmitter();
 
+  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/Exceptions.cpp 
b/clang/lib/AST/ByteCode/Exceptions.cpp
new file mode 100644
index 0000000000000..ad8f5476f16a0
--- /dev/null
+++ b/clang/lib/AST/ByteCode/Exceptions.cpp
@@ -0,0 +1,56 @@
+//===-------------------------- Exceptions.cpp ------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Exceptions.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+bool ExceptionTableEntry::canCatch(const Type *ThrowType) const {
+  const Type *CatchType = this->CatchType;
+
+  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;
+}
diff --git a/clang/lib/AST/ByteCode/Exceptions.h 
b/clang/lib/AST/ByteCode/Exceptions.h
new file mode 100644
index 0000000000000..c8a15ce879ef9
--- /dev/null
+++ b/clang/lib/AST/ByteCode/Exceptions.h
@@ -0,0 +1,51 @@
+//===----------------------- Exceptions.h  ----------------------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_EXCEPTIONS_H
+#define LLVM_CLANG_AST_INTERP_EXCEPTIONS_H
+
+#include "PrimType.h"
+#include "clang/Basic/OptionalUnsigned.h"
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+class Type;
+
+namespace interp {
+class Block;
+
+struct ExceptionTableEntry {
+  unsigned CodeStart;
+  unsigned CodeEnd;
+  unsigned Target;
+  UnsignedOrNone DeclOffset;
+  /// If CatchType is nullptr, this is a catch-all handler.
+  const Type *CatchType;
+
+  /// Check if this exception table entry can catch an exception thrown of the
+  /// given type.
+  bool canCatch(const Type *ThrowType) const;
+};
+
+/// A thrown value.
+struct ThrowValue {
+  const Type *Ty;
+  Block *B;
+  SourceLocation Loc;
+  OptPrimType T;
+  unsigned CastOffset = 0;
+  bool Caught = false;
+
+  explicit ThrowValue(const Type *Ty, SourceLocation Loc, Block *B,
+                      OptPrimType T)
+      : Ty(Ty), B(B), Loc(Loc), T(T) {}
+};
+
+} // namespace interp
+} // namespace clang
+#endif
diff --git a/clang/lib/AST/ByteCode/Function.cpp 
b/clang/lib/AST/ByteCode/Function.cpp
index 22d26e498c5b6..4392e50d86e73 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/Function.cpp
@@ -70,3 +70,12 @@ SourceInfo Function::getSource(CodePtr PC) const {
     return SrcMap.back().second;
   return It->second;
 }
+
+std::optional<ExceptionTableEntry>
+Function::findCatchHandler(unsigned CodeOffset, const Type *Ty) const {
+  for (const auto &E : ExceptionTable) {
+    if (CodeOffset >= E.CodeStart && CodeOffset <= E.CodeEnd && E.canCatch(Ty))
+      return E;
+  }
+  return std::nullopt;
+}
diff --git a/clang/lib/AST/ByteCode/Function.h 
b/clang/lib/AST/ByteCode/Function.h
index 9742a16b50f2c..8bf89385bd64b 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -16,6 +16,7 @@
 #define LLVM_CLANG_AST_INTERP_FUNCTION_H
 
 #include "Descriptor.h"
+#include "Exceptions.h"
 #include "Source.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
@@ -252,6 +253,9 @@ class Function final {
     return ArgSize - (align(primSize(PT_Ptr)) * (hasThisPointer() + hasRVO()));
   }
 
+  std::optional<ExceptionTableEntry> findCatchHandler(unsigned CodeOffset,
+                                                      const Type *Ty) const;
+
 private:
   /// Construct a function representing an actual function.
   Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
@@ -261,13 +265,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;
   }
@@ -298,6 +304,7 @@ class Function final {
   llvm::SmallVector<Scope, 2> Scopes;
   /// List of all parameters, including RVO and instance pointer.
   llvm::SmallVector<ParamDescriptor> ParamDescriptors;
+  llvm::SmallVector<ExceptionTableEntry> ExceptionTable;
   /// Flag to indicate if the function is valid.
   LLVM_PREFERRED_TYPE(bool)
   unsigned IsValid : 1;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index ece2e71408731..44d3ed21d126a 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -234,6 +234,25 @@ 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;
+
+  if (S.ThrownValue->T) {
+    TYPE_SWITCH(*S.ThrownValue->T, {
+      ValString =
+          S.ThrownValue->B->deref<T>().toDiagnosticString(S.getASTContext());
+    });
+  } else {
+    ValString = 
Pointer(S.ThrownValue->B).toDiagnosticString(S.getASTContext());
+  }
+  S.FFDiag(S.ThrownValue->Loc, diag::note_constexpr_uncaught_exception)
+      << UncaughtType << ValString;
+  return false;
+}
+
 PRESERVE_NONE static bool BCP(InterpState &S, CodePtr OpPC, int32_t Offset,
                               PrimType PT);
 
@@ -1862,6 +1881,141 @@ 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);
+
+  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();
+
+  if (!NeedsCast) {
+    // We dont need to change the value at all. Just jump to the exception 
table
+    // entry.
+    S.PC = CurrFunction->getCodeBegin() + CatchEntry->Target;
+    S.ThrownValue->Caught = true;
+    return true;
+  }
+
+  unsigned BaseOffset = S.getContext().collectBaseOffset(
+      CaughtType->getAsRecordDecl(), ThrownType->getAsRecordDecl());
+
+  S.ThrownValue->CastOffset = BaseOffset;
+  S.ThrownValue->Caught = true;
+  // Jump to the catch handler in this function.
+  S.PC = CurrFunction->getCodeBegin() + CatchEntry->Target;
+
+  return true;
+}
+
+bool Throw(InterpState &S, CodePtr OpPC) {
+  assert(S.getContext().ExceptionsEnabled);
+  return catchException(S, OpPC);
+}
+
+bool ReThrow(InterpState &S, CodePtr OpPC) {
+  assert(S.getContext().ExceptionsEnabled);
+
+  if (!S.ThrownValue) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_no_active_exception);
+    return false;
+  }
+
+  assert(S.ThrownValue);
+  S.ThrownValue->Caught = false;
+  return catchException(S, OpPC);
+}
+
+bool SaveException(InterpState &S, CodePtr OpPC, const Type *Ty,
+                   OptPrimType T) {
+  assert(S.getContext().ExceptionsEnabled);
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+  // Must've been allocated via AllocException
+  assert(Ptr.block()->getEvalID() == ~0u);
+  assert(Ptr.block()->isInitialized());
+
+  // If we already have a thrown exception, it must be caught already,
+  // in which case we simply override it below.
+  // If it isn't caught, we need to diagnose.
+  if (S.ThrownValue && !S.ThrownValue->Caught) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_throw_with_active_exception, 1);
+    S.Note(S.ThrownValue->Loc, diag::note_previous_throw);
+    return false;
+  }
+
+  SourceLocation Loc = S.Current->getLocation(OpPC);
+  S.ThrownValue = std::make_unique<ThrowValue>(
+      Ty, Loc, const_cast<Block *>(Ptr.block()), T);
+  return true;
+}
+
 bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
              uint32_t VarArgSize) {
   if (Func->hasThisPointer()) {
@@ -1901,6 +2055,9 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function 
*Func,
 
   InterpStateCCOverride CCOverride(S, Func->isImmediate());
   if (Interpret(S)) {
+    if (S.ThrownValue && !S.ThrownValue->Caught)
+      return catchException(S, OpPC);
+
     assert(S.Current == FrameBefore);
     return true;
   }
@@ -1911,6 +2068,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function 
*Func,
   S.Current = FrameBefore;
   return false;
 }
+
 bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
           uint32_t VarArgSize) {
 
@@ -2006,6 +2164,9 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
     return false;
   }
 
+  if (S.ThrownValue && !S.ThrownValue->Caught)
+    return catchException(S, OpPC);
+
   assert(S.Current == FrameBefore);
   return true;
 }
@@ -3237,7 +3398,7 @@ bool CastFloatingIntegralAPS(InterpState &S, CodePtr 
OpPC, uint32_t BitWidth,
          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
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 582cb108f5816..7a292c95ac6ee 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -87,6 +87,8 @@ bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, bool 
Extern,
                            const Block *B, Lifetime LT = Lifetime::Started,
                            AccessKinds AK = AK_Read);
 
+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);
@@ -153,6 +155,9 @@ bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC, 
uint32_t BitWidth,
 bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth,
                              uint32_t FPOI);
 
+bool Throw(InterpState &S, CodePtr OpPC);
+bool ReThrow(InterpState &S, CodePtr OpPC);
+
 enum class ShiftDir { Left, Right };
 
 enum class ShiftFailure {
@@ -333,6 +338,72 @@ PRESERVE_NONE inline bool RetVoid(InterpState &S) {
   return true;
 }
 
+// Inserted at the very end of functions that can throw, if exceptions are
+// enabled. We rewind the stack to the start of the function frame and return
+// nothing. If we arrive at an AfterRet op, we must have thrown an exception.
+PRESERVE_NONE inline bool AfterRet(InterpState &S) {
+  assert(S.getContext().ExceptionsEnabled);
+  assert(S.ThrownValue);
+
+  while (S.Stk.size() != S.Current->getFrameOffset())
+    S.Stk.discardSlow();
+
+  return RetVoid(S);
+}
+
+bool SaveException(InterpState &S, CodePtr OpPC, const Type *Ty, OptPrimType 
T);
+
+inline bool ThrowTrap(InterpState &S) {
+  assert(S.getContext().ExceptionsEnabled);
+  S.ThrowTrapStackSize = S.Stk.size();
+  return true;
+}
+
+/// Allocate memory to store an exception object.
+inline bool AllocException(InterpState &S, const Descriptor *Desc) {
+  assert(S.getContext().ExceptionsEnabled);
+  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;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetExceptionValue(InterpState &S) {
+  assert(S.getContext().ExceptionsEnabled);
+  assert(S.ThrownValue);
+  assert(S.ThrownValue->T && "Thrown value must be primitive");
+
+  // The catch handler might require a cast of the thrown value.
+  // The actual cast happens in catchException(), here we apply
+  // the offset to the pointer (also in GetPtrExceptionValue).
+  if constexpr (std::is_same_v<T, Pointer>) {
+    if (S.ThrownValue->CastOffset != 0)
+      S.Stk.push<Pointer>(S.ThrownValue->B->deref<Pointer>().atField(
+          S.ThrownValue->CastOffset));
+    else
+      S.Stk.push<Pointer>(S.ThrownValue->B->deref<Pointer>());
+    return true;
+  }
+
+  S.Stk.push<T>(S.ThrownValue->B->deref<T>());
+  return true;
+}
+
+inline bool GetPtrExceptionValue(InterpState &S) {
+  assert(S.getContext().ExceptionsEnabled);
+  assert(S.ThrownValue);
+
+  Pointer P = Pointer(S.ThrownValue->B);
+  if (S.ThrownValue->CastOffset != 0)
+    S.Stk.push<Pointer>(P.atField(S.ThrownValue->CastOffset));
+  else
+    S.Stk.push<Pointer>(P);
+  return true;
+}
+
 
//===----------------------------------------------------------------------===//
 // Add, Sub, Mul
 
//===----------------------------------------------------------------------===//
@@ -2419,7 +2490,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;
 }
@@ -2430,7 +2502,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;
 }
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h 
b/clang/lib/AST/ByteCode/InterpFrame.h
index 9498a911f2892..109b1a7e3189f 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -92,10 +92,8 @@ class InterpFrame final : public Frame {
   /// Returns the current function.
   const Function *getFunction() const { return Func; }
 
-#ifndef NDEBUG
   /// Returns the offset on the stack at which the frame starts.
   size_t getFrameOffset() const { return FrameOffset; }
-#endif
 
   /// Returns the value of a local variable.
   template <typename T> const T &getLocal(unsigned Offset) const {
@@ -231,10 +229,8 @@ class InterpFrame final : public Frame {
   const unsigned ArgSize;
   /// Pointer to the arguments in the callee's frame.
   char *Args = nullptr;
-#ifndef NDEBUG
   /// Offset on the stack at entry.
   size_t FrameOffset = 0;
-#endif
 
 public:
   unsigned MSVCConstexprAllowed = 0;
diff --git a/clang/lib/AST/ByteCode/InterpStack.h 
b/clang/lib/AST/ByteCode/InterpStack.h
index 2c02979ee6eec..b877a11abaae6 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -57,6 +57,8 @@ class InterpStack final {
     }
     shrink(aligned_size<T>());
   }
+
+  /// Like discard(), but not type-aware. Avoid using this.
   void discardSlow();
 
   /// Returns a reference to the value on the top of the stack.
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp 
b/clang/lib/AST/ByteCode/InterpState.cpp
index 6ac1b2610dd09..6330acbdca8e0 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -51,6 +51,14 @@ bool InterpState::inConstantContext() const {
 }
 
 InterpState::~InterpState() {
+  // Invoke the dtor func of the allocated exception object block.
+  if (ThrownValue) {
+    Block *B = ThrownValue->B;
+    if (B && B->isInitialized())
+      B->invokeDtor();
+    ThrownValue = nullptr;
+  }
+
   while (Current && !Current->isBottomFrame()) {
     InterpFrame *Next = Current->Caller;
     delete Current;
diff --git a/clang/lib/AST/ByteCode/InterpState.h 
b/clang/lib/AST/ByteCode/InterpState.h
index 050fa4c77cd2f..1a1c314fc89e9 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -15,6 +15,7 @@
 
 #include "Context.h"
 #include "DynamicAllocator.h"
+#include "Exceptions.h"
 #include "Floating.h"
 #include "Function.h"
 #include "InterpFrame.h"
@@ -199,8 +200,10 @@ class InterpState final : public State, public 
SourceMapper {
   const bool InfiniteSteps = false;
   /// ID identifying this evaluation.
   const unsigned EvalID;
+  unsigned ThrowTrapStackSize = 0;
 
   EvaluationKind EvalKind = EvaluationKind::None;
+  std::unique_ptr<ThrowValue> ThrownValue;
 
   /// 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 74d3cc84e06ab..35b61e29fdbb7 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -71,6 +71,7 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgPrimType : ArgType { let Name = "PrimType"; }
 def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
 def ArgTypePtr : ArgType { let Name = "const Type *"; }
+def ArgOptPrimType : ArgType { let Name = "OptPrimType"; }
 
 
//===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -225,6 +226,31 @@ def NoRet : Opcode {
 }
 
 
+/// Exceptions
+def AfterRet : SuccessOpcode {
+  let CanReturn = 1;
+}
+
+def Throw : Opcode;
+def ReThrow : Opcode;
+def SaveException : Opcode {
+  let Args = [ArgTypePtr, ArgOptPrimType];
+}
+
+def GetExceptionValue : SuccessOpcode {
+  let Types = [AllTypeClass];
+  let HasGroup = 1;
+}
+def GetPtrExceptionValue : SuccessOpcode;
+
+def AllocException : SuccessOpcode {
+  let Args = [ArgDesc];
+}
+
+def ThrowTrap : SuccessOpcode;
+
+
+/// Calls
 def Call : Opcode {
   let Args = [ArgFunction, ArgUint32];
 }
diff --git a/clang/lib/AST/ByteCode/PrimType.h 
b/clang/lib/AST/ByteCode/PrimType.h
index 8f725942fedb9..279ec22f0d409 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -102,6 +102,16 @@ class OptPrimType final {
 };
 static_assert(sizeof(OptPrimType) == sizeof(PrimType));
 
+inline OptPrimType getSwappedBytes(OptPrimType T) { return T; }
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, OptPrimType T) {
+  if (!T)
+    OS << "None";
+  else
+    OS << static_cast<int>(*T);
+  return OS;
+}
+
 enum class CastKind : uint8_t {
   Reinterpret,
   ReinterpretLike,
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index e3f74d73f21da..f723af7fa5465 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -75,6 +75,7 @@ add_clang_library(clangAST
   ByteCode/Descriptor.cpp
   ByteCode/Disasm.cpp
   ByteCode/EvalEmitter.cpp
+  ByteCode/Exceptions.cpp
   ByteCode/Function.cpp
   ByteCode/InterpBuiltin.cpp
   ByteCode/InterpBuiltinBitCast.cpp
diff --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index 70ff1045a74da..20801a26389ef 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 {
@@ -1190,9 +1190,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 5607ec9b59cb5..5b549f1e2d887 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..3bf43b08a94c1
--- /dev/null
+++ b/clang/test/AST/ByteCode/exceptions.cpp
@@ -0,0 +1,1162 @@
+// 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);
+
+  constexpr void t() {
+    int p = 3; // expected-note {{declared here}}
+    throw &p;
+  }
+  consteval int test2() {
+    try {
+      t();
+    } catch (void * ptr) {
+      return *static_cast<int *>(ptr); // expected-note {{read of object 
outside its lifetime is not allowed in a constant expression}}
+    }
+  }
+  static_assert(test2() == 3); // expected-error {{not an integral constant 
expression}} \
+                               // expected-note {{in call to}}
+}
+
+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);
+}
+
+#if 0
+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);
+}
+#endif
+
+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}}
+
+
+}
+
+namespace UnusualTypes {
+  constexpr int foo1() {
+    throw (int[]){1,2,3}; // expected-note {{uncaught exception of type 'int 
*': '&(int[3]){1, 2, 3}[0]'}}
+    return 1;
+  }
+  static_assert(foo1() == 1); // expected-error {{not an integral constant 
expression}}
+
+
+  constexpr int foo2() {
+    throw 1i; // expected-note {{uncaught exception of type '_Complex int': 
'&1i'}}
+    return 1;
+  }
+  static_assert(foo2() == 1); // expected-error {{not an integral constant 
expression}}
+}
+
+namespace VirtCallOnException {
+  struct A {
+    virtual constexpr int getNumber()const  { return 10; }
+  };
+  struct B : A {};
+  struct C : B {
+    constexpr int getNumber() const override {
+      return 100;
+    }
+  };
+
+  constexpr int foo() {
+    try {
+      throw C{};
+    } catch (A& a) {
+      return a.getNumber();
+    }
+    return 1;
+  }
+  static_assert(foo() == 100);
+
+}
+
+namespace VirtCall {
+  struct A {
+    virtual constexpr int getNumber()const  { return 10; }
+  };
+  struct B : A {};
+  struct C : B {
+    constexpr int getNumber() const override {
+      return 100;
+    }
+  };
+
+  constexpr void nested() {
+    throw C{};
+  }
+
+  struct F {
+    constexpr virtual int foo() {
+      throw C{};
+      return 0;
+    }
+  };
+
+  constexpr int foo() {
+    try {
+      F f;
+      f.foo();
+    } catch (A& a) {
+      return a.getNumber();
+    }
+    return 1;
+  }
+  static_assert(foo() == 100);
+}
+
+namespace Variadic {
+  constexpr void variadic(...) {
+    throw 123;
+  }
+  constexpr int foo() {
+    try {
+      variadic(1,2,3,4);
+    } catch (int a) {
+      return a;
+    }
+    return 1;
+  }
+  static_assert(foo() == 123);
+}
+
+namespace StmtExpr {
+  constexpr int foo() {
+    try {
+    return  ({
+       int a = 123;
+       throw 100;
+       12;
+       })
+      == 12;
+    } catch (int) {
+      return 200;
+    }
+
+    return -1;
+  }
+  static_assert(foo() == 200);
+}
+
+namespace TryBody {
+  constexpr int t() {
+    throw 100;
+  };
+
+  constexpr int test() try {
+    t();
+    return 1;
+  } catch (...) {
+
+    return 20;
+  }
+  static_assert(test() == 20);
+}
+
+namespace ReThrow {
+  constexpr int t() {
+    throw 100;
+  };
+
+  constexpr int test() {
+    try {
+      t();
+      return 1;
+    } catch (...) {
+      throw;
+      return -4;
+    }
+    return -10;
+  }
+  constexpr int test2() {
+    try {
+      test();
+    } catch (int a) {
+      return a;
+    }
+    return -1;
+  }
+  static_assert(test2() == 100);
+
+
+  constexpr int test3() {
+    throw; // expected-note {{rethrow with no active exception}}
+  }
+  static_assert(test3() == 100); // expected-error {{static assertion 
expression is not an integral constant expression}} \
+                                 // expected-note {{in call to}}
+
+  constexpr int test4() {
+    try {
+      throw 100; // expected-note {{uncaught exception of type 'int': '100'}}
+    } catch(int) {
+      throw;
+    }
+  }
+  static_assert(test4() == 100); // expected-error {{static assertion 
expression is not an integral constant expression}}
+}
+
+namespace PointerInThrownValue {
+  /// Used to crash because of lifetime issues between the Pointer
+  /// saved in ThrownValue and the InterpState.
+  struct P    {};
+  struct C : P{
+    constexpr C() {}
+  };
+  constexpr int foo() {
+
+      try {
+      } catch (int e) {
+      }
+
+    try {
+      auto thrower = []() { throw C(); };
+      thrower();
+    } catch (const P&) {
+      return 100;
+    }
+
+    return -1;
+  }
+  static_assert(foo() == 100);
+}
+
+namespace CatchAfterCallWithExceptionAlreadySet {
+  struct P {};
+  struct C : P{
+    constexpr C() {}
+  };
+  constexpr int foo() {
+
+      try {
+        throw 42;
+      } catch (int e) {
+      }
+    try {
+      auto thrower = []() { throw C(); };
+      thrower();
+    } catch (const P&) {
+      return 100;
+    }
+
+    return -1;
+  }
+}
+
+namespace PtrIntoLocal {
+  constexpr int foo() {
+    try {
+      throw "help";
+    } catch (const char *p) {
+      return (int)p[2];
+    }
+    return -1;
+  }
+  static_assert(foo() == 'l');
+}
+
+namespace NestedTry {
+  constexpr int func(int n, int m) {
+    return 10;
+  }
+  constexpr int foo() {
+    try {
+      int a;
+      a = 10;
+      try {
+        int b;
+        b = 20;
+        func(10, ({throw 30; 20;}));
+        throw b;
+
+
+      } catch(...) {
+        throw;
+      }
+    } catch(int x) {
+      return x;
+    }
+    return -1;
+  }
+  static_assert(foo() == 30);
+
+}
+
+namespace ThrowWithActiveException {
+  struct S {
+    constexpr ~S() noexcept(false) {
+      throw 2; // expected-note {{throw while another exception is already 
active}}
+    }
+  };
+
+  constexpr int f() {
+    try {
+      S s; // expected-note {{in call to 's.~S()'}}
+      throw 1; // expected-note {{currently active exception thrown here}}
+    } catch (...) {
+    }
+    return 0;
+  }
+  static_assert(f() == 0); // expected-error {{not an integral constant 
expression}} \
+                           // expected-note {{in call to}}
+}
+
+namespace DifferentCastAfterRetgrow {
+  struct A {};
+  struct B : A {};
+  struct C : B {};
+
+  constexpr void a() {
+    throw C{};
+  };
+  constexpr void b() {
+    try {
+      a();
+    } catch (A) {
+      throw;
+    }
+  }
+  constexpr int c() {
+    try {
+      b();
+    } catch (B) {
+      return 10;
+    } catch (...) {
+      return -2;
+    }
+    return -1;
+  }
+  static_assert(c() == 10);
+}
diff --git a/clang/test/AST/ByteCode/invalid.cpp 
b/clang/test/AST/ByteCode/invalid.cpp
index 247c8ad732708..cbe371f3d09e3 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) {

>From 0c8cec7b042647969e3e266c4b43e5350ca1d9b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Mon, 10 Aug 2026 15:11:04 +0200
Subject: [PATCH 2/3] review

---
 clang/lib/AST/ByteCode/Compiler.cpp    |  34 +++++++-
 clang/lib/AST/ByteCode/Exceptions.cpp  |  47 +++++++++--
 clang/lib/AST/ByteCode/Exceptions.h    |   3 +-
 clang/lib/AST/ByteCode/Function.cpp    |   6 +-
 clang/lib/AST/ByteCode/Function.h      |   5 +-
 clang/lib/AST/ByteCode/Interp.cpp      |   3 +-
 clang/lib/AST/ByteCode/Interp.h        |  11 +++
 clang/lib/AST/ByteCode/Opcodes.td      |   3 +-
 clang/test/AST/ByteCode/exceptions.cpp | 112 ++++++++++++++++++++++++-
 9 files changed, 203 insertions(+), 21 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 9abb1edda3ecb..14bb1c075ff10 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -61,6 +61,20 @@ static bool isSideEffectFree(const Expr *E) {
   return false;
 }
 
+const CXXConstructorDecl *findCopyCtor(const CXXRecordDecl *RD,
+                                       unsigned ExpectedQualifiers) {
+  if (!RD)
+    return nullptr;
+
+  unsigned Qualifiers;
+  for (CXXConstructorDecl *Ctor : RD->ctors()) {
+    if (Ctor->isCopyConstructor(Qualifiers) && Qualifiers == 
ExpectedQualifiers)
+      return Ctor;
+  }
+
+  return nullptr;
+}
+
 /// Scope chain managing the variable lifetimes.
 template <class Emitter> class VariableScope {
 public:
@@ -3857,10 +3871,22 @@ bool Compiler<Emitter>::visitCXXTryStmt(const 
CXXTryStmt *S) {
           return false;
         if (!this->emitGetPtrExceptionValue(Handler))
           return false;
-        if (!this->emitMemcpy(Handler))
-          return false;
-        if (!this->emitPopPtr(Handler))
-          return false;
+
+        // Copy the exception object to the local variable, either via copy 
ctor
+        // or memcpy.
+        if (const auto *Ctor = findCopyCtor(CatchType->getAsCXXRecordDecl(),
+                                            CatchType.getCVRQualifiers())) {
+          const Function *Func = getFunction(Ctor);
+          if (!Func)
+            return false;
+          if (!this->emitCall(Func, 0, Handler))
+            return false;
+        } else {
+          if (!this->emitMemcpy(Handler))
+            return false;
+          if (!this->emitPopPtr(Handler))
+            return false;
+        }
       }
     } else {
       // This is a catch-all handler.
diff --git a/clang/lib/AST/ByteCode/Exceptions.cpp 
b/clang/lib/AST/ByteCode/Exceptions.cpp
index ad8f5476f16a0..e1c7016718e77 100644
--- a/clang/lib/AST/ByteCode/Exceptions.cpp
+++ b/clang/lib/AST/ByteCode/Exceptions.cpp
@@ -8,11 +8,18 @@
 
 #include "Exceptions.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
 
 using namespace clang;
 using namespace clang::interp;
 
-bool ExceptionTableEntry::canCatch(const Type *ThrowType) const {
+static bool isRecordOrPointerToRecordType(const Type *T) {
+  return T->isRecordType() ||
+         (T->isPointerOrReferenceType() && 
T->getPointeeType()->isRecordType());
+}
+
+bool ExceptionTableEntry::canCatch(const Type *ThrowType,
+                                   const ASTContext &ASTCtx) const {
   const Type *CatchType = this->CatchType;
 
   if (!CatchType || ASTContext::hasSameType(CatchType, ThrowType))
@@ -20,15 +27,34 @@ bool ExceptionTableEntry::canCatch(const Type *ThrowType) 
const {
 
   assert(CatchType);
 
-  // nullptr_t can be caught by any pointer type.
-  if (ThrowType->isNullPtrType() && CatchType->isPointerType())
-    return true;
+  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+  ThrowType->dump();
+  CatchType->dump();
+
+  // nullptr_t can be caught by any pointer type (including member pointers)
+  // and references of pointer types.
+  if (ThrowType->isNullPtrType()) {
+    if (CatchType->isPointerType() || CatchType->isMemberPointerType())
+      return true;
+    if (CatchType->isReferenceType() &&
+        CatchType->getPointeeType()->isPointerType())
+      return true;
+  }
 
   // void* can catch all thown pointer types.
   if (ThrowType->isPointerType() && CatchType->isVoidPointerType())
     return true;
 
-  if (CatchType->isPointerType() && !ThrowType->isPointerType())
+  // T& can catch T.
+  if (CatchType->isReferenceType() &&
+      ASTContext::hasSameType(CatchType->getPointeeType().getTypePtr(),
+                              ThrowType))
+    return true;
+
+  // From this point foward, we only care about T, T* or T& where T is a record
+  // type.
+  if (!isRecordOrPointerToRecordType(ThrowType) ||
+      !isRecordOrPointerToRecordType(CatchType))
     return false;
 
   if (CatchType->isPointerOrReferenceType())
@@ -48,8 +74,17 @@ bool ExceptionTableEntry::canCatch(const Type *ThrowType) 
const {
     if (CatchDecl == ThrowDecl)
       return true;
 
-    if (ThrowDecl->isDerivedFrom(CatchDecl))
+    // "T is an unambiguous public base class of E."
+    CXXBasePaths Paths;
+    if (ThrowDecl->isDerivedFrom(CatchDecl, Paths)) {
+      if (Paths.isAmbiguous(ASTCtx.getCanonicalTagType(CatchDecl)))
+        return false;
+
+      if (Paths.front().Access != AS_public)
+        return false;
+
       return true;
+    }
   }
 
   return false;
diff --git a/clang/lib/AST/ByteCode/Exceptions.h 
b/clang/lib/AST/ByteCode/Exceptions.h
index c8a15ce879ef9..8987b330a8889 100644
--- a/clang/lib/AST/ByteCode/Exceptions.h
+++ b/clang/lib/AST/ByteCode/Exceptions.h
@@ -15,6 +15,7 @@
 
 namespace clang {
 class Type;
+class ASTContext;
 
 namespace interp {
 class Block;
@@ -29,7 +30,7 @@ struct ExceptionTableEntry {
 
   /// Check if this exception table entry can catch an exception thrown of the
   /// given type.
-  bool canCatch(const Type *ThrowType) const;
+  bool canCatch(const Type *ThrowType, const ASTContext &ASTCtx) const;
 };
 
 /// A thrown value.
diff --git a/clang/lib/AST/ByteCode/Function.cpp 
b/clang/lib/AST/ByteCode/Function.cpp
index 4392e50d86e73..d0f2309220a3a 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/Function.cpp
@@ -72,9 +72,11 @@ SourceInfo Function::getSource(CodePtr PC) const {
 }
 
 std::optional<ExceptionTableEntry>
-Function::findCatchHandler(unsigned CodeOffset, const Type *Ty) const {
+Function::findCatchHandler(unsigned CodeOffset, const Type *Ty,
+                           const ASTContext &ASTCtx) const {
   for (const auto &E : ExceptionTable) {
-    if (CodeOffset >= E.CodeStart && CodeOffset <= E.CodeEnd && E.canCatch(Ty))
+    if (CodeOffset >= E.CodeStart && CodeOffset <= E.CodeEnd &&
+        E.canCatch(Ty, ASTCtx))
       return E;
   }
   return std::nullopt;
diff --git a/clang/lib/AST/ByteCode/Function.h 
b/clang/lib/AST/ByteCode/Function.h
index 8bf89385bd64b..57c7891b4737e 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -253,8 +253,9 @@ class Function final {
     return ArgSize - (align(primSize(PT_Ptr)) * (hasThisPointer() + hasRVO()));
   }
 
-  std::optional<ExceptionTableEntry> findCatchHandler(unsigned CodeOffset,
-                                                      const Type *Ty) const;
+  std::optional<ExceptionTableEntry>
+  findCatchHandler(unsigned CodeOffset, const Type *Ty,
+                   const ASTContext &ASTCtx) const;
 
 private:
   /// Construct a function representing an actual function.
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 44d3ed21d126a..93771a5a8043c 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1902,7 +1902,8 @@ static bool catchException(InterpState &S, CodePtr OpPC) {
 
   unsigned CodeOffset = S.PC - CurrFunction->getCodeBegin();
   std::optional<ExceptionTableEntry> CatchEntry =
-      CurrFunction->findCatchHandler(CodeOffset, S.ThrownValue->Ty);
+      CurrFunction->findCatchHandler(CodeOffset, S.ThrownValue->Ty,
+                                     S.getASTContext());
 
   if (!CatchEntry) {
     // We didn't find an appropriate catch handler in the current function.
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 7a292c95ac6ee..fda40bcc5a209 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -388,6 +388,17 @@ bool GetExceptionValue(InterpState &S) {
     return true;
   }
 
+  if constexpr (std::is_same_v<T, MemberPointer>) {
+    PrimType BlockPrimT = S.ThrownValue->B->getDescriptor()->getPrimType();
+    if (BlockPrimT == PT_Ptr) {
+      // This should only happen for null pointers.
+      S.Stk.push<T>();
+      return true;
+    } else if (BlockPrimT != PT_MemberPtr)
+      return false;
+  }
+
+  assert(S.ThrownValue->B->getDescriptor()->getPrimType() == Name);
   S.Stk.push<T>(S.ThrownValue->B->deref<T>());
   return true;
 }
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 35b61e29fdbb7..12e7303379a93 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -237,9 +237,10 @@ def SaveException : Opcode {
   let Args = [ArgTypePtr, ArgOptPrimType];
 }
 
-def GetExceptionValue : SuccessOpcode {
+def GetExceptionValue : Opcode {
   let Types = [AllTypeClass];
   let HasGroup = 1;
+  let NeedsOpPC = 0;
 }
 def GetPtrExceptionValue : SuccessOpcode;
 
diff --git a/clang/test/AST/ByteCode/exceptions.cpp 
b/clang/test/AST/ByteCode/exceptions.cpp
index 3bf43b08a94c1..03aa9fb2d9b16 100644
--- a/clang/test/AST/ByteCode/exceptions.cpp
+++ b/clang/test/AST/ByteCode/exceptions.cpp
@@ -318,6 +318,17 @@ namespace Nullptr {
     }
   }
   static_assert(test_zero());
+
+
+  constexpr int reference() {
+    try {
+      throw nullptr;
+    } catch (const int *&a) {
+      return 20;
+    }
+    return -1;
+  }
+  static_assert(reference() == 20);
 }
 
 namespace CatchAll {
@@ -428,6 +439,20 @@ namespace Inheritance {
     return 0;
   }
   static_assert(foo4() == 5);
+
+
+
+  class Base {};
+  class S : private Base {};
+  constexpr int foo5() {
+    try {
+      throw S{}; // expected-note {{uncaught exception of type 'S': '&S{}'}}
+    } catch (Base) {
+      return 10;
+    }
+    return -1;
+  }
+  static_assert(foo5() == -1); // expected-error {{not an integral constant 
expression}}
 }
 
 namespace Pointer {
@@ -490,6 +515,18 @@ namespace Pointer {
 
   constexpr auto r4 = test([] { throw new special_child{17}; });
   static_assert(r4 == 17 * via_special_child_catch);
+
+  constexpr int pointeeCatch() {
+    int x = 10;
+    try {
+      throw &x; // expected-note {{uncaught exception of type 'int *': '&x'}}
+    } catch (int ) {
+      return 10;
+    }
+    return 1;
+  }
+
+  static_assert(pointeeCatch() == 1); // expected-error {{not an integral 
constant expression}}
 }
 
 namespace References1 {
@@ -603,10 +640,6 @@ namespace References1 {
       }
   }
   static_assert(conversion_test(10) == 1);
-
-
-
-
 }
 
 namespace References2 {
@@ -1160,3 +1193,74 @@ namespace DifferentCastAfterRetgrow {
   }
   static_assert(c() == 10);
 }
+
+namespace MemberPointers {
+  struct S {
+    int m, n;
+  };
+  // constexpr int S::*pm = &S::m;
+  constexpr int S::* foo() {
+    try {
+      throw &S::m;
+    } catch (int S::* pm) {
+      return pm;
+    }
+    return nullptr;
+  }
+  static_assert(foo() == &S::m);
+}
+
+namespace CopCtors {
+  struct S {
+    int &ConstCC;
+    int &CC;
+
+    constexpr S(int &CC, int &ConstCC) : CC(CC), ConstCC(ConstCC) {}
+
+    constexpr S(const S& s) :CC(s.CC), ConstCC(s.ConstCC) {
+      ++ConstCC;
+    }
+    constexpr S(S& s) :CC(s.CC), ConstCC(s.ConstCC) {
+      ++CC;
+    }
+  };
+
+  constexpr int foo() {
+    int CopyCtorCalled = 0;
+    int ConstCopyCtorCalled = 0;
+    try {
+      throw S(CopyCtorCalled, ConstCopyCtorCalled);
+    } catch (S) {
+    }
+    return CopyCtorCalled + (ConstCopyCtorCalled * 10);
+  }
+  static_assert(foo() == 1);
+
+  constexpr int foo2() {
+    int CopyCtorCalled = 0;
+    int ConstCopyCtorCalled = 0;
+    auto M = S(CopyCtorCalled, ConstCopyCtorCalled);
+    try {
+      throw M;
+    } catch (S) {
+    }
+    return CopyCtorCalled + (ConstCopyCtorCalled * 10);
+  }
+  static_assert(foo2() == 2);
+
+  constexpr int foo3() {
+    int CopyCtorCalled = 0;
+    int ConstCopyCtorCalled = 0;
+
+    const auto M = S(CopyCtorCalled, ConstCopyCtorCalled);
+    try {
+      throw M;
+    } catch (S) {
+    }
+
+    return CopyCtorCalled + (ConstCopyCtorCalled * 10);
+  }
+  static_assert(foo3() == 11);
+
+
+}

>From 53113c05aad4d9df1a827ae3340e75737acf2906 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Tue, 11 Aug 2026 06:52:34 +0200
Subject: [PATCH 3/3] Review

---
 clang/lib/AST/ByteCode/Exceptions.cpp  |  21 +++--
 clang/test/AST/ByteCode/exceptions.cpp | 117 +++++++++++++++++++++++++
 2 files changed, 129 insertions(+), 9 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Exceptions.cpp 
b/clang/lib/AST/ByteCode/Exceptions.cpp
index e1c7016718e77..65fc5a9eea784 100644
--- a/clang/lib/AST/ByteCode/Exceptions.cpp
+++ b/clang/lib/AST/ByteCode/Exceptions.cpp
@@ -27,10 +27,6 @@ bool ExceptionTableEntry::canCatch(const Type *ThrowType,
 
   assert(CatchType);
 
-  llvm::errs() << __PRETTY_FUNCTION__ << '\n';
-  ThrowType->dump();
-  CatchType->dump();
-
   // nullptr_t can be caught by any pointer type (including member pointers)
   // and references of pointer types.
   if (ThrowType->isNullPtrType()) {
@@ -57,14 +53,21 @@ bool ExceptionTableEntry::canCatch(const Type *ThrowType,
       !isRecordOrPointerToRecordType(CatchType))
     return false;
 
-  if (CatchType->isPointerOrReferenceType())
-    CatchType = CatchType->getPointeeType().getTypePtr();
-  if (ThrowType->isPointerOrReferenceType())
-    ThrowType = ThrowType->getPointeeType().getTypePtr();
-
+  // T can catch T.
   if (CatchType == ThrowType)
     return true;
 
+  // T& can catch T.
+  if (CatchType->isReferenceType())
+    CatchType = CatchType->getPointeeType().getTypePtr();
+
+  // T* can only catch T*, not T.
+  if (CatchType->isPointerType() && ThrowType->isPointerType()) {
+    CatchType = CatchType->getPointeeType().getTypePtr();
+    ThrowType = ThrowType->getPointeeType().getTypePtr();
+  }
+
+  // Check for base casts.
   if (CatchType->isRecordType() && ThrowType->isRecordType()) {
     const CXXRecordDecl *CatchDecl = CatchType->getAsCXXRecordDecl();
     const CXXRecordDecl *ThrowDecl = ThrowType->getAsCXXRecordDecl();
diff --git a/clang/test/AST/ByteCode/exceptions.cpp 
b/clang/test/AST/ByteCode/exceptions.cpp
index 03aa9fb2d9b16..1df8907cb824c 100644
--- a/clang/test/AST/ByteCode/exceptions.cpp
+++ b/clang/test/AST/ByteCode/exceptions.cpp
@@ -440,6 +440,21 @@ namespace Inheritance {
   }
   static_assert(foo4() == 5);
 
+  constexpr int doublePointer() {
+    auto s = Child{};
+    auto *ss = &s;
+
+    try {
+      throw &ss;;
+    } catch (Parent**) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+    return -1;
+  }
+  static_assert(doublePointer() == 1);
+
 
 
   class Base {};
@@ -453,6 +468,108 @@ namespace Inheritance {
     return -1;
   }
   static_assert(foo5() == -1); // expected-error {{not an integral constant 
expression}}
+
+  constexpr int foo6() {
+    auto s = Child{};
+    try {
+      throw &s;
+    } catch (Child*) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo6() == 2);
+
+  constexpr int foo7() {
+    try {
+      throw Child{};
+    } catch (Child*) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo7() == 1);
+
+  constexpr int foo8() {
+    auto s = Child{};
+    try {
+      throw &s;
+    } catch (Parent&) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo8() == 1);
+
+  constexpr int foo9() {
+    auto s = Child{};
+    try {
+      throw (Child&)s;
+    } catch (Parent&) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo9() == 2);
+
+  constexpr int foo10() {
+    auto s = Child{};
+    try {
+      throw (Child&)s;
+    } catch (Parent) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo10() == 2);
+
+  constexpr int foo11() {
+    auto s = Child{};
+    try {
+      throw (Child&&)s;
+    } catch (Parent2) {
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo11() == 2);
+
+
+  constexpr int foo12() {
+    auto s = Child{};
+    try {
+      throw s;
+    } catch (Parent&&) { // expected-error {{cannot catch exceptions by rvalue 
reference}}
+      return 2;
+    } catch(...) {
+      return 1;
+    }
+
+    return -1;
+  }
+  static_assert(foo12() == 2);
+
+
+
+
 }
 
 namespace Pointer {

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

Reply via email to