[clang] [clang][bytecode] Support compile-time exceptions (PR #189410)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 7 09:29:39 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/189410
>From 62e1d986269cd18dad078395c3582c1b9ab53c8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 10 Feb 2026 16:06:17 +0100
Subject: [PATCH] Exceptions
---
clang/include/clang/Basic/Builtins.td | 6 +
.../include/clang/Basic/DiagnosticASTKinds.td | 3 +
clang/include/clang/Basic/UnsignedOrNone.h | 6 +
clang/lib/AST/ByteCode/ByteCodeEmitter.cpp | 3 +-
clang/lib/AST/ByteCode/ByteCodeEmitter.h | 10 +
clang/lib/AST/ByteCode/Compiler.cpp | 203 ++++-
clang/lib/AST/ByteCode/Disasm.cpp | 2 +
clang/lib/AST/ByteCode/EvalEmitter.h | 11 +
clang/lib/AST/ByteCode/Function.h | 25 +-
clang/lib/AST/ByteCode/Interp.cpp | 198 ++++-
clang/lib/AST/ByteCode/Interp.h | 315 +++++++-
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 11 +
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 | 15 +
clang/lib/AST/ByteCode/Opcodes.td | 32 +-
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 | 705 ++++++++++++++++++
clang/test/AST/ByteCode/invalid.cpp | 30 +-
clang/utils/TableGen/ClangOpcodesEmitter.cpp | 12 +-
24 files changed, 1544 insertions(+), 82 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 b8bbc544595e2..0e120bdd47fe4 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5596,3 +5596,9 @@ def CountedByRef : Builtin {
let Attributes = [NoThrow, CustomTypeChecking];
let Prototype = "int(...)";
}
+
+def ConstexprCurrentException: LangBuiltin<"CXX_LANG"> {
+ let Spellings = ["__builtin_current_exception"];
+ let Attributes = [NoThrow, Constexpr];
+ let Prototype = "void*()";
+}
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/include/clang/Basic/UnsignedOrNone.h b/clang/include/clang/Basic/UnsignedOrNone.h
index 659fd8c6487d2..aa09ae998dee0 100644
--- a/clang/include/clang/Basic/UnsignedOrNone.h
+++ b/clang/include/clang/Basic/UnsignedOrNone.h
@@ -35,6 +35,12 @@ struct UnsignedOrNone {
return Rep - 1;
}
+ unsigned value_or(unsigned U) const {
+ if (operator bool())
+ return operator*();
+ return U;
+ }
+
friend constexpr bool operator==(UnsignedOrNone LHS, UnsignedOrNone RHS) {
return LHS.Rep == RHS.Rep;
}
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 102ce939c6717..97a3b141cae71 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -76,6 +76,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;
@@ -91,9 +99,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 4f517266336f2..5fc134bd9c415 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -36,6 +36,21 @@ static std::optional<bool> getBoolValue(const Expr *E) {
return std::nullopt;
}
+// FIXME: Use this again.
+#if 0
+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;
+}
+#endif
+
/// Scope used to handle temporaries in toplevel variable declarations.
template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
public:
@@ -3455,10 +3470,152 @@ 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
+
+ llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+ E->dumpColor();
+
+ QualType ExceptionType = SubExpr->getType();
+ OptPrimType ExceptionT = classify(SubExpr);
+ if (ExceptionT)
+ llvm::errs() << "ExceptionT: " << *ExceptionT << '\n';
+ else
+ llvm::errs() << "ExceptionT: None\n";
+
+ 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;
- return this->emitInvalid(E);
+ bool Comp;
+ if (ExceptionT) {
+ Comp = false;
+ if (!this->visit(SubExpr))
+ return false;
+ if (!this->emitInit(*ExceptionT, E))
+ return false;
+ } else {
+ Comp = true;
+ if (!this->visitInitializer(SubExpr))
+ return false;
+ }
+
+ this->VarScope->destroyLocals();
+
+ PrimType T = classify(E->getSubExpr()).value_or(PT_Ptr);
+ return this->emitThrow(T, ExceptionType.getTypePtr(), Comp, 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());
+ }
+
+ // LocalScope<Emitter> FullScope(this);
+
+ S->dumpColor();
+ unsigned NumHandlers = S->getNumHandlers();
+ llvm::errs() << "Handlers: " << NumHandlers << '\n';
+
+ if (!this->emitThrowTrap(S))
+ return false;
+
+ unsigned s = this->currentCodeSize();
+
+ // Emit try block contents.
+ const auto *TryBlock = cast<CompoundStmt>(S->getTryBlock());
+ LocalScope<Emitter> TryBlockScope(this);
+ for (const auto *InnerStmt : TryBlock->body()) {
+ if (!visitStmt(InnerStmt))
+ return false;
+ }
+
+ if (!TryBlockScope.destroyLocals())
+ return false;
+ // Unlink this scope. We will emit cleanups for it again later.
+ assert(this->VarScope == &TryBlockScope);
+ this->VarScope = TryBlockScope.getParent();
+
+ 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) {
+ llvm::errs() << "EXCEPTION DECL:\n";
+ ExceptionDecl->dump();
+
+ if (OptPrimType T = classify(CatchType)) {
+ unsigned LocalOffset = allocateLocalPrimitive(ExceptionDecl, *T,
+ /*IsConst=*/true);
+ 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->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;
+ }
+
+ const Type *CatchTypePtr = CatchType.getTypePtrOrNull();
+
+ this->registerExceptionHandler(s, e, t, ExceptionDeclOffset, CatchTypePtr);
+ if (!this->visitStmt(HandlerBlock))
+ return false;
+ // FIXME: Re-enable this.
+ // if (blockEndsInReturn(Block))
+ // continue;
+ this->jump(EndLabel, S);
+ }
+
+ this->fallthrough(EndLabel);
+ this->emitLabel(EndLabel);
+
+ // return FullScope.destroyLocals();
+
+ // if (!TryBlockScope.destroyLocals())
+ // return false;
+
+ return true;
}
template <class Emitter>
@@ -6579,12 +6736,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());
@@ -6847,8 +6998,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{});
@@ -6915,6 +7075,9 @@ bool Compiler<Emitter>::compileUnionAssignmentOperator(
template <class Emitter>
bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
+ llvm::errs() << __PRETTY_FUNCTION__ << '\n';
+ F->dumpColor();
+
// Classify the return type.
ReturnType = this->classify(F->getReturnType());
@@ -6938,14 +7101,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 6caa33261dad6..a5c61178bffd4 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -179,6 +179,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.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index 8f6da7aef422a..479c659d7a120 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -54,6 +54,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 90732d6dc01a4..8f3b11c215916 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 *>;
@@ -247,6 +256,16 @@ class Function final {
return false;
}
+ 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,
@@ -256,13 +275,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 8cc3c9216f7f4..f73461209e820 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;
@@ -754,7 +755,7 @@ 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, AK_Read))
return false;
if (!Desc.IsInitialized)
@@ -1232,7 +1233,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) {
@@ -1638,16 +1639,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;
};
@@ -1705,6 +1706,8 @@ 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();
+
InterpFrame *FrameBefore = S.Current;
S.Current = NewFrame;
@@ -1713,6 +1716,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
// Ret() above only sets the APValue if the curent frame doesn't
// have a caller set.
bool Success = Interpret(S);
+
// Remove initializing block again.
if (Func->isConstructor() || Func->isDestructor())
S.InitializingBlocks.pop_back();
@@ -1725,7 +1729,159 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
}
- assert(S.Current == FrameBefore);
+ if (S.ThrownValue) {
+ 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()) {
+ QualType UncaughtType = QualType(S.ThrownValue->Ty, 0);
+ std::string ValString;
+ TYPE_SWITCH(S.ThrownValue->T, {
+ ValString = std::get<T>(S.ThrownValue->Value)
+ .toDiagnosticString(S.getASTContext());
+ });
+ S.FFDiag(S.ThrownValue->ThrowSite,
+ diag::note_constexpr_uncaught_exception)
+ << UncaughtType << ValString;
+ }
+ return false;
+ }
+ auto canCatch = [](const Type *CatchType, const Type *ThrowType) -> bool {
+ if (!CatchType || ASTContext::hasSameType(CatchType, ThrowType))
+ return true;
+
+ assert(CatchType);
+
+ // if (const auto *L = CatchType->getAs<ReferenceType>()) {
+ // return L->getPointeeType().getTypePtr() == ThrowType;
+ // }
+
+ // 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))) { //(cast<CXXRecordDecl>(B.Decl)->
+ // if (B.R == CaughtRecord) {
+ 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;
+ }
+
+ 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;
}
@@ -1819,7 +1975,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
@@ -1855,7 +2011,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>();
@@ -1914,7 +2070,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(const Pointer &Ptr) {
@@ -2609,14 +2765,22 @@ bool Interpret(InterpState &S) {
return InterpNext(S, PC);
#else
while (true) {
- auto Op = PC.read<Opcode>();
- auto Fn = InterpFunctions[Op];
+ // Empty program.
+ if (!PC)
+ return true;
- if (!Fn(S, PC))
- return false;
- if (OpReturns(Op))
- break;
- }
+ for (;;) {
+ if (S.Current->getFunction())
+ llvm::errs() << "Interpret loop: "
+ << S.Current->getFunction()->getName() << '\n';
+ auto Op = PC.read<Opcode>();
+ auto Fn = InterpFunctions[Op];
+
+ if (!Fn(S, PC))
+ return false;
+ if (OpReturns(Op))
+ break;
+ }
return true;
#endif
}
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 91e9461befcd9..3cf670cc85297 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -26,6 +26,7 @@
#include "InterpStack.h"
#include "InterpState.h"
#include "MemberPointer.h"
+#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
#include "State.h"
@@ -116,13 +117,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);
@@ -249,6 +250,8 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC,
template <PrimType Name, class T = typename PrimConv<Name>::T>
PRESERVE_NONE bool Ret(InterpState &S, CodePtr &PC) {
+ llvm::errs() << "Ret in " << S.Current->getFunction()->getName() << "!\n";
+ S.Stk.dump();
const T &Ret = S.Stk.pop<T>();
assert(S.Current);
@@ -289,6 +292,261 @@ PRESERVE_NONE inline bool RetVoid(InterpState &S, CodePtr &PC) {
return true;
}
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Throw(InterpState &S, CodePtr &PC, const Type *Ty, bool IsComp) {
+ llvm::errs() << "############################################################"
+ "###################################\n";
+ CodePtr OpPC;
+ if (PC)
+ OpPC = PC - align(sizeof(Opcode)) - align(sizeof(bool)); // XXX
+ else
+ OpPC = PC;
+
+ 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;
+ };
+
+ // TODO: Diagnose.
+ if (S.Current->isBottomFrame())
+ return false;
+
+ const auto &ThrownValue = S.Stk.pop<Pointer>();
+
+ assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+ llvm::errs() << "\n\n" << __PRETTY_FUNCTION__ << '\n';
+ llvm::errs() << "Throwing " << ThrownValue << '\n';
+ llvm::errs() << "In " << S.Current->getName() << '\n';
+ unsigned O = PC - S.Current->getFunction()->getCodeBegin();
+ llvm::errs() << "O: " << O << ". Type: " << Ty << '\n';
+ bool Caught = false;
+
+ llvm::errs() << "Thrown Type:\n";
+ Ty->dump();
+
+ const Function *F = S.Current->getFunction();
+ if (!F) {
+ assert(false);
+ }
+ CodePtr CurrentPC = PC;
+ llvm::errs() << "Current function: " << F->getName() << '\n';
+ unsigned CodeOffset = CurrentPC - F->getCodeBegin();
+ llvm::errs() << "CodeOffset: " << CodeOffset << '\n';
+
+ const Type *CaughtType = nullptr;
+
+ for (const auto &E : F->ExceptionTable) {
+ if (E.CatchType) {
+ llvm::errs() << "Catching:\n";
+ E.CatchType->dump();
+ }
+ llvm::errs() << "Can catch: " << canCatch(E.CatchType, Ty) << '\n';
+
+ if (E.CodeStart <= CodeOffset && E.CodeEnd >= CodeOffset &&
+ canCatch(E.CatchType, Ty)) {
+ llvm::errs() << "SAME FRAME\n";
+ PC = S.Current->getFunction()->getCodeBegin() + E.Target;
+
+ llvm::errs() << "new offset: "
+ << (PC - S.Current->getFunction()->getCodeBegin()) << '\n';
+
+ llvm::errs() << "FOUND!\n";
+ Caught = true;
+ CaughtType = E.CatchType;
+
+ break;
+ }
+ }
+
+ const CXXThrowExpr *Source = cast<CXXThrowExpr>(S.Current->getExpr(OpPC));
+ llvm::errs() << "Comp: " << IsComp << '\n';
+ if (!IsComp) {
+
+ Pointer CastedValue = ThrownValue;
+ const Type *It = ThrownValue.getType().getTypePtr();
+
+ llvm::errs() << "----------------------------------------------------------"
+ "------------\n";
+ bool NeedsCast =
+ CaughtType && ((CaughtType->isRecordType() && It->isRecordType()) ||
+ (CaughtType->isPointerOrReferenceType() &&
+ CaughtType->getPointeeType()->isRecordType() &&
+ It->isPointerOrReferenceType() &&
+ It->getPointeeType()->isRecordType()));
+
+ llvm::errs() << "NeedsCast: " << NeedsCast << '\n';
+
+ // if (CaughtType && CaughtType->isRecordType() && It->isRecordType()) {
+ if (NeedsCast) {
+ CastedValue = CastedValue.deref<Pointer>();
+ llvm::errs() << "WE ARE CASTING\n";
+ It = It->getPointeeType().getTypePtr();
+ const Record *CaughtRecord = S.getContext().getRecord(
+ CaughtType->getPointeeType()->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))) { //(cast<CXXRecordDecl>(B.Decl)->
+ // if (B.R == CaughtRecord) {
+ 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, Name};
+ } else {
+ S.ThrownValue = ThrowValue{Ty, Source, ThrownValue.deref<T>(), Name};
+ }
+
+ } else {
+ llvm::errs() << "----------------------------------------------------------"
+ "------------\n";
+ llvm::errs() << "Value: " << ThrownValue << '\n';
+ assert((std::is_same_v<T, Pointer>));
+ // llvm::errs() << "Direct hit: "
+ // << (ThrownValue.getType().getTypePtr() == CaughtType) << '\n';
+ // We need to cast the thrown value to the expected caught type.
+ // FIXME: Add more tests.
+ // FIXME: We need to be careful here becuase of virtual and multiple
+ // inheritance.
+ // Add tests for that as well.
+ Pointer CastedValue = ThrownValue;
+ const Type *It = ThrownValue.getType().getTypePtr();
+
+ 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 (CaughtType && CaughtType->isPointerOrReferenceType())
+ CaughtType = CaughtType->getPointeeType().getTypePtr();
+
+ if (NeedsCast) { // CaughtType && CaughtType->isRecordType() &&
+ // It->isRecordType()) {
+ 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))) { //(cast<CXXRecordDecl>(B.Decl)->
+ // if (B.R == CaughtRecord) {
+ 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, Name};
+ }
+
+ if (Caught) {
+ llvm::errs() << "CAUGHT IN " << F->getName() << '\n';
+ // llvm::errs() << "As\n";
+ // CaughtType->dump();
+ } else {
+ llvm::errs() << "Uncaught exception!\n";
+ // If we didnt' catch the exception in the current frame, save the thrown
+ // value in InterpState to be caught by callers of this function.
+ // const CXXThrowExpr *Source =
+ // cast<CXXThrowExpr>(S.Current->getExpr(OpPC)); S.ThrownValue =
+ // ThrowValue{Ty, Source, ThrownValue, Name};
+ PC = S.Current->getFunction()->getCodeEnd() - align(sizeof(Opcode));
+ llvm::errs() << "Offset now: "
+ << (PC - S.Current->getFunction()->getCodeBegin()) << '\n';
+ // PC = S.Current->getFunction()->getCodeEnd();// - align(sizeof(Opcode));
+ }
+
+ 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;
+}
+
+/// 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();
+
+ // B->dump();
+
+ S.Stk.push<Pointer>(B);
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Add, Sub, Mul
//===----------------------------------------------------------------------===//
@@ -2204,7 +2462,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;
}
@@ -2215,7 +2474,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;
}
@@ -2565,6 +2825,7 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) {
}
inline bool InitScope(InterpState &S, CodePtr OpPC, uint32_t I) {
+ llvm::errs() << __PRETTY_FUNCTION__ << '\n';
S.Current->initScope(I);
return true;
}
@@ -2904,6 +3165,7 @@ inline bool This(InterpState &S, CodePtr OpPC) {
if (!CheckThis(S, OpPC))
return false;
const Pointer &This = S.Current->getThis();
+ llvm::errs() << "This: " << This << '\n';
// Ensure the This pointer has been cast to the correct base.
if (!This.isDummy()) {
@@ -2912,8 +3174,10 @@ inline bool This(InterpState &S, CodePtr OpPC) {
[[maybe_unused]] const Record *R = This.getRecord();
if (!R)
R = This.narrow().getRecord();
- if (!R)
+ if (!R) {
+ llvm::errs() << "NO THIS RECORD\n";
return false;
+ }
assert(R->getDecl() ==
cast<CXXMethodDecl>(S.Current->getFunction()->getDecl())
->getParent());
@@ -3144,6 +3408,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
@@ -3776,6 +4064,23 @@ 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);
+
+ S.Stk.push<T>(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 e7b3ef6ce1510..8a18482f5b5ff 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -4189,6 +4189,15 @@ static bool interp__builtin_ia32_gfni_mul(InterpState &S, CodePtr OpPC,
return true;
}
+static bool interp__builtin_current_exception(InterpState &S, CodePtr OpPC,
+ const CallExpr *Call) {
+ // llvm::errs() <<__PRETTY_FUNCTION__ << '\n';
+ // Call->dumpColor();
+
+ S.Stk.push<Pointer>();
+ return true;
+}
+
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
uint32_t BuiltinID) {
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -6050,6 +6059,8 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
},
/*IsScalar=*/true);
+ case Builtin::BI__builtin_current_exception:
+ return interp__builtin_current_exception(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 8b4da6a14de70..0f5992bda9189 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -102,6 +102,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;
}
diff --git a/clang/lib/AST/ByteCode/InterpStack.cpp b/clang/lib/AST/ByteCode/InterpStack.cpp
index 992546560eec4..26c37796b125d 100644
--- a/clang/lib/AST/ByteCode/InterpStack.cpp
+++ b/clang/lib/AST/ByteCode/InterpStack.cpp
@@ -94,6 +94,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 c647dfa6d85ea..67ea5d77aa41b 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 499a21a094e2c..1f4b6eee0d97b 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -13,12 +13,17 @@
#ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H
#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H
+#include "Boolean.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 {
@@ -32,6 +37,13 @@ struct StdAllocatorCaller {
explicit operator bool() { return Call; }
};
+struct ThrowValue {
+ const Type *Ty;
+ const Expr *ThrowSite;
+ AnyPrimType Value;
+ PrimType T;
+};
+
/// Interpreter context.
class InterpState final : public State, public SourceMapper {
public:
@@ -140,6 +152,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.
@@ -162,6 +175,8 @@ class InterpState final : public State, public SourceMapper {
/// ID identifying this evaluation.
const unsigned EvalID;
+ std::optional<ThrowValue> ThrownValue = std::nullopt;
+
/// Things needed to do speculative execution.
SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr;
#ifndef NDEBUG
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 0215cd92966ee..d0f9082f59f54 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;
@@ -217,10 +218,37 @@ def RetValue : Opcode {
def NoRet : Opcode {}
+/// Exceptions
+def AfterRet : Opcode {
+ let CanReturn = 1;
+}
+
+def Throw : Opcode {
+ let Types = [AllTypeClass];
+ let Args = [ArgTypePtr, ArgBool];
+ let ChangesPC = 1;
+ let HasGroup = 1;
+}
+
+def GetExceptionValue : Opcode {
+ let Types = [AllTypeClass];
+ let HasGroup = 1;
+}
+
+def ClearExceptionValue : Opcode;
+
+def AllocException : Opcode {
+ let Args = [ArgDesc];
+}
+
+def ThrowTrap : Opcode;
+
+
def Call : Opcode {
let Args = [ArgFunction, ArgUint32];
+ let ChangesPC = 1;
+ let BothPCs = 1;
}
-
def CallVirt : Opcode {
let Args = [ArgFunction, ArgUint32];
}
@@ -229,6 +257,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 2fa553b7b4a47..e6a65c9297fa1 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 {
@@ -48,6 +49,14 @@ enum PrimType : uint8_t {
PT_MemberPtr = 14,
};
+// Alias for using any one of our primitive types.
+using AnyPrimType =
+ std::variant<Integral<8, true>, Integral<8, 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 9800fe01fcaf5..300dc0f02591e 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
void test_alignas_operand() {
alignas(8) char dummy;
@@ -654,7 +654,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; }
};
@@ -757,7 +757,7 @@ namespace FailingDestructor {
constexpr ~D() {
if (!can_destroy)
- throw "oh no";
+ __builtin_abort();
}
};
template<D d>
@@ -1043,7 +1043,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 {
@@ -1172,9 +1172,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 a1aecf329327a..111b7f88eaa29 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..e390b42aa88ce
--- /dev/null
+++ b/clang/test/AST/ByteCode/exceptions.cpp
@@ -0,0 +1,705 @@
+// 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;
+ };
+};
+
+
+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-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);
+}
+
+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}}
+}
diff --git a/clang/test/AST/ByteCode/invalid.cpp b/clang/test/AST/ByteCode/invalid.cpp
index 9f157db889a22..207dce44c10b5 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 3192891801ae9..504492feb9e55 100644
--- a/clang/utils/TableGen/ClangOpcodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpcodesEmitter.cpp
@@ -127,6 +127,7 @@ void ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
bool CanReturn = R->getValueAsBit("CanReturn");
const auto &Args = R->getValueAsListOfDefs("Args");
bool ChangesPC = R->getValueAsBit("ChangesPC");
+ bool BothPCs = R->getValueAsBit("BothPCs");
if (Args.empty()) {
if (CanReturn) {
@@ -152,7 +153,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.
@@ -171,9 +172,11 @@ void ClangOpcodesEmitter::EmitInterpFnDispatchers(raw_ostream &OS, StringRef N,
OS << " if (!" << 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;
@@ -407,6 +410,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";
@@ -414,6 +418,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";
More information about the cfe-commits
mailing list