Skip to content

Commit 39d6563

Browse files
tbaederrkbluck
authored andcommitted
[clang][Interp] Diagnose comparisons against one-past-end pointers
1 parent ecc0298 commit 39d6563

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

clang/lib/AST/Interp/Interp.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
922922
return true;
923923
}
924924

925+
// Reject comparisons to weak pointers.
925926
for (const auto &P : {LHS, RHS}) {
926927
if (P.isZero())
927928
continue;
@@ -934,6 +935,20 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
934935
}
935936

936937
if (!Pointer::hasSameBase(LHS, RHS)) {
938+
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
939+
RHS.getOffset() == 0) {
940+
const SourceInfo &Loc = S.Current->getSource(OpPC);
941+
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
942+
<< LHS.toDiagnosticString(S.getCtx());
943+
return false;
944+
} else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
945+
LHS.getOffset() == 0) {
946+
const SourceInfo &Loc = S.Current->getSource(OpPC);
947+
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
948+
<< RHS.toDiagnosticString(S.getCtx());
949+
return false;
950+
}
951+
937952
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
938953
return true;
939954
} else {

clang/lib/AST/Interp/Pointer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ APValue Pointer::toAPValue() const {
143143

144144
if (isDummy() || isUnknownSizeArray() || Desc->asExpr())
145145
return APValue(Base, CharUnits::Zero(), Path,
146-
/*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
146+
/*IsOnePastEnd=*/isOnePastEnd(), /*IsNullPtr=*/false);
147147

148148
// TODO: compute the offset into the object.
149149
CharUnits Offset = CharUnits::Zero();
@@ -181,7 +181,8 @@ APValue Pointer::toAPValue() const {
181181
// Just invert the order of the elements.
182182
std::reverse(Path.begin(), Path.end());
183183

184-
return APValue(Base, Offset, Path, /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
184+
return APValue(Base, Offset, Path, /*IsOnePastEnd=*/isOnePastEnd(),
185+
/*IsNullPtr=*/false);
185186
}
186187

187188
void Pointer::print(llvm::raw_ostream &OS) const {
@@ -348,7 +349,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
348349

349350
// Invalid pointers.
350351
if (Ptr.isDummy() || !Ptr.isLive() || !Ptr.isBlockPointer() ||
351-
(!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
352+
Ptr.isPastEnd())
352353
return false;
353354

354355
// Primitive values.

clang/lib/AST/Interp/Pointer.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,18 @@ class Pointer {
556556
if (isUnknownSizeArray())
557557
return false;
558558

559-
return isElementPastEnd() ||
559+
return isElementPastEnd() || isPastEnd() ||
560560
(getSize() == getOffset() && !isZeroSizeArray());
561561
}
562562

563+
/// Checks if the pointer points past the end of the object.
564+
bool isPastEnd() const {
565+
if (isIntegralPointer())
566+
return false;
567+
568+
return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize();
569+
}
570+
563571
/// Checks if the pointer is an out-of-bounds element pointer.
564572
bool isElementPastEnd() const { return Offset == PastEndMark; }
565573

clang/test/AST/Interp/literals.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,3 +1266,13 @@ static_assert(ReturnInStmtExpr() == 1, ""); // both-error {{not an integral cons
12661266
// both-note {{in call to}}
12671267

12681268
#endif
1269+
1270+
namespace ComparisonAgainstOnePastEnd {
1271+
int a, b;
1272+
static_assert(&a + 1 == &b, ""); // both-error {{not an integral constant expression}} \
1273+
// both-note {{comparison against pointer '&a + 1' that points past the end of a complete object has unspecified value}}
1274+
static_assert(&a == &b + 1, ""); // both-error {{not an integral constant expression}} \
1275+
// both-note {{comparison against pointer '&b + 1' that points past the end of a complete object has unspecified value}}
1276+
1277+
static_assert(&a + 1 == &b + 1, ""); // both-error {{static assertion failed}}
1278+
};

0 commit comments

Comments
 (0)