[clang] [clang][bytecode] Don't start record field lifetime by default (PR #193496)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 28 01:38:44 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/193496
>From 113457ffbd1201ed743d10419a292c88feb56539 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 21 Apr 2026 11:06:43 +0200
Subject: [PATCH] asdf
---
clang/lib/AST/ByteCode/Compiler.cpp | 40 +++---
clang/lib/AST/ByteCode/Descriptor.cpp | 4 +
clang/lib/AST/ByteCode/Descriptor.h | 1 +
clang/lib/AST/ByteCode/EvalEmitter.cpp | 1 +
clang/lib/AST/ByteCode/Interp.cpp | 50 ++++++--
clang/lib/AST/ByteCode/Interp.h | 9 +-
.../lib/AST/ByteCode/InterpBuiltinBitCast.cpp | 121 +++++++++++-------
clang/lib/AST/ByteCode/InterpFrame.h | 1 +
clang/lib/AST/ByteCode/Opcodes.td | 3 +-
clang/lib/AST/ByteCode/Pointer.cpp | 1 +
.../ByteCode/builtin-bit-cast-bitfields.cpp | 22 ++--
clang/test/AST/ByteCode/new-delete.cpp | 17 ++-
clang/test/AST/ByteCode/records.cpp | 13 ++
13 files changed, 181 insertions(+), 102 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 2428b31daac4e..6850956d5347e 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2171,7 +2171,7 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
if (!this->visitInitializer(Init))
return false;
- if (!this->emitFinishInitPop(E))
+ if (!this->emitPopPtr(E))
return false;
// Base initializers don't increase InitIndex, since they don't count
// into the Record's fields.
@@ -2351,7 +2351,7 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init,
return false;
if (!this->visitInitializer(Init))
return false;
- return this->emitFinishInitPop(Init);
+ return this->emitPopPtr(Init);
}
template <class Emitter>
@@ -3280,7 +3280,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
- return this->visitInitializer(Inner) && this->emitFinishInit(E);
+ return this->visitInitializer(Inner);
}
return false;
}
@@ -3312,7 +3312,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
if (Initializing) {
// We already have a value, just initialize that.
- return this->visitInitializer(Init) && this->emitFinishInit(E);
+ return this->visitInitializer(Init);
}
OptPrimType T = classify(E->getType());
@@ -3339,7 +3339,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
return this->emitInitGlobal(*T, *GlobalIndex, E);
}
- return this->visitInitializer(Init) && this->emitFinishInit(E);
+ return this->visitInitializer(Init);
}
// Otherwise, use a local variable.
@@ -3361,7 +3361,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
if (T)
return this->visit(Init) && this->emitInit(*T, E);
- return this->visitInitializer(Init) && this->emitFinishInit(E);
+ return this->visitInitializer(Init);
}
template <class Emitter>
@@ -3616,7 +3616,7 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
if (DiscardResult)
return this->emitPopPtr(E);
- return this->emitFinishInit(E);
+ return true;
}
if (T->isArrayType()) {
@@ -4635,7 +4635,7 @@ bool Compiler<Emitter>::visitInitializer(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/false,
/*NewInitializing=*/true, /*ToLValue=*/false);
- return this->Visit(E);
+ return this->Visit(E) && this->emitFinishInit(E);
}
template <class Emitter> bool Compiler<Emitter>::visitAsLValue(const Expr *E) {
@@ -5106,9 +5106,6 @@ bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
if (!visitInitializer(E))
return false;
-
- if (!this->emitFinishInit(E))
- return false;
// We are destroying the locals AFTER the Ret op.
// The Ret op needs to copy the (alive) values, but the
// destructors may still turn the entire expression invalid.
@@ -5298,8 +5295,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
if (!visitInitializer(Init))
return false;
-
- return this->emitFinishInitPop(Init);
+ return this->emitPopPtr(Init);
}
return false;
}
@@ -6732,6 +6728,10 @@ template <class Emitter>
bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
assert(!ReturnType);
+ // Only start the lifetime of the instance pointer.
+ if (!this->emitStartThisLifetime1(Ctor))
+ return false;
+
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
const Expr *InitExpr,
bool Activate = false) -> bool {
@@ -6762,8 +6762,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->visitInitializer(InitExpr))
return false;
-
- return this->emitFinishInitPop(InitExpr);
+ return this->emitPopPtr(InitExpr);
};
const RecordDecl *RD = Ctor->getParent();
@@ -6789,6 +6788,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
this->emitRetVoid(Ctor);
}
+ unsigned FieldInits = 0;
InitLinkScope<Emitter> InitScope(this, InitLink::This());
for (const auto *Init : Ctor->inits()) {
// Scope needed for the initializers.
@@ -6802,6 +6802,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
initNeedsOverridenLoc(Init));
if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion))
return false;
+ ++FieldInits;
} else if (const Type *Base = Init->getBaseClass()) {
const auto *BaseDecl = Base->getAsCXXRecordDecl();
assert(BaseDecl);
@@ -6825,7 +6826,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
if (!this->visitInitializer(InitExpr))
return false;
- if (!this->emitFinishInitPop(InitExpr))
+ if (!this->emitPopPtr(InitExpr))
return false;
} else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
LocOverrideScope<Emitter> LOS(this, SourceInfo{},
@@ -6888,6 +6889,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
return false;
}
+ if (FieldInits != R->getNumFields()) {
+ assert(FieldInits < R->getNumFields());
+ // Start the lifetime of all members.
+ if (!this->emitStartThisLifetime(Ctor))
+ return false;
+ }
+
if (const Stmt *Body = Ctor->getBody()) {
// Only emit the CtorCheck op for non-empty CompoundStmt bodies.
// For non-CompoundStmts, always assume they are non-empty and emit it.
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index 13c106b817974..729df4f200e30 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -140,6 +140,10 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable,
Desc->IsVolatile = IsVolatile || D->IsVolatile;
// True if this field is const AND the parent is mutable.
Desc->IsConstInMutable = Desc->IsConst && IsMutable;
+ Desc->LifeState =
+ D->isPrimitiveArray()
+ ? Lifetime::Started
+ : (Desc->IsActive ? Lifetime::NotStarted : Lifetime::Started);
if (auto Fn = D->CtorFn)
Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable,
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index daf7251aa0603..498a01e8f070c 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -54,6 +54,7 @@ static_assert(sizeof(GlobalInlineDescriptor) == sizeof(void *), "");
enum class Lifetime : uint8_t {
Started,
+ NotStarted,
Destroyed,
Ended,
};
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 036cd2b9a62fa..319ef7edd57b9 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -326,6 +326,7 @@ bool EvalEmitter::emitSetLocal(uint32_t I, SourceInfo Info) {
B->deref<T>() = S.Stk.pop<T>();
auto &Desc = B->getBlockDesc<InlineDescriptor>();
Desc.IsInitialized = true;
+ Desc.LifeState = Lifetime::Started;
return true;
}
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 729c0cc4ab541..06f3b6f3dad94 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -819,10 +819,10 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return false;
if (!CheckActive(S, OpPC, Ptr, AK))
return false;
- if (!CheckLifetime(S, OpPC, Ptr, AK))
- return false;
if (!Ptr.isInitialized())
return DiagnoseUninitialized(S, OpPC, Ptr, AK);
+ if (!CheckLifetime(S, OpPC, Ptr, AK))
+ return false;
if (!CheckTemporary(S, OpPC, Ptr.block(), AK))
return false;
@@ -899,7 +899,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
}
-static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ bool IsCtorDtor = false) {
if (!Ptr.isDummy() && !isConstexprUnknown(Ptr)) {
if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
return false;
@@ -907,6 +908,8 @@ static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
return false;
+ if (!IsCtorDtor && !CheckLifetime(S, OpPC, Ptr, AK_MemberCall))
+ return false;
}
return true;
}
@@ -1775,7 +1778,8 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
Func->isLambdaCallOperator()) {
assert(ThisPtr.isZero());
} else {
- if (!CheckInvoke(S, OpPC, ThisPtr))
+ if (!CheckInvoke(S, OpPC, ThisPtr,
+ Func->isConstructor() || Func->isDestructor()))
return cleanup();
if (!Func->isConstructor() && !Func->isDestructor() &&
!CheckActive(S, OpPC, ThisPtr, AK_MemberCall))
@@ -2023,27 +2027,47 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
static void startLifetimeRecurse(const Pointer &Ptr) {
if (const Record *R = Ptr.getRecord()) {
Ptr.startLifetime();
- for (const Record::Field &Fi : R->fields())
- startLifetimeRecurse(Ptr.atField(Fi.Offset));
+
+ for (const Record::Field &Fi : R->fields()) {
+ Pointer FP = Ptr.atField(Fi.Offset);
+ if (FP.getLifetime() != Lifetime::Started)
+ startLifetimeRecurse(FP);
+ }
return;
}
if (const Descriptor *FieldDesc = Ptr.getFieldDesc();
FieldDesc->isCompositeArray()) {
- assert(Ptr.getLifetime() == Lifetime::Started);
- for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I)
- startLifetimeRecurse(Ptr.atIndex(I).narrow());
+ for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+ Pointer EP = Ptr.atIndex(I).narrow();
+ if (EP.getLifetime() != Lifetime::Started)
+ startLifetimeRecurse(EP);
+ }
return;
}
Ptr.startLifetime();
}
-bool StartLifetime(InterpState &S, CodePtr OpPC) {
- const auto &Ptr = S.Stk.peek<Pointer>();
- if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
+bool StartThisLifetime(InterpState &S, CodePtr OpPC) {
+ if (S.checkingPotentialConstantExpression())
+ return true;
+
+ const auto &Ptr = S.Current->getThis();
+ if (!Ptr.isBlockPointer())
return false;
- startLifetimeRecurse(Ptr.narrow());
+ startLifetimeRecurse(Ptr);
+ return true;
+}
+
+bool StartThisLifetime1(InterpState &S, CodePtr OpPC) {
+ if (S.checkingPotentialConstantExpression())
+ return true;
+
+ const auto &Ptr = S.Current->getThis();
+ if (!Ptr.isBlockPointer())
+ return false;
+ Ptr.startLifetime();
return true;
}
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 0f1c81fe300e6..84f71d1a05724 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1556,7 +1556,8 @@ bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
bool EndLifetime(InterpState &S, CodePtr OpPC);
bool EndLifetimePop(InterpState &S, CodePtr OpPC);
-bool StartLifetime(InterpState &S, CodePtr OpPC);
+bool StartThisLifetime(InterpState &S, CodePtr OpPC);
+bool StartThisLifetime1(InterpState &S, CodePtr OpPC);
bool MarkDestroyed(InterpState &S, CodePtr OpPC);
/// 1) Pops the value from the stack.
@@ -3932,9 +3933,13 @@ inline bool BitCastPrim(InterpState &S, CodePtr OpPC, bool TargetIsUCharOrByte,
}
inline bool BitCast(InterpState &S, CodePtr OpPC) {
- const Pointer &FromPtr = S.Stk.pop<Pointer>();
+ Pointer FromPtr = S.Stk.pop<Pointer>();
Pointer &ToPtr = S.Stk.peek<Pointer>();
+ const Descriptor *D = FromPtr.getFieldDesc();
+ if (D->isPrimitiveArray() && FromPtr.isArrayRoot())
+ FromPtr = FromPtr.atIndex(0);
+
if (!CheckLoad(S, OpPC, FromPtr))
return false;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 84d4509864029..e48b0dc8e6a3e 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -35,10 +35,12 @@ using namespace clang::interp;
// - Optimize the common case of only pushing and pulling full
// bytes to/from the buffer.
+enum class Result { Success, Skip, Failure };
+
/// Used to iterate over pointer fields.
using DataFunc =
- llvm::function_ref<bool(const Pointer &P, PrimType Ty, Bits BitOffset,
- Bits FullBitWidth, bool PackedBools)>;
+ llvm::function_ref<Result(const Pointer &P, PrimType Ty, Bits BitOffset,
+ Bits FullBitWidth, bool PackedBools)>;
#define BITCAST_TYPE_SWITCH(Expr, B) \
do { \
@@ -78,8 +80,8 @@ using DataFunc =
/// We use this to recursively iterate over all fields and elements of a pointer
/// and extract relevant data for a bitcast.
-static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
- Bits BitsToRead, DataFunc F) {
+static Result enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
+ Bits BitsToRead, DataFunc F, bool Initialize) {
const Descriptor *FieldDesc = P.getFieldDesc();
assert(FieldDesc);
@@ -102,12 +104,14 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
unsigned NumElems = FieldDesc->getNumElems();
bool Ok = true;
for (unsigned I = P.getIndex(); I != NumElems; ++I) {
- Ok = Ok && F(P.atIndex(I), ElemT, Offset, ElemSize, PackedBools);
+ Result Res = F(P.atIndex(I), ElemT, Offset, ElemSize, PackedBools);
+
+ Ok = Ok && (Res == Result::Success);
Offset += PackedBools ? Bits(1) : ElemSize;
if (Offset >= BitsToRead)
break;
}
- return Ok;
+ return Ok ? Result::Success : Result::Skip;
}
// Composite arrays.
@@ -115,12 +119,13 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
QualType ElemType = FieldDesc->getElemQualType();
Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {
- enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);
+ enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F,
+ Initialize);
Offset += ElemSize;
if (Offset >= BitsToRead)
break;
}
- return true;
+ return Result::Success;
}
// Records.
@@ -133,32 +138,46 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
for (const Record::Field &Fi : R->fields()) {
if (Fi.isUnnamedBitField())
continue;
+
Pointer Elem = P.atField(Fi.Offset);
Bits BitOffset =
Offset + Bits(Layout.getFieldOffset(Fi.Decl->getFieldIndex()));
- Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);
+ Result Res =
+ enumerateData(Elem, Ctx, BitOffset, BitsToRead, F, Initialize);
+ if (Initialize) {
+ if (Res == Result::Success)
+ Elem.initialize();
+ else if (Res == Result::Skip)
+ Elem.startLifetime();
+ }
+ Ok = Ok && Res != Result::Failure;
}
for (const Record::Base &B : R->bases()) {
Pointer Elem = P.atField(B.Offset);
CharUnits ByteOffset =
Layout.getBaseClassOffset(cast<CXXRecordDecl>(B.Decl));
Bits BitOffset = Offset + Bits(Ctx.getASTContext().toBits(ByteOffset));
- Ok = Ok && enumerateData(Elem, Ctx, BitOffset, BitsToRead, F);
- // FIXME: We should only (need to) do this when bitcasting OUT of the
- // buffer, not when copying data into it.
- if (Ok)
- Elem.initialize();
+ Result Res =
+ enumerateData(Elem, Ctx, BitOffset, BitsToRead, F, Initialize);
+ if (Initialize) {
+ if (Res == Result::Success)
+ Elem.initialize();
+ else if (Res == Result::Skip)
+ Elem.startLifetime();
+ }
+ Ok = Ok && Res != Result::Failure;
}
-
- return Ok;
+ return Ok ? Result::Success : Result::Failure;
}
llvm_unreachable("Unhandled data type");
}
static bool enumeratePointerFields(const Pointer &P, const Context &Ctx,
- Bits BitsToRead, DataFunc F) {
- return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F);
+ Bits BitsToRead, DataFunc F,
+ bool Initialize) {
+ return enumerateData(P, Ctx, Bits::zero(), BitsToRead, F, Initialize) !=
+ Result::Failure;
}
// This function is constexpr if and only if To, From, and the types of
@@ -269,7 +288,7 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
return enumeratePointerFields(
FromPtr, Ctx, Buffer.size(),
[&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
- bool PackedBools) -> bool {
+ bool PackedBools) -> Result {
Bits BitWidth = FullBitWidth;
if (const FieldDecl *FD = P.getField(); FD && FD->isBitField())
@@ -279,18 +298,18 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
BitWidth = Bits(1);
if (BitWidth.isZero())
- return true;
+ return Result::Skip;
// Bits will be left uninitialized and diagnosed when reading.
if (!P.isInitialized())
- return true;
+ return Result::Skip;
if (T == PT_Ptr) {
assert(P.getType()->isNullPtrType());
// Clang treats nullptr_t has having NO bits in its value
// representation. So, we accept it here and leave its bits
// uninitialized.
- return true;
+ return Result::Skip;
}
assert(P.isInitialized());
@@ -315,7 +334,7 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
BITCAST_TYPE_SWITCH(T, {
auto Val = P.deref<T>();
if (!Val.isNumber())
- return false;
+ return Result::Failure;
Val.bitcastToMemory(Buff.get());
});
@@ -325,8 +344,9 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
}
Buffer.pushData(Buff.get(), BitOffset, BitWidth, TargetEndianness);
- return true;
- });
+ return Result::Success;
+ },
+ false);
}
bool clang::interp::DoBitCast(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
@@ -396,7 +416,7 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
bool Success = enumeratePointerFields(
ToPtr, S.getContext(), Buffer.size(),
[&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
- bool PackedBools) -> bool {
+ bool PackedBools) -> Result {
QualType PtrType = P.getType();
if (T == PT_Float) {
const auto &Semantics = ASTCtx.getFloatTypeSemantics(PtrType);
@@ -413,7 +433,7 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
Floating::bitcastFromMemory(M.get(), Semantics, &R);
P.deref<Floating>() = R;
P.initialize();
- return true;
+ return Result::Success;
}
Bits BitWidth;
@@ -437,9 +457,9 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
<< PtrType << S.getLangOpts().CharIsSigned
<< E->getSourceRange();
- return false;
+ return Result::Failure;
}
- return true;
+ return Result::Skip;
}
auto Memory = Buffer.copyBits(BitOffset, BitWidth, FullBitWidth,
@@ -469,8 +489,9 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr OpPC,
});
}
P.initialize();
- return true;
- });
+ return Result::Success;
+ },
+ true);
return Success;
}
@@ -495,25 +516,29 @@ bool clang::interp::DoMemcpy(InterpState &S, CodePtr OpPC,
assert(DestPtr.isBlockPointer());
llvm::SmallVector<PrimTypeVariant> Values;
- enumeratePointerFields(SrcPtr, S.getContext(), Size,
- [&](const Pointer &P, PrimType T, Bits BitOffset,
- Bits FullBitWidth, bool PackedBools) -> bool {
- TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });
- return true;
- });
+ enumeratePointerFields(
+ SrcPtr, S.getContext(), Size,
+ [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
+ bool PackedBools) -> Result {
+ TYPE_SWITCH(T, { Values.push_back(P.deref<T>()); });
+ return Result::Success;
+ },
+ false);
unsigned ValueIndex = 0;
- enumeratePointerFields(DestPtr, S.getContext(), Size,
- [&](const Pointer &P, PrimType T, Bits BitOffset,
- Bits FullBitWidth, bool PackedBools) -> bool {
- TYPE_SWITCH(T, {
- P.deref<T>() = std::get<T>(Values[ValueIndex]);
- P.initialize();
- });
-
- ++ValueIndex;
- return true;
- });
+ enumeratePointerFields(
+ DestPtr, S.getContext(), Size,
+ [&](const Pointer &P, PrimType T, Bits BitOffset, Bits FullBitWidth,
+ bool PackedBools) -> Result {
+ TYPE_SWITCH(T, {
+ P.deref<T>() = std::get<T>(Values[ValueIndex]);
+ P.initialize();
+ });
+
+ ++ValueIndex;
+ return Result::Success;
+ },
+ true);
// We should've read all the values into DestPtr.
assert(ValueIndex == Values.size());
diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h
index 8b4da6a14de70..7dbf58c23fd5c 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.h
+++ b/clang/lib/AST/ByteCode/InterpFrame.h
@@ -104,6 +104,7 @@ class InterpFrame final : public Frame {
template <typename T> void setLocal(unsigned Offset, const T &Value) {
localRef<T>(Offset) = Value;
localInlineDesc(Offset)->IsInitialized = true;
+ localInlineDesc(Offset)->LifeState = Lifetime::Started;
}
/// Returns a pointer to a local variables.
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 849dde1dd1152..78f33672b7be0 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -433,7 +433,8 @@ def SetLocal : AccessOpcode { let HasCustomEval = 1; }
def EndLifetimePop : Opcode;
def EndLifetime : Opcode;
def MarkDestroyed : Opcode;
-def StartLifetime : Opcode;
+def StartThisLifetime : Opcode;
+def StartThisLifetime1 : Opcode;
def CheckDecl : Opcode {
let Args = [ArgVarDecl];
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 4b50b80ae66bb..851e9d5742ccb 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -556,6 +556,7 @@ void Pointer::initialize() const {
// Field has its bit in an inline descriptor.
assert(BS.Base != 0 && "Only composite fields can be initialised");
getInlineDesc()->IsInitialized = true;
+ getInlineDesc()->LifeState = Lifetime::Started;
}
void Pointer::initializeElement(unsigned Index) const {
diff --git a/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
index 2eefa138f7f0a..6de3e03531c5d 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp
@@ -141,12 +141,10 @@ namespace BitFields {
// expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'byte' is invalid}}
struct M {
- // expected-note at +1 {{subobject declared here}}
- unsigned char mem[sizeof(BF)];
+ unsigned char mem[sizeof(BF)]; // expected-note {{subobject declared here}}
};
- // expected-error at +2 {{initialized by a constant expression}}
- // expected-note at +1 {{not initialized}}
- constexpr M m = bit_cast<M>(bf);
+ constexpr M m = bit_cast<M>(bf); // expected-error {{must be initialized by a constant expression}} \
+ // expected-note {{not initialized}}
constexpr auto f = []() constexpr {
// bits<24, unsigned int, LITTLE_END ? 0 : 8> B = {0xc0ffee};
@@ -156,10 +154,9 @@ namespace BitFields {
static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee);
{
- // expected-error at +3 {{initialized by a constant expression}}
- // expected-note at +2 {{in call to}}
- // expected-note at +1 {{temporary created here}}
- constexpr auto _bad = f()[3];
+ constexpr auto _bad = f()[3]; // expected-error {{initialized by a constant expression}} \
+ // expected-note {{in call to}} \
+ // expected-note {{temporary created here}}
}
struct B {
@@ -174,10 +171,9 @@ namespace BitFields {
};
static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe);
{
- // expected-error at +3 {{initialized by a constant expression}}
- // expected-note at +2 {{read of uninitialized object is not allowed in a constant expression}}
- // expected-note at +1 {{temporary created here}}
- constexpr auto _bad = g().b2;
+ constexpr auto _bad = g().b2; // expected-error {{initialized by a constant expression}} \
+ // expected-note {{read of uninitialized object is not allowed in a constant expression}} \
+ // expected-note {{temporary created here}}
}
}
}
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index 2a04f28032b6c..4ade50b7c02e4 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -729,31 +729,30 @@ namespace OperatorNewDelete {
constexpr ~S() { }
};
- /// FIXME: This is broken in the current interpreter.
constexpr bool structAlloc() {
- S *s = std::allocator<S>().allocate(1); // ref-note {{heap allocation performed here}}
+ S *s = std::allocator<S>().allocate(1); // both-note {{heap allocation performed here}}
- s->i = 12; // ref-note {{assignment to object outside its lifetime is not allowed in a constant expression}}
+ s->i = 12; // both-note {{assignment to object outside its lifetime is not allowed in a constant expression}}
bool Res = (s->i == 12);
std::allocator<S>().deallocate(s);
return Res;
}
- static_assert(structAlloc()); // ref-error {{not an integral constant expression}} \
- // ref-note {{in call to}}
+ static_assert(structAlloc()); // both-error {{not an integral constant expression}} \
+ // both-note {{in call to}}
constexpr bool structAllocArray() {
- S *s = std::allocator<S>().allocate(9); // ref-note {{heap allocation performed here}}
+ S *s = std::allocator<S>().allocate(9); // both-note {{heap allocation performed here}}
- s[2].i = 12; // ref-note {{assignment to object outside its lifetime is not allowed in a constant expression}}
+ s[2].i = 12; // both-note {{assignment to object outside its lifetime is not allowed in a constant expression}}
bool Res = (s[2].i == 12);
std::allocator<S>().deallocate(s);
return Res;
}
- static_assert(structAllocArray()); // ref-error {{not an integral constant expression}} \
- // ref-note {{in call to}}
+ static_assert(structAllocArray()); // both-error {{not an integral constant expression}} \
+ // both-note {{in call to}}
constexpr bool alloc_from_user_code() {
void *p = __builtin_operator_new(sizeof(int)); // both-note {{cannot allocate untyped memory in a constant expression; use 'std::allocator<T>::allocate'}}
diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp
index bb2e0821fc8f8..cf80b3709ac75 100644
--- a/clang/test/AST/ByteCode/records.cpp
+++ b/clang/test/AST/ByteCode/records.cpp
@@ -1944,3 +1944,16 @@ namespace ErroneousVoidDecl {
// ref-note {{in call to}}
#endif
}
+
+namespace FieldLifetimeNotStarted {
+ struct R { // both-note {{during field initialization in the implicit default constructor}}
+ struct Inner { constexpr int f() const { return 0; } };
+ int a = b.f(); // both-warning {{field 'b' is uninitialized when used here}} \
+ // both-note {{member call on object outside its lifetime}}
+ Inner b;
+ };
+ constexpr R r; // both-error {{constant expression}} \
+ // both-note {{in call to}} \
+ // both-note {{declared here}} \
+ // both-note {{in implicit default constructor for 'FieldLifetimeNotStarted::R' first required here}}
+}
More information about the cfe-commits
mailing list