[clang] [clang][bytecode] Don't start record field lifetime by default (PR #193496)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 22 06:23:15 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

Even though we have per-field lifetime information we did not previously diagnose this test:
```c++
  struct R {
    struct Inner { constexpr int f() const { return 0; } };
    int a = b.f();
    Inner b;
  };
  constexpr R r;
```
because the life time was started by default.

This patch makes record members be `Lifetime::NotStarted` by default (unless they are primitive arrays) and then starts the lifetime when in `Pointer::initialize()`.

---

Patch is 28.58 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/193496.diff


13 Files Affected:

- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+19-16) 
- (modified) clang/lib/AST/ByteCode/Descriptor.cpp (+4) 
- (modified) clang/lib/AST/ByteCode/Descriptor.h (+1) 
- (modified) clang/lib/AST/ByteCode/EvalEmitter.cpp (+1) 
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+43-10) 
- (modified) clang/lib/AST/ByteCode/Interp.h (+7-1) 
- (modified) clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp (+73-48) 
- (modified) clang/lib/AST/ByteCode/InterpFrame.h (+1) 
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+2) 
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+1) 
- (modified) clang/test/AST/ByteCode/builtin-bit-cast-bitfields.cpp (+9-13) 
- (modified) clang/test/AST/ByteCode/new-delete.cpp (+8-9) 
- (modified) clang/test/AST/ByteCode/records.cpp (+13) 


``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index dbc71cca65747..58dbf362308d2 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2146,7 +2146,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.
@@ -2326,7 +2326,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>
@@ -3248,7 +3248,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;
 }
@@ -3280,7 +3280,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());
@@ -3307,7 +3307,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.
@@ -3329,7 +3329,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>
@@ -3584,7 +3584,7 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const CXXConstructExpr *E) {
 
     if (DiscardResult)
       return this->emitPopPtr(E);
-    return this->emitFinishInit(E);
+    return true;
   }
 
   if (T->isArrayType()) {
@@ -4603,7 +4603,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) {
@@ -5074,9 +5074,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.
@@ -5266,8 +5263,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD,
 
     if (!visitInitializer(Init))
       return false;
-
-    return this->emitFinishInitPop(Init);
+    return this->emitPopPtr(Init);
   }
   return false;
 }
@@ -6639,6 +6635,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 {
@@ -6669,8 +6669,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();
@@ -6732,7 +6731,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{},
@@ -6795,6 +6794,10 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) {
       return false;
   }
 
+  // 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 6d1598655659d..c148301079ca0 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;
 
@@ -882,7 +882,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
       return false;
     return CheckDummy(S, OpPC, Ptr.block(), AK_Assign);
   }
-  if (!CheckLifetime(S, OpPC, Ptr, AK_Assign))
+  if (!WillBeActivated && !CheckLifetime(S, OpPC, Ptr, AK_Assign))
     return false;
   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
     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,9 @@ 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;
 }
@@ -1698,7 +1702,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))
@@ -1946,16 +1951,22 @@ 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;
   }
 
@@ -1970,6 +1981,28 @@ bool StartLifetime(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+bool StartThisLifetime(InterpState &S, CodePtr OpPC) {
+  if (S.checkingPotentialConstantExpression())
+    return true;
+
+  const auto &Ptr = S.Current->getThis();
+  if (!Ptr.isBlockPointer())
+    return false;
+  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;
+}
+
 // FIXME: It might be better to the recursing as part of the generated code for
 // a destructor?
 static void setLifeStateRecurse(const Pointer &Ptr, Lifetime L) {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 0f7f2c4e1fdec..772543f8e2c91 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1544,6 +1544,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.
@@ -3984,9 +3986,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...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/193496


More information about the cfe-commits mailing list