llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> Just parking this here as a draft so I don't forget about it again. --- Patch is 71.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/189410.diff 29 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticASTKinds.td (+3) - (modified) clang/lib/AST/ByteCode/ByteCodeEmitter.cpp (+2-1) - (modified) clang/lib/AST/ByteCode/ByteCodeEmitter.h (+9) - (modified) clang/lib/AST/ByteCode/Compiler.cpp (+201-22) - (modified) clang/lib/AST/ByteCode/Compiler.h (+3) - (modified) clang/lib/AST/ByteCode/Context.cpp (+3) - (modified) clang/lib/AST/ByteCode/Context.h (+2) - (modified) clang/lib/AST/ByteCode/Disasm.cpp (+8) - (modified) clang/lib/AST/ByteCode/EvalEmitter.h (+10) - (added) clang/lib/AST/ByteCode/Exceptions.cpp (+56) - (added) clang/lib/AST/ByteCode/Exceptions.h (+34) - (modified) clang/lib/AST/ByteCode/Function.cpp (+9) - (modified) clang/lib/AST/ByteCode/Function.h (+9-2) - (modified) clang/lib/AST/ByteCode/Interp.cpp (+154-10) - (modified) clang/lib/AST/ByteCode/Interp.h (+84-6) - (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+1) - (modified) clang/lib/AST/ByteCode/InterpFrame.h (-4) - (modified) clang/lib/AST/ByteCode/InterpStack.cpp (+6) - (modified) clang/lib/AST/ByteCode/InterpStack.h (+3) - (modified) clang/lib/AST/ByteCode/InterpState.h (+18) - (modified) clang/lib/AST/ByteCode/Opcodes.td (+40-1) - (modified) clang/lib/AST/ByteCode/PrimType.h (+9) - (modified) clang/lib/AST/ByteCode/Source.h (+2) - (modified) clang/lib/AST/CMakeLists.txt (+1) - (modified) clang/test/AST/ByteCode/cxx20.cpp (+7-8) - (modified) clang/test/AST/ByteCode/cxx23.cpp (+2-2) - (added) clang/test/AST/ByteCode/exceptions.cpp (+1076) - (modified) clang/test/AST/ByteCode/invalid.cpp (+2-28) - (modified) clang/utils/TableGen/ClangOpcodesEmitter.cpp (+12-3) ``````````diff diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index bde418695f647..9318390db1da2 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 note_constexpr_no_active_exception : Note<"throw with no caught exception active">; 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 95f9a2f507335..f826cb4f8c096 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -37,6 +37,17 @@ static std::optional<bool> getBoolValue(const Expr *E) { return std::nullopt; } +[[maybe_unused]] static bool blockEndsInReturn(const Stmt *S) { + if (isa<ReturnStmt>(S)) + return true; + + if (const auto *CS = dyn_cast<CompoundStmt>(S); CS && !CS->body_empty()) { + return isa<ReturnStmt>(CS->body_back()); + } + + return false; +} + /// Scope used to handle temporaries in toplevel variable declarations. template <class Emitter> class DeclScope final : public LocalScope<Emitter> { public: @@ -3471,10 +3482,136 @@ 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(T.value_or(PT_Ptr), ExceptionType.getTypePtr(), + (bool)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; + + // FIXME: Re-enable this. + // if (blockEndsInReturn(TryBlock)) + // continue; + this->jump(EndLabel, S); + } + + this->fallthrough(EndLabel); + this->emitLabel(EndLabel); + + return true; } template <class Emitter> @@ -6877,12 +7014,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()); @@ -6943,11 +7074,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> @@ -7153,11 +7282,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> @@ -7207,7 +7346,11 @@ bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) { return false; // FIXME: Virtual bases. - 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> @@ -7250,14 +7393,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) { @@ -8384,6 +8545,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 0a2e75887711d..1cdc2a3dce297 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -462,6 +462,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 92a2ba57e08bf..afb9ce6c7eb99 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 f939ef4839a19..4e2bc2da5556f 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..cb750b7acd0cf --- /dev/null +++ b/clang/lib/AST/ByteCode/Exceptions.h @@ -0,0 +1,34 @@ +//===----------------------- 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 "clang/Basic/OptionalUnsigned.h" + +namespace clang { +class Type; + +namespace interp { + +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; +}; + +} // namespace interp +} // namespace clang +#endif diff --git a/clang/lib/AST/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp index cd0f7eb125230..02d5c9b13fa78 100644 --- a/clang/lib/AST/ByteCode/Function.cpp +++ b/clang/lib/AST/ByteCode/Function.cpp @@ -69,3 +69,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 289bd64124004..f5dce4d069189 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" @@ -250,6 +251,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, @@ -259,13 +263,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; } @@ -296,6 +302,7 @@ class Function final { llvm... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/189410 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
