Skip to content

Commit fb2c761

Browse files
authored
[clang][bytecode] Fix comparing pointers pointing to base classes (llvm#146285)
In the attached test case, one pointer points to the `Derived` class and one to `Base`, but they should compare equal. They didn't because those two bases are saved at different offsets in the block. Use `computeOffsetForComparison` not just for unions and fix it to work in the more general cases.
1 parent 38b8ef1 commit fb2c761

File tree

5 files changed

+83
-26
lines changed

5 files changed

+83
-26
lines changed

clang/lib/AST/ByteCode/Interp.h

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,30 +1139,12 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
11391139
}
11401140

11411141
if (Pointer::hasSameBase(LHS, RHS)) {
1142-
if (LHS.inUnion() && RHS.inUnion()) {
1143-
// If the pointers point into a union, things are a little more
1144-
// complicated since the offset we save in interp::Pointer can't be used
1145-
// to compare the pointers directly.
1146-
size_t A = LHS.computeOffsetForComparison();
1147-
size_t B = RHS.computeOffsetForComparison();
1148-
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(A, B))));
1149-
return true;
1150-
}
1151-
1152-
unsigned VL = LHS.getByteOffset();
1153-
unsigned VR = RHS.getByteOffset();
1154-
// In our Pointer class, a pointer to an array and a pointer to the first
1155-
// element in the same array are NOT equal. They have the same Base value,
1156-
// but a different Offset. This is a pretty rare case, so we fix this here
1157-
// by comparing pointers to the first elements.
1158-
if (!LHS.isZero() && LHS.isArrayRoot())
1159-
VL = LHS.atIndex(0).getByteOffset();
1160-
if (!RHS.isZero() && RHS.isArrayRoot())
1161-
VR = RHS.atIndex(0).getByteOffset();
1162-
1163-
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
1142+
size_t A = LHS.computeOffsetForComparison();
1143+
size_t B = RHS.computeOffsetForComparison();
1144+
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(A, B))));
11641145
return true;
11651146
}
1147+
11661148
// Otherwise we need to do a bunch of extra checks before returning Unordered.
11671149
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
11681150
RHS.getOffset() == 0) {

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,16 +338,28 @@ void Pointer::print(llvm::raw_ostream &OS) const {
338338
}
339339
}
340340

341-
/// Compute an integer that can be used to compare this pointer to
342-
/// another one.
343341
size_t Pointer::computeOffsetForComparison() const {
342+
if (isIntegralPointer())
343+
return asIntPointer().Value + Offset;
344+
if (isTypeidPointer())
345+
return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset;
346+
344347
if (!isBlockPointer())
345348
return Offset;
346349

347350
size_t Result = 0;
348351
Pointer P = *this;
349-
while (!P.isRoot()) {
350-
if (P.isArrayRoot()) {
352+
while (true) {
353+
354+
if (P.isVirtualBaseClass()) {
355+
Result += getInlineDesc()->Offset;
356+
P = P.getBase();
357+
continue;
358+
}
359+
360+
if (P.isBaseClass()) {
361+
if (P.getRecord()->getNumVirtualBases() > 0)
362+
Result += P.getInlineDesc()->Offset;
351363
P = P.getBase();
352364
continue;
353365
}
@@ -358,14 +370,26 @@ size_t Pointer::computeOffsetForComparison() const {
358370
continue;
359371
}
360372

373+
if (P.isRoot()) {
374+
if (P.isOnePastEnd())
375+
++Result;
376+
break;
377+
}
378+
361379
if (const Record *R = P.getBase().getRecord(); R && R->isUnion()) {
362380
// Direct child of a union - all have offset 0.
363381
P = P.getBase();
364382
continue;
365383
}
366384

385+
// Fields, etc.
367386
Result += P.getInlineDesc()->Offset;
387+
if (P.isOnePastEnd())
388+
++Result;
389+
368390
P = P.getBase();
391+
if (P.isRoot())
392+
break;
369393
}
370394

371395
return Result;

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,9 @@ class Pointer {
761761
/// Prints the pointer.
762762
void print(llvm::raw_ostream &OS) const;
763763

764+
/// Compute an integer that can be used to compare this pointer to
765+
/// another one. This is usually NOT the same as the pointer offset
766+
/// regarding the AST record layout.
764767
size_t computeOffsetForComparison() const;
765768

766769
private:

clang/test/AST/ByteCode/literals.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,3 +1418,15 @@ constexpr int usingDirectiveDecl() {
14181418
return FB;
14191419
}
14201420
static_assert(usingDirectiveDecl() == 10, "");
1421+
1422+
namespace OnePastEndCmp {
1423+
struct S {
1424+
int a;
1425+
int b;
1426+
};
1427+
1428+
constexpr S s{12,13};
1429+
constexpr const int *p = &s.a;
1430+
constexpr const int *q = &s.a + 1;
1431+
static_assert(p != q, "");
1432+
}

clang/test/AST/ByteCode/new-delete.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,42 @@ namespace OpNewNothrow {
10221022
// both-note {{in call to}}
10231023
}
10241024

1025+
namespace BaseCompare {
1026+
struct Cmp {
1027+
void *p;
1028+
1029+
template<typename T>
1030+
constexpr Cmp(T *t) : p(t) {}
1031+
1032+
constexpr friend bool operator==(Cmp a, Cmp b) {
1033+
return a.p == b.p;
1034+
}
1035+
};
1036+
1037+
class Base {};
1038+
class Derived : public Base {};
1039+
constexpr bool foo() {
1040+
Derived *D = std::allocator<Derived>{}.allocate(1);;
1041+
std::construct_at<Derived>(D);
1042+
1043+
Derived *d = D;
1044+
Base *b = D;
1045+
1046+
Cmp ca(d);
1047+
Cmp cb(b);
1048+
1049+
if (ca == cb) {
1050+
std::allocator<Derived>{}.deallocate(D);
1051+
return true;
1052+
}
1053+
std::allocator<Derived>{}.deallocate(D);
1054+
1055+
return false;
1056+
1057+
}
1058+
static_assert(foo());
1059+
}
1060+
10251061
#else
10261062
/// Make sure we reject this prior to C++20
10271063
constexpr int a() { // both-error {{never produces a constant expression}}

0 commit comments

Comments
 (0)