[clang] [clang][bytecode] Stop relying on `CheckEvaluationResult()` (PR #186045)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 15 07:39:58 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/186045
>From f40aa1e606cdf2b414b622e2aba84d8f6d75b319 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 12 Mar 2026 06:05:56 +0100
Subject: [PATCH] 2
---
clang/lib/AST/ByteCode/ByteCodeEmitter.h | 1 +
clang/lib/AST/ByteCode/Compiler.cpp | 6 +
clang/lib/AST/ByteCode/Context.cpp | 19 +-
clang/lib/AST/ByteCode/Disasm.cpp | 5 +-
clang/lib/AST/ByteCode/EvalEmitter.cpp | 41 +-
clang/lib/AST/ByteCode/EvalEmitter.h | 19 +-
clang/lib/AST/ByteCode/EvaluationResult.cpp | 362 +++++++++++++++++-
clang/lib/AST/ByteCode/EvaluationResult.h | 41 +-
clang/lib/AST/ByteCode/Interp.cpp | 2 +-
clang/lib/AST/ExprConstShared.h | 43 +++
clang/lib/AST/ExprConstant.cpp | 144 +++----
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 +
16 files changed, 553 insertions(+), 153 deletions(-)
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 4b42b7eb4063b..3577c02337758 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -64,6 +64,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(Descriptor *D);
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 638e6ecafb295..505703ac1a5f4 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3254,6 +3254,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;
@@ -5143,6 +5147,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 4beb35a9a7b43..ee3365e96803e 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);
- 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);
+ Compiler<EvalEmitter> C(*this, *P, Parent, Stk, 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);
bool CheckGlobalInitialized =
- shouldBeGloballyIndexed(VD) &&
(VD->getType()->isRecordType() || VD->getType()->isArrayType());
+
auto Res = C.interpretDecl(VD, Init, CheckGlobalInitialized);
if (Res.isInvalid()) {
C.cleanup();
@@ -192,7 +191,8 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
return false;
uint64_t Size = SizeValue.getInt().getZExtValue();
- auto PtrRes = C.interpretAsPointer(PtrExpr, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(PtrExpr, [&](InterpState &S,
+ const Pointer &Ptr) {
if (Size == 0) {
if constexpr (std::is_same_v<ResultT, APValue>)
Result = APValue(APValue::UninitArray{}, 0, 0);
@@ -261,7 +261,8 @@ bool Context::evaluateString(State &Parent, const Expr *E,
assert(Stk.empty());
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S,
+ const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -310,7 +311,8 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) {
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &S,
+ const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -361,7 +363,8 @@ Context::tryEvaluateObjectSize(State &Parent, const Expr *E, unsigned Kind) {
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsLValuePointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S,
+ const Pointer &Ptr) {
const Descriptor *DeclDesc = Ptr.getDeclDesc();
if (!DeclDesc)
return false;
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 4caf830a0a1b4..535523d49190b 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -655,9 +655,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 6e986f4bcbda5..29042435b1db5 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -18,8 +18,9 @@ using namespace clang;
using namespace clang::interp;
EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
- InterpStack &Stk)
- : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
+ InterpStack &Stk, ConstantExprKind ConstexprKind)
+ : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(Ctx),
+ ConstexprKind(ConstexprKind) {}
EvalEmitter::~EvalEmitter() {
for (auto &V : Locals) {
@@ -223,6 +224,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;
@@ -230,15 +243,19 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
// If we're returning a raw pointer, call our callback.
if (this->PtrCB)
- return (*this->PtrCB)(Ptr);
+ return (*this->PtrCB)(S, Ptr);
- if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
+ if (!EvalResult.checkDynamicAllocations(S, Ctx, Ptr, Info))
return false;
+
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;
// Function pointers are always returned as lvalues.
if (Ptr.isFunctionPointer()) {
+ if (!EvalResult.checkFunctionPointer(S, Ptr, Info, ConstexprKind))
+ return false;
+
EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
return true;
}
@@ -260,6 +277,9 @@ 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));
@@ -267,13 +287,8 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
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;
- }
+ if (!EvalResult.checkLValue(S, Ptr, Info, ConstexprKind))
+ return false;
if (!Ptr.isLive() && !Ptr.isTemporary())
return false;
@@ -292,10 +307,12 @@ bool EvalEmitter::emitRetVoid(SourceInfo Info) {
bool EvalEmitter::emitRetValue(SourceInfo Info) {
const auto &Ptr = S.Stk.pop<Pointer>();
- if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
+ if (!EvalResult.checkDynamicAllocations(S, Ctx, Ptr, 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 6fd50da8cad76..b24aff480215b 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -33,11 +33,16 @@ class EvalEmitter : public SourceMapper {
using LabelTy = uint32_t;
using AddrTy = uintptr_t;
using Local = Scope::Local;
- using PtrCallback = llvm::function_ref<bool(const Pointer &)>;
+ using PtrCallback = llvm::function_ref<bool(InterpState &, 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);
@@ -51,8 +56,13 @@ class EvalEmitter : public SourceMapper {
/// Clean up all resources.
void cleanup();
+ bool constantFolding() const {
+ return S.EvalMode == EvaluationMode::ConstantFold;
+ }
+
protected:
- EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk);
+ EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
+ ConstantExprKind ConstexprKind = ConstantExprKind::Normal);
virtual ~EvalEmitter();
@@ -109,6 +119,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 24f242758324e..cb0cffdce442b 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.cpp
+++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp
@@ -7,16 +7,36 @@
//===----------------------------------------------------------------------===//
#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/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include <iterator>
namespace clang {
namespace interp {
+QualType EvaluationResult::getStorageType() const {
+ if (const auto *E = Source.dyn_cast<const Expr *>()) {
+ if (E->isPRValue())
+ return E->getType();
+
+ return Ctx.getASTContext().getLValueReferenceType(E->getType());
+ }
+
+ if (const auto *D =
+ dyn_cast_if_present<ValueDecl>(Source.dyn_cast<const Decl *>()))
+ return D->getType();
+ return QualType();
+}
+
static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
const FieldDecl *SubObjDecl) {
assert(SubObjDecl && "Subobject declaration does not exist");
@@ -198,11 +218,14 @@ static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) {
}
}
-bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
- const Pointer &Ptr,
- const SourceInfo &Info) {
+bool EvaluationResult::checkDynamicAllocations(InterpState &S,
+ const Context &Ctx,
+ const Pointer &Ptr,
+ SourceInfo Info) {
+
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::SetVector<const Block *> Blocks;
@@ -226,5 +249,338 @@ bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
return true;
}
+static bool isGlobalLValue(const Pointer &Ptr) {
+ if (Ptr.isBlockPointer() && Ptr.block()->isDynamic())
+ return true;
+ if (Ptr.isTypeidPointer())
+ return true;
+
+ const Descriptor *Desc = Ptr.getDeclDesc();
+ return ::isGlobalLValue(Desc->asValueDecl(), Desc->asExpr());
+}
+
+/// 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();
+ 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,
+ const Pointer &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())
+ return true;
+
+ const Descriptor *DeclDesc = Ptr.block()->getDescriptor();
+ const Expr *BaseE = DeclDesc->asExpr();
+ const ValueDecl *BaseVD = DeclDesc->asValueDecl();
+ 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
+ << !!DeclDesc->asValueDecl() // DeclDesc->IsTemporary
+ << DeclDesc->asValueDecl();
+ const VarDecl *VarD = DeclDesc->asVarDecl();
+ 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 (const ValueDecl *VD = DeclDesc->asValueDecl())
+ S.Note(VD->getLocation(), diag::note_declared_at);
+ else if (const Expr *E = DeclDesc->asExpr())
+ S.Note(E->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;
+
+ // 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()) ||
+ VD->hasAttr<HIPManagedAttr>())
+ 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 (CheckedBlocks.insert(Pointee.block()).second) {
+ if (!lval(S, Ctx, Pointee, Pointee.getType(),
+ Ptr.getDeclDesc()->getLoc(), ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ } else if (Ptr.getRecord()) {
+ return lvalFields(S, Ctx, Ptr, Ptr.getType(), Info,
+ ConstantExprKind::Normal, CheckedBlocks);
+ }
+ }
+
+ return true;
+}
+
+static bool lvalFields(InterpState &S, const ASTContext &Ctx,
+ const Pointer &Ptr, QualType PtrType, SourceInfo Info,
+ ConstantExprKind ConstexprKind,
+ llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) {
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ const Descriptor *FieldDesc = Ptr.getFieldDesc();
+ if (const Record *R = Ptr.getRecord()) {
+ if (!R->hasPtrField())
+ return true;
+ for (const Record::Field &F : R->fields()) {
+ if (F.Desc->isPrimitive() && F.Desc->getPrimType() == PT_Ptr) {
+ QualType FieldType = F.Decl->getType();
+ if (!Ptr.atField(F.Offset).isLive())
+ return false;
+
+ Pointer Pointee = Ptr.atField(F.Offset).deref<Pointer>();
+ if (CheckedBlocks.insert(Pointee.block()).second) {
+ if (!lval(S, Ctx, Pointee, FieldType, Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+ } else {
+ Pointer FieldPtr = Ptr.atField(F.Offset);
+ if (!lvalFields(S, Ctx, FieldPtr, F.Decl->getType(), Info,
+ ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ }
+
+ for (const Record::Base &B : R->bases()) {
+ Pointer BasePtr = Ptr.atField(B.Offset);
+ if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+ for (const Record::Base &B : R->virtual_bases()) {
+ Pointer BasePtr = Ptr.atField(B.Offset);
+ if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+
+ return true;
+ }
+ if (FieldDesc->isPrimitiveArray()) {
+ if (FieldDesc->getPrimType() == PT_Ptr) {
+ for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+ if (!Ptr.isLive())
+ return false;
+ Pointer Pointee = Ptr.elem<Pointer>(I);
+ if (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) {
+ Pointer 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) {
+ QualType SourceType = getStorageType();
+ llvm::SmallPtrSet<const Block *, 4> CheckedBlocks;
+
+ return lvalFields(S, Ctx.getASTContext(), Ptr, SourceType, Info,
+ ConstexprKind, CheckedBlocks);
+}
+
+bool EvaluationResult::checkLValue(InterpState &S, const Pointer &Ptr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ if (Ptr.isZero())
+ return true;
+
+ QualType SourceType = getStorageType();
+ if (Ptr.isFunctionPointer())
+ return checkFunctionPtr(S, Ptr, SourceType, Info, ConstexprKind);
+
+ 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.isBlockPointer())
+ return true;
+
+ const Descriptor *DeclDesc = Ptr.getDeclDesc();
+ const Expr *BaseE = DeclDesc->asExpr();
+ const ValueDecl *BaseVD = DeclDesc->asValueDecl();
+ assert(BaseE || BaseVD);
+ bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot());
+
+ // 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)) {
+ int InvalidBaseKind = -1;
+ StringRef Ident;
+ if (isa_and_nonnull<StringLiteral>(BaseE))
+ InvalidBaseKind = 1;
+ else if (isa_and_nonnull<MaterializeTemporaryExpr>(BaseE) ||
+ isa_and_nonnull<LifetimeExtendedTemporaryDecl>(BaseVD))
+ InvalidBaseKind = 2;
+ else if (const auto *PE = dyn_cast_if_present<PredefinedExpr>(BaseE)) {
+ InvalidBaseKind = 3;
+ Ident = PE->getIdentKindName();
+ IsSubObj = true;
+ }
+
+ if (InvalidBaseKind != -1) {
+ S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+ << IsReferenceType << IsSubObj << InvalidBaseKind << Ident;
+ 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) {
+ 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 c296cc98ca375..a98ee1dc32c9b 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.h
+++ b/clang/lib/AST/ByteCode/EvaluationResult.h
@@ -17,6 +17,7 @@ namespace clang {
namespace interp {
class EvalEmitter;
class Context;
+class MemberPointer;
class Pointer;
class SourceInfo;
class InterpState;
@@ -39,9 +40,7 @@ class EvaluationResult final {
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
private:
-#ifndef NDEBUG
- const Context *Ctx = nullptr;
-#endif
+ const Context &Ctx;
APValue Value;
ResultKind Kind = Empty;
DeclTy Source = nullptr;
@@ -62,31 +61,37 @@ 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; }
-
- /// Returns an APValue for the evaluation result.
- APValue toAPValue() const {
- assert(!empty());
- assert(!isInvalid());
- return Value;
- }
-
APValue stealAPValue() { return std::move(Value); }
/// Check that all subobjects of the given pointer have been initialized.
bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const;
/// Check that none of the blocks the given pointer (transitively) points
/// to are dynamically allocated.
- bool checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr,
- const SourceInfo &Info);
+ bool checkDynamicAllocations(InterpState &S, const Context &Ctx,
+ const Pointer &Ptr, SourceInfo Info);
+
+ /// 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);
+ /// Check all fields of the given pointer.
+ bool checkLValueFields(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+ ConstantExprKind ConstexprKind);
+
+ /// 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);
QualType getSourceType() const {
if (const auto *D =
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 201499ea6e027..4167a433580cc 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -904,7 +904,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckMutable(S, OpPC, Ptr))
return false;
- if (Ptr.isConstexprUnknown())
+ if (!S.inConstantContext() && isConstexprUnknown(Ptr))
return false;
return true;
}
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 619c79a1408f3..5035306a8249c 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/TypeTraits.h"
#include <cstdint>
#include <optional>
@@ -89,4 +90,46 @@ std::optional<llvm::APFloat>
EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B,
std::optional<llvm::APSInt> RoundingMode, bool IsMin);
+/// 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 bc98c0d86bb65..fe32142e17b9a 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -145,37 +145,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.
@@ -1922,31 +1891,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();
@@ -1958,10 +1926,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;
@@ -1984,7 +1950,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;
@@ -2005,6 +1971,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) {
@@ -10351,7 +10319,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) {
@@ -21216,12 +21184,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;
@@ -21422,14 +21386,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() ||
@@ -21481,12 +21439,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();
@@ -21565,39 +21519,35 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
if (Info.EnableNewConstInterp) {
auto &InterpCtx = Ctx.getInterpContext();
- if (!InterpCtx.evaluateAsInitializer(Info, VD, this, Value))
- return false;
+ return InterpCtx.evaluateAsInitializer(Info, VD, this, Value);
+ }
- return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
- ConstantExprKind::Normal);
- } else {
- LValue LVal;
- LVal.set(VD);
+ 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(Value, Info, LVal, this,
- /*AllowNonLiteralTypes=*/true) ||
- EStatus.HasSideEffects)
- return false;
- }
+ {
+ // 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(Value, Info, LVal, this,
+ /*AllowNonLiteralTypes=*/true) ||
+ EStatus.HasSideEffects)
+ return false;
+ }
- // At this point, any lifetime-extended temporaries are completely
- // initialized.
- Info.performLifetimeExtension();
+ // At this point, any lifetime-extended temporaries are completely
+ // initialized.
+ Info.performLifetimeExtension();
- if (!Info.discardCleanups())
- llvm_unreachable("Unhandled cleanup; missing full expression marker?");
- }
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
ConstantExprKind::Normal) &&
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 57157392f6a6e..77ba2ef3d2720 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -1972,9 +1972,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 8b70f5a4251ad..3d1e31d5292ed 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 a4a8100f5b31e..3da3996aaca75 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 52039a5208223..f10f1be4ce95d 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 cbbe2fe9af164..6fa22ab846374 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}}
More information about the cfe-commits
mailing list