Timm =?utf-8?q?Bäder?= <[email protected]>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>


https://github.com/tbaederr updated 
https://github.com/llvm/llvm-project/pull/221218

>From 54bcbf58c523183ca11fa6cb1ea60384f8786f3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Thu, 3 Sep 2026 14:29:25 +0200
Subject: [PATCH 1/2] opaque decl

---
 clang/lib/AST/ByteCode/Compiler.cpp           |  19 +-
 clang/lib/AST/ByteCode/Interp.cpp             | 218 +++++++++++++++---
 clang/lib/AST/ByteCode/Interp.h               | 107 ++++++---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      |  57 +++--
 clang/lib/AST/ByteCode/InterpHelpers.h        |   5 +
 clang/lib/AST/ByteCode/MemberPointer.h        |   2 +
 clang/lib/AST/ByteCode/Opcodes.td             |   9 +-
 clang/lib/AST/ByteCode/Pointer.cpp            |  82 +++++--
 clang/lib/AST/ByteCode/Pointer.h              |  37 ++-
 clang/test/AST/ByteCode/records.cpp           |  11 +
 clang/test/CodeGen/pr4349.c                   |   3 +-
 clang/test/SemaCXX/new-delete.cpp             |  14 +-
 .../SemaTemplate/temp_arg_nontype_cxx1z.cpp   |   1 +
 clang/unittests/AST/ByteCode/toAPValue.cpp    |   2 -
 14 files changed, 444 insertions(+), 123 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 03f9478a6e4e0..85abeb7e1bd04 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7936,12 +7936,18 @@ bool Compiler<Emitter>::VisitUnaryOperator(const 
UnaryOperator *E) {
     // check), so that e.g. '&*(int *)0' is not rejected.
     if (!Ctx.getLangOpts().CPlusPlus) {
       const Expr *Sub = SubExpr->IgnoreParens();
+
       if (const auto *Deref = dyn_cast<UnaryOperator>(Sub);
-          Deref && Deref->getOpcode() == UO_Deref)
-        return this->delegate(Deref->getSubExpr());
+          Deref && Deref->getOpcode() == UO_Deref) {
+        if (DiscardResult)
+          return this->discard(Deref->getSubExpr());
+        return this->visit(Deref->getSubExpr()) && this->emitAddrOf(E);
+      }
     }
     // We should already have a pointer when we get here.
-    return this->delegate(SubExpr);
+    if (DiscardResult)
+      return this->discard(SubExpr);
+    return this->delegate(SubExpr) && this->emitAddrOf(E);
   case UO_Deref: // *x
     if (DiscardResult)
       return this->discard(SubExpr);
@@ -8730,11 +8736,10 @@ template <class Emitter>
 bool Compiler<Emitter>::emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU) {
   assert(!DiscardResult && "Should've been checked before");
 
-  if (ToLValue) {
-    if (const auto *VD = D.asValueDecl())
-      return this->emitGetOpaquePtr(VD, CU, E);
-  }
+  if (const auto *VD = D.asValueDecl())
+    return this->emitGetOpaquePtr(VD, CU, E);
 
+  assert(D.asExpr());
   unsigned DummyID = P.getOrCreateDummy(D, CU);
   if (!this->emitGetPtrGlobal(DummyID, E))
     return false;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index a41cc1f5a564b..6831c6311c93c 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -97,6 +97,23 @@ static void noteValueLocation(InterpState &S, const Block 
*B) {
     S.Note(Desc->getLocation(), diag::note_declared_at);
 }
 
+static void noteValueLocation(InterpState &S, const Pointer &Ptr) {
+  if (Ptr.isBlockPointer()) {
+    const Block *B = Ptr.block();
+    const Descriptor *Desc = B->getDescriptor();
+    if (B->isDynamic())
+      S.Note(Desc->getLocation(), diag::note_constexpr_dynamic_alloc_here);
+    else if (B->isTemporary())
+      S.Note(Desc->getLocation(), diag::note_constexpr_temporary_here);
+    else
+      S.Note(Desc->getLocation(), diag::note_declared_at);
+    return;
+  }
+
+  if (Ptr.isOpaquePointer())
+    S.Note(Ptr.asOpaquePointer().Base->getLocation(), diag::note_declared_at);
+}
+
 static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
                                      const ValueDecl *VD,
                                      AccessKinds AK = AK_Read);
@@ -192,6 +209,38 @@ static void diagnoseNonConstVariable(InterpState &S, 
CodePtr OpPC,
   S.Note(VD->getLocation(), diag::note_declared_at);
 }
 
+static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                           AccessKinds AK) {
+
+  if (!Ptr.isBlockPointer())
+    return true;
+
+  const Block *B = Ptr.block();
+  if (B->getDeclID()) {
+    if (!(B->isStatic() && B->isTemporary()))
+      return true;
+
+    const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(
+        B->getDescriptor()->asExpr());
+    if (!MTE)
+      return true;
+
+    // FIXME(perf): Since we do this check on every Load from a static
+    // temporary, it might make sense to cache the value of the
+    // isUsableInConstantExpressions call.
+    if (S.checkingConstantDestruction() ||
+        (B->getEvalID() != S.EvalID &&
+         !MTE->isUsableInConstantExpressions(S.getASTContext()))) {
+      const SourceInfo &E = S.Current->getSource(OpPC);
+      S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
+      noteValueLocation(S, B);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
                            AccessKinds AK) {
   if (B->getDeclID()) {
@@ -452,7 +501,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
     } else if (!S.checkingPotentialConstantExpression()) {
       S.FFDiag(Src, diag::note_constexpr_access_uninit)
           << AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
-      noteValueLocation(S, Ptr.block());
+      noteValueLocation(S, Ptr);
     }
 
     return false;
@@ -889,7 +938,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
   }
   // Block and string pointers are the only ones we can actually read from.
   if (!Ptr.isReadablePointerType())
-    return false;
+    return CheckDummy(S, OpPC, Ptr, AK);
 
   if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) {
     if (!CheckLive(S, OpPC, Ptr, AK))
@@ -956,7 +1005,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
 bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   assert(!Ptr.isZero());
   if (!Ptr.isReadablePointerType())
-    return false;
+    return CheckDummy(S, OpPC, Ptr, AK_Read);
 
   if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) {
     if (!CheckLive(S, OpPC, Ptr, AK_Read))
@@ -988,7 +1037,13 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr) {
 
 bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                 bool WillBeActivated) {
-  if (!Ptr.isBlockPointer() || Ptr.isZero())
+  if (Ptr.isZero())
+    return false;
+
+  if (Ptr.isOpaquePointer())
+    return CheckDummy(S, OpPC, Ptr, AK_Assign);
+
+  if (!Ptr.isBlockPointer())
     return false;
 
   if (!Ptr.block()->isAccessible()) {
@@ -1035,6 +1090,8 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr) {
     return false;
   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
     return false;
+  if (!Ptr.isBlockPointer())
+    return false;
   return true;
 }
 
@@ -1255,6 +1312,8 @@ bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
 
 bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
                        const Pointer &Ptr) {
+  if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer())
+    return false;
   // Regular new type(...) call.
   if (isa_and_nonnull<CXXNewExpr>(Source))
     return true;
@@ -1271,7 +1330,7 @@ bool CheckDeleteSource(InterpState &S, CodePtr OpPC, 
const Expr *Source,
   const SourceInfo &Loc = S.Current->getSource(OpPC);
   S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc)
       << Ptr.toDiagnosticString(S.getASTContext());
-  noteValueLocation(S, Ptr.block());
+  noteValueLocation(S, Ptr);
   return false;
 }
 
@@ -1297,6 +1356,24 @@ bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const 
DeclRefExpr *DR,
   return CheckDeclRef(S, OpPC, DR);
 }
 
+bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                AccessKinds AK) {
+  if (!Ptr.isDummy())
+    return true;
+
+  const VarDecl *D = Ptr.getRootVarDecl();
+  if (!D)
+    return false;
+
+  if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement)
+    return diagnoseUnknownDecl(S, OpPC, D, AK);
+
+  if (AK == AK_Destroy || S.getLangOpts().CPlusPlus14)
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_modify_global);
+  return false;
+}
+
+// FIXME: Remove this once all dummy pointers are opaque pointers.
 bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) {
   if (!B->isDummy())
     return true;
@@ -1421,7 +1498,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool 
DeleteIsArrayForm,
       return true;
 
     if (!Ptr.isBlockPointer())
-      return false;
+      return CheckDeleteSource(S, OpPC, nullptr, Ptr);
 
     // Remove base casts.
     QualType InitialType = Ptr.getType();
@@ -1803,7 +1880,7 @@ static bool diagnoseOutOfLifetimeDestroy(InterpState &S, 
CodePtr OpPC,
 bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   if (!CheckLive(S, OpPC, Ptr, AK_Destroy))
     return false;
-  if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Destroy))
+  if (!CheckTemporary(S, OpPC, Ptr, AK_Destroy))
     return false;
   if (!CheckRange(S, OpPC, Ptr, AK_Destroy))
     return false;
@@ -1819,7 +1896,7 @@ bool checkDestructor(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr) {
     return true;
 
   // Can't call a dtor on a global variable.
-  if (Ptr.block()->isStatic()) {
+  if (Ptr.isOpaquePointer() || Ptr.block()->isStatic()) {
     const SourceInfo &E = S.Current->getSource(OpPC);
     S.FFDiag(E, diag::note_constexpr_modify_global);
     return false;
@@ -2048,9 +2125,22 @@ bool Call(InterpState &S, CodePtr OpPC, const Function 
*Func,
   return true;
 }
 
-static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
+static bool getDynamicDecl(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                            const CXXRecordDecl *&DynamicDecl) {
 
+  auto diagUnknownDynamicType = [&](const Pointer &P) -> bool {
+    APValue V = P.toAPValue(S.getASTContext());
+    QualType TT = S.getASTContext().getLValueReferenceType(P.getType());
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_polymorphic_unknown_dynamic_type)
+        << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
+    return false;
+  };
+
+  if (!Ptr.isBlockPointer())
+    return diagUnknownDynamicType(Ptr);
+
+  PtrView TypePtr = Ptr.view();
   if (S.InitializingPtrs.empty()) {
     TypePtr = TypePtr.stripBaseCasts();
   } else {
@@ -2082,14 +2172,8 @@ static bool getDynamicDecl(InterpState &S, CodePtr OpPC, 
PtrView TypePtr,
   QualType DynamicType = TypePtr.getType();
   if (TypePtr.Pointee->isStatic() || TypePtr.isConst()) {
     if (const VarDecl *VD = Pointer(TypePtr).getRootVarDecl();
-        VD && !VD->isConstexpr()) {
-      const Expr *E = S.Current->getExpr(OpPC);
-      APValue V = Pointer(TypePtr).toAPValue(S.getASTContext());
-      QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
-      S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
-          << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
-      return false;
-    }
+        VD && !VD->isConstexpr())
+      return diagUnknownDynamicType(Pointer(TypePtr));
   }
 
   if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
@@ -2154,7 +2238,7 @@ bool DynamicCast(InterpState &S, CodePtr OpPC, const Type 
*DestTypePtr,
   const auto &Ptr = S.Stk.pop<Pointer>();
   QualType TargetType = QualType(DestTypePtr, 0);
 
-  if (Ptr.isConstexprUnknown()) {
+  if (Ptr.isConstexprUnknown() || Ptr.isOpaquePointer()) {
     QualType T = Ptr.getType();
     const Expr *E = S.Current->getExpr(OpPC);
     APValue V = Ptr.toAPValue(S.getASTContext());
@@ -2323,13 +2407,13 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const 
Function *Func,
   size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
   Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
 
-  if (!ThisPtr.isBlockPointer())
+  if (!ThisPtr.isBlockPointer() && !ThisPtr.isOpaquePointer())
     return false;
 
   const FunctionDecl *Callee = Func->getDecl();
 
   const CXXRecordDecl *DynamicDecl = nullptr;
-  if (!getDynamicDecl(S, OpPC, ThisPtr.view(), DynamicDecl))
+  if (!getDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
     return false;
   assert(DynamicDecl);
 
@@ -2603,7 +2687,7 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, 
const Expr *E,
   }
 
   if (!Ptr.isBlockPointer())
-    return false;
+    return CheckDummy(S, OpPC, Ptr, AK_Construct);
 
   if (!CheckRange(S, OpPC, Ptr, AK_Construct))
     return false;
@@ -2617,9 +2701,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, 
const Expr *E,
       return false;
     if (!CheckLive(S, OpPC, Ptr, AK_Construct))
       return false;
-    return CheckDummy(S, OpPC, Ptr.block(), AK_Construct);
+    return CheckDummy(S, OpPC, Ptr, AK_Construct);
   }
-  if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Construct))
+  if (!CheckTemporary(S, OpPC, Ptr, AK_Construct))
     return false;
 
   // CheckLifetime for this and all base pointers.
@@ -2765,6 +2849,12 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr 
OpPC,
   if (Ptr.isIntegralPointer())
     return true;
 
+  if (Ptr.isOpaquePointer()) {
+    if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
+      return false;
+    return Ptr.isRoot();
+  }
+
   if (Ptr.isDummy()) {
     if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
       return false;
@@ -2854,7 +2944,7 @@ bool GetTypeid(InterpState &S, const Type *TypePtr, const 
Type *TypeInfoType) {
 bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) {
   const auto &P = S.Stk.pop<Pointer>();
 
-  if (!P.isBlockPointer())
+  if (!P.isBlockPointer() && !P.isOpaquePointer())
     return false;
 
   if (P.isConstexprUnknown()) {
@@ -2868,7 +2958,12 @@ bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const 
Type *TypeInfoType) {
   }
 
   // Pick the most-derived type.
-  CanQualType T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified();
+  CanQualType T;
+  if (P.isBlockPointer())
+    T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified();
+  else
+    T = P.getType()->getCanonicalTypeUnqualified();
+
   // ... unless we're currently constructing this object.
   // FIXME: We have a similar check to this in more places.
   if (S.Current->getFunction()) {
@@ -2971,6 +3066,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView 
Ptr, PrimType T) {
     auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
     std::copy_n(Val.path(), PathLength, NewPath);
     Val.takePath(NewPath);
+  } else if (T == PT_Ptr) {
+    auto &Val = Ptr.deref<Pointer>();
+    if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
+      const OpaquePointer &OP = Val.asOpaquePointer();
+      auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
+      std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry));
+      Val = Pointer(OP.withPath(NewPath, OP.PathLength,
+                                OP.getFieldType().getTypePtr(),
+                                OP.isOnePastEnd()),
+                    Val.getByteOffset());
+    }
   }
 }
 
@@ -2983,6 +3089,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView 
Ptr) {
     auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
     std::copy_n(Val.path(), PathLength, NewPath);
     Val.takePath(NewPath);
+  } else if constexpr (std::is_same_v<T, Pointer>) {
+    auto &Val = Ptr.deref<Pointer>();
+    if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
+      const OpaquePointer &OP = Val.asOpaquePointer();
+      auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
+      std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry));
+      Val = Pointer(OP.withPath(NewPath, OP.PathLength,
+                                OP.getFieldType().getTypePtr(),
+                                OP.isOnePastEnd()),
+                    Val.getByteOffset());
+    }
   } else {
     auto &Val = Ptr.deref<T>();
     if (!Val.singleWord()) {
@@ -3042,6 +3159,8 @@ static void finishGlobalRecurse(InterpState &S, PtrView 
Ptr) {
 
 bool FinishInitGlobal(InterpState &S) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
+  if (!Ptr.isBlockPointer())
+    return true;
 
   finishGlobalRecurse(S, Ptr.view());
   if (Ptr.canBeInitialized()) {
@@ -3347,13 +3466,16 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState 
&S, CodePtr OpPC,
     return Ptr;
 
   const OpaquePointer &OP = Ptr.asOpaquePointer();
-  QualType ArrTy = OP.getSurroundingArray();
-  QualType ElemTy = ArrTy;
+  QualType ArrTy = OP.getSurroundingArray().getCanonicalType();
+  QualType ElemTy = OP.getFieldType();
   unsigned NumElems = 1;
-  if (const ArrayType *AT = ArrTy->getAsArrayTypeUnsafe()) {
-    ElemTy = AT->getElementType();
-    if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
+
+  if (OP.isArrayElement()) {
+    if (const ConstantArrayType *CAT =
+            S.getASTContext().getAsConstantArrayType(ArrTy))
       NumElems = CAT->getZExtSize();
+  } else {
+    ArrTy = ElemTy;
   }
 
   if (isa<IncompleteArrayType>(ArrTy)) {
@@ -3365,10 +3487,10 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState 
&S, CodePtr OpPC,
   if (Offset > NumElems) {
     if (Op == ArithOp::Add)
       S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems;
+          << Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
     else
       S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << -Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems;
+          << -Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
   }
 
   if (!validType(ElemTy) || !validType(ArrTy)) {
@@ -3404,6 +3526,38 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState 
&S, CodePtr OpPC,
   return Pointer(OP.withPastEnd(true), NewOffset);
 }
 
+bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl,
+                    const Pointer &Ptr) {
+  if (Ptr.isOpaquePointer()) {
+    const OpaquePointer &OP = Ptr.asOpaquePointer();
+    if (!OP.getFieldType()->isRecordType()) {
+      S.Stk.push<Pointer>(Ptr);
+      return true;
+    }
+
+    PointerPathEntry *NewPath =
+        S.extendPointerPath(OP.PathLength + 1, OP.Path,
+                            PointerPathEntry::base(Decl, /*IsVirtual=*/true));
+
+    S.Stk.push<Pointer>(
+        OP.withPath(NewPath, OP.PathLength + 1,
+                    S.getASTContext().getCanonicalTagType(Decl).getTypePtr()),
+        Ptr.getByteOffset());
+    return true;
+  }
+
+  if (!Ptr.isBlockPointer())
+    return false;
+  if (!Ptr.getFieldDesc()->isRecord())
+    return false;
+  Pointer Base = Ptr.stripBaseCasts();
+  const Record::Base *VirtBase = Base.getRecord()->findVirtualBase(Decl);
+  if (!VirtBase)
+    return false;
+  S.Stk.push<Pointer>(Base.atField(VirtBase->Offset));
+  return true;
+}
+
 // FIXME: Would be nice to generate this instead of hardcoding it here.
 [[maybe_unused]] static constexpr bool OpReturns(Opcode Op) {
   return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 61118d77b7ac2..343883872728b 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1494,15 +1494,30 @@ bool CMP3(InterpState &S, CodePtr OpPC, const 
ComparisonCategoryInfo *CmpInfo) {
   const T &LHS = S.Stk.pop<T>();
   const Pointer &P = S.Stk.peek<Pointer>();
 
-  ComparisonCategoryResult CmpResult = LHS.compare(RHS);
+  ComparisonCategoryResult CmpResult;
   if constexpr (std::is_same_v<T, Pointer>) {
-    if (CmpResult == ComparisonCategoryResult::Unordered) {
-      const SourceInfo &Loc = S.Current->getSource(OpPC);
-      S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
+    if (!Pointer::hasSameBase(LHS, RHS)) {
+      S.FFDiag(S.Current->getSource(OpPC),
+               diag::note_constexpr_pointer_comparison_unspecified)
           << LHS.toDiagnosticString(S.getASTContext())
           << RHS.toDiagnosticString(S.getASTContext());
       return false;
     }
+    std::optional<size_t> LHSOffset =
+        LHS.computeLayoutOffset(S.getASTContext());
+    std::optional<size_t> RHSOffset =
+        RHS.computeLayoutOffset(S.getASTContext());
+    if (!LHSOffset || !RHSOffset)
+      return false;
+
+    if (LHSOffset < RHSOffset)
+      CmpResult = ComparisonCategoryResult::Less;
+    else if (LHSOffset > RHSOffset)
+      CmpResult = ComparisonCategoryResult::Greater;
+    else
+      CmpResult = ComparisonCategoryResult::Equal;
+  } else {
+    CmpResult = LHS.compare(RHS);
   }
 
   assert(CmpInfo);
@@ -1673,6 +1688,9 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
   if (!CheckRange(S, OpPC, Obj, CSK_Field))
     return false;
 
+  if (!Obj.isBlockPointer())
+    return false;
+
   // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
   // to keep an invalid sample producing the same diagnostics as the current
   // interpreter.
@@ -1696,6 +1714,9 @@ bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t 
I) {
   if (!CheckRange(S, OpPC, Obj, CSK_Field))
     return false;
 
+  if (!Obj.isBlockPointer())
+    return false;
+
   // FIXME(postswitch): The isUnknownSizeArray() check here is only needed
   // to keep an invalid sample producing the same diagnostics as the current
   // interpreter.
@@ -1716,6 +1737,10 @@ bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t 
I) {
   if (!CheckThis(S, OpPC))
     return false;
   const Pointer &This = S.Current->getThis();
+
+  if (!This.isBlockPointer())
+    return false;
+
   const Pointer &Field = This.atField(I);
   if (!CheckLoad(S, OpPC, Field))
     return false;
@@ -1773,6 +1798,18 @@ bool InitGlobal(InterpState &S, uint32_t I) {
       NewPath[I] = Val.getPathEntry(I);
     }
     Val.takePath(NewPath);
+  } else if constexpr (std::is_same_v<T, Pointer>) {
+    auto &Val = P.deref<Pointer>();
+    if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
+      const OpaquePointer &OP = Val.asOpaquePointer();
+      auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
+      std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry));
+      Val = Pointer(OP.withPath(NewPath, OP.PathLength,
+                                OP.getFieldType().getTypePtr(),
+                                OP.isOnePastEnd()),
+                    Val.getByteOffset());
+    }
+
   } else if constexpr (needsAlloc<T>()) {
     auto &Val = P.deref<T>();
     if (!Val.singleWord()) {
@@ -2090,6 +2127,8 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, 
uint32_t Off) {
   if (!CheckThis(S, OpPC))
     return false;
   const Pointer &This = S.Current->getThis();
+  if (!This.isBlockPointer())
+    return false;
   S.Stk.push<Pointer>(This.atField(Off));
   return true;
 }
@@ -2153,46 +2192,36 @@ inline bool CheckNull(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
-inline bool VirtBaseHelper(InterpState &S, const RecordDecl *Decl,
-                           const Pointer &Ptr) {
-  if (!Ptr.isBlockPointer())
-    return false;
-  if (!Ptr.getFieldDesc()->isRecord())
-    return false;
-  Pointer Base = Ptr.stripBaseCasts();
-  const Record::Base *VirtBase = Base.getRecord()->findVirtualBase(Decl);
-  if (!VirtBase)
-    return false;
-  S.Stk.push<Pointer>(Base.atField(VirtBase->Offset));
-  return true;
-}
+bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl,
+                    const Pointer &Ptr);
 
 inline bool GetPtrVirtBasePop(InterpState &S, CodePtr OpPC,
-                              const RecordDecl *D) {
+                              const CXXRecordDecl *D) {
   assert(D);
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckNull(S, OpPC, Ptr, CSK_Base))
     return false;
-  return VirtBaseHelper(S, D, Ptr);
+  return virtBaseHelper(S, D, Ptr);
 }
 
-inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) {
+inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC,
+                           const CXXRecordDecl *D) {
   assert(D);
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckNull(S, OpPC, Ptr, CSK_Base))
     return false;
-  return VirtBaseHelper(S, D, Ptr);
+  return virtBaseHelper(S, D, Ptr);
 }
 
 inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
-                               const RecordDecl *D) {
+                               const CXXRecordDecl *D) {
   assert(D);
   if (S.checkingPotentialConstantExpression())
     return false;
   if (!CheckThis(S, OpPC))
     return false;
   const Pointer &This = S.Current->getThis();
-  return VirtBaseHelper(S, D, This);
+  return virtBaseHelper(S, D, This);
 }
 
 
//===----------------------------------------------------------------------===//
@@ -3037,6 +3066,22 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, 
uint32_t FPOI) {
   }
 }
 
+inline bool AddrOf(InterpState &S, CodePtr OpPC) {
+  const Pointer Ptr = S.Stk.pop<Pointer>();
+
+  if (Ptr.isOpaquePointer()) {
+    const OpaquePointer &OP = Ptr.asOpaquePointer();
+    QualType T = QualType(OP.FieldType.getPointer(), 0);
+    T = S.getASTContext().getPointerType(T);
+
+    S.Stk.push<Pointer>(OP.withFieldType(T.getTypePtr(), OP.isOnePastEnd()));
+  } else {
+    S.Stk.push<Pointer>(Ptr);
+  }
+
+  return true;
+}
+
 bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
                                 const Pointer &Ptr, unsigned BitWidth);
 bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth);
@@ -3068,6 +3113,8 @@ bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
         Kind = IntegralKind::BlockAddress;
       }
       S.Stk.push<T>(Kind, PtrVal, /*Offset=*/0);
+    } else if (Ptr.isOpaquePointer()) {
+      S.Stk.push<T>(IntegralKind::Address, Ptr.asOpaquePointer().Base, 0);
     } else if (Ptr.isFunctionPointer()) {
       const void *FuncDecl = Ptr.asFunctionPointer().Func->getDecl();
       S.Stk.push<T>(IntegralKind::FunctionAddress, FuncDecl, /*Offset=*/0);
@@ -3496,15 +3543,17 @@ inline bool ExpandPtr(InterpState &S) {
   return true;
 }
 
-bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-                        APSInt &&Index, bool AllowReplace = true);
-
 // Implementation for ArrayElemPtr and ArrayElemPtrPop ops.
 template <typename T>
 inline bool arrayElemPtr(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                          const T &Offset) {
-  if (Ptr.isOpaquePointer())
+  if (Ptr.isOpaquePointer()) {
+    if (S.inConstantContext() && !Offset.isZero() &&
+        !CheckArray(S, OpPC, Ptr)) {
+      return false;
+    }
     return arrayElemPtrOpaque(S, OpPC, Ptr, Offset.toAPSInt());
+  }
 
   if (Offset.isZero()) {
     if (const Descriptor *Desc = Ptr.getFieldDesc();
@@ -4141,6 +4190,10 @@ inline bool BitCast(InterpState &S, CodePtr OpPC) {
   Pointer FromPtr = S.Stk.pop<Pointer>();
   Pointer &ToPtr = S.Stk.peek<Pointer>();
 
+  // FIXME: Could allow reading from string pointers?
+  if (!FromPtr.isBlockPointer() || !ToPtr.isBlockPointer())
+    return false;
+
   const Descriptor *D = FromPtr.getFieldDesc();
   if (D->isPrimitiveArray() && FromPtr.isArrayRoot())
     FromPtr = FromPtr.atIndex(0);
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b78ce614e290d..94a0e1e154a5c 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -165,6 +165,9 @@ static QualType getElemType(const Pointer &P) {
         ->getElementType();
   }
 
+  if (P.isOpaquePointer() || P.isIntegralPointer())
+    return P.getType();
+
   const Descriptor *Desc = P.getFieldDesc();
   QualType T = Desc->getType();
   if (Desc->isPrimitive())
@@ -409,7 +412,7 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr 
OpPC,
   if (!StrPtr.isBlockPointer())
     return false;
 
-  if (!CheckDummy(S, OpPC, StrPtr.block(), AK_Read))
+  if (!CheckDummy(S, OpPC, StrPtr, AK_Read))
     return false;
 
   if (!StrPtr.getFieldDesc()->isPrimitiveArray())
@@ -1318,13 +1321,13 @@ static bool 
interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
   }
   assert(FirstArgT == PT_Ptr);
   const Pointer &Ptr = S.Stk.pop<Pointer>();
-  if (!Ptr.isBlockPointer()) {
+  if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer()) {
     S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_compute)
         << Alignment;
     return false;
   }
 
-  const ValueDecl *PtrDecl = Ptr.getDeclDesc()->asValueDecl();
+  const VarDecl *PtrDecl = Ptr.getRootVarDecl();
   // We need a pointer for a declaration here.
   if (!PtrDecl) {
     if (BuiltinOp == Builtin::BI__builtin_is_aligned)
@@ -1336,10 +1339,19 @@ static bool 
interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
     return false;
   }
 
-  // For one-past-end pointers, we can't call getIndex() since it asserts.
-  // Use getNumElems() instead which gives the correct index for past-end.
-  unsigned PtrOffset =
-      Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex();
+  unsigned PtrOffset;
+  if (Ptr.isBlockPointer()) {
+    // For one-past-end pointers, we can't call getIndex() since it asserts.
+    // Use getNumElems() instead which gives the correct index for past-end.
+    PtrOffset = Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex();
+  } else {
+    if (std::optional<size_t> PtrOff =
+            Ptr.computeLayoutOffset(S.getASTContext()))
+      PtrOffset = *PtrOff;
+    else
+      return false;
+  }
+
   CharUnits BaseAlignment = S.getASTContext().getDeclAlign(PtrDecl);
   CharUnits PtrAlign =
       BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset));
@@ -1388,8 +1400,18 @@ static bool 
interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
                                     ? llvm::alignDown(PtrOffset, Alignment64)
                                     : llvm::alignTo(PtrOffset, Alignment64));
 
-    S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity()));
-    return true;
+    if (Ptr.isBlockPointer()) {
+      S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity()));
+      return true;
+    }
+
+    assert(Ptr.isOpaquePointer());
+
+    APSInt APOffset =
+        APSInt(APInt(64, NewOffset.getQuantity(), /*IsSigned=*/true),
+               /*IsUnsigned=*/false);
+    return arrayElemPtrOpaque(S, OpPC, Ptr, std::move(APOffset),
+                              /*AllocReplace=*/true);
   }
 
   // Otherwise, we cannot constant-evaluate the result.
@@ -1420,9 +1442,9 @@ static bool interp__builtin_assume_aligned(InterpState 
&S, CodePtr OpPC,
   CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue());
 
   // If there is a base object, then it must have the correct alignment.
-  if (Ptr.isBlockPointer()) {
+  if (Ptr.isBlockPointer() || Ptr.isOpaquePointer()) {
     CharUnits BaseAlignment;
-    if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
+    if (const auto *VD = Ptr.getRootVarDecl())
       BaseAlignment = ASTCtx.getDeclAlign(VD);
     else if (const auto *E = Ptr.getRootExpr())
       BaseAlignment = GetAlignOfExpr(ASTCtx, E, UETT_AlignOf);
@@ -1443,7 +1465,7 @@ static bool interp__builtin_assume_aligned(InterpState 
&S, CodePtr OpPC,
   if (ExtraOffset)
     AVOffset -= CharUnits::fromQuantity(ExtraOffset->getZExtValue());
   if (AVOffset.alignTo(Align) != AVOffset) {
-    if (Ptr.isBlockPointer())
+    if (Ptr.isBlockPointer() || Ptr.isOpaquePointer())
       S.CCEDiag(Call->getArg(0),
                 diag::note_constexpr_baa_insufficient_alignment)
           << 1 << AVOffset.getQuantity() << Align.getQuantity();
@@ -2123,10 +2145,6 @@ static bool interp__builtin_memcmp(InterpState &S, 
CodePtr OpPC,
     pushInteger(S, 0, Call->getType());
     return true;
   }
-
-  if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType())
-    return false;
-
   bool IsWide =
       (ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp);
 
@@ -2144,6 +2162,9 @@ static bool interp__builtin_memcmp(InterpState &S, 
CodePtr OpPC,
     return false;
   }
 
+  if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType())
+    return false;
+
   if (!CheckLoad(S, OpPC, PtrA, AK_Read) || !CheckLoad(S, OpPC, PtrB, AK_Read))
     return false;
 
@@ -2423,14 +2444,14 @@ static bool 
interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC,
       return false;
     if (!CheckMutable(S, OpPC, Ptr))
       return false;
-    if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read))
+    if (!CheckDummy(S, OpPC, Ptr, AK_Read))
       return false;
   }
 
   // Check if we're currently running an initializer.
   if (S.initializingBlock(Ptr.block()))
     return Error(2);
-  if (S.EvaluatingDecl && Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl)
+  if (S.EvaluatingDecl && Ptr.getRootVarDecl() == S.EvaluatingDecl)
     return Error(2);
 
   pushInteger(S, Result, Call->getType());
diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h 
b/clang/lib/AST/ByteCode/InterpHelpers.h
index 4c60670ac5a0c..f183efb5b19d1 100644
--- a/clang/lib/AST/ByteCode/InterpHelpers.h
+++ b/clang/lib/AST/ByteCode/InterpHelpers.h
@@ -41,6 +41,11 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
 
 /// Checks if a pointer is a dummy pointer.
 bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK);
+bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                AccessKinds AK);
+
+bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                        APSInt &&Index, bool AllowReplace = true);
 
 /// Checks if a pointer is in range.
 template <typename T>
diff --git a/clang/lib/AST/ByteCode/MemberPointer.h 
b/clang/lib/AST/ByteCode/MemberPointer.h
index b23acf7befc67..39c7c24e1a304 100644
--- a/clang/lib/AST/ByteCode/MemberPointer.h
+++ b/clang/lib/AST/ByteCode/MemberPointer.h
@@ -101,6 +101,8 @@ class MemberPointer final {
   std::optional<Pointer> toPointer(const Context &Ctx) const;
 
   bool isBaseCastPossible() const {
+    if (!Base.isBlockPointer())
+      return false;
     if (PtrOffset < 0)
       return true;
     return static_cast<uint64_t>(PtrOffset) <= Base.getByteOffset();
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 4be5495a7ed25..1831dc161f0a5 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -55,6 +55,7 @@ def ArgFixedPoint : ArgType { let Name = "FixedPoint"; let 
AsRef = true; }
 def ArgFunction : ArgType { let Name = "const Function *"; }
 def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; }
 def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
+def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; }
 def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
 def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; }
 def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
@@ -328,6 +329,8 @@ class OffsetOpcode : Opcode {
   let Args = [ArgUint32];
 }
 
+def AddrOf : Opcode;
+
 // [] -> [Pointer]
 def GetPtrLocal : OffsetOpcode {
   bit HasCustomEval = 1;
@@ -379,11 +382,11 @@ def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, 
ArgBool, ArgTypePtr]; }
 // [Pointer] -> [Pointer]
 def GetPtrVirtBasePop : Opcode {
   // RecordDecl of base class.
-  let Args = [ArgRecordDecl];
+  let Args = [ArgCXXRecordDecl];
 }
 def GetPtrVirtBase : Opcode {
   // RecordDecl of base class.
-  let Args = [ArgRecordDecl];
+  let Args = [ArgCXXRecordDecl];
 }
 
 def IsBaseClass : SuccessOpcode;
@@ -397,7 +400,7 @@ def GetPtrThisBase : Opcode {
 // [] -> [Pointer]
 def GetPtrThisVirtBase : Opcode {
   // RecordDecl of base class.
-  let Args = [ArgRecordDecl];
+  let Args = [ArgCXXRecordDecl];
 }
 // [] -> [Pointer]
 def This : Opcode;
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index 81fa2beaedb24..8016680ef9c0a 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -242,9 +242,34 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const 
{
     return APValue(APValue::LValueBase(Str.Base),
                    CharUnits::fromQuantity(Offset * elemSize()), Path,
                    /*OnePastTheEnd=*/false, /*IsNull=*/false);
-  case Storage::Opaque:
-    return APValue(APValue::LValueBase(Opaque.Base), CharUnits::Zero(), Path,
-                   /*IsOnePastEnd=*/Opaque.isOnePastEnd(), 
/*IsNullPtr=*/false);
+  case Storage::Opaque: {
+    if (!Opaque.Base->getType()->isPointerType()) {
+      for (const PointerPathEntry &Entry : Opaque.path()) {
+        switch (Entry.Kind) {
+        case PointerPathEntry::Field:
+          Path.push_back(APValue::LValuePathEntry({Entry.FD, false}));
+          break;
+        case PointerPathEntry::Base:
+          Path.push_back(APValue::LValuePathEntry(
+              {Entry.RD.getPointer(), Entry.RD.getInt()}));
+          break;
+        case PointerPathEntry::Array:
+          Path.push_back(APValue::LValuePathEntry::ArrayIndex(Entry.Index));
+          break;
+        case PointerPathEntry::NegativeArray:
+          Path.push_back(APValue::LValuePathEntry::ArrayIndex(-Entry.Index));
+          break;
+        }
+      }
+    }
+    size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(0);
+    auto Offset = CharUnits::fromQuantity(LayoutOffset + getByteOffset());
+    auto Result =
+        APValue(Opaque.Base, Offset, Path,
+                /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
+    Result.setConstexprUnknown(Opaque.isConstexprUnknown());
+    return Result;
+  }
   }
 
   assert(isBlockPointer());
@@ -446,7 +471,9 @@ Pointer::computeOffsetForComparison(const ASTContext 
&ASTCtx) const {
   case Storage::String:
     return reinterpret_cast<uintptr_t>(Str.getLiteral()) + Offset;
   case Storage::Opaque:
-    return reinterpret_cast<uintptr_t>(asOpaquePointer().Base) + Offset;
+    if (auto O = Opaque.computeLayoutOffset(ASTCtx))
+      return *O + Offset;
+    return std::nullopt;
   }
 
   auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
@@ -527,7 +554,9 @@ Pointer::computeLayoutOffset(const ASTContext &ASTCtx) 
const {
   case Storage::String:
     return Offset * Str.getLiteral()->getCharByteWidth();
   case Storage::Opaque:
-    return Opaque.computeLayoutOffset(ASTCtx);
+    if (auto O = Opaque.computeLayoutOffset(ASTCtx))
+      return *O + Offset;
+    return std::nullopt;
   }
 
   auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
@@ -876,17 +905,39 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer 
&B) {
   if (A.isZero() && B.isZero())
     return true;
 
-  if (A.isIntegralPointer() && B.isIntegralPointer())
+  // We allow comparisons between opaque pointers and block pointers, provided
+  // they have the same declaration as base.
+  if (A.StorageKind != B.StorageKind) {
+    if (A.isOpaquePointer() && B.isBlockPointer()) {
+      if (const VarDecl *BDecl = B.block()->getDescriptor()->asVarDecl())
+        return BDecl == A.Opaque.Base->getMostRecentDecl();
+
+      return false;
+    }
+    if (B.isOpaquePointer() && A.isBlockPointer()) {
+      if (const VarDecl *ADecl = A.block()->getDescriptor()->asVarDecl())
+        return ADecl == B.Opaque.Base->getMostRecentDecl();
+      return false;
+    }
+    return false;
+  }
+
+  switch (A.StorageKind) {
+  case Storage::Int:
     return true;
-  if (A.isFunctionPointer() && B.isFunctionPointer())
+  case Storage::Block:
+    // See below.
+    break;
+  case Storage::Fn:
     return true;
-  if (A.isTypeidPointer() && B.isTypeidPointer())
+  case Storage::Typeid:
     return A.asTypeidPointer().TypePtr == B.asTypeidPointer().TypePtr;
-  if (A.isStringPointer() && B.isStringPointer())
+  case Storage::String:
     return A.Str.ID == B.Str.ID && A.Str.getLiteral() == B.Str.getLiteral();
-
-  if (A.StorageKind != B.StorageKind)
-    return false;
+  case Storage::Opaque:
+    return A.asOpaquePointer().Base->getMostRecentDecl() ==
+           B.asOpaquePointer().Base->getMostRecentDecl();
+  }
 
   return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
 }
@@ -1292,7 +1343,12 @@ OpaquePointer::computeLayoutOffset(const ASTContext 
&ASTCtx) const {
         return std::nullopt;
 
       const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
-      Offset += Layout.getBaseClassOffset(Entry.RD.getPointer()).getQuantity();
+      if (Entry.RD.getInt())
+        Offset +=
+            Layout.getVBaseClassOffset(Entry.RD.getPointer()).getQuantity();
+      else
+        Offset +=
+            Layout.getBaseClassOffset(Entry.RD.getPointer()).getQuantity();
 
       CurType = ASTCtx.getCanonicalTagType(Entry.RD.getPointer());
     } break;
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index fd09b4908f0b5..3bf6a363d6afd 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -494,7 +494,6 @@ struct OpaquePointer {
   bool isUnknownSizeArray() const;
   bool isRoot() const;
 };
-struct OpaqueTag {};
 
 enum class Storage { Int, Block, Fn, Typeid, String, Opaque };
 
@@ -570,18 +569,33 @@ class Pointer {
 
   /// Equality operators are just for tests.
   bool operator==(const Pointer &P) const {
-    if (P.StorageKind != StorageKind)
+    if (StorageKind != P.StorageKind)
       return false;
-    if (isIntegralPointer())
+
+    switch (StorageKind) {
+    case Storage::Int:
       return P.Int.Value == Int.Value && P.Int.Ty == Int.Ty &&
              P.Offset == Offset;
-
-    if (isFunctionPointer())
+    case Storage::Block:
+      return P.view() == view();
+    case Storage::Fn:
       return P.Fn.Func == Fn.Func && P.Offset == Offset;
-    if (isStringPointer())
+    case Storage::Typeid:
+      llvm_unreachable("typeid in operator==?");
+    case Storage::String:
       return Str.Base == P.Str.Base && Offset == P.Offset;
-
-    return P.view() == view();
+    case Storage::Opaque:
+      if (!(P.Opaque.Base == Opaque.Base &&
+            P.Opaque.PathLength == Opaque.PathLength))
+        return false;
+      if (P.Offset != Offset)
+        return false;
+      if (Opaque.PathLength == 0)
+        return true;
+      return std::memcmp(P.Opaque.Path, Opaque.Path,
+                         sizeof(PointerPathEntry) * Opaque.PathLength) == 0;
+    }
+    llvm_unreachable("Unhandled storage kind");
   }
 
   bool operator!=(const Pointer &P) const { return !(P == *this); }
@@ -922,6 +936,9 @@ class Pointer {
 
       return Fn.Func->getDecl()->isWeak();
     }
+
+    if (isOpaquePointer())
+      return Opaque.Base->isWeak();
     if (!isBlockPointer())
       return false;
 
@@ -940,6 +957,8 @@ class Pointer {
 
   /// Checks if the pointer points to a dummy value.
   bool isDummy() const {
+    if (isOpaquePointer())
+      return true;
     if (!isBlockPointer())
       return false;
     return view().isDummy();
@@ -951,6 +970,8 @@ class Pointer {
       return true;
     if (isStringPointer())
       return true;
+    if (!isBlockPointer())
+      return false;
     return view().isConst();
   }
   bool isConstInMutable() const {
diff --git a/clang/test/AST/ByteCode/records.cpp 
b/clang/test/AST/ByteCode/records.cpp
index 36b5cb62fe95f..90e7c1eb6ec64 100644
--- a/clang/test/AST/ByteCode/records.cpp
+++ b/clang/test/AST/ByteCode/records.cpp
@@ -2054,3 +2054,14 @@ namespace BaseInitViaDIE {
   constexpr SS ss {};
   static_assert(ss.b == 42, "");
 }
+
+namespace OPEOpaque {
+  struct S {char c[14];};
+  extern S s;
+  static_assert((&s + 1) - &s == 1, "");
+
+  extern int a[12];
+  static_assert ((&a + 12 - &a) == 12, ""); // both-error {{not an integral 
constant expression}} \
+                                            // both-note {{cannot refer to 
element 12 of non-array object in a constant expression}}
+
+}
diff --git a/clang/test/CodeGen/pr4349.c b/clang/test/CodeGen/pr4349.c
index 3bec499e0b3f5..025a9b3903775 100644
--- a/clang/test/CodeGen/pr4349.c
+++ b/clang/test/CodeGen/pr4349.c
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o -                                         
| FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -fexperimental-new-constant-interpreter 
| FileCheck %s
 // PR 4349
 
 union reg
diff --git a/clang/test/SemaCXX/new-delete.cpp 
b/clang/test/SemaCXX/new-delete.cpp
index 595d0325be12f..bd1eb23023d6f 100644
--- a/clang/test/SemaCXX/new-delete.cpp
+++ b/clang/test/SemaCXX/new-delete.cpp
@@ -721,19 +721,9 @@ int (*const_fold)[12] = new int[3][&const_fold + 12 - 
&const_fold];
 #if __cplusplus >= 201402L
 // expected-error@-2 {{array size is not a constant expression}}
 // expected-note@-3 {{cannot refer to element 12 of non-array}}
-#elif __cplusplus == 201103L
-#if defined(NEW_INTERP)
-// expected-error@-6 {{only the first dimension of an allocated array may have 
dynamic size}}
-// expected-note@-7 {{cannot refer to element 12 of non-array}}
-#endif
 #elif __cplusplus < 201103L
-#if defined(NEW_INTERP)
-// expected-error@-11 {{only the first dimension of an allocated array may 
have dynamic size}}
-// expected-note@-12 {{cannot refer to element 12 of non-array}}
-#else
-// expected-error@-14 {{cannot allocate object of variably modified type}}
-// expected-warning@-15 {{variable length arrays in C++ are a Clang extension}}
-#endif
+// expected-error@-5 {{cannot allocate object of variably modified type}}
+// expected-warning@-6 {{variable length arrays in C++ are a Clang extension}}
 #endif
 
 #if __cplusplus >= 201103L
diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp 
b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index 9c25e26f43c36..cede91cd41997 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s 
-fexperimental-new-constant-interpreter
 
 template<typename T, T val> struct A {}; // expected-note 3{{template 
parameter is declared here}}
 
diff --git a/clang/unittests/AST/ByteCode/toAPValue.cpp 
b/clang/unittests/AST/ByteCode/toAPValue.cpp
index 702a07a638915..a8e1d5e217597 100644
--- a/clang/unittests/AST/ByteCode/toAPValue.cpp
+++ b/clang/unittests/AST/ByteCode/toAPValue.cpp
@@ -102,8 +102,6 @@ TEST(ToAPValue, Pointers) {
     ASSERT_EQ(A.getLValuePath()[0].getAsArrayIndex(), 2u);
     ASSERT_EQ(A.getLValuePath()[1].getAsArrayIndex(), 4u);
     ASSERT_EQ(A.getLValueOffset().getQuantity(), 56u);
-    ASSERT_TRUE(
-        GP.atIndex(0).getFieldDesc()->getElemQualType()->isIntegerType());
   }
 }
 

>From e514ee5bf9129a712861d1c5538f7105758f4fee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Fri, 4 Sep 2026 08:07:34 +0200
Subject: [PATCH 2/2] opaque Expr

---
 clang/lib/AST/ByteCode/Compiler.cpp           | 19 +----
 clang/lib/AST/ByteCode/DeclOrExpr.h           |  7 ++
 clang/lib/AST/ByteCode/Disasm.cpp             |  6 +-
 clang/lib/AST/ByteCode/EvaluationResult.cpp   |  4 +-
 clang/lib/AST/ByteCode/Interp.cpp             | 78 +++----------------
 clang/lib/AST/ByteCode/Interp.h               | 33 ++++----
 clang/lib/AST/ByteCode/InterpBlock.cpp        | 11 +--
 clang/lib/AST/ByteCode/InterpBlock.h          | 14 +---
 .../AST/ByteCode/InterpBuiltinObjectSize.cpp  |  4 +-
 clang/lib/AST/ByteCode/InterpHelpers.h        |  1 -
 clang/lib/AST/ByteCode/Opcodes.td             |  3 +-
 clang/lib/AST/ByteCode/Pointer.cpp            | 53 ++++++++-----
 clang/lib/AST/ByteCode/Pointer.h              | 34 ++++----
 clang/lib/AST/ByteCode/Program.cpp            | 63 ---------------
 clang/lib/AST/ByteCode/Program.h              |  6 --
 15 files changed, 94 insertions(+), 242 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 85abeb7e1bd04..d2feac9c03b30 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -8735,24 +8735,7 @@ bool Compiler<Emitter>::emitDestructionPop(const 
Descriptor *Desc,
 template <class Emitter>
 bool Compiler<Emitter>::emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU) {
   assert(!DiscardResult && "Should've been checked before");
-
-  if (const auto *VD = D.asValueDecl())
-    return this->emitGetOpaquePtr(VD, CU, E);
-
-  assert(D.asExpr());
-  unsigned DummyID = P.getOrCreateDummy(D, CU);
-  if (!this->emitGetPtrGlobal(DummyID, E))
-    return false;
-  if (E->getType()->isVoidType())
-    return true;
-
-  // Convert the dummy pointer to another pointer type if we have to.
-  if (PrimType PT = classifyPrim(E); PT != PT_Ptr) {
-    if (isPtrType(PT))
-      return this->emitDecayPtr(PT_Ptr, PT, E);
-    return false;
-  }
-  return true;
+  return this->emitGetOpaquePtr(D, CU, E);
 }
 
 template <class Emitter>
diff --git a/clang/lib/AST/ByteCode/DeclOrExpr.h 
b/clang/lib/AST/ByteCode/DeclOrExpr.h
index e170b52c6e51d..b844c5644a233 100644
--- a/clang/lib/AST/ByteCode/DeclOrExpr.h
+++ b/clang/lib/AST/ByteCode/DeclOrExpr.h
@@ -28,6 +28,7 @@ struct DeclOrExpr {
   bool isExpr() const { return isa_and_nonnull<const Expr *>(V); }
   bool isDecl() const { return isa_and_nonnull<const Decl *>(V); }
   bool isValueDecl() const { return isa_and_nonnull<ValueDecl>(asDecl()); }
+  bool isVarDecl() const { return isa_and_nonnull<VarDecl>(asDecl()); }
 
   const Expr *asExpr() const { return V.dyn_cast<const Expr *>(); }
   const Decl *asDecl() const { return V.dyn_cast<const Decl *>(); }
@@ -49,6 +50,12 @@ struct DeclOrExpr {
       return VD->getType();
     return asExpr()->getType();
   }
+
+  SourceLocation getLocation() const {
+    if (const auto *VD = asValueDecl())
+      return VD->getLocation();
+    return asExpr()->getExprLoc();
+  }
 };
 static_assert(sizeof(DeclOrExpr) == sizeof(void *));
 
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp 
b/clang/lib/AST/ByteCode/Disasm.cpp
index 03187529541d8..c319591b1fee9 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -345,7 +345,6 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) 
const {
     // All the maps.
     Bytes += GlobalIndices.getMemorySize();
     Bytes += Records.getMemorySize();
-    Bytes += DummyVariables.getMemorySize();
 
     // All Records.
     for (const Record *R : Records.values()) {
@@ -372,8 +371,6 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) 
const {
                         : TerminalColor{llvm::raw_ostream::RED, false});
       OS << (GP.isInitialized() ? "initialized " : "uninitialized ");
     }
-    if (GP.block()->isDummy())
-      OS << "dummy ";
     Desc->dump(OS);
 
     if (GP.isInitialized() && Desc->IsTemporary) {
@@ -402,7 +399,7 @@ LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) 
const {
     }
 
     OS << "\n";
-    if (GP.isInitialized() && Desc->isPrimitive() && !G->block()->isDummy()) {
+    if (GP.isInitialized() && Desc->isPrimitive()) {
       OS << "   ";
       {
         ColorScope SC(OS, true, {llvm::raw_ostream::BRIGHT_CYAN, false});
@@ -634,7 +631,6 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) 
const {
   OS << "  Extern: " << isExtern() << "\n";
   OS << "  Initialized: " << IsInitialized << "\n";
   OS << "  Weak: " << isWeak() << "\n";
-  OS << "  Dummy: " << isDummy() << '\n';
   OS << "  Dynamic: " << isDynamic() << "\n";
   OS << "  Metadata: " << MDSize << '\n';
 }
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp 
b/clang/lib/AST/ByteCode/EvaluationResult.cpp
index bc939c9a5c8fc..5d232c5414e04 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.cpp
+++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp
@@ -179,8 +179,8 @@ static void collectBlocks(PtrView Ptr,
            P.isDereferencable() && !P.isUnknownSizeArray() && 
!P.isOnePastEnd();
   };
 
-  if (!Ptr.isLive() || Ptr.isZero() || Ptr.isDummy() ||
-      Ptr.isUnknownSizeArray() || Ptr.isOnePastEnd())
+  if (!Ptr.isLive() || Ptr.isZero() || Ptr.isUnknownSizeArray() ||
+      Ptr.isOnePastEnd())
     return;
 
   Blocks.insert(Ptr.Pointee);
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 6831c6311c93c..9185f1d619a00 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -111,7 +111,7 @@ static void noteValueLocation(InterpState &S, const Pointer 
&Ptr) {
   }
 
   if (Ptr.isOpaquePointer())
-    S.Note(Ptr.asOpaquePointer().Base->getLocation(), diag::note_declared_at);
+    S.Note(Ptr.asOpaquePointer().Base.getLocation(), diag::note_declared_at);
 }
 
 static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
@@ -209,13 +209,8 @@ static void diagnoseNonConstVariable(InterpState &S, 
CodePtr OpPC,
   S.Note(VD->getLocation(), diag::note_declared_at);
 }
 
-static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
                            AccessKinds AK) {
-
-  if (!Ptr.isBlockPointer())
-    return true;
-
-  const Block *B = Ptr.block();
   if (B->getDeclID()) {
     if (!(B->isStatic() && B->isTemporary()))
       return true;
@@ -241,31 +236,11 @@ static bool CheckTemporary(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
   return true;
 }
 
-static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
+static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                            AccessKinds AK) {
-  if (B->getDeclID()) {
-    if (!(B->isStatic() && B->isTemporary()))
-      return true;
-
-    const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(
-        B->getDescriptor()->asExpr());
-    if (!MTE)
-      return true;
-
-    // FIXME(perf): Since we do this check on every Load from a static
-    // temporary, it might make sense to cache the value of the
-    // isUsableInConstantExpressions call.
-    if (S.checkingConstantDestruction() ||
-        (B->getEvalID() != S.EvalID &&
-         !MTE->isUsableInConstantExpressions(S.getASTContext()))) {
-      const SourceInfo &E = S.Current->getSource(OpPC);
-      S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
-      noteValueLocation(S, B);
-      return false;
-    }
-  }
-
-  return true;
+  if (!Ptr.isBlockPointer())
+    return true;
+  return CheckTemporary(S, OpPC, Ptr.block(), AK);
 }
 
 static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
@@ -340,8 +315,6 @@ void cleanupAfterFunctionCall(InterpState &S, const 
Function *Func) {
 }
 
 bool isConstexprUnknown(const Block *B) {
-  if (B->isDummy())
-    return isa_and_nonnull<ParmVarDecl>(B->getDescriptor()->asValueDecl());
   return B->getDescriptor()->IsConstexprUnknown;
 }
 
@@ -868,8 +841,6 @@ bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const 
Block *B) {
   if (!B->isAccessible()) {
     if (!CheckExtern(S, OpPC, Pointer(const_cast<Block *>(B))))
       return false;
-    if (!CheckDummy(S, OpPC, B, AK_Read))
-      return false;
     return CheckWeak(S, OpPC, B);
   }
 
@@ -945,8 +916,6 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
       return false;
     if (!CheckExtern(S, OpPC, Ptr))
       return false;
-    if (!CheckDummy(S, OpPC, Ptr.block(), AK))
-      return false;
     return CheckWeak(S, OpPC, Ptr.block());
   }
 
@@ -1012,8 +981,6 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr) {
       return false;
     if (!CheckExtern(S, OpPC, Ptr))
       return false;
-    if (!CheckDummy(S, OpPC, Ptr.block(), AK_Read))
-      return false;
     return CheckWeak(S, OpPC, Ptr.block());
   }
 
@@ -1049,9 +1016,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   if (!Ptr.block()->isAccessible()) {
     if (!CheckLive(S, OpPC, Ptr, AK_Assign))
       return false;
-    if (!CheckExtern(S, OpPC, Ptr))
-      return false;
-    return CheckDummy(S, OpPC, Ptr.block(), AK_Assign);
+    return CheckExtern(S, OpPC, Ptr);
   }
   if (!WillBeActivated && !CheckLifetime(S, OpPC, Ptr, AK_Assign))
     return false;
@@ -1373,25 +1338,6 @@ bool CheckDummy(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   return false;
 }
 
-// FIXME: Remove this once all dummy pointers are opaque pointers.
-bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) {
-  if (!B->isDummy())
-    return true;
-
-  const ValueDecl *D = B->getDescriptor()->asValueDecl();
-  if (!D)
-    return false;
-
-  if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement)
-    return diagnoseUnknownDecl(S, OpPC, D, AK);
-
-  if (AK == AK_Destroy || S.getLangOpts().CPlusPlus14) {
-    const SourceInfo &E = S.Current->getSource(OpPC);
-    S.FFDiag(E, diag::note_constexpr_modify_global);
-  }
-  return false;
-}
-
 static bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
                              const CallExpr *CE, unsigned ArgSize) {
   auto Args = ArrayRef(CE->getArgs(), CE->getNumArgs());
@@ -2640,7 +2586,7 @@ static void setLifeStateRecurse(PtrView Ptr, Lifetime L) {
 /// Ends the lifetime of the peek'd pointer.
 bool EndLifetime(InterpState &S, CodePtr OpPC) {
   const auto &Ptr = S.Stk.peek<Pointer>();
-  if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
+  if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
     return false;
 
   setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Ended);
@@ -2658,7 +2604,7 @@ bool PseudoDtor(InterpState &S, CodePtr OpPC) {
 
 bool MarkDestroyed(InterpState &S, CodePtr OpPC) {
   const auto &Ptr = S.Stk.peek<Pointer>();
-  if (Ptr.isBlockPointer() && !CheckDummy(S, OpPC, Ptr.block(), AK_Destroy))
+  if (!CheckDummy(S, OpPC, Ptr, AK_Destroy))
     return false;
 
   setLifeStateRecurse(Ptr.view().narrow(), Lifetime::Destroyed);
@@ -2855,12 +2801,6 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr 
OpPC,
     return Ptr.isRoot();
   }
 
-  if (Ptr.isDummy()) {
-    if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
-      return false;
-    return Ptr.getIndex() == 0;
-  }
-
   if (!Ptr.isZero()) {
     // Only allow based lvalue casts if they are lossless.
     if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 343883872728b..a208f8d1eed0a 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2274,7 +2274,7 @@ inline bool LoadPopL(InterpState &S, CodePtr OpPC) {
     if (Ptr.isOpaquePointer()) {
       const OpaquePointer &OP = Ptr.asOpaquePointer();
 
-      if (!Ptr.asOpaquePointer().Base->getType()->isPointerType())
+      if (!Ptr.asOpaquePointer().Base.getType()->isPointerType())
         return false;
 
       QualType T = Ptr.getType();
@@ -2747,9 +2747,9 @@ bool SubOffset(InterpState &S, CodePtr OpPC) {
   return false;
 }
 
-inline bool GetOpaquePtr(InterpState &S, const ValueDecl *VD,
+inline bool GetOpaquePtr(InterpState &S, DeclOrExpr DOE,
                          bool ConstexprUnknown) {
-  S.Stk.push<Pointer>(VD, ConstexprUnknown);
+  S.Stk.push<Pointer>(DOE, ConstexprUnknown);
   return true;
 }
 
@@ -3072,8 +3072,8 @@ inline bool AddrOf(InterpState &S, CodePtr OpPC) {
   if (Ptr.isOpaquePointer()) {
     const OpaquePointer &OP = Ptr.asOpaquePointer();
     QualType T = QualType(OP.FieldType.getPointer(), 0);
-    T = S.getASTContext().getPointerType(T);
 
+    T = S.getASTContext().getPointerType(T);
     S.Stk.push<Pointer>(OP.withFieldType(T.getTypePtr(), OP.isOnePastEnd()));
   } else {
     S.Stk.push<Pointer>(Ptr);
@@ -3098,23 +3098,18 @@ bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
     S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
   } else if constexpr (isIntegralOrPointer<T>()) {
     if (Ptr.isBlockPointer()) {
-      IntegralKind Kind = IntegralKind::Address;
-      const void *PtrVal;
-      if (Ptr.isDummy()) {
-        if (const Expr *E = Ptr.getRootExpr()) {
-          PtrVal = E;
-          if (isa<AddrLabelExpr>(E))
-            Kind = IntegralKind::LabelAddress;
-        } else {
-          PtrVal = Ptr.getDeclDesc()->asDecl();
-        }
+      S.Stk.push<T>(IntegralKind::BlockAddress, Ptr.block(), /*Offset=*/0);
+    } else if (Ptr.isOpaquePointer()) {
+      if (const Expr *BaseExpr = Ptr.asOpaquePointer().getBaseExpr()) {
+        IntegralKind Kind = IntegralKind::ExprAddress;
+        if (isa<AddrLabelExpr>(BaseExpr))
+          Kind = IntegralKind::LabelAddress;
+        S.Stk.push<T>(Kind, BaseExpr, 0);
       } else {
-        PtrVal = Ptr.block();
-        Kind = IntegralKind::BlockAddress;
+        S.Stk.push<T>(IntegralKind::Address,
+                      Ptr.asOpaquePointer().Base.asVarDecl(), 0);
       }
-      S.Stk.push<T>(Kind, PtrVal, /*Offset=*/0);
-    } else if (Ptr.isOpaquePointer()) {
-      S.Stk.push<T>(IntegralKind::Address, Ptr.asOpaquePointer().Base, 0);
+
     } else if (Ptr.isFunctionPointer()) {
       const void *FuncDecl = Ptr.asFunctionPointer().Func->getDecl();
       S.Stk.push<T>(IntegralKind::FunctionAddress, FuncDecl, /*Offset=*/0);
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp 
b/clang/lib/AST/ByteCode/InterpBlock.cpp
index f43b477dff7f9..888a660719590 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -102,20 +102,11 @@ bool Block::hasPointer(const Pointer *P) const {
 
 void Block::movePointersTo(Block *B) {
   assert(B != this);
-  unsigned MDDiff = static_cast<int>(B->MDSize) - static_cast<int>(MDSize);
 
   while (Pointers) {
     Pointer *P = Pointers;
-
     this->removePointer(P);
     P->BS.Pointee = B;
-
-    // If the metadata size changed between the two blocks, move the pointer
-    // base/offset. Realistically, this should only happen when we move 
pointers
-    // from a dummy pointer to a global one.
-    P->BS.Base += MDDiff;
-    P->Offset += MDDiff;
-
     B->addPointer(P);
   }
   assert(!this->hasPointers());
@@ -135,7 +126,7 @@ void Block::removePointers() {
 
 DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
     : Root(Root), B(~0u, Blk->Desc, Blk->MDSize, Blk->isExtern(), 
Blk->IsStatic,
-                    Blk->isWeak(), Blk->isDummy(),
+                    Blk->isWeak(),
                     /*IsDead=*/true) {
   // Add the block to the chain of dead blocks.
   if (Root)
diff --git a/clang/lib/AST/ByteCode/InterpBlock.h 
b/clang/lib/AST/ByteCode/InterpBlock.h
index ffb4bf2e75654..4d6a2ecf81321 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.h
+++ b/clang/lib/AST/ByteCode/InterpBlock.h
@@ -45,7 +45,6 @@ class Block final {
   static constexpr uint8_t ExternFlag = 1 << 0;
   static constexpr uint8_t DeadFlag = 1 << 1;
   static constexpr uint8_t WeakFlag = 1 << 2;
-  static constexpr uint8_t DummyFlag = 1 << 3;
 
 public:
   static constexpr uint8_t InlineDescMD = sizeof(InlineDescriptor);
@@ -54,23 +53,20 @@ class Block final {
   /// Creates a new block.
   Block(unsigned EvalID, UnsignedOrNone DeclID, const Descriptor *Desc,
         unsigned MDSize = 0, bool IsStatic = false, bool IsExtern = false,
-        bool IsWeak = false, bool IsDummy = false)
+        bool IsWeak = false)
       : Desc(Desc), DeclID(DeclID), EvalID(EvalID), MDSize(MDSize),
         IsStatic(IsStatic) {
     assert(Desc);
     AccessFlags |= (ExternFlag * IsExtern);
     AccessFlags |= (WeakFlag * IsWeak);
-    AccessFlags |= (DummyFlag * IsDummy);
   }
 
   Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize = 0,
-        bool IsStatic = false, bool IsExtern = false, bool IsWeak = false,
-        bool IsDummy = false)
+        bool IsStatic = false, bool IsExtern = false, bool IsWeak = false)
       : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) {
     assert(Desc);
     AccessFlags |= (ExternFlag * IsExtern);
     AccessFlags |= (WeakFlag * IsWeak);
-    AccessFlags |= (DummyFlag * IsDummy);
   }
 
   /// Returns the block's descriptor.
@@ -85,7 +81,6 @@ class Block final {
   bool isTemporary() const { return Desc->IsTemporary; }
   bool isWeak() const { return AccessFlags & WeakFlag; }
   bool isDynamic() const { return (DynAllocId != std::nullopt); }
-  bool isDummy() const { return AccessFlags & DummyFlag; }
   bool isDead() const { return AccessFlags & DeadFlag; }
   /// Returns the size of the block, including metadata.
   unsigned getSize() const { return Desc->getAllocSize() + MDSize; }
@@ -168,13 +163,12 @@ class Block final {
   friend class Program;
 
   Block(unsigned EvalID, const Descriptor *Desc, unsigned MDSize, bool 
IsExtern,
-        bool IsStatic, bool IsWeak, bool IsDummy, bool IsDead)
+        bool IsStatic, bool IsWeak, bool IsDead)
       : Desc(Desc), EvalID(EvalID), MDSize(MDSize), IsStatic(IsStatic) {
     assert(Desc);
     AccessFlags |= (ExternFlag * IsExtern);
     AccessFlags |= (DeadFlag * IsDead);
     AccessFlags |= (WeakFlag * IsWeak);
-    AccessFlags |= (DummyFlag * IsDummy);
   }
 
   /// To be called by DynamicAllocator.
@@ -200,7 +194,7 @@ class Block final {
   const unsigned EvalID = ~0u;
   /// Allocation ID for this dynamic allocation, if it is one.
   UnsignedOrNone DynAllocId = std::nullopt;
-  /// AccessFlags containing IsExtern, IsDead, IsWeak, and IsDummy bits.
+  /// AccessFlags containing IsExtern, IsDead and IsWeak bits.
   uint8_t AccessFlags = 0;
   /// Size of the metadata.
   const uint8_t MDSize = 0;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
index c1a69e03c0e73..1483dbbedeb29 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
@@ -352,7 +352,7 @@ computeOpaqueSize(const ASTContext &ASTCtx, const Pointer 
&Ptr,
     return TypeSize.getQuantity();
 
   // Check if we need to add the flexible array member size.
-  const VarDecl *Base = dyn_cast<VarDecl>(OP.Base);
+  const VarDecl *Base = OP.getBaseDecl();
   if (!Base)
     return TypeSize.getQuantity();
 
@@ -390,7 +390,7 @@ UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext 
&ASTCtx,
   if (Ptr.isOpaquePointer()) {
     bool UseClosestSurroundingVariable = (Kind == 1) || (Kind == 3);
     const OpaquePointer &OP = Ptr.asOpaquePointer();
-    InvalidBase = OP.Base->getType()->isPointerType();
+    InvalidBase = OP.Base.getType()->isPointerType();
     bool DetermineForCompleteObject = pointsToCompleteObject(ASTCtx, Ptr);
     bool WritingOffTheEnd = isUserWritingOffTheEnd(ASTCtx, OP);
 
diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h 
b/clang/lib/AST/ByteCode/InterpHelpers.h
index f183efb5b19d1..703dec4d43d72 100644
--- a/clang/lib/AST/ByteCode/InterpHelpers.h
+++ b/clang/lib/AST/ByteCode/InterpHelpers.h
@@ -40,7 +40,6 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer 
&Ptr,
                AccessKinds AK);
 
 /// Checks if a pointer is a dummy pointer.
-bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK);
 bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                 AccessKinds AK);
 
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 1831dc161f0a5..a3aaeff67e301 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -72,6 +72,7 @@ def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgPrimType : ArgType { let Name = "PrimType"; }
 def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
 def ArgTypePtr : ArgType { let Name = "const Type *"; }
+def ArgDeclOrExpr : ArgType { let Name = "DeclOrExpr"; }
 
 
//===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -618,7 +619,7 @@ def AddOffset : Opcode {
 }
 
 def GetOpaquePtr : SuccessOpcode {
-  let Args = [ArgValueDecl, ArgBool];
+  let Args = [ArgDeclOrExpr, ArgBool];
 }
 
 // [Pointer, Integral] -> [Pointer]
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index 8016680ef9c0a..e67dc3c9328a1 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -243,7 +243,7 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const {
                    CharUnits::fromQuantity(Offset * elemSize()), Path,
                    /*OnePastTheEnd=*/false, /*IsNull=*/false);
   case Storage::Opaque: {
-    if (!Opaque.Base->getType()->isPointerType()) {
+    if (!Opaque.Base.getType()->isPointerType()) {
       for (const PointerPathEntry &Entry : Opaque.path()) {
         switch (Entry.Kind) {
         case PointerPathEntry::Field:
@@ -264,9 +264,15 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const 
{
     }
     size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(0);
     auto Offset = CharUnits::fromQuantity(LayoutOffset + getByteOffset());
-    auto Result =
-        APValue(Opaque.Base, Offset, Path,
-                /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
+    APValue Result;
+    if (const Expr *E = Opaque.Base.asExpr())
+      Result =
+          APValue(E, Offset, Path,
+                  /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
+    else
+      Result =
+          APValue(Opaque.Base.asValueDecl(), Offset, Path,
+                  /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
     Result.setConstexprUnknown(Opaque.isConstexprUnknown());
     return Result;
   }
@@ -908,15 +914,17 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer 
&B) {
   // We allow comparisons between opaque pointers and block pointers, provided
   // they have the same declaration as base.
   if (A.StorageKind != B.StorageKind) {
-    if (A.isOpaquePointer() && B.isBlockPointer()) {
+    if (A.isOpaquePointer() && A.asOpaquePointer().Base.isVarDecl() &&
+        B.isBlockPointer()) {
       if (const VarDecl *BDecl = B.block()->getDescriptor()->asVarDecl())
-        return BDecl == A.Opaque.Base->getMostRecentDecl();
+        return BDecl == A.Opaque.Base.asVarDecl()->getMostRecentDecl();
 
       return false;
     }
-    if (B.isOpaquePointer() && A.isBlockPointer()) {
+    if (B.isOpaquePointer() && B.asOpaquePointer().Base.isVarDecl() &&
+        A.isBlockPointer()) {
       if (const VarDecl *ADecl = A.block()->getDescriptor()->asVarDecl())
-        return ADecl == B.Opaque.Base->getMostRecentDecl();
+        return ADecl == B.Opaque.Base.asVarDecl()->getMostRecentDecl();
       return false;
     }
     return false;
@@ -935,8 +943,12 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer 
&B) {
   case Storage::String:
     return A.Str.ID == B.Str.ID && A.Str.getLiteral() == B.Str.getLiteral();
   case Storage::Opaque:
-    return A.asOpaquePointer().Base->getMostRecentDecl() ==
-           B.asOpaquePointer().Base->getMostRecentDecl();
+    if (A.Opaque.Base.isExpr() && B.Opaque.Base.isExpr())
+      return A.Opaque.Base == B.Opaque.Base;
+    else if (A.Opaque.Base.isVarDecl() && B.Opaque.Base.isVarDecl())
+      return A.Opaque.Base.asVarDecl()->getMostRecentDecl() ==
+             B.Opaque.Base.asVarDecl()->getMostRecentDecl();
+    return false;
   }
 
   return A.asBlockPointer().Pointee == B.asBlockPointer().Pointee;
@@ -989,23 +1001,24 @@ bool Pointer::elemsOfSameArray(const Pointer &A, const 
Pointer &B) {
   return true;
 }
 
+// FIXME: This should return true for string pointers.
 bool Pointer::pointsToLiteral() const {
-  if (isZero() || !isBlockPointer())
+  if (isZero())
     return false;
 
-  if (block()->isDynamic())
+  if (isDynamic())
     return false;
 
-  const Expr *E = block()->getDescriptor()->asExpr();
+  const Expr *E = getRootExpr();
   return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);
 }
 
 bool Pointer::pointsToLabel() const {
-  if (isZero() || !isBlockPointer())
+  if (isZero())
     return false;
 
-  if (const Expr *E = BS.Pointee->getDescriptor()->asExpr())
-    return isa<AddrLabelExpr>(E);
+  if (isOpaquePointer())
+    return isa_and_nonnull<AddrLabelExpr>(Opaque.Base.asExpr());
   return false;
 }
 
@@ -1063,7 +1076,7 @@ static bool toRValue(const Context &Ctx, QualType Ty, 
PtrView Ptr, APValue &R) {
     Ty = AT->getValueType();
 
   // Invalid pointers.
-  if (Ptr.isDummy() || !Ptr.isLive() || Ptr.isPastEnd())
+  if (!Ptr.isLive() || Ptr.isPastEnd())
     return false;
 
   // Primitives should never end up here.
@@ -1261,7 +1274,7 @@ const VarDecl *Pointer::getRootVarDecl() const {
   if (isBlockPointer())
     return getDeclDesc()->asVarDecl();
   if (isOpaquePointer())
-    return dyn_cast<VarDecl>(Opaque.Base);
+    return Opaque.getBaseDecl();
   return nullptr;
 }
 
@@ -1270,6 +1283,8 @@ const Expr *Pointer::getRootExpr() const {
     return getDeclDesc()->asExpr();
   if (isStringPointer())
     return Str.getLiteral();
+  if (isOpaquePointer())
+    return Opaque.getBaseExpr();
   return nullptr;
 }
 
@@ -1465,7 +1480,7 @@ bool OpaquePointer::isUnknownSizeArray() const {
   // base to see if this array is a flexible array member _and_ has actually
   // been initialized by data we know the size of.
   if (isa<IncompleteArrayType>(FieldType)) {
-    const VarDecl *Base = cast<VarDecl>(this->Base);
+    const VarDecl *Base = this->Base.asVarDecl();
     if (!Base || !Base->getType()->isRecordType() || !Base->hasInit())
       Result = true;
     else
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 3bf6a363d6afd..44548268bda5e 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -42,7 +42,6 @@ struct PtrView {
 
   bool isZero() const { return !Pointee; }
   bool isLive() const { return Pointee && !Pointee->isDead(); }
-  bool isDummy() const { return Pointee && Pointee->isDummy(); }
   bool isActive() const { return isRoot() || getInlineDesc()->IsActive; }
   bool isArrayRoot() const { return inArray() && Offset == Base; }
   bool isElementPastEnd() const { return Offset == PastEndMark; }
@@ -430,13 +429,15 @@ struct PointerPathEntry {
 };
 
 struct OpaquePointer {
-  const ValueDecl *Base = nullptr;
+  DeclOrExpr Base;
   // FieldType and IsOnePastEnd/IsConstexprUnknown bits.
   llvm::PointerIntPair<const Type *, 2, unsigned> FieldType = {};
   const PointerPathEntry *Path = nullptr;
   unsigned PathLength = 0;
 
   ArrayRef<PointerPathEntry> path() const { return ArrayRef(Path, PathLength); 
}
+  const VarDecl *getBaseDecl() const { return Base.asVarDecl(); }
+  const Expr *getBaseExpr() const { return Base.asExpr(); }
 
   OpaquePointer
   withFieldType(const Type *FieldTy,
@@ -467,14 +468,14 @@ struct OpaquePointer {
   }
 
   QualType getObjectType() const {
-    QualType T = Base->getType();
+    QualType T = Base.getType();
     if (T->isPointerOrReferenceType())
       return T->getPointeeType();
     return T;
   }
 
   QualType getFieldType() const {
-    if (FieldType.getPointer()->isPointerOrReferenceType())
+    if (FieldType.getPointer()->isPointerOrReferenceType() && Base.isDecl())
       return FieldType.getPointer()->getPointeeType();
     return QualType(FieldType.getPointer(), 0);
   }
@@ -549,11 +550,11 @@ class Pointer {
       : Offset(0), StorageKind(Storage::String), Str{Base, Id} {}
   Pointer(StringPointer Str, uint64_t Offset = 0)
       : Offset(Offset), StorageKind(Storage::String), Str(Str) {}
-  Pointer(const ValueDecl *Base, bool ConstexprUnknown = false)
+
+  Pointer(DeclOrExpr DOE, bool ConstexprUnknown = false)
       : Offset(0), StorageKind(Storage::Opaque) {
-    Opaque.Base = Base;
-    Opaque.FieldType = {Base->getType().getTypePtr(),
-                        ConstexprUnknown ? 2u : 0u};
+    Opaque.Base = DOE;
+    Opaque.FieldType = {DOE.getType().getTypePtr(), ConstexprUnknown ? 2u : 
0u};
     Opaque.Path = nullptr;
     Opaque.PathLength = 0;
   }
@@ -937,8 +938,11 @@ class Pointer {
       return Fn.Func->getDecl()->isWeak();
     }
 
-    if (isOpaquePointer())
-      return Opaque.Base->isWeak();
+    if (isOpaquePointer()) {
+      if (const VarDecl *BaseDecl = Opaque.getBaseDecl())
+        return BaseDecl->isWeak();
+      return false;
+    }
     if (!isBlockPointer())
       return false;
 
@@ -959,9 +963,7 @@ class Pointer {
   bool isDummy() const {
     if (isOpaquePointer())
       return true;
-    if (!isBlockPointer())
-      return false;
-    return view().isDummy();
+    return false;
   }
 
   /// Checks if an object or a subfield is mutable.
@@ -1308,9 +1310,7 @@ class Pointer {
   bool pointsToLabel() const;
   /// Returns the AddrLabelExpr the Pointer points to, if any.
   const AddrLabelExpr *getPointedToLabel() const {
-    if (const Descriptor *Desc = getDeclDesc())
-      return dyn_cast_if_present<AddrLabelExpr>(Desc->asExpr());
-    return nullptr;
+    return dyn_cast_if_present<AddrLabelExpr>(getRootExpr());
   }
 
   /// Prints the pointer.
@@ -1397,7 +1397,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream 
&OS, const Pointer &P) {
   } else if (P.isBlockPointer() && P.isArrayRoot())
     OS << " arrayroot";
 
-  if (P.isBlockPointer() && P.block() && P.block()->isDummy())
+  if (P.isDummy())
     OS << " dummy";
   if (!P.isLive())
     OS << " dead";
diff --git a/clang/lib/AST/ByteCode/Program.cpp 
b/clang/lib/AST/ByteCode/Program.cpp
index 137ca8c03fad5..b9a8476d41659 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -60,60 +60,6 @@ UnsignedOrNone Program::getOrCreateGlobal(const ValueDecl 
*VD,
   return std::nullopt;
 }
 
-unsigned Program::getOrCreateDummy(DeclOrExpr D, bool IsConstexprUnknown) {
-  assert(D);
-
-  if (const auto *VD = D.asVarDecl())
-    D = VD->getFirstDecl();
-
-  // Dedup blocks since they are immutable and pointers cannot be compared.
-  if (auto It = DummyVariables.find(D.getOpaqueValue());
-      It != DummyVariables.end())
-    return It->second;
-
-  QualType QT;
-  bool IsWeak = false;
-  if (const auto *E = D.asExpr()) {
-    QT = E->getType();
-  } else {
-    const auto *VD = D.asValueDecl();
-    IsWeak = VD->isWeak();
-    QT = VD->getType();
-
-    if (QT->isReferenceType())
-      QT = QT->getPointeeType();
-  }
-
-  assert(!QT.isNull());
-
-  Descriptor *Desc;
-  if (OptPrimType T = Ctx.classify(QT))
-    Desc = createDescriptor(D, *T, /*SourceTy=*/nullptr,
-                            /*IsConst=*/QT.isConstQualified());
-  else
-    Desc = createDescriptor(D, QT.getTypePtr(),
-                            /*IsConst=*/QT.isConstQualified());
-  if (!Desc)
-    Desc = allocateDescriptor(D);
-
-  Desc->IsConstexprUnknown = IsConstexprUnknown;
-
-  assert(Desc);
-
-  // Allocate a block for storage.
-  unsigned I = Globals.size();
-
-  auto *G = new (Allocator, Desc->getAllocSize())
-      Global(Ctx.getEvalID(), getCurrentDecl(), Desc, /*MDSize=*/0u,
-             /*IsStatic=*/true, /*IsExtern=*/false, IsWeak, /*IsDummy=*/true);
-  G->block()->invokeCtor();
-  assert(G->block()->isDummy());
-
-  Globals.push_back(G);
-  DummyVariables[D.getOpaqueValue()] = I;
-  return I;
-}
-
 UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init,
                                      bool IsConstexprUnknown) {
   bool IsStatic, IsExtern;
@@ -142,15 +88,6 @@ UnsignedOrNone Program::createGlobal(const ValueDecl *VD, 
const Expr *Init,
 
   for (const Decl *Redecl = VD->getPreviousDecl(); Redecl;
        Redecl = Redecl->getPreviousDecl()) {
-    // If this redecl was registered as a dummy variable, it is now a proper
-    // global variable and points to the block we just created.
-    if (auto DummyIt = DummyVariables.find(Redecl);
-        DummyIt != DummyVariables.end()) {
-      Global *Dummy = Globals[DummyIt->second];
-      Dummy->block()->movePointersTo(NewGlobal->block());
-      Globals[DummyIt->second] = NewGlobal;
-      DummyVariables.erase(DummyIt);
-    }
     // If the redeclaration hasn't been registered yet at all, we just set its
     // global index to Idx. If it has been registered yet, it might have
     // pointers pointing to it and we need to transfer those pointers to the 
new
diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h
index 6c9a21728775d..ccd6720cce738 100644
--- a/clang/lib/AST/ByteCode/Program.h
+++ b/clang/lib/AST/ByteCode/Program.h
@@ -81,9 +81,6 @@ class Program final {
   UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD,
                                    const Expr *Init = nullptr);
 
-  /// Returns or creates a dummy value for unknown declarations.
-  unsigned getOrCreateDummy(DeclOrExpr D, bool IsConstexprUnknown = false);
-
   /// Creates a global and returns its index.
   UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init,
                               bool IsConstexprUnknown = false);
@@ -211,9 +208,6 @@ class Program final {
   /// Mapping from decls to record metadata.
   llvm::DenseMap<const RecordDecl *, Record *> Records;
 
-  /// Dummy parameter to generate pointers from.
-  llvm::DenseMap<const void *, unsigned> DummyVariables;
-
   /// Creates a new descriptor.
   template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
     return new (Allocator) Descriptor(std::forward<Ts>(Args)...);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to