[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