Timm =?utf-8?q?Bäder?= <[email protected]> Message-ID: <llvm.org/llvm/llvm-project/pull/[email protected]> In-Reply-To:
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/226175 Once https://github.com/llvm/llvm-project/pull/186045 is merged. >From 70b3efc6edd08272ff6fd8afb93f1ff5bea18f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Thu, 12 Mar 2026 06:05:56 +0100 Subject: [PATCH 1/2] 2 --- clang/lib/AST/ByteCode/ByteCodeEmitter.h | 1 + clang/lib/AST/ByteCode/Compiler.cpp | 6 + clang/lib/AST/ByteCode/Context.cpp | 7 +- clang/lib/AST/ByteCode/Disasm.cpp | 5 +- clang/lib/AST/ByteCode/EvalEmitter.cpp | 70 ++-- clang/lib/AST/ByteCode/EvalEmitter.h | 17 +- clang/lib/AST/ByteCode/EvaluationResult.cpp | 382 +++++++++++++++++- clang/lib/AST/ByteCode/EvaluationResult.h | 32 +- clang/lib/AST/ByteCode/Interp.cpp | 4 +- .../AST/ByteCode/InterpBuiltinObjectSize.cpp | 2 +- clang/lib/AST/ByteCode/Pointer.cpp | 6 +- clang/lib/AST/ByteCode/Pointer.h | 11 +- clang/lib/AST/ExprConstShared.h | 42 ++ clang/lib/AST/ExprConstant.cpp | 145 +++---- clang/test/AST/ByteCode/builtin-functions.cpp | 6 +- .../ByteCode/codegen-constexpr-unknown.cpp | 1 + clang/test/AST/ByteCode/references.cpp | 5 +- clang/test/CodeGenCXX/global-init.cpp | 8 + clang/test/SemaCXX/PR19955.cpp | 3 + clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 4 +- 20 files changed, 599 insertions(+), 158 deletions(-) diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h index 57d967c2b11cd4..4174a26b804640 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h @@ -68,6 +68,7 @@ class ByteCodeEmitter { /// We're always emitting bytecode. bool isActive() const { return true; } bool checkingForUndefinedBehavior() const { return false; } + bool constantFolding() const { return false; } /// Callback for local registration. Local createLocal(const Descriptor *D); diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 81c8fb0b9f17d7..631a91519974dc 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3579,6 +3579,10 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( bool IsStatic = E->getStorageDuration() == SD_Static; if (IsStatic || (ExtendingDecl && Context::shouldBeGloballyIndexed(ExtendingDecl))) { + + if (this->constantFolding()) + return false; + UnsignedOrNone GlobalIndex = P.createGlobal(E, Inner->getType()); if (!GlobalIndex) return false; @@ -5491,6 +5495,8 @@ const Function *Compiler<Emitter>::getFunction(const FunctionDecl *FD) { template <class Emitter> bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) { + assert(E); + assert(!E->getType().isNull()); LocalScope<Emitter> RootScope(this, ScopeKind::FullExpression); auto maybeDestroyLocals = [&]() -> bool { diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index 61ee2d255e9051..9cdb2725a92040 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -76,7 +76,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { size_t StackSizeBefore = Stk.size(); Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); - auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue()); + auto Res = C.interpretExpr(E); if (Res.isInvalid()) { C.cleanup(); @@ -96,7 +96,6 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { } Result = Res.stealAPValue(); - return true; } @@ -105,7 +104,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, ++EvalID; bool Recursing = !Stk.empty(); size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc, Kind); auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false, /*DestroyToplevelScope=*/true); @@ -137,8 +136,8 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); bool CheckGlobalInitialized = - shouldBeGloballyIndexed(VD) && (VD->getType()->isRecordType() || VD->getType()->isArrayType()); + auto Res = C.interpretDecl(VD, Init, CheckGlobalInitialized); if (Res.isInvalid()) { C.cleanup(); diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index 98fbac4dd0eec6..22d0828df423f2 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -643,9 +643,6 @@ LLVM_DUMP_METHOD void EvaluationResult::dump() const { OS << "Invalid\n"; } else { OS << "Value: "; -#ifndef NDEBUG - assert(Ctx); - Value.dump(OS, Ctx->getASTContext()); -#endif + Value.dump(OS, Ctx.getASTContext()); } } diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 0e8b6f9dce56ff..ec7550eb4cc17a 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -18,12 +18,14 @@ using namespace clang; using namespace clang::interp; EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, - InterpStack &Stk, FrameAllocator &FA) - : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(&Ctx) {} + InterpStack &Stk, FrameAllocator &FA, + ConstantExprKind ConstexprKind) + : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(Ctx), + ConstexprKind(ConstexprKind) {} EvalEmitter::EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FA) - : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(&Ctx) {} + : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(Ctx) {} /// Clean up all our resources. This needs to done in failed evaluations before /// we call InterpStack::clear(), because there might be a Pointer on the stack @@ -240,6 +242,18 @@ template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) { return true; } +template <> bool EvalEmitter::emitRet<PT_MemberPtr>(SourceInfo Info) { + if (!isActive()) + return true; + + const MemberPointer &MP = S.Stk.pop<MemberPointer>(); + if (!EvalResult.checkMemberPointer(S, MP, Info, ConstexprKind)) + return false; + + EvalResult.takeValue(MP.toAPValue(Ctx.getASTContext())); + return true; +} + template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) { if (!isActive()) return true; @@ -251,6 +265,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) { if (!EvalResult.checkDynamicAllocations(S, Ptr, Info)) return false; + if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) return false; @@ -258,6 +273,9 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) { if (Ptr.isFunctionPointer()) { if (ConvertResultToRValue && Ptr.asFunctionPointer().Func->getDecl()) return false; + if (!EvalResult.checkFunctionPointer(S, Ptr, Info, ConstexprKind)) + return false; + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } @@ -276,37 +294,39 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) { Ptr.block()->getEvalID() != Ctx.getEvalID()) return false; + if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind)) + return false; + if (std::optional<APValue> V = Ptr.toRValue(Ctx, EvalResult.getSourceType())) { EvalResult.takeValue(std::move(*V)); - } else { - return false; - } - } else { - // If this is pointing to a local variable, just return - // the result, even if the pointer is dead. - // This will later be diagnosed by CheckLValueConstantExpression. - if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) { - EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } + return false; + } - if (!Ptr.isLive() && !Ptr.isTemporary()) - return false; + // Return as lvalue. + if (!EvalResult.checkLValue(S, Ptr, Info, ConstexprKind)) + return false; - // If the variable of this pointer is being evaluated when returning - // its value, mark it as constexpr-unknown. - APValue V = Ptr.toAPValue(Ctx.getASTContext()); - if (const Descriptor *DeclDesc = Ptr.getDeclDesc(); - DeclDesc && S.EvaluatingDecl && - DeclDesc->asVarDecl() == S.EvaluatingDecl && + if (!Ptr.isLive() && !Ptr.isTemporary()) + return false; + + if (const Descriptor *DeclDesc = Ptr.getDeclDesc(); + DeclDesc && S.EvaluatingDecl && + ((DeclDesc->asVarDecl() == S.EvaluatingDecl && S.getLangOpts().CPlusPlus23 && - S.EvaluatingDecl->getType()->isReferenceType()) { - V.setConstexprUnknown(true); - } - EvalResult.takeValue(std::move(V)); + S.EvaluatingDecl->getType()->isReferenceType()) || + DeclDesc->IsConstexprUnknown)) { + S.FFDiag(Info, diag::note_constexpr_var_init_non_constant, 1) + << DeclDesc->asVarDecl(); + S.Note(DeclDesc->asVarDecl()->getLocation(), diag::note_declared_at); + + return false; } + APValue V = Ptr.toAPValue(Ctx.getASTContext()); + EvalResult.takeValue(std::move(V)); return true; } @@ -322,6 +342,8 @@ bool EvalEmitter::emitRetValue(SourceInfo Info) { return false; if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) return false; + if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind)) + return false; if (std::optional<APValue> APV = Ptr.toRValue(Ctx, EvalResult.getSourceType())) { diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 069758615012d7..70ad775e84f81c 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -37,9 +37,14 @@ class EvalEmitter : public SourceMapper { using PtrCallback = llvm::function_ref<bool(InterpState &S, CodePtr OpPC, const Pointer &)>; - EvaluationResult interpretExpr(const Expr *E, - bool ConvertResultToRValue = false, + EvaluationResult interpretExpr(const Expr *E) { + return interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue(), + /*DestroyToplevelScope=*/false); + } + + EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue, bool DestroyToplevelScope = false); + EvaluationResult interpretDecl(const VarDecl *VD, const Expr *Init, bool CheckFullyInitialized); EvaluationResult interpretDestructor(const VarDecl *VD, const APValue &Value); @@ -61,9 +66,14 @@ class EvalEmitter : public SourceMapper { /// Returns the source location of the current opcode. SourceInfo getSource(CodePtr PC) const override { return CurrentSource; } + bool constantFolding() const { + return S.EvalMode == EvaluationMode::ConstantFold; + } + protected: EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, - FrameAllocator &FrameAlloc); + FrameAllocator &FrameAlloc, + ConstantExprKind ConstexprKind = ConstantExprKind::Normal); EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FrameAlloc); @@ -120,6 +130,7 @@ class EvalEmitter : public SourceMapper { InterpState S; /// Location to write the result to. EvaluationResult EvalResult; + ConstantExprKind ConstexprKind = ConstantExprKind::Normal; /// Whether the result should be converted to an RValue. bool ConvertResultToRValue = false; /// Whether we should check if the result has been fully diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index 09b1eb822b13e2..30976fcf1d5213 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -7,9 +7,14 @@ //===----------------------------------------------------------------------===// #include "EvaluationResult.h" +#include "../ExprConstShared.h" #include "InterpState.h" #include "Pointer.h" #include "Record.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include <iterator> @@ -17,6 +22,19 @@ namespace clang { namespace interp { +QualType EvaluationResult::getStorageType() const { + if (const auto *E = Source.asExpr()) { + if (E->isPRValue()) + return E->getType(); + + return Ctx.getASTContext().getLValueReferenceType(E->getType()); + } + + if (const auto *D = Source.asValueDecl()) + return D->getType(); + return QualType(); +} + static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, const FieldDecl *SubObjDecl) { assert(SubObjDecl && "Subobject declaration does not exist"); @@ -245,9 +263,10 @@ static void collectBlocks(PtrView Ptr, bool EvaluationResult::checkDynamicAllocations(InterpState &S, const Pointer &Ptr, - SourceInfo Info) { + SourceInfo Info) const { if (!Ptr.isBlockPointer()) return true; + // Collect all blocks that this pointer (transitively) points to and // return false if any of them is a dynamic block. llvm::SmallPtrSet<const Block *, 4> Blocks; @@ -271,5 +290,366 @@ bool EvaluationResult::checkDynamicAllocations(InterpState &S, return true; } +static bool isGlobalLValue(const Pointer &Ptr) { + if (Ptr.isDynamic()) + return true; + if (Ptr.isTypeidPointer()) + return true; + + return ::isGlobalLValue(Ptr.getRootValueDecl(), Ptr.getRootExpr()); +} + +/// Check if the given function pointer can be returned from an evaluation. +static bool checkFunctionPtr(InterpState &S, const Pointer &Ptr, + QualType PtrType, SourceInfo Info, + ConstantExprKind ConstexprKind) { + assert(Ptr.isFunctionPointer()); + const FunctionPointer &FuncPtr = Ptr.asFunctionPointer(); + + if (!FuncPtr.Func) + return true; + + const FunctionDecl *FD = FuncPtr.Func->getDecl(); + // E.g. ObjC block pointers. + if (!FD) + return true; + if (FD->isImmediateFunction()) { + S.FFDiag(Info, diag::note_consteval_address_accessible) + << !PtrType->isAnyPointerType(); + S.Note(FD->getLocation(), diag::note_declared_at); + return false; + } + + // __declspec(dllimport) must be handled very carefully: + // We must never initialize an expression with the thunk in C++. + // Doing otherwise would allow the same id-expression to yield + // different addresses for the same function in different translation + // units. However, this means that we must dynamically initialize the + // expression with the contents of the import address table at runtime. + // + // The C language has no notion of ODR; furthermore, it has no notion of + // dynamic initialization. This means that we are permitted to + // perform initialization with the address of the thunk. + if (S.getLangOpts().CPlusPlus && !isForManglingOnly(ConstexprKind) && + FD->hasAttr<DLLImportAttr>()) + // FIXME: Diagnostic! + return false; + return true; +} + +static bool lvalFields(InterpState &S, const ASTContext &Ctx, PtrView Ptr, + QualType PtrType, SourceInfo Info, + ConstantExprKind ConstexprKind, + llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks); +static bool lval(InterpState &S, const ASTContext &Ctx, const Pointer &Ptr, + QualType PtrType, SourceInfo Info, + ConstantExprKind ConstexprKind, + llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) { + if (Ptr.isFunctionPointer()) + return checkFunctionPtr(S, Ptr, PtrType, Info, ConstexprKind); + + if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer()) + return true; + + const Expr *BaseE = Ptr.getRootExpr(); + const ValueDecl *BaseVD = Ptr.getRootValueDecl(); + assert(BaseE || BaseVD); + bool IsReferenceType = PtrType->isReferenceType(); + bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot()); + + if (!isGlobalLValue(Ptr)) { + if (S.getLangOpts().CPlusPlus11) { + S.FFDiag(Info, diag::note_constexpr_non_global, 1) + << IsReferenceType << IsSubObj << !!BaseVD << BaseVD; + const VarDecl *VarD = dyn_cast_if_present<VarDecl>(BaseVD); + if (VarD && VarD->isConstexpr()) { + // Non-static local constexpr variables have unintuitive semantics: + // constexpr int a = 1; + // constexpr const int *p = &a; + // ... is invalid because the address of 'a' is not constant. Suggest + // adding a 'static' in this case. + S.Note(VarD->getLocation(), diag::note_constexpr_not_static) + << VarD + << FixItHint::CreateInsertion(VarD->getBeginLoc(), "static "); + } else { + if (BaseVD) + S.Note(BaseVD->getLocation(), diag::note_declared_at); + else if (BaseE) + S.Note(BaseE->getExprLoc(), diag::note_constexpr_temporary_here); + } + } else { + S.FFDiag(Info); + } + return false; + } + + if (const auto *VD = dyn_cast_if_present<VarDecl>(BaseVD)) { + // Check if this is a thread-local variable. + if (VD->getTLSKind()) { + // FIXME: Diagnostic! + return false; + } + + // A dllimport variable never acts like a constant, unless we're + // evaluating a value for use only in name mangling, and unless it's a + // static local. For the latter case, we'd still need to evaluate the + // constant expression in case we're inside a (inlined) function. + if (!isForManglingOnly(ConstexprKind) && VD->hasAttr<DLLImportAttr>() && + !VD->isStaticLocal()) + return false; + + // Address of a managed variable is never a constant expression. + if (S.getLangOpts().CUDA && VD->hasAttr<HIPManagedAttr>()) + return false; + + // In CUDA/HIP device compilation, only device side variables have + // constant addresses. + if (S.getLangOpts().CUDA && S.getLangOpts().CUDAIsDevice && + Ctx.CUDAConstantEvalCtx.NoWrongSidedVars) { + if ((!VD->hasAttr<CUDADeviceAttr>() && !VD->hasAttr<CUDAConstantAttr>() && + !VD->getType()->isCUDADeviceBuiltinSurfaceType() && + !VD->getType()->isCUDADeviceBuiltinTextureType())) + return false; + } + + return true; + } + + if (const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(BaseE)) { + QualType TempType = Ptr.getType(); + + if (TempType.isDestructedType()) { + S.FFDiag(MTE->getExprLoc(), + diag::note_constexpr_unsupported_temporary_nontrivial_dtor) + << TempType; + return false; + } + + if (Ptr.getFieldDesc()->isPrimitive() && + Ptr.getFieldDesc()->getPrimType() == PT_Ptr) { + // Recurse! + Pointer Pointee = Ptr.deref<Pointer>(); + if (!Pointee.isBlockPointer() || + CheckedBlocks.insert(Pointee.block()).second) { + if (!lval(S, Ctx, Pointee, Pointee.getType(), + Ptr.getDeclDesc()->getLoc(), ConstexprKind, CheckedBlocks)) + return false; + } + } else { + if (!lvalFields(S, Ctx, Ptr.view(), TempType, Info, ConstexprKind, + CheckedBlocks)) + return false; + } + } + + return true; +} + +static bool lvalFields(InterpState &S, const ASTContext &Ctx, PtrView Ptr, + QualType PtrType, SourceInfo Info, + ConstantExprKind ConstexprKind, + llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) { + const Descriptor *FieldDesc = Ptr.getFieldDesc(); + if (const Record *R = Ptr.getRecord()) { + if (!R->hasPtrField()) + return true; + + for (const Record::Base &B : R->bases()) { + if (!B.R->hasPtrField()) + continue; + + PtrView BasePtr = Ptr.atField(B.Offset); + if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind, + CheckedBlocks)) + return false; + } + + for (const Record::Field &F : R->fields()) { + PtrView FieldPtr = Ptr.atField(F.Offset); + if (!isOrHasPtr(F.Desc)) + continue; + + if (F.Desc->isPrimitive() && F.Desc->getPrimType() == PT_Ptr) { + if (!FieldPtr.isLive()) + return false; + + Pointer Pointee = FieldPtr.deref<Pointer>(); + if (!Pointee.isBlockPointer() || + CheckedBlocks.insert(Pointee.block()).second) { + QualType FieldType = F.Decl->getType(); + if (!lval(S, Ctx, Pointee, FieldType, Info, ConstexprKind, + CheckedBlocks)) + return false; + } + } else { + if (!lvalFields(S, Ctx, FieldPtr, F.Decl->getType(), Info, + ConstexprKind, CheckedBlocks)) + return false; + } + } + + for (const Record::Base &B : R->virtual_bases()) { + if (!B.R->hasPtrField()) + continue; + PtrView BasePtr = Ptr.atField(B.Offset); + if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind, + CheckedBlocks)) + return false; + } + return true; + } + + if (FieldDesc->isPrimitiveArray() && FieldDesc->getPrimType() == PT_Ptr) { + for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { + if (!Ptr.isLive()) + return false; + Pointer Pointee = Ptr.elem<Pointer>(I); + + if (!Pointee.isBlockPointer() || + CheckedBlocks.insert(Pointee.block()).second) { + if (!lval(S, Ctx, Pointee, FieldDesc->getElemQualType(), Info, + ConstexprKind, CheckedBlocks)) + return false; + } + } + return true; + } + + if (FieldDesc->isCompositeArray()) { + if (FieldDesc->ElemRecord && !FieldDesc->ElemRecord->hasPtrField()) + return true; + + for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) { + PtrView Elem = Ptr.atIndex(I).narrow(); + if (!lvalFields(S, Ctx, Elem, FieldDesc->getElemQualType(), Info, + ConstexprKind, CheckedBlocks)) + return false; + } + return true; + } + if (FieldDesc->isPrimitive() && FieldDesc->getPrimType() == PT_MemberPtr) { + MemberPointer MP = Ptr.deref<MemberPointer>(); + if (!EvaluationResult::checkMemberPointer(S, MP, Info, ConstexprKind)) + return false; + } + + return true; +} + +/// Toplevel accessor to check all lvalue fields. +bool EvaluationResult::checkLValueFields(InterpState &S, const Pointer &Ptr, + SourceInfo Info, + ConstantExprKind ConstexprKind) const { + if (!Ptr.isBlockPointer()) + return true; + + QualType SourceType = getStorageType(); + llvm::SmallPtrSet<const Block *, 4> CheckedBlocks; + + return lvalFields(S, Ctx.getASTContext(), Ptr.view(), SourceType, Info, + ConstexprKind, CheckedBlocks); +} + +bool EvaluationResult::checkLValue(InterpState &S, const Pointer &Ptr, + SourceInfo Info, + ConstantExprKind ConstexprKind) const { + QualType SourceType = getStorageType(); + if (Ptr.isFunctionPointer()) + return checkFunctionPtr(S, Ptr, SourceType, Info, ConstexprKind); + + if (Ptr.isZero()) + return true; + + bool IsReferenceType = SourceType->isReferenceType(); + if (Ptr.isTypeidPointer()) { + if (isTemplateArgument(ConstexprKind)) { + S.FFDiag(Info, diag::note_constexpr_invalid_template_arg) + << IsReferenceType << /*IsSubObj=*/false << /*InvalidBaseKind=*/0; + return false; + } + return true; + } + + if (Ptr.isStringPointer()) { + // Additional restrictions apply in a template argument. We only enforce the + // C++20 restrictions here; additional syntactic and semantic restrictions + // are applied elsewhere. + if (isTemplateArgument(ConstexprKind)) { + bool IsSubObj = Ptr.asStringPointer().Decayed || Ptr.getIndex() != 0; + int InvalidBaseKind = -1; + StringRef Ident; + const Expr *BaseE = Ptr.asStringPointer().Base; + if (isa_and_nonnull<StringLiteral>(BaseE)) + InvalidBaseKind = 1; + else if (const auto *PE = dyn_cast_if_present<PredefinedExpr>(BaseE)) { + InvalidBaseKind = 3; + Ident = PE->getIdentKindName(); + } + + if (InvalidBaseKind != -1) { + S.FFDiag(Info, diag::note_constexpr_invalid_template_arg) + << IsReferenceType << IsSubObj << InvalidBaseKind << Ident; + return false; + } + } + return true; + } + + if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer()) + return true; + + // Additional restrictions apply in a template argument. We only enforce the + // C++20 restrictions here; additional syntactic and semantic restrictions + // are applied elsewhere. + if (isTemplateArgument(ConstexprKind)) { + const Expr *BaseE = Ptr.getRootExpr(); + const ValueDecl *BaseVD = Ptr.getRootValueDecl(); + assert(BaseE || BaseVD); + if (isa_and_nonnull<MaterializeTemporaryExpr>(BaseE) || + isa_and_nonnull<LifetimeExtendedTemporaryDecl>(BaseVD)) { + bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot()); + S.FFDiag(Info, diag::note_constexpr_invalid_template_arg) + << IsReferenceType << IsSubObj << 2; + return false; + } + } + + llvm::SmallPtrSet<const Block *, 4> CheckedBlocks; + if (!lval(S, Ctx.getASTContext(), Ptr, SourceType, Info, ConstexprKind, + CheckedBlocks)) { + return false; + } + + return true; +} + +bool EvaluationResult::checkMemberPointer(InterpState &S, + const MemberPointer &MemberPtr, + SourceInfo Info, + ConstantExprKind ConstexprKind) { + const CXXMethodDecl *MD = MemberPtr.getMemberFunction(); + if (!MD) + return true; + + if (MD->isImmediateFunction()) { + S.FFDiag(Info, diag::note_consteval_address_accessible) + << /*pointer=*/false; + S.Note(MD->getLocation(), diag::note_declared_at); + return false; + } + + if (isForManglingOnly(ConstexprKind) || MD->isVirtual() || + !MD->hasAttr<DLLImportAttr>()) { + return true; + } + return false; +} + +bool EvaluationResult::checkFunctionPointer( + InterpState &S, const Pointer &Ptr, SourceInfo Info, + ConstantExprKind ConstexprKind) const { + return checkFunctionPtr(S, Ptr, getStorageType(), Info, ConstexprKind); +} + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/ByteCode/EvaluationResult.h b/clang/lib/AST/ByteCode/EvaluationResult.h index b8d8ddf981f5aa..948f822e69142b 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.h +++ b/clang/lib/AST/ByteCode/EvaluationResult.h @@ -18,6 +18,7 @@ namespace clang { namespace interp { class EvalEmitter; class Context; +class MemberPointer; class Pointer; class SourceInfo; class InterpState; @@ -38,9 +39,7 @@ class EvaluationResult final { }; private: -#ifndef NDEBUG - const Context *Ctx = nullptr; -#endif + const Context &Ctx; APValue Value; ResultKind Kind = Empty; DeclOrExpr Source = nullptr; @@ -63,17 +62,14 @@ class EvaluationResult final { Kind = Valid; } + QualType getStorageType() const; + public: -#ifndef NDEBUG - EvaluationResult(const Context *Ctx) : Ctx(Ctx) {} -#else - EvaluationResult(const Context *Ctx) {} -#endif + EvaluationResult(const Context &Ctx) : Ctx(Ctx) {} bool empty() const { return Kind == Empty; } bool isInvalid() const { return Kind == Invalid; } - /// Moves the APValue containing the evaluation result to the caller. APValue stealAPValue() { return std::move(Value); } /// Check that all subobjects of the given pointer have been initialized. @@ -81,7 +77,23 @@ class EvaluationResult final { /// Check that none of the blocks the given pointer (transitively) points /// to are dynamically allocated. bool checkDynamicAllocations(InterpState &S, const Pointer &Ptr, - SourceInfo Info); + SourceInfo Info) const; + + /// Check the given pointer as an lvalue, i.e. make sure it's a global + /// lvalue and diagnose if it's not. + bool checkLValue(InterpState &S, const Pointer &Ptr, SourceInfo Info, + ConstantExprKind ConstexprKind) const; + /// Check all fields of the given pointer. + bool checkLValueFields(InterpState &S, const Pointer &Ptr, SourceInfo Info, + ConstantExprKind ConstexprKind) const; + + /// Check if the given member pointer can be returned from an evaluation. + static bool checkMemberPointer(InterpState &S, const MemberPointer &MemberPtr, + SourceInfo Info, + ConstantExprKind ConstexprKind); + /// Check if the given function pointer can be returned from an evaluation. + bool checkFunctionPointer(InterpState &S, const Pointer &Ptr, SourceInfo Info, + ConstantExprKind ConstexprKind) const; QualType getSourceType() const { if (const auto *D = Source.asValueDecl()) diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index f885c0dc9cdb2a..aa47b6115eb27f 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -875,6 +875,8 @@ static bool CheckLifetime(InterpState &S, CodePtr OpPC, Lifetime LT, } static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { + if (!Ptr.isBlockPointer()) + return true; return CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK); } @@ -1096,7 +1098,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckMutable(S, OpPC, Ptr.view())) return false; } - if (Ptr.isConstexprUnknown()) + if (!S.inConstantContext() && isConstexprUnknown(Ptr)) return false; return true; } diff --git a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp index ec3a7af4e1e137..41b347150886f8 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp @@ -352,7 +352,7 @@ computeOpaqueSize(const ASTContext &ASTCtx, const Pointer &Ptr, return TypeSize.getQuantity(); // Check if we need to add the flexible array member size. - const VarDecl *Base = OP.getBaseDecl(); + const VarDecl *Base = OP.getBaseVarDecl(); if (!Base) return TypeSize.getQuantity(); diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 552cdf1bf8ec71..c4c3b1be46a327 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -1330,8 +1330,12 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, } const VarDecl *Pointer::getRootVarDecl() const { + return dyn_cast_if_present<VarDecl>(getRootValueDecl()); +} + +const ValueDecl *Pointer::getRootValueDecl() const { if (isBlockPointer()) - return getDeclDesc()->asVarDecl(); + return getDeclDesc()->asValueDecl(); if (isOpaquePointer()) return Opaque.getBaseDecl(); return nullptr; diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 207ec06a32a34d..c7f0da0951121a 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -445,7 +445,8 @@ struct OpaquePointer { ArrayRef<PointerPathEntry> path() const { return ArrayRef(Path, PathLength); } bool hasDeclBase() const { return Base.isDecl(); } - const VarDecl *getBaseDecl() const { return Base.asVarDecl(); } + const ValueDecl *getBaseDecl() const { return Base.asValueDecl(); } + const VarDecl *getBaseVarDecl() const { return Base.asVarDecl(); } const Expr *getBaseExpr() const { return Base.asExpr(); } bool hasValidBase() const; @@ -742,6 +743,7 @@ class Pointer { } const VarDecl *getRootVarDecl() const; + const ValueDecl *getRootValueDecl() const; const Expr *getRootExpr() const; [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); } @@ -921,7 +923,7 @@ class Pointer { } if (isOpaquePointer()) { - if (const VarDecl *BaseDecl = Opaque.getBaseDecl()) + if (const VarDecl *BaseDecl = Opaque.getBaseVarDecl()) return BaseDecl->isWeak(); return false; } @@ -998,7 +1000,10 @@ class Pointer { return view().getNumElems(); } - const Block *block() const { return BS.Pointee; } + const Block *block() const { + assert(isBlockPointer()); + return BS.Pointee; + } /// If backed by actual data (i.e. a block or string pointer), return /// an address to that data. diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h index ca3481270605e0..7693ea25592f12 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H #define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +#include "ByteCode/State.h" #include "clang/Basic/BuiltinTraits.h" #include <cstdint> #include <optional> @@ -115,5 +116,46 @@ const Expr *ignorePointerCastsAndParens(const Expr *E); bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD); bool isReadByLvalueToRvalueConversion(QualType T); +/// Determines whether the given kind of constant expression is only ever +/// used for name mangling. If so, it's permitted to reference things that we +/// can't generate code for (in particular, dllimported functions). +inline bool isForManglingOnly(ConstantExprKind Kind) { + switch (Kind) { + case ConstantExprKind::Normal: + case ConstantExprKind::ClassTemplateArgument: + case ConstantExprKind::ImmediateInvocation: + // Note that non-type template arguments of class type are emitted as + // template parameter objects. + return false; + + case ConstantExprKind::NonClassTemplateArgument: + return true; + } + llvm_unreachable("unknown ConstantExprKind"); +} + +inline bool isTemplateArgument(ConstantExprKind Kind) { + switch (Kind) { + case ConstantExprKind::Normal: + case ConstantExprKind::ImmediateInvocation: + return false; + + case ConstantExprKind::ClassTemplateArgument: + case ConstantExprKind::NonClassTemplateArgument: + return true; + } + llvm_unreachable("unknown ConstantExprKind"); +} + +/// Should this call expression be treated as forming an opaque constant? +inline bool isOpaqueConstantCall(const CallExpr *E) { + unsigned Builtin = E->getBuiltinCallee(); + return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || + Builtin == Builtin::BI__builtin___NSStringMakeConstantString || + Builtin == Builtin::BI__builtin_ptrauth_sign_constant || + Builtin == Builtin::BI__builtin_function_start); +} + +bool isGlobalLValue(const ValueDecl *D, const Expr *E); #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 2df754dc9007f2..3efc6ffe7e9079 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -150,37 +150,6 @@ namespace { return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E); } - /// Determines whether the given kind of constant expression is only ever - /// used for name mangling. If so, it's permitted to reference things that we - /// can't generate code for (in particular, dllimported functions). - static bool isForManglingOnly(ConstantExprKind Kind) { - switch (Kind) { - case ConstantExprKind::Normal: - case ConstantExprKind::ClassTemplateArgument: - case ConstantExprKind::ImmediateInvocation: - // Note that non-type template arguments of class type are emitted as - // template parameter objects. - return false; - - case ConstantExprKind::NonClassTemplateArgument: - return true; - } - llvm_unreachable("unknown ConstantExprKind"); - } - - static bool isTemplateArgument(ConstantExprKind Kind) { - switch (Kind) { - case ConstantExprKind::Normal: - case ConstantExprKind::ImmediateInvocation: - return false; - - case ConstantExprKind::ClassTemplateArgument: - case ConstantExprKind::NonClassTemplateArgument: - return true; - } - llvm_unreachable("unknown ConstantExprKind"); - } - /// The bound to claim that an array of unknown bound has. /// The value in MostDerivedArraySize is undefined in this case. So, set it /// to an arbitrary value that's likely to loudly break things if it's used. @@ -1940,31 +1909,30 @@ static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { return true; } -/// Should this call expression be treated as forming an opaque constant? -static bool IsOpaqueConstantCall(const CallExpr *E) { - unsigned Builtin = E->getBuiltinCallee(); - return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString || - Builtin == Builtin::BI__builtin___NSStringMakeConstantString || - Builtin == Builtin::BI__builtin_ptrauth_sign_constant || - Builtin == Builtin::BI__builtin_function_start); -} - static bool IsOpaqueConstantCall(const LValue &LVal) { const auto *BaseExpr = llvm::dyn_cast_if_present<CallExpr>(LVal.Base.dyn_cast<const Expr *>()); - return BaseExpr && IsOpaqueConstantCall(BaseExpr); + return BaseExpr && isOpaqueConstantCall(BaseExpr); } static bool IsGlobalLValue(APValue::LValueBase B) { + if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>()) + return true; + + return isGlobalLValue(B.dyn_cast<const ValueDecl *>(), + B.dyn_cast<const Expr *>()); +} + +bool isGlobalLValue(const ValueDecl *D, const Expr *E) { // C++11 [expr.const]p3 An address constant expression is a prvalue core // constant expression of pointer type that evaluates to... // ... a null pointer value, or a prvalue core constant expression of type // std::nullptr_t. - if (!B) + if (!D && !E) return true; - if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) { + if (D) { // ... the address of an object with static storage duration, if (const VarDecl *VD = dyn_cast<VarDecl>(D)) return VD->hasGlobalStorage(); @@ -1976,10 +1944,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { return isa<FunctionDecl, MSGuidDecl, UnnamedGlobalConstantDecl>(D); } - if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>()) - return true; + assert(E); - const Expr *E = B.get<const Expr*>(); switch (E->getStmtClass()) { default: return false; @@ -2002,7 +1968,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) { case Expr::ObjCDictionaryLiteralClass: return cast<ObjCObjectLiteral>(E)->isExpressibleAsConstantInitializer(); case Expr::CallExprClass: - return IsOpaqueConstantCall(cast<CallExpr>(E)); + return isOpaqueConstantCall(cast<CallExpr>(E)); // For GCC compatibility, &&label has static storage duration. case Expr::AddrLabelExprClass: return true; @@ -2023,6 +1989,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) { // an expression might be a global lvalue. return true; } + + return false; } static const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { @@ -10636,7 +10604,7 @@ static bool isOneByteCharacterType(QualType T) { bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinOp) { - if (IsOpaqueConstantCall(E)) + if (isOpaqueConstantCall(E)) return Success(E); switch (BuiltinOp) { @@ -21837,12 +21805,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { if (!CheckLiteralType(Info, E)) return false; - if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result)) - return false; - return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result, - ConstantExprKind::Normal); - } + if (Info.EnableNewConstInterp) + return Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result); if (!::Evaluate(Result, Info, E)) return false; @@ -22043,14 +22007,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, CheckedTemporaries CheckedTemps; if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, - ConstantExprKind::Normal)) - return false; - - LV.setFrom(Ctx, Result.Val); - return CheckLValueConstantExpression( - Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV, - ConstantExprKind::Normal, CheckedTemps); + return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, + ConstantExprKind::Normal); } if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || @@ -22102,12 +22060,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind)) - return false; - return CheckConstantExpression(Info, getExprLoc(), - getStorageType(Ctx, this), Result.Val, Kind); - } + if (Info.EnableNewConstInterp) + return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind); // The type of the object we're initializing is 'const T' for a class NTTP. QualType T = getType(); @@ -22181,40 +22135,35 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, if (Info.EnableNewConstInterp) { auto &InterpCtx = Ctx.getInterpContext(); - if (!InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val)) - return false; - - return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val, - ConstantExprKind::Normal); - } else { - LValue LVal; - LVal.set(VD); - - { - // C++23 [intro.execution]/p5 - // A full-expression is ... an init-declarator ([dcl.decl]) or a - // mem-initializer. - // So we need to make sure temporary objects are destroyed after having - // evaluated the expression (per C++23 [class.temporary]/p4). - // - // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the - // serialization code calls ParmVarDecl::getDefaultArg() which strips the - // outermost FullExpr, such as ExprWithCleanups. - FullExpressionRAII Scope(Info); - if (!EvaluateInPlace(EStatus.Val, Info, LVal, this, - /*AllowNonLiteralTypes=*/true) || - EStatus.HasSideEffects) - return false; - } + return InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val); + } - // At this point, any lifetime-extended temporaries are completely - // initialized. - Info.performLifetimeExtension(); + LValue LVal; + LVal.set(VD); - if (!Info.discardCleanups()) - llvm_unreachable("Unhandled cleanup; missing full expression marker?"); + { + // C++23 [intro.execution]/p5 + // A full-expression is ... an init-declarator ([dcl.decl]) or a + // mem-initializer. + // So we need to make sure temporary objects are destroyed after having + // evaluated the expression (per C++23 [class.temporary]/p4). + // + // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the + // serialization code calls ParmVarDecl::getDefaultArg() which strips the + // outermost FullExpr, such as ExprWithCleanups. + FullExpressionRAII Scope(Info); + if (!EvaluateInPlace(EStatus.Val, Info, LVal, this, + /*AllowNonLiteralTypes=*/true) || + EStatus.HasSideEffects) + return false; } + // At this point, any lifetime-extended temporaries are completely + // initialized. + Info.performLifetimeExtension(); + + if (!Info.discardCleanups()) + llvm_unreachable("Unhandled cleanup; missing full expression marker?"); return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val, ConstantExprKind::Normal) && CheckMemoryLeaks(Info); diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp index 51e1b6cde47791..5d8c0fa2b7b6b0 100644 --- a/clang/test/AST/ByteCode/builtin-functions.cpp +++ b/clang/test/AST/ByteCode/builtin-functions.cpp @@ -2040,9 +2040,9 @@ namespace WithinLifetime { constexpr const int &temp = 0; // both-error {{must be initialized by a constant expression}} \ // both-note {{reference to temporary is not a constant expression}} \ // both-note {{temporary created here}} \ - // ref-note {{declared here}} - static_assert(__builtin_is_within_lifetime(&temp)); // ref-error {{not an integral constant expression}} \ - // ref-note {{initializer of 'temp' is not a constant expression}} + // both-note {{declared here}} + static_assert(__builtin_is_within_lifetime(&temp)); // both-error {{not an integral constant expression}} \ + // both-note {{initializer of 'temp' is not a constant expression}} } } diff --git a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp index 8b70f5a4251adf..3d1e31d5292ed6 100644 --- a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp +++ b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp @@ -21,6 +21,7 @@ void rightscope() { // CHECK-NEXT: entry: // CHECK-NEXT: %p = alloca i32 // CHECK-NEXT: store i32 0, ptr %p +// CHECK-NEXT: ret void /// In the if expression below, the read from s.i should fail. diff --git a/clang/test/AST/ByteCode/references.cpp b/clang/test/AST/ByteCode/references.cpp index a4a8100f5b31e1..3da3996aaca756 100644 --- a/clang/test/AST/ByteCode/references.cpp +++ b/clang/test/AST/ByteCode/references.cpp @@ -188,10 +188,9 @@ namespace ReadFromNullBlockPtr { constexpr S s = {&x}; // both-error {{must be initialized by a constant expression}} \ // both-note {{reference to temporary}} \ // both-note {{created here}} \ - // ref-note {{declared here}} \ - // expected-note {{created here}} + // ref-note {{declared here}} static_assert(s.t == &x, ""); // both-error {{not an integral constant expression}} \ - // expected-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}} \ + // expected-note {{read of dereferenced null pointer}} \ // ref-note {{initializer of 's' is not a constant expression}} } } diff --git a/clang/test/CodeGenCXX/global-init.cpp b/clang/test/CodeGenCXX/global-init.cpp index 52039a52082238..f10f1be4ce95db 100644 --- a/clang/test/CodeGenCXX/global-init.cpp +++ b/clang/test/CodeGenCXX/global-init.cpp @@ -6,6 +6,14 @@ // RUN: | FileCheck -check-prefix CHECK-NOBUILTIN %s // RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - | FileCheck %s +// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,PRE17 +// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK-NOEXC,PRE17 +// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -mframe-pointer=non-leaf %s -o - -fexperimental-new-constant-interpreter \ +// RUN: | FileCheck -check-prefix CHECK-FP %s +// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fno-builtin -fexperimental-new-constant-interpreter \ +// RUN: | FileCheck -check-prefix CHECK-NOBUILTIN %s +// RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s + struct A { A(); ~A(); diff --git a/clang/test/SemaCXX/PR19955.cpp b/clang/test/SemaCXX/PR19955.cpp index cbbe2fe9af1641..6fa22ab8463749 100644 --- a/clang/test/SemaCXX/PR19955.cpp +++ b/clang/test/SemaCXX/PR19955.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s // RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s +// RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter + extern int __attribute__((dllimport)) var; constexpr int *varp = &var; // expected-error {{must be initialized by a constant expression}} diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp index 44189ff31dae8e..9045c9b4ae7aef 100644 --- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp +++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp @@ -799,8 +799,8 @@ void not_constant_expression() { expected-note {{in instantiation of expansion statement requested here}} \ old-interp-note {{read of variable '[__u0]' whose value is not known}} \ old-interp-note {{declared here}} \ - new-interp-note {{temporary created here}} \ - new-interp-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}} + new-interp-note {{declared here}} \ + new-interp-note {{initializer of '[__u0]' is not a constant expression}} g(x); } } >From dae4d36bd5a083bc8d844fdd13d54088359710d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Thu, 24 Sep 2026 16:08:13 +0200 Subject: [PATCH 2/2] [clang][bytecode] Use EvalSettings struct --- clang/lib/AST/ByteCode/Context.cpp | 114 +++++++++++++------- clang/lib/AST/ByteCode/Context.h | 41 ++++--- clang/lib/AST/ByteCode/EvalEmitter.cpp | 6 ++ clang/lib/AST/ByteCode/EvalEmitter.h | 3 + clang/lib/AST/ByteCode/InterpState.cpp | 30 ++++-- clang/lib/AST/ByteCode/InterpState.h | 6 +- clang/lib/AST/ExprConstant.cpp | 143 +++++++++++++++---------- 7 files changed, 225 insertions(+), 118 deletions(-) diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index 9cdb2725a92040..a08f93335957ef 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -12,6 +12,7 @@ #include "Char.h" #include "Compiler.h" #include "EvalEmitter.h" +#include "EvalSettings.h" #include "Integral.h" #include "InterpFrame.h" #include "InterpHelpers.h" @@ -37,7 +38,8 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) { Context::~Context() = default; -bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { +bool Context::isPotentialConstantExpr(const EvalSettings &Settings, + const FunctionDecl *FD) { assert(Stk.empty()); // Get a function handle. @@ -54,15 +56,16 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { ++EvalID; // And run it. - return Run(Parent, Func); + return Run(Settings, Func); } -void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E, +void Context::isPotentialConstantExprUnevaluated(const EvalSettings &Settings, + const Expr *E, const FunctionDecl *FD) { assert(Stk.empty()); ++EvalID; size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); if (!C.interpretCall(FD, E)) { C.cleanup(); @@ -99,12 +102,42 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { return true; } -bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, - ConstantExprKind Kind) { +bool Context::evaluateAsRValue(const EvalSettings &Settings, const Expr *E, + APValue &Result) { ++EvalID; bool Recursing = !Stk.empty(); size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc, Kind); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); + + auto Res = C.interpretExpr(E); + + if (Res.isInvalid()) { + C.cleanup(); + Stk.clearTo(StackSizeBefore); + return false; + } + + if (!Recursing) { + // We *can* actually get here with a non-empty stack, since + // things like InterpState::noteSideEffect() exist. + C.cleanup(); +#ifndef NDEBUG + // Make sure we don't rely on some value being still alive in + // InterpStack memory. + Stk.clearTo(StackSizeBefore); +#endif + } + + Result = Res.stealAPValue(); + return true; +} + +bool Context::evaluate(const EvalSettings &Settings, const Expr *E, + APValue &Result, ConstantExprKind Kind) { + ++EvalID; + bool Recursing = !Stk.empty(); + size_t StackSizeBefore = Stk.size(); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc, Kind); auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false, /*DestroyToplevelScope=*/true); @@ -128,12 +161,13 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, return true; } -bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, - const Expr *Init, APValue &Result) { +bool Context::evaluateAsInitializer(const EvalSettings &Settings, + const VarDecl *VD, const Expr *Init, + APValue &Result) { ++EvalID; bool Recursing = !Stk.empty(); size_t StackSizeBefore = Stk.size(); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); bool CheckGlobalInitialized = (VD->getType()->isRecordType() || VD->getType()->isArrayType()); @@ -160,10 +194,10 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, return true; } -bool Context::evaluateDestruction(State &Parent, const VarDecl *VD, - APValue Value) { +bool Context::evaluateDestruction(const EvalSettings &Settings, + const VarDecl *VD, APValue Value) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); auto Res = C.interpretDestructor(VD, Value); @@ -186,14 +220,15 @@ void Context::registerRedecl(const VarDecl *VD, const APValue &V) { } template <typename ResultT> -bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, ResultT &Result) { +bool Context::evaluateStringRepr(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + ResultT &Result) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); // Evaluate size value. APValue SizeValue; - if (!evaluateAsRValue(Parent, SizeExpr, SizeValue)) + if (!evaluateAsRValue(Settings, SizeExpr, SizeValue)) return false; if (!SizeValue.isInt()) @@ -269,26 +304,28 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr, return true; } -bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, APValue &Result) { +bool Context::evaluateCharRange(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + APValue &Result) { assert(SizeExpr); assert(PtrExpr); - return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result); + return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result); } -bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr, - const Expr *PtrExpr, std::string &Result) { +bool Context::evaluateCharRange(const EvalSettings &Settings, + const Expr *SizeExpr, const Expr *PtrExpr, + std::string &Result) { assert(SizeExpr); assert(PtrExpr); - return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result); + return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result); } -bool Context::evaluateString(State &Parent, const Expr *E, +bool Context::evaluateString(const EvalSettings &Settings, const Expr *E, std::string &Result) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC, const Pointer &Ptr) { @@ -350,9 +387,10 @@ bool Context::evaluateString(State &Parent, const Expr *E, return true; } -std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) { +std::optional<uint64_t> Context::evaluateStrlen(const EvalSettings &Settings, + const Expr *E) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<uint64_t> Result; auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S, CodePtr OpPC, @@ -416,12 +454,11 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) { return Result; } -std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent, - const Expr *E, - unsigned Kind, - bool IsDynamic) { +std::optional<uint64_t> +Context::tryEvaluateObjectSize(const EvalSettings &Settings, const Expr *E, + unsigned Kind, bool IsDynamic) { assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<uint64_t> Result; auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC, @@ -448,17 +485,16 @@ std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent, return Result; } -std::optional<bool> -Context::evaluateWithSubstitution(State &Parent, const FunctionDecl *Callee, - ArrayRef<const Expr *> Args, const Expr *This, - const Expr *Condition) { +std::optional<bool> Context::evaluateWithSubstitution( + const EvalSettings &Settings, const FunctionDecl *Callee, + ArrayRef<const Expr *> Args, const Expr *This, const Expr *Condition) { if (OptPrimType ConditionT = classify(Condition); !ConditionT || ConditionT != PT_Bool) { return std::nullopt; } assert(Stk.empty()); - Compiler<EvalEmitter> C(*this, *P, Parent, Stk, FrameAlloc); + Compiler<EvalEmitter> C(*this, *P, Settings, Stk, FrameAlloc); std::optional<bool> Result = C.interpretWithSubstitutions(Callee, Args, This, Condition); @@ -599,9 +635,9 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const { return Ctx.getFloatTypeSemantics(T); } -bool Context::Run(State &Parent, const Function *Func) { +bool Context::Run(const EvalSettings &Settings, const Function *Func) { auto Memory = std::make_unique<char[]>(InterpFrame::allocSize(Func)); - InterpState State(Parent, *P, Stk, FrameAlloc, *this, Func); + InterpState State(Settings, *P, Stk, FrameAlloc, *this, Func); InterpFrame *Frame = new (Memory.get()) InterpFrame( State, Func, /*Caller=*/nullptr, CodePtr(), Func->getArgSize()); State.Current = Frame; diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h index 1390bc280602e4..0565322d3f4e33 100644 --- a/clang/lib/AST/ByteCode/Context.h +++ b/clang/lib/AST/ByteCode/Context.h @@ -32,6 +32,7 @@ class Function; class Program; class State; enum PrimType : uint8_t; +struct EvalSettings; struct ParamOffset { unsigned Offset; @@ -54,37 +55,46 @@ class Context final { ~Context(); /// Checks if a function is a potential constant expression. - bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FD); - void isPotentialConstantExprUnevaluated(State &Parent, const Expr *E, + bool isPotentialConstantExpr(const EvalSettings &Settings, + const FunctionDecl *FD); + void isPotentialConstantExprUnevaluated(const EvalSettings &Settings, + const Expr *E, const FunctionDecl *FD); /// Evaluates a toplevel expression as an rvalue. + // FIXME: Get rid of the version using a parent State. bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result); + bool evaluateAsRValue(const EvalSettings &Settings, const Expr *E, + APValue &Result); /// Like evaluateAsRvalue(), but does no implicit lvalue-to-rvalue conversion. - bool evaluate(State &Parent, const Expr *E, APValue &Result, + // FIXME: Remove the Kind parameter, it's already in Settings. + bool evaluate(const EvalSettings &Settings, const Expr *E, APValue &Result, ConstantExprKind Kind); /// Evaluates a toplevel initializer. - bool evaluateAsInitializer(State &Parent, const VarDecl *VD, const Expr *Init, - APValue &Result); + bool evaluateAsInitializer(const EvalSettings &Settings, const VarDecl *VD, + const Expr *Init, APValue &Result); void registerRedecl(const VarDecl *VD, const APValue &V); /// Evaluates the destruction of a variable. - bool evaluateDestruction(State &Parent, const VarDecl *VD, APValue Value); + bool evaluateDestruction(const EvalSettings &Settings, const VarDecl *VD, + APValue Value); - bool evaluateCharRange(State &Parent, const Expr *SizeExpr, + bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, APValue &Result); - bool evaluateCharRange(State &Parent, const Expr *SizeExpr, + bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, std::string &Result); /// Evaluate \param E and if it can be evaluated to a null-terminated string, /// copy the result into \param Result. - bool evaluateString(State &Parent, const Expr *E, std::string &Result); + bool evaluateString(const EvalSettings &Settings, const Expr *E, + std::string &Result); /// Evalute \param E and if it can be evaluated to a string literal, /// run strlen() on it. - std::optional<uint64_t> evaluateStrlen(State &Parent, const Expr *E); + std::optional<uint64_t> evaluateStrlen(const EvalSettings &Settings, + const Expr *E); /// If \param E evaluates to a pointer the number of accessible bytes /// past the pointer is estimated in \param Result as if evaluated by @@ -96,10 +106,11 @@ class Context final { /// as the one referred to by E are considered, when Kind & 1 == 0 /// bytes belonging to the same storage (stack, heap allocation, /// global variable) are considered. - std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E, - unsigned Kind, bool IsDynamic); + std::optional<uint64_t> tryEvaluateObjectSize(const EvalSettings &Settings, + const Expr *E, unsigned Kind, + bool IsDynamic); - std::optional<bool> evaluateWithSubstitution(State &Parent, + std::optional<bool> evaluateWithSubstitution(const EvalSettings &Settings, const FunctionDecl *Callee, ArrayRef<const Expr *> Args, const Expr *This, @@ -192,10 +203,10 @@ class Context final { private: friend class EvalIDScope; /// Runs a function. - bool Run(State &Parent, const Function *Func); + bool Run(const EvalSettings &Settings, const Function *Func); template <typename ResultT> - bool evaluateStringRepr(State &Parent, const Expr *SizeExpr, + bool evaluateStringRepr(const EvalSettings &Settings, const Expr *SizeExpr, const Expr *PtrExpr, ResultT &Result); /// Current compilation context. diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index ec7550eb4cc17a..caa7f4b28ddd81 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -23,6 +23,12 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, : Ctx(Ctx), P(P), S(Parent, P, Stk, FA, Ctx, this), EvalResult(Ctx), ConstexprKind(ConstexprKind) {} +EvalEmitter::EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings, + InterpStack &Stk, FrameAllocator &FA, + ConstantExprKind ConstexprKind) + : Ctx(Ctx), P(P), S(Settings, P, Stk, FA, Ctx, this), EvalResult(Ctx), + ConstexprKind(ConstexprKind) {} + EvalEmitter::EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FA) : Ctx(Ctx), P(P), S(Status, P, Stk, FA, Ctx, this), EvalResult(Ctx) {} diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 70ad775e84f81c..8a4312bad5c589 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -74,6 +74,9 @@ class EvalEmitter : public SourceMapper { EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, FrameAllocator &FrameAlloc, ConstantExprKind ConstexprKind = ConstantExprKind::Normal); + EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings, + InterpStack &Stk, FrameAllocator &FrameAlloc, + ConstantExprKind ConstexprKind = ConstantExprKind::Normal); EvalEmitter(Context &Ctx, Program &P, Expr::EvalStatus &Status, InterpStack &Stk, FrameAllocator &FrameAlloc); diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index 7d0364fa7d10d8..e69880d9e1a87e 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "InterpState.h" +#include "EvalSettings.h" #include "InterpFrame.h" #include "InterpStack.h" #include "Program.h" @@ -30,19 +31,32 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; EvalMode = Parent.EvalMode; } +InterpState::InterpState(const EvalSettings &Settings, Program &P, + InterpStack &Stk, FrameAllocator &FrameAlloc, + Context &Ctx, SourceMapper *M) + : State(Ctx.getASTContext(), Settings.EvalStatus), M(M), + FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), + Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { + InConstantContext = Settings.InConstantContext; + CheckingPotentialConstantExpression = + Settings.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior; + EvalMode = Settings.EvalMode; +} -InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, - FrameAllocator &FrameAlloc, Context &Ctx, - const Function *Func) - : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), +InterpState::InterpState(const EvalSettings &Settings, Program &P, + InterpStack &Stk, FrameAllocator &FrameAlloc, + Context &Ctx, const Function *Func) + : State(Ctx.getASTContext(), Settings.EvalStatus), M(nullptr), FrameAlloc(FrameAlloc), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { - InConstantContext = Parent.InConstantContext; + InConstantContext = Settings.InConstantContext; CheckingPotentialConstantExpression = - Parent.CheckingPotentialConstantExpression; - CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; - EvalMode = Parent.EvalMode; + Settings.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior; + EvalMode = Settings.EvalMode; } InterpState::InterpState(Expr::EvalStatus &Status, Program &P, InterpStack &Stk, diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index b4f89e9583d972..eeb010ed216496 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -27,6 +27,7 @@ namespace clang { namespace interp { class Context; class SourceMapper; +struct EvalSettings; struct StdAllocatorCaller { @@ -48,8 +49,11 @@ class InterpState final : public State { InterpState(const State &Parent, Program &P, InterpStack &Stk, FrameAllocator &FrameAlloc, Context &Ctx, SourceMapper *M = nullptr); + InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk, + FrameAllocator &FrameAlloc, Context &Ctx, + SourceMapper *M = nullptr); - InterpState(const State &Parent, Program &P, InterpStack &Stk, + InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk, FrameAllocator &FA, Context &Ctx, const Function *Func); InterpState(Expr::EvalStatus &Status, Program &P, InterpStack &Stk, diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3efc6ffe7e9079..895bfa0576a720 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -33,6 +33,7 @@ //===----------------------------------------------------------------------===// #include "ByteCode/Context.h" +#include "ByteCode/EvalSettings.h" #include "ByteCode/Frame.h" #include "ByteCode/State.h" #include "ExprConstShared.h" @@ -22001,16 +22002,19 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue"); + + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Result); + Settings.InConstantContext = InConstantContext; + return Ctx.getInterpContext().evaluate(Settings, this, Result.Val, + ConstantExprKind::Normal); + } + EvalInfo Info(Ctx, Result, EvaluationMode::ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; CheckedTemporaries CheckedTemps; - if (Info.EnableNewConstInterp) { - return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, - ConstantExprKind::Normal); - } - if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() || Result.HasSideEffects || !CheckLValueConstantExpression(Info, getExprLoc(), @@ -22056,13 +22060,16 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, return true; ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr"); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Result); + Settings.InConstantContext = true; + return Ctx.getInterpContext().evaluate(Settings, this, Result.Val, Kind); + } + EvaluationMode EM = EvaluationMode::ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind); - // The type of the object we're initializing is 'const T' for a class NTTP. QualType T = getType(); if (Kind == ConstantExprKind::ClassTemplateArgument) @@ -22122,6 +22129,21 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, return Name; }); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings( + (IsConstantInitialization && + (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) + ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold, + EStatus); + Settings.InConstantContext = IsConstantInitialization; + return Ctx.getInterpContext().evaluateAsInitializer(Settings, VD, this, + EStatus.Val); + } + + SourceLocation DeclLoc = VD->getLocation(); + QualType DeclTy = VD->getType(); + EvalInfo Info(Ctx, EStatus, (IsConstantInitialization && (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) @@ -22130,14 +22152,6 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, Info.setEvaluatingDecl(VD, EStatus.Val); Info.InConstantContext = IsConstantInitialization; - SourceLocation DeclLoc = VD->getLocation(); - QualType DeclTy = VD->getType(); - - if (Info.EnableNewConstInterp) { - auto &InterpCtx = Ctx.getInterpContext(); - return InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val); - } - LValue LVal; LVal.set(VD); @@ -22202,11 +22216,12 @@ bool VarDecl::evaluateDestruction( return false; if (Ctx.getLangOpts().EnableNewConstInterp) { - EvalInfo Info(Ctx, EStatus, - IsConstantDestruction ? EvaluationMode::ConstantExpression - : EvaluationMode::ConstantFold); - Info.InConstantContext = IsConstantDestruction; - if (!Ctx.getInterpContext().evaluateDestruction(Info, this, + interp::EvalSettings Settings(IsConstantDestruction + ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold, + EStatus); + Settings.InConstantContext = IsConstantDestruction; + if (!Ctx.getInterpContext().evaluateDestruction(Settings, this, std::move(DestroyedValue))) return false; ensureEvaluatedStmt()->HasConstantDestruction = true; @@ -22905,19 +22920,23 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, }); Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); - Info.InConstantContext = true; - if (Info.EnableNewConstInterp) { + if (Ctx.getLangOpts().EnableNewConstInterp) { + auto Settings = interp::EvalSettings( + EvaluationMode::ConstantExpressionUnevaluated, Status); + Settings.InConstantContext = true; if (std::optional<bool> BoolResult = - Info.Ctx.getInterpContext().evaluateWithSubstitution( - Info, Callee, Args, This, this)) { + Ctx.getInterpContext().evaluateWithSubstitution(Settings, Callee, + Args, This, this)) { Value = APValue(APSInt(APInt(1, static_cast<uint64_t>(*BoolResult)))); return true; } return false; } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); + Info.InConstantContext = true; + LValue ThisVal; const LValue *ThisPtr = nullptr; if (This) { @@ -22988,20 +23007,23 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, return Name; }); + const ASTContext &Ctx = FD->getASTContext(); Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, - EvaluationMode::ConstantExpression); - Info.InConstantContext = true; - Info.CheckingPotentialConstantExpression = true; - // The constexpr VM attempts to compile all methods to bytecode here. - if (Info.EnableNewConstInterp) { - Info.Ctx.getInterpContext().isPotentialConstantExpr(Info, FD); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Status); + Settings.InConstantContext = true; + Settings.CheckingPotentialConstantExpression = true; + Ctx.getInterpContext().isPotentialConstantExpr(Settings, FD); return Diags.empty(); } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; + const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr; @@ -23038,19 +23060,23 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, assert(!E->isValueDependent() && "Expression evaluator can't be called on a dependent expression."); + const ASTContext &Ctx = FD->getASTContext(); Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, - EvaluationMode::ConstantExpressionUnevaluated); - Info.InConstantContext = true; - Info.CheckingPotentialConstantExpression = true; - - if (Info.EnableNewConstInterp) { - Info.Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Info, E, FD); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpressionUnevaluated, + Status); + Settings.InConstantContext = true; + Settings.CheckingPotentialConstantExpression = true; + Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Settings, E, FD); return Diags.empty(); } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); + Info.InConstantContext = true; + Info.CheckingPotentialConstantExpression = true; + // Fabricate a call stack frame to give the arguments a plausible cover story. CallStackFrame Frame(Info, SourceLocation(), FD, /*This=*/nullptr, /*CallExpr=*/nullptr, CallRef()); @@ -23066,12 +23092,13 @@ std::optional<uint64_t> Expr::tryEvaluateObjectSize(const ASTContext &Ctx, return std::nullopt; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().tryEvaluateObjectSize( - Info, this, Type, - /*IsDynamic=*/false); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + return Ctx.getInterpContext().tryEvaluateObjectSize(Settings, this, Type, + /*IsDynamic=*/false); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); return tryEvaluateBuiltinObjectSize(this, Type, Info); } @@ -23122,15 +23149,16 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info, std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); std::string StringResult; - if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluateString(Info, this, StringResult)) + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + if (!Ctx.getInterpContext().evaluateString(Settings, this, StringResult)) return std::nullopt; return StringResult; } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); if (EvaluateBuiltinStrLen(this, Info, &StringResult)) return StringResult; return std::nullopt; @@ -23142,13 +23170,16 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, const Expr *PtrExpression, ASTContext &Ctx, Expr::EvalResult &Status) { + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantExpression, Status); + Settings.InConstantContext = true; + return Ctx.getInterpContext().evaluateCharRange(Settings, SizeExpression, + PtrExpression, Result); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluateCharRange(Info, SizeExpression, - PtrExpression, Result); - LValue String; FullExpressionRAII Scope(Info); APSInt SizeValue; @@ -23210,10 +23241,12 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result, std::optional<uint64_t> Expr::tryEvaluateStrLen(const ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); - if (Info.EnableNewConstInterp) - return Info.Ctx.getInterpContext().evaluateStrlen(Info, this); + if (Ctx.getLangOpts().EnableNewConstInterp) { + interp::EvalSettings Settings(EvaluationMode::ConstantFold, Status); + return Ctx.getInterpContext().evaluateStrlen(Settings, this); + } + EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); return EvaluateBuiltinStrLen(this, Info); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
