Author: Timm Baeder
Date: 2026-06-30T08:13:47+02:00
New Revision: 06cac128adafc6c93120db7a3ac44e97c5e2c23b

URL: 
https://github.com/llvm/llvm-project/commit/06cac128adafc6c93120db7a3ac44e97c5e2c23b
DIFF: 
https://github.com/llvm/llvm-project/commit/06cac128adafc6c93120db7a3ac44e97c5e2c23b.diff

LOG: [clang][bytecode] Use ASTRecordLayout offsets when subtracting pointers 
(#206496)

What we did here didn't work properly for pointers casted to bases. Add
`Pointer::computeLayoutOffset()` and use that to return the proper
values.

Added: 
    

Modified: 
    clang/lib/AST/ByteCode/Compiler.cpp
    clang/lib/AST/ByteCode/Descriptor.cpp
    clang/lib/AST/ByteCode/Interp.h
    clang/lib/AST/ByteCode/Opcodes.td
    clang/lib/AST/ByteCode/Pointer.cpp
    clang/lib/AST/ByteCode/Pointer.h
    clang/test/AST/ByteCode/cxx20.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 398c2997ca4da..95f9a2f507335 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -1250,7 +1250,7 @@ bool Compiler<Emitter>::VisitPointerArithBinOp(const 
BinaryOperator *E) {
       ElemTypeSize = Ctx.getASTContext().getTypeSizeInChars(ElemType);
 
     PrimType IntT = classifyPrim(E->getType());
-    if (!this->emitSubPtr(IntT, ElemTypeSize.isZero(), E))
+    if (!this->emitSubPtr(IntT, ElemTypeSize.getQuantity(), E))
       return false;
     return DiscardResult ? this->emitPop(IntT, E) : true;
   }

diff  --git a/clang/lib/AST/ByteCode/Descriptor.cpp 
b/clang/lib/AST/ByteCode/Descriptor.cpp
index 8d9b87107e65d..fb41c98dd68cb 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -402,7 +402,26 @@ QualType Descriptor::getType() const {
 
 QualType Descriptor::getElemQualType() const {
   assert(isArray());
-  QualType T = getType();
+  QualType T;
+
+  if (SourceType) {
+    T = QualType(SourceType, 0);
+  } else if (const auto *TDecl = dyn_cast_if_present<TypeDecl>(asDecl())) {
+    T = TDecl->getASTContext().getTypeDeclType(TDecl);
+  } else if (isRecord()) {
+    const RecordDecl *RD = ElemRecord->getDecl();
+    T = RD->getASTContext().getTagType(ElaboratedTypeKeyword::None,
+                                       std::nullopt, RD, false);
+    if (IsConst)
+      T.addConst();
+  } else if (const auto *E = asExpr()) {
+    T = E->getType();
+  } else if (const auto *D = asValueDecl()) {
+    T = D->getType();
+  }
+
+  assert(!T.isNull());
+
   if (const auto *AT = T->getAs<AtomicType>())
     T = AT->getValueType();
   if (T->isPointerOrReferenceType())

diff  --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index aa2dffc2b982a..97d9e7cc14e32 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2702,7 +2702,7 @@ static inline bool DecPtr(InterpState &S, CodePtr OpPC) {
 /// 2) Pops another Pointer from the stack.
 /// 3) Pushes the 
diff erence of the indices of the two pointers on the stack.
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-inline bool SubPtr(InterpState &S, CodePtr OpPC, bool ElemSizeIsZero) {
+inline bool SubPtr(InterpState &S, CodePtr OpPC, uint32_t ElemSize) {
   const Pointer &LHS = S.Stk.pop<Pointer>().expand();
   const Pointer &RHS = S.Stk.pop<Pointer>().expand();
 
@@ -2737,11 +2737,8 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool 
ElemSizeIsZero) {
     return false;
   }
 
-  if (ElemSizeIsZero) {
-    QualType PtrT = LHS.getType();
-    while (auto *AT = dyn_cast<ArrayType>(PtrT))
-      PtrT = AT->getElementType();
-
+  if (ElemSize == 0) {
+    QualType PtrT = S.getASTContext().getBaseElementType(LHS.getType());
     QualType ArrayTy = S.getASTContext().getConstantArrayType(
         PtrT, APInt::getZero(1), nullptr, ArraySizeModifier::Normal, 0);
     S.FFDiag(S.Current->getSource(OpPC),
@@ -2756,17 +2753,16 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool 
ElemSizeIsZero) {
     return true;
   }
 
-  int64_t A64 =
-      LHS.isBlockPointer()
-          ? (LHS.isElementPastEnd() ? LHS.getNumElems() : LHS.getIndex())
-          : LHS.getIntegerRepresentation();
-
-  int64_t B64 =
-      RHS.isBlockPointer()
-          ? (RHS.isElementPastEnd() ? RHS.getNumElems() : RHS.getIndex())
-          : RHS.getIntegerRepresentation();
+  std::optional<size_t> VL = LHS.computeLayoutOffset(S.getASTContext());
+  if (!VL)
+    return false;
+  std::optional<size_t> VR = RHS.computeLayoutOffset(S.getASTContext());
+  if (!VR)
+    return false;
 
-  int64_t R64 = A64 - B64;
+  assert(((int64_t)*VL - (int64_t)*VR) % ElemSize == 0);
+  int64_t R64 =
+      (static_cast<int64_t>(*VL) - static_cast<int64_t>(*VR)) / ElemSize;
   if (static_cast<int64_t>(T::from(R64)) != R64)
     return handleOverflow(S, OpPC, R64);
 

diff  --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index e350d7b2e547d..b6190554178f3 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -594,7 +594,7 @@ def SubOffset : Opcode {
 // [Pointer, Pointer] -> [Integral]
 def SubPtr : Opcode {
   let Types = [IntegerTypeClass];
-  let Args = [ArgBool];
+  let Args = [ArgUint32];
   let HasGroup = 1;
 }
 

diff  --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index de2b3421f404b..37659eb79e1df 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -437,6 +437,104 @@ Pointer::computeOffsetForComparison(const ASTContext 
&ASTCtx) const {
   return Result;
 }
 
+std::optional<size_t>
+Pointer::computeLayoutOffset(const ASTContext &ASTCtx) const {
+  switch (StorageKind) {
+  case Storage::Int:
+    return Int.Value + Offset;
+  case Storage::Block:
+    // See below.
+    break;
+  case Storage::Fn:
+    return getIntegerRepresentation();
+  case Storage::Typeid:
+    return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
+  }
+
+  auto getTypeSize = [&](QualType T) -> std::optional<size_t> {
+    if (const RecordType *RT = T->getAs<RecordType>()) {
+      // We cannot get the type size of a forward declaration.
+      if (!RT->getDecl()->getDefinition())
+        return std::nullopt;
+    }
+    return ASTCtx.getTypeSizeInChars(T).getQuantity();
+  };
+
+  auto getRecordDecl = [&](PtrView P) -> const CXXRecordDecl * {
+    if (const Record *R = P.getRecord())
+      return cast<CXXRecordDecl>(R->getDecl());
+    return cast<CXXRecordDecl>(P.getFieldDesc()->asDecl());
+  };
+
+  auto getRecordSize = [&](const RecordDecl *RD) -> unsigned {
+    CanQualType RecordTy = ASTCtx.getCanonicalTagType(RD);
+    return ASTCtx.getTypeSizeInChars(RecordTy).getQuantity();
+  };
+
+  size_t Result = 0;
+  PtrView P = view();
+  while (true) {
+    if (P.isBaseClass()) {
+      const ASTRecordLayout &Layout =
+          ASTCtx.getASTRecordLayout(getRecordDecl(P.getBase()));
+      const CXXRecordDecl *RD = getRecordDecl(P);
+      if (P.isVirtualBaseClass())
+        Result += Layout.getVBaseClassOffset(RD).getQuantity();
+      else
+        Result += Layout.getBaseClassOffset(RD).getQuantity();
+
+      if (P.isOnePastEnd())
+        Result += getRecordSize(RD);
+
+      P = P.getBase();
+      continue;
+    }
+
+    if (P.isArrayElement()) {
+      P = P.expand();
+      assert(P.getFieldDesc()->isArray());
+      if (std::optional<size_t> ElemSize =
+              getTypeSize(P.getFieldDesc()->getElemQualType()))
+        Result += *ElemSize * P.getIndex();
+      else
+        return std::nullopt;
+
+      P = P.getArray();
+      continue;
+    }
+
+    if (P.isRoot()) {
+      if (P.isPastEnd() || P.isOnePastEnd()) {
+        if (std::optional<size_t> Size =
+                getTypeSize(P.getDeclDesc()->getType()))
+          Result += *Size * P.getIndex();
+        else
+          return std::nullopt;
+      }
+      break;
+    }
+
+    assert(P.getField());
+    const FieldDecl *F = P.getField();
+    const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(F->getParent());
+    Result +=
+        ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(F->getFieldIndex()))
+            .getQuantity();
+
+    if (P.isPastEnd() || P.isOnePastEnd()) {
+      if (std::optional<size_t> Size = getTypeSize(F->getType()))
+        Result += *Size * P.getIndex();
+      else
+        return std::nullopt;
+    }
+
+    P = P.getBase();
+    if (P.isRoot())
+      break;
+  }
+  return Result;
+}
+
 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
   if (isZero())
     return "nullptr";

diff  --git a/clang/lib/AST/ByteCode/Pointer.h 
b/clang/lib/AST/ByteCode/Pointer.h
index d4d92d62c0146..8a413d01d2dd5 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -1035,6 +1035,9 @@ class Pointer {
   /// regarding the AST record layout.
   std::optional<size_t>
   computeOffsetForComparison(const ASTContext &ASTCtx) const;
+  /// Compute the pointer offset as given by the ASTRecordLayout.
+  /// Returns the result in bytes.
+  std::optional<size_t> computeLayoutOffset(const ASTContext &ASTCtx) const;
 
 private:
   friend class Block;

diff  --git a/clang/test/AST/ByteCode/cxx20.cpp 
b/clang/test/AST/ByteCode/cxx20.cpp
index 7ff70076ee6e4..b7ca154a0a987 100644
--- a/clang/test/AST/ByteCode/cxx20.cpp
+++ b/clang/test/AST/ByteCode/cxx20.cpp
@@ -1462,3 +1462,73 @@ namespace ConstWrites {
   constexpr B bad(true); // both-error {{must be initialized by a constant 
expression}} \
                          // both-note {{in call to 'B(true)'}}
 }
+
+namespace SubPtr {
+#define fold(x) (__builtin_constant_p(x) ? (x) : (x))
+  struct A { int a; };
+
+  struct B : A {
+    char Padding[12] = {};
+    constexpr B() : A{127} {}
+  };
+
+  struct C : B {
+    int c;
+    constexpr C(int) : B(), c(1000) {}
+  };
+
+  constexpr C c(123);
+
+  static_assert(fold((&c.c - &c.a)) == 4, "");
+  static_assert(fold(((char*)&c.c - (char*)&c.a)) == 16, "");
+
+  constexpr const B* b1 = (B*)&c;
+  constexpr const B* b2 = (B*)&c + 1;
+  static_assert(fold((char*)b2) - fold((char*)b1) == sizeof(B), "");
+
+
+  constexpr int arr[] = {1,2,3};
+  static_assert(&arr[1] - &arr[0] == 1, "");
+  static_assert(&arr[3] - &arr[0] == 3, "");
+
+  constexpr double arr2[] = {1,2,3};
+  static_assert(&arr2[1] - &arr2[0] == 1, "");
+  static_assert(&arr2[3] - &arr2[0] == 3, "");
+
+  struct S {
+    char c[3];
+    int a;
+  };
+  constexpr S s[] = {{}, {}};
+  static_assert(&s[1] - &s[0] == 1, "");
+  static_assert(&s[2] - &s[0] == 2, "");
+  static_assert(fold((char*)&s[1]) - fold((char*)&s[0]) ==     sizeof(S), "");
+  static_assert(fold((char*)&s[2]) - fold((char*)&s[0]) == 2 * sizeof(S), "");
+
+
+  constexpr int m = 10;
+  constexpr const int *p = &m;
+  constexpr const int *p2 = &m + 1;
+  static_assert( p2 - p == 1, "");
+
+
+  struct Ints {
+    int a;
+    int b;
+  };
+  constexpr Ints I = {};
+  static_assert(&I.a + 1 - &I.a == 1, "");
+
+
+  constexpr int **intptr[] = {nullptr};
+  static_assert(&intptr[1] - &intptr[0] == 1, "");
+
+  constexpr int dynAlloc() {
+    int **p = new int*[1];
+    int r = &p[1] - &p[0];
+    delete[] p;
+
+    return r;
+  }
+  static_assert(dynAlloc() == 1);
+}


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

Reply via email to