[clang] 06cac12 - [clang][bytecode] Use ASTRecordLayout offsets when subtracting pointers (#206496)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 29 23:13:52 PDT 2026
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);
+}
More information about the cfe-commits
mailing list