Skip to content

Commit 360e4ab

Browse files
authored
[clang][bytecode] Diagnose comparisons with literals (#106734)
This requires adding a new opcode for PointerToBoolean casts, since we otherwise emit too many diagnostics. But that fixes an older problem when casting weak pointers to bool.
1 parent ef50970 commit 360e4ab

File tree

8 files changed

+76
-34
lines changed

8 files changed

+76
-34
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -505,14 +505,9 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
505505
case CK_MemberPointerToBoolean: {
506506
PrimType PtrT = classifyPrim(SubExpr->getType());
507507

508-
// Just emit p != nullptr for this.
509508
if (!this->visit(SubExpr))
510509
return false;
511-
512-
if (!this->emitNull(PtrT, nullptr, CE))
513-
return false;
514-
515-
return this->emitNE(PtrT, CE);
510+
return this->emitIsNonNull(PtrT, CE);
516511
}
517512

518513
case CK_IntegralComplexToBoolean:
@@ -2323,8 +2318,8 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
23232318

23242319
// For everyhing else, use local variables.
23252320
if (SubExprT) {
2326-
unsigned LocalIndex = allocateLocalPrimitive(
2327-
SubExpr, *SubExprT, /*IsConst=*/true, /*IsExtended=*/true);
2321+
unsigned LocalIndex = allocateLocalPrimitive(E, *SubExprT, /*IsConst=*/true,
2322+
/*IsExtended=*/true);
23282323
if (!this->visit(SubExpr))
23292324
return false;
23302325
if (!this->emitSetLocal(*SubExprT, LocalIndex, E))

clang/lib/AST/ByteCode/Interp.h

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -986,24 +986,7 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
986986
}
987987
}
988988

989-
if (!Pointer::hasSameBase(LHS, RHS)) {
990-
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
991-
RHS.getOffset() == 0) {
992-
const SourceInfo &Loc = S.Current->getSource(OpPC);
993-
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
994-
<< LHS.toDiagnosticString(S.getASTContext());
995-
return false;
996-
} else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
997-
LHS.getOffset() == 0) {
998-
const SourceInfo &Loc = S.Current->getSource(OpPC);
999-
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
1000-
<< RHS.toDiagnosticString(S.getASTContext());
1001-
return false;
1002-
}
1003-
1004-
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
1005-
return true;
1006-
} else {
989+
if (Pointer::hasSameBase(LHS, RHS)) {
1007990
unsigned VL = LHS.getByteOffset();
1008991
unsigned VR = RHS.getByteOffset();
1009992

@@ -1019,6 +1002,35 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
10191002
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
10201003
return true;
10211004
}
1005+
// Otherwise we need to do a bunch of extra checks before returning Unordered.
1006+
if (LHS.isOnePastEnd() && !RHS.isOnePastEnd() && !RHS.isZero() &&
1007+
RHS.getOffset() == 0) {
1008+
const SourceInfo &Loc = S.Current->getSource(OpPC);
1009+
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
1010+
<< LHS.toDiagnosticString(S.getASTContext());
1011+
return false;
1012+
} else if (RHS.isOnePastEnd() && !LHS.isOnePastEnd() && !LHS.isZero() &&
1013+
LHS.getOffset() == 0) {
1014+
const SourceInfo &Loc = S.Current->getSource(OpPC);
1015+
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_past_end)
1016+
<< RHS.toDiagnosticString(S.getASTContext());
1017+
return false;
1018+
}
1019+
1020+
bool BothNonNull = !LHS.isZero() && !RHS.isZero();
1021+
// Reject comparisons to literals.
1022+
for (const auto &P : {LHS, RHS}) {
1023+
if (P.isZero())
1024+
continue;
1025+
if (BothNonNull && P.pointsToLiteral()) {
1026+
const SourceInfo &Loc = S.Current->getSource(OpPC);
1027+
S.FFDiag(Loc, diag::note_constexpr_literal_comparison);
1028+
return false;
1029+
}
1030+
}
1031+
1032+
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
1033+
return true;
10221034
}
10231035

10241036
template <>
@@ -1030,9 +1042,10 @@ inline bool CmpHelperEQ<MemberPointer>(InterpState &S, CodePtr OpPC,
10301042
// If either operand is a pointer to a weak function, the comparison is not
10311043
// constant.
10321044
for (const auto &MP : {LHS, RHS}) {
1033-
if (const CXXMethodDecl *MD = MP.getMemberFunction(); MD && MD->isWeak()) {
1045+
if (MP.isWeak()) {
10341046
const SourceInfo &Loc = S.Current->getSource(OpPC);
1035-
S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison) << MD;
1047+
S.FFDiag(Loc, diag::note_constexpr_mem_pointer_weak_comparison)
1048+
<< MP.getMemberFunction();
10361049
return false;
10371050
}
10381051
}
@@ -2291,6 +2304,15 @@ inline bool Null(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
22912304
return true;
22922305
}
22932306

2307+
template <PrimType Name, class T = typename PrimConv<Name>::T>
2308+
inline bool IsNonNull(InterpState &S, CodePtr OpPC) {
2309+
const auto &P = S.Stk.pop<T>();
2310+
if (P.isWeak())
2311+
return false;
2312+
S.Stk.push<Boolean>(Boolean::from(!P.isZero()));
2313+
return true;
2314+
}
2315+
22942316
//===----------------------------------------------------------------------===//
22952317
// This, ImplicitThis
22962318
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/MemberPointer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class MemberPointer final {
8484

8585
bool isZero() const { return Base.isZero() && !Dcl; }
8686
bool hasBase() const { return !Base.isZero(); }
87+
bool isWeak() const {
88+
if (const auto *MF = getMemberFunction())
89+
return MF->isWeak();
90+
return false;
91+
}
8792

8893
void print(llvm::raw_ostream &OS) const {
8994
OS << "MemberPtr(" << Base << " " << (const void *)Dcl << " + " << PtrOffset

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,11 @@ def Comp: Opcode {
597597
let HasGroup = 1;
598598
}
599599

600+
def IsNonNull : Opcode {
601+
let Types = [PtrTypeClass];
602+
let HasGroup = 1;
603+
}
604+
600605
//===----------------------------------------------------------------------===//
601606
// Cast, CastFP.
602607
//===----------------------------------------------------------------------===//

clang/lib/AST/ByteCode/Pointer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,17 @@ bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
463463
A.getFieldDesc()->IsArray;
464464
}
465465

466+
bool Pointer::pointsToLiteral() const {
467+
if (isZero() || !isBlockPointer())
468+
return false;
469+
470+
const Expr *E = block()->getDescriptor()->asExpr();
471+
if (block()->isDynamic())
472+
return false;
473+
474+
return E && !isa<MaterializeTemporaryExpr, StringLiteral>(E);
475+
}
476+
466477
std::optional<APValue> Pointer::toRValue(const Context &Ctx,
467478
QualType ResultType) const {
468479
const ASTContext &ASTCtx = Ctx.getASTContext();

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,10 @@ class Pointer {
692692
/// Checks if both given pointers point to the same block.
693693
static bool pointToSameBlock(const Pointer &A, const Pointer &B);
694694

695+
/// Whether this points to a block that's been created for a "literal lvalue",
696+
/// i.e. a non-MaterializeTemporaryExpr Expr.
697+
bool pointsToLiteral() const;
698+
695699
/// Prints the pointer.
696700
void print(llvm::raw_ostream &OS) const;
697701

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,3 +951,9 @@ namespace shufflevector {
951951
}
952952

953953
#endif
954+
955+
namespace FunctionStart {
956+
void a(void) {}
957+
static_assert(__builtin_function_start(a) == a, ""); // both-error {{not an integral constant expression}} \
958+
// both-note {{comparison of addresses of literals has unspecified value}}
959+
}

clang/test/AST/ByteCode/weak.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s
22
// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s
33

4-
5-
6-
7-
/// FIXME: The new interpreter also emits the "address of weak declaration" note in the pointer-to-bool case.
8-
94
[[gnu::weak]] extern int a;
105
int ha[(bool)&a]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
11-
// expected-note {{comparison against address of weak declaration}} \
126
// both-error {{variable length array declaration not allowed at file scope}}
137
int ha2[&a == nullptr]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
148
// both-note {{comparison against address of weak declaration '&a' can only be performed at runtime}} \

0 commit comments

Comments
 (0)