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

>From b07934b4674104fc5b62ab3d2dc9f50bd58e5cfa 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] 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      |  53 +++--
 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, 443 insertions(+), 120 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..de09d019387cc 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())
+    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();
@@ -2124,7 +2146,8 @@ static bool interp__builtin_memcmp(InterpState &S, 
CodePtr OpPC,
     return true;
   }
 
-  if (!PtrA.isReadablePointerType() || !PtrB.isReadablePointerType())
+  if ((!PtrA.isReadablePointerType() && !PtrA.isOpaquePointer()) ||
+      (!PtrB.isReadablePointerType() && !PtrB.isOpaquePointer()))
     return false;
 
   bool IsWide =
@@ -2423,14 +2446,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());
   }
 }
 

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

Reply via email to