https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/223353

None

>From 0bd2ec250b73c3354209ed357e9567a62b4e1788 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Sun, 13 Sep 2026 09:44:39 +0200
Subject: [PATCH] zmog

---
 clang/lib/AST/ByteCode/Interp.cpp             | 83 +++++++++++++------
 clang/lib/AST/ByteCode/Interp.h               | 15 ++--
 .../AST/ByteCode/InterpBuiltinObjectSize.cpp  |  3 +-
 clang/lib/AST/ByteCode/Pointer.cpp            | 26 ++++--
 clang/lib/AST/ByteCode/Pointer.h              | 11 +++
 clang/test/AST/ByteCode/cxx11.cpp             | 19 +++++
 6 files changed, 113 insertions(+), 44 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index b0b8cfd7198ac..cc1c61c87da50 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -322,6 +322,16 @@ bool diagnoseShiftFailure(InterpState &S, CodePtr OpPC, 
ShiftFailure Failure,
   return S.noteUndefinedBehavior();
 }
 
+bool diagnoseArrayIndex(InterpState &S, CodePtr OpPC, const APSInt &Index,
+                        std::optional<uint64_t> NumElems, bool IsArray) {
+  if (IsArray)
+    assert(NumElems);
+
+  S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+      << Index << /*non-array=*/!IsArray << NumElems.value_or(0u);
+  return false;
+}
+
 void cleanupAfterFunctionCall(InterpState &S, const Function *Func) {
   assert(S.Current);
   assert(Func);
@@ -3436,6 +3446,18 @@ bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
     ElemType = ArrTy;
 
   if (ArrTy->isArrayType()) {
+    unsigned IndexBits = std::max(Index.getBitWidth(), 32u) + 1;
+    APSInt NewIndex =
+        Index.extend(IndexBits) +
+        APSInt(APInt(IndexBits, Ptr.getIndex()), Index.isUnsigned());
+
+    if (NewIndex > Ptr.getNumElems() || NewIndex.isNegative())
+      diagnoseArrayIndex(S, OpPC, NewIndex, Ptr.getNumElems(),
+                         OP.isArrayElement());
+
+    if (NewIndex.getActiveBits() > 64)
+      return false;
+
     unsigned NewPathLength;
     if (AllowReplace && OP.isArrayElement()) {
       // This is what happens after an array-to-pointer-decay. We don't enter
@@ -3459,15 +3481,21 @@ bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
         Ptr.getByteOffset());
 
   } else {
-    if (!validType(ElemType))
+    unsigned IndexBits = std::max(Index.getBitWidth(), 64u) + 1;
+    size_t CurrentIndex = Ptr.getByteOffset();
+    APSInt NewOffset =
+        Index.extend(IndexBits) +
+        APSInt(APInt(IndexBits, CurrentIndex), Index.isUnsigned());
+    if (NewOffset > 1 || NewOffset.isNegative())
+      diagnoseArrayIndex(S, OpPC, NewOffset, 0, false);
+
+    if (NewOffset.getActiveBits() > 64)
       return false;
-    unsigned ElemSize =
-        S.getASTContext().getTypeSizeInChars(ElemType).getQuantity();
-    size_t NewOffset = Ptr.getByteOffset() + (Index.getZExtValue() * ElemSize);
-    bool PastEnd = Index != 0;
 
+    size_t NewByteOffset = CurrentIndex + Index.getZExtValue();
+    bool PastEnd = NewByteOffset != 0;
     S.Stk.push<Pointer>(OP.withFieldType(ElemType.getTypePtr(), PastEnd),
-                        NewOffset);
+                        NewByteOffset);
   }
   return true;
 }
@@ -3498,21 +3526,32 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState 
&S, CodePtr OpPC,
     return std::nullopt;
   }
 
-  if (Offset > NumElems) {
-    if (Op == ArithOp::Add)
-      S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
-    else
-      S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << -Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
-  }
-
   if (!validType(ElemTy) || !validType(ArrTy)) {
     Invalid(S, OpPC);
     return std::nullopt;
   }
 
-  if (Offset.getActiveBits() > 64)
+  APSInt NewIndex;
+  if (Op == ArithOp::Add) {
+    if (OP.isArrayElement()) {
+      NewIndex = Ptr.getIndex() + (Offset.extend(Offset.getBitWidth() + 2));
+    } else {
+      NewIndex =
+          (Ptr.getByteOffset()) + (Offset.extend(Offset.getBitWidth() + 2));
+    }
+  } else {
+    if (OP.isArrayElement()) {
+      NewIndex = Ptr.getIndex() - (Offset.extend(Offset.getBitWidth() + 2));
+    } else {
+      NewIndex =
+          (Ptr.getByteOffset()) - (Offset.extend(Offset.getBitWidth() + 2));
+    }
+  }
+
+  if (NewIndex > NumElems || NewIndex < 0)
+    diagnoseArrayIndex(S, OpPC, NewIndex, NumElems, OP.isArrayElement());
+
+  if (NewIndex.getActiveBits() > 64)
     return std::nullopt;
 
   // If the pointer is an array element, advance that index.
@@ -3527,17 +3566,7 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState 
&S, CodePtr OpPC,
     return OP.withPath(NewPath, NewPathLength, OP.FieldType.getPointer());
   }
 
-  unsigned ElemSize =
-      S.getASTContext().getTypeSizeInChars(ElemTy).getQuantity();
-  unsigned NewOffset;
-  if (Op == ArithOp::Add)
-    NewOffset = Ptr.getByteOffset() + (ElemSize * Offset.getZExtValue());
-  else
-    NewOffset = Ptr.getByteOffset() - (ElemSize * Offset.getZExtValue());
-
-  // We already checked offset != before, so this is a non-array type being
-  // offset by > 0.
-  return Pointer(OP.withPastEnd(true), NewOffset);
+  return Pointer(OP.withPastEnd(true), NewIndex.getZExtValue());
 }
 
 bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl,
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 7c71635561708..747311f42d757 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -83,6 +83,10 @@ bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, 
bool Extern,
                            const Block *B, Lifetime LT = Lifetime::Started,
                            AccessKinds AK = AK_Read);
 
+bool diagnoseArrayIndex(InterpState &S, CodePtr OpPC, const APSInt &Index,
+                        std::optional<uint64_t> NumElems = std::nullopt,
+                        bool IsArray = true);
+
 /// Checks a direct load of a primitive value from a global or local variable.
 bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Block *B);
 bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B);
@@ -2608,8 +2612,7 @@ std::optional<Pointer> OffsetHelper(InterpState &S, 
CodePtr OpPC,
       N = Ptr.getByteOffset() - O;
 
     if (N > 1)
-      S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << N << /*non-array*/ true << 0;
+      diagnoseArrayIndex(S, OpPC, APSInt::getUnsigned(N), 0, 
/*IsArray=*/false);
     return Pointer(Ptr.asFunctionPointer().Func, N);
   } else if (Ptr.isStringPointer()) {
     int64_t NewOffset;
@@ -2619,9 +2622,8 @@ std::optional<Pointer> OffsetHelper(InterpState &S, 
CodePtr OpPC,
       NewOffset = Ptr.getRawOffset() - static_cast<int64_t>(Offset);
     if (NewOffset < 0 ||
         NewOffset > (Ptr.asStringPointer().getLiteral()->getLength() + 1)) {
-      S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-          << NewOffset << /*non-array*/ false
-          << (Ptr.asStringPointer().getLiteral()->getLength() + 1);
+      diagnoseArrayIndex(S, OpPC, APSInt::get(NewOffset),
+                         (Ptr.asStringPointer().getLiteral()->getLength() + 
1));
       return std::nullopt;
     }
     return Pointer(Ptr.asStringPointer(), NewOffset);
@@ -2647,8 +2649,7 @@ std::optional<Pointer> OffsetHelper(InterpState &S, 
CodePtr OpPC,
                    /*IsUnsigned=*/false);
     APSInt NewIndex =
         (Op == ArithOp::Add) ? (APIndex + APOffset) : (APIndex - APOffset);
-    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-        << NewIndex << /*array*/ static_cast<int>(!Ptr.inArray()) << MaxIndex;
+    diagnoseArrayIndex(S, OpPC, NewIndex, MaxIndex, Ptr.inArray());
     Invalid = true;
   };
 
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
index c1a69e03c0e73..efb764cd72b19 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp
@@ -445,7 +445,8 @@ UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext 
&ASTCtx,
         return std::nullopt;
     }
 
-    *Offset += Ptr.getByteOffset();
+    *Offset += (Ptr.getByteOffset() *
+                ASTCtx.getTypeSizeInChars(OP.getFieldType()).getQuantity());
 
     if (*Offset > *FullSize)
       return 0u;
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index 9a068071df570..c07b01da41f09 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -26,11 +26,13 @@ using namespace clang;
 using namespace clang::interp;
 
 // Helper to check if a Type can be passed to
-// ASTContext::getRecordLayout().
+// ASTContext::getTypeSize().
 static bool validType(QualType T) {
   if (const RecordDecl *RD = T->getAsRecordDecl())
     return ASTContext::hasLayout(RD);
-  return true;
+  return !T->isDependentType() && !T->isUndeducedAutoType() &&
+         !T->isSpecificBuiltinType(BuiltinType::UnknownAny) &&
+         !T->isIncompleteType();
 }
 
 Pointer::Pointer(Block *Pointee)
@@ -306,8 +308,12 @@ APValue Pointer::toAPValue(const ASTContext &ASTCtx) const 
{
       }
     }
     size_t LayoutOffset = Opaque.computeLayoutOffset(ASTCtx).value_or(0);
-    auto Offset = CharUnits::fromQuantity(LayoutOffset + getByteOffset());
-    auto Result =
+    size_t ElemSize = 0;
+    if (validType(Opaque.getFieldType()))
+      ElemSize = 
ASTCtx.getTypeSizeInChars(Opaque.getFieldType()).getQuantity();
+    auto Offset =
+        CharUnits::fromQuantity(LayoutOffset + (this->Offset * ElemSize));
+    APValue Result =
         APValue(Opaque.Base, Offset, Path,
                 /*IsOnePastEnd=*/Opaque.isOnePastEnd(), /*IsNullPtr=*/false);
     Result.setConstexprUnknown(Opaque.isConstexprUnknown());
@@ -514,9 +520,7 @@ Pointer::computeOffsetForComparison(const ASTContext 
&ASTCtx) const {
   case Storage::String:
     return reinterpret_cast<uintptr_t>(Str.getLiteral()) + Offset;
   case Storage::Opaque:
-    if (auto O = Opaque.computeLayoutOffset(ASTCtx))
-      return *O + Offset;
-    return std::nullopt;
+    return computeLayoutOffset(ASTCtx);
   }
 
   auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
@@ -597,8 +601,12 @@ Pointer::computeLayoutOffset(const ASTContext &ASTCtx) 
const {
   case Storage::String:
     return Offset * Str.getLiteral()->getCharByteWidth();
   case Storage::Opaque:
-    if (auto O = Opaque.computeLayoutOffset(ASTCtx))
-      return *O + Offset;
+    if (auto O = Opaque.computeLayoutOffset(ASTCtx)) {
+      size_t TypeSize = 0;
+      if (QualType FT = Opaque.getFieldType(); validType(FT))
+        TypeSize = ASTCtx.getTypeSizeInChars(FT).getQuantity();
+      return *O + (Offset * TypeSize);
+    }
     return std::nullopt;
   }
 
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 5b43df9db49c4..0953c897b2564 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -985,6 +985,12 @@ class Pointer {
   unsigned getNumElems() const {
     if (isStringPointer())
       return Str.getLiteral()->getLength() + 1;
+    if (isOpaquePointer()) {
+      const ArrayType *AT =
+          Opaque.getSurroundingArray()->getAsArrayTypeUnsafe();
+      if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(AT))
+        return CAT->getZExtSize();
+    }
     if (!isBlockPointer())
       return ~0u;
     return view().getNumElems();
@@ -1008,6 +1014,11 @@ class Pointer {
   int64_t getIndex() const {
     if (isStringPointer())
       return Offset;
+    if (isOpaquePointer()) {
+      if (Opaque.isArrayElement())
+        return Opaque.Path[Opaque.PathLength - 1].Index;
+      return 0;
+    }
     if (!isBlockPointer())
       return getIntegerRepresentation();
 
diff --git a/clang/test/AST/ByteCode/cxx11.cpp 
b/clang/test/AST/ByteCode/cxx11.cpp
index f2e0ce45154de..b5b01949d7098 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -509,3 +509,22 @@ namespace ConstexprForRangeVar {
                                    // both-note-re {{read of implicit variable 
'__begin{{[0-9]+}}' of range-based 'for' loop is not allowed in a constant 
expression}}
   }
 }
+
+namespace OpaqueArrayIndex {
+
+  int n;
+  int a[1];
+  constexpr int *r = &(&n + 1)[(unsigned __int128)-1]; // both-error 
{{constant expression}} \
+                                                       // both-note {{456 of 
non-array object}}
+  constexpr int *r2 = &a[(unsigned __int128)-1]; // both-error {{constant 
expression}} \
+                                                 // both-note {{455 of array 
of 1 element}}
+  constexpr int *r3 = &a[2];  // both-error {{constant expression}} \
+                              // both-note {{2 of array of 1 element}}
+  constexpr int *r4 = &a[-1];  // both-error {{constant expression}} \
+                               // both-note {{-1 of array of 1 element}}
+  constexpr int *q = (&n + 1) - (unsigned __int128)-1; // both-error 
{{constant expression}} \
+                                                       // both-note {{cannot 
refer to element -3402}}
+  constexpr int *f = &a[0] + 1 + (unsigned long)-1; // both-error {{constant 
expression}} \
+                                                    // both-note {{cannot 
refer to element 1844}}
+
+}

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

Reply via email to