[clang] [clang][bytecode] Use opaque pointers for decl-based dummy pointer (PR #222897)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 11 02:37:08 PDT 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/222897 at github.com>
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
This uses the recently introduced opaque pointers for what we used to
use dummy pointers for, if the base of the pointer is a declaration.
This of course means we see opaque pointers in a lot more places.
However, we don't need to actually allocate anything for their data
anymore, resulting in a lot fewer allocations for such pointers.
---
Patch is 45.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/222897.diff
14 Files Affected:
- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+12-7)
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+186-32)
- (modified) clang/lib/AST/ByteCode/Interp.h (+80-27)
- (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+39-18)
- (modified) clang/lib/AST/ByteCode/InterpHelpers.h (+5)
- (modified) clang/lib/AST/ByteCode/MemberPointer.h (+2)
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+6-3)
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+112-13)
- (modified) clang/lib/AST/ByteCode/Pointer.h (+8-17)
- (modified) clang/test/AST/ByteCode/records.cpp (+11)
- (modified) clang/test/CodeGen/pr4349.c (+2-1)
- (modified) clang/test/SemaCXX/new-delete.cpp (+2-12)
- (modified) clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp (+1)
- (modified) clang/unittests/AST/ByteCode/toAPValue.cpp (-2)
``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index daa5307c92298..473494c694a98 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -7935,12 +7935,18 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
// check), so that e.g. '&*(int *)0' is not rejected.
if (!Ctx.getLangOpts().CPlusPlus) {
const Expr *Sub = SubExpr->IgnoreParens();
+
if (const auto *Deref = dyn_cast<UnaryOperator>(Sub);
- Deref && Deref->getOpcode() == UO_Deref)
- return this->delegate(Deref->getSubExpr());
+ Deref && Deref->getOpcode() == UO_Deref) {
+ if (DiscardResult)
+ return this->discard(Deref->getSubExpr());
+ return this->visit(Deref->getSubExpr()) && this->emitAddrOf(E);
+ }
}
// We should already have a pointer when we get here.
- return this->delegate(SubExpr);
+ if (DiscardResult)
+ return this->discard(SubExpr);
+ return this->delegate(SubExpr) && this->emitAddrOf(E);
case UO_Deref: // *x
if (DiscardResult)
return this->discard(SubExpr);
@@ -8729,11 +8735,10 @@ template <class Emitter>
bool Compiler<Emitter>::emitDummyPtr(DeclOrExpr D, const Expr *E, bool CU) {
assert(!DiscardResult && "Should've been checked before");
- if (ToLValue) {
- if (const auto *VD = D.asValueDecl())
- return this->emitGetOpaquePtr(VD, CU, E);
- }
+ if (const auto *VD = D.asValueDecl())
+ return this->emitGetOpaquePtr(VD, CU, E);
+ assert(D.asExpr());
unsigned DummyID = P.getOrCreateDummy(D, CU);
if (!this->emitGetPtrGlobal(DummyID, E))
return false;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index e205b025bbe01..160cc745c89b6 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -97,6 +97,23 @@ static void noteValueLocation(InterpState &S, const Block *B) {
S.Note(Desc->getLocation(), diag::note_declared_at);
}
+static void noteValueLocation(InterpState &S, const Pointer &Ptr) {
+ if (Ptr.isBlockPointer()) {
+ const Block *B = Ptr.block();
+ const Descriptor *Desc = B->getDescriptor();
+ if (B->isDynamic())
+ S.Note(Desc->getLocation(), diag::note_constexpr_dynamic_alloc_here);
+ else if (B->isTemporary())
+ S.Note(Desc->getLocation(), diag::note_constexpr_temporary_here);
+ else
+ S.Note(Desc->getLocation(), diag::note_declared_at);
+ return;
+ }
+
+ if (Ptr.isOpaquePointer())
+ S.Note(Ptr.asOpaquePointer().Base->getLocation(), diag::note_declared_at);
+}
+
static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
const ValueDecl *VD,
AccessKinds AK = AK_Read);
@@ -200,6 +217,38 @@ static void diagnoseNonConstVariable(InterpState &S, CodePtr OpPC,
S.Note(VD->getLocation(), diag::note_declared_at);
}
+static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ const Block *B = Ptr.block();
+ if (B->getDeclID()) {
+ if (!(B->isStatic() && B->isTemporary()))
+ return true;
+
+ const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(
+ B->getDescriptor()->asExpr());
+ if (!MTE)
+ return true;
+
+ // FIXME(perf): Since we do this check on every Load from a static
+ // temporary, it might make sense to cache the value of the
+ // isUsableInConstantExpressions call.
+ if (S.checkingConstantDestruction() ||
+ (B->getEvalID() != S.EvalID &&
+ !MTE->isUsableInConstantExpressions(S.getASTContext()))) {
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
+ noteValueLocation(S, B);
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Block *B,
AccessKinds AK) {
if (B->getDeclID()) {
@@ -460,7 +509,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
} else if (!S.checkingPotentialConstantExpression()) {
S.FFDiag(Src, diag::note_constexpr_access_uninit)
<< AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
- noteValueLocation(S, Ptr.block());
+ noteValueLocation(S, Ptr);
}
return false;
@@ -897,7 +946,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
}
// Block and string pointers are the only ones we can actually read from.
if (!Ptr.isReadablePointerType())
- return false;
+ return CheckDummy(S, OpPC, Ptr, AK);
if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) {
if (!CheckLive(S, OpPC, Ptr, AK))
@@ -964,7 +1013,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
assert(!Ptr.isZero());
if (!Ptr.isReadablePointerType())
- return false;
+ return CheckDummy(S, OpPC, Ptr, AK_Read);
if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) {
if (!CheckLive(S, OpPC, Ptr, AK_Read))
@@ -996,7 +1045,13 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool WillBeActivated) {
- if (!Ptr.isBlockPointer() || Ptr.isZero())
+ if (Ptr.isZero())
+ return false;
+
+ if (Ptr.isOpaquePointer())
+ return CheckDummy(S, OpPC, Ptr, AK_Assign);
+
+ if (!Ptr.isBlockPointer())
return false;
if (!Ptr.block()->isAccessible()) {
@@ -1043,6 +1098,8 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckRange(S, OpPC, Ptr, AK_Assign))
return false;
+ if (!Ptr.isBlockPointer())
+ return false;
return true;
}
@@ -1263,6 +1320,8 @@ bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
const Pointer &Ptr) {
+ if (!Ptr.isBlockPointer() && !Ptr.isOpaquePointer())
+ return false;
// Regular new type(...) call.
if (isa_and_nonnull<CXXNewExpr>(Source))
return true;
@@ -1279,7 +1338,7 @@ bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_delete_not_heap_alloc)
<< Ptr.toDiagnosticString(S.getASTContext());
- noteValueLocation(S, Ptr.block());
+ noteValueLocation(S, Ptr);
return false;
}
@@ -1305,6 +1364,24 @@ bool InvalidDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR,
return CheckDeclRef(S, OpPC, DR);
}
+bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK) {
+ if (!Ptr.isDummy())
+ return true;
+
+ const VarDecl *D = Ptr.getRootVarDecl();
+ if (!D)
+ return false;
+
+ if (AK == AK_Read || AK == AK_Increment || AK == AK_Decrement)
+ return diagnoseUnknownDecl(S, OpPC, D, AK);
+
+ if (AK == AK_Destroy || S.getLangOpts().CPlusPlus14)
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_modify_global);
+ return false;
+}
+
+// FIXME: Remove this once all dummy pointers are opaque pointers.
bool CheckDummy(InterpState &S, CodePtr OpPC, const Block *B, AccessKinds AK) {
if (!B->isDummy())
return true;
@@ -1429,7 +1506,7 @@ bool Free(InterpState &S, CodePtr OpPC, bool DeleteIsArrayForm,
return true;
if (!Ptr.isBlockPointer())
- return false;
+ return CheckDeleteSource(S, OpPC, nullptr, Ptr);
// Remove base casts.
QualType InitialType = Ptr.getType();
@@ -1811,7 +1888,7 @@ static bool diagnoseOutOfLifetimeDestroy(InterpState &S, CodePtr OpPC,
bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!CheckLive(S, OpPC, Ptr, AK_Destroy))
return false;
- if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Destroy))
+ if (!CheckTemporary(S, OpPC, Ptr, AK_Destroy))
return false;
if (!CheckRange(S, OpPC, Ptr, AK_Destroy))
return false;
@@ -1827,7 +1904,7 @@ bool checkDestructor(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
// Can't call a dtor on a global variable.
- if (Ptr.block()->isStatic()) {
+ if (Ptr.isOpaquePointer() || Ptr.block()->isStatic()) {
const SourceInfo &E = S.Current->getSource(OpPC);
S.FFDiag(E, diag::note_constexpr_modify_global);
return false;
@@ -2056,9 +2133,22 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return true;
}
-static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
+static bool getDynamicDecl(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
const CXXRecordDecl *&DynamicDecl) {
+ auto diagUnknownDynamicType = [&](const Pointer &P) -> bool {
+ APValue V = P.toAPValue(S.getASTContext());
+ QualType TT = S.getASTContext().getLValueReferenceType(P.getType());
+ S.FFDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_polymorphic_unknown_dynamic_type)
+ << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
+ return false;
+ };
+
+ if (!Ptr.isBlockPointer())
+ return diagUnknownDynamicType(Ptr);
+
+ PtrView TypePtr = Ptr.view();
if (S.InitializingPtrs.empty()) {
TypePtr = TypePtr.stripBaseCasts();
} else {
@@ -2090,14 +2180,8 @@ static bool getDynamicDecl(InterpState &S, CodePtr OpPC, PtrView TypePtr,
QualType DynamicType = TypePtr.getType();
if (TypePtr.Pointee->isStatic() || TypePtr.isConst()) {
if (const VarDecl *VD = Pointer(TypePtr).getRootVarDecl();
- VD && !VD->isConstexpr()) {
- const Expr *E = S.Current->getExpr(OpPC);
- APValue V = Pointer(TypePtr).toAPValue(S.getASTContext());
- QualType TT = S.getASTContext().getLValueReferenceType(DynamicType);
- S.FFDiag(E, diag::note_constexpr_polymorphic_unknown_dynamic_type)
- << AK_MemberCall << V.getAsString(S.getASTContext(), TT);
- return false;
- }
+ VD && !VD->isConstexpr())
+ return diagUnknownDynamicType(Pointer(TypePtr));
}
if (DynamicType->isPointerType() || DynamicType->isReferenceType()) {
@@ -2162,7 +2246,7 @@ bool DynamicCast(InterpState &S, CodePtr OpPC, const Type *DestTypePtr,
const auto &Ptr = S.Stk.pop<Pointer>();
QualType TargetType = QualType(DestTypePtr, 0);
- if (Ptr.isConstexprUnknown()) {
+ if (Ptr.isConstexprUnknown() || Ptr.isOpaquePointer()) {
QualType T = Ptr.getType();
const Expr *E = S.Current->getExpr(OpPC);
APValue V = Ptr.toAPValue(S.getASTContext());
@@ -2331,13 +2415,13 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
- if (!ThisPtr.isBlockPointer())
+ if (!ThisPtr.isBlockPointer() && !ThisPtr.isOpaquePointer())
return false;
const FunctionDecl *Callee = Func->getDecl();
const CXXRecordDecl *DynamicDecl = nullptr;
- if (!getDynamicDecl(S, OpPC, ThisPtr.view(), DynamicDecl))
+ if (!getDynamicDecl(S, OpPC, ThisPtr, DynamicDecl))
return false;
assert(DynamicDecl);
@@ -2611,7 +2695,7 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
}
if (!Ptr.isBlockPointer())
- return false;
+ return CheckDummy(S, OpPC, Ptr, AK_Construct);
if (!CheckRange(S, OpPC, Ptr, AK_Construct))
return false;
@@ -2625,9 +2709,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
return false;
if (!CheckLive(S, OpPC, Ptr, AK_Construct))
return false;
- return CheckDummy(S, OpPC, Ptr.block(), AK_Construct);
+ return CheckDummy(S, OpPC, Ptr, AK_Construct);
}
- if (!CheckTemporary(S, OpPC, Ptr.block(), AK_Construct))
+ if (!CheckTemporary(S, OpPC, Ptr, AK_Construct))
return false;
// CheckLifetime for this and all base pointers.
@@ -2773,6 +2857,12 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
if (Ptr.isIntegralPointer())
return true;
+ if (Ptr.isOpaquePointer()) {
+ if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
+ return false;
+ return Ptr.isRoot();
+ }
+
if (Ptr.isDummy()) {
if (!CheckIntegralAddressCast(S, OpPC, BitWidth))
return false;
@@ -2862,7 +2952,7 @@ bool GetTypeid(InterpState &S, const Type *TypePtr, const Type *TypeInfoType) {
bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) {
const auto &P = S.Stk.pop<Pointer>();
- if (!P.isBlockPointer())
+ if (!P.isBlockPointer() && !P.isOpaquePointer())
return false;
if (P.isConstexprUnknown()) {
@@ -2876,7 +2966,12 @@ bool GetTypeidPtr(InterpState &S, CodePtr OpPC, const Type *TypeInfoType) {
}
// Pick the most-derived type.
- CanQualType T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified();
+ CanQualType T;
+ if (P.isBlockPointer())
+ T = P.stripBaseCasts().getType()->getCanonicalTypeUnqualified();
+ else
+ T = P.getType()->getCanonicalTypeUnqualified();
+
// ... unless we're currently constructing this object.
// FIXME: We have a similar check to this in more places.
if (S.Current->getFunction()) {
@@ -2979,6 +3074,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView Ptr, PrimType T) {
auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
std::copy_n(Val.path(), PathLength, NewPath);
Val.takePath(NewPath);
+ } else if (T == PT_Ptr) {
+ auto &Val = Ptr.deref<Pointer>();
+ if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
+ const OpaquePointer &OP = Val.asOpaquePointer();
+ auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
+ std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry));
+ Val = Pointer(OP.withPath(NewPath, OP.PathLength,
+ OP.getFieldType().getTypePtr(),
+ OP.isOnePastEnd()),
+ Val.getByteOffset());
+ }
}
}
@@ -2991,6 +3097,17 @@ static void copyPrimitiveMemory(InterpState &S, PtrView Ptr) {
auto *NewPath = new (S.P) const CXXRecordDecl *[PathLength];
std::copy_n(Val.path(), PathLength, NewPath);
Val.takePath(NewPath);
+ } else if constexpr (std::is_same_v<T, Pointer>) {
+ auto &Val = Ptr.deref<Pointer>();
+ if (Val.isOpaquePointer() && Val.asOpaquePointer().PathLength != 0) {
+ const OpaquePointer &OP = Val.asOpaquePointer();
+ auto *NewPath = new (S.P) PointerPathEntry[OP.PathLength];
+ std::memcpy(NewPath, OP.Path, OP.PathLength * sizeof(PointerPathEntry));
+ Val = Pointer(OP.withPath(NewPath, OP.PathLength,
+ OP.getFieldType().getTypePtr(),
+ OP.isOnePastEnd()),
+ Val.getByteOffset());
+ }
} else {
auto &Val = Ptr.deref<T>();
if (!Val.singleWord()) {
@@ -3050,6 +3167,8 @@ static void finishGlobalRecurse(InterpState &S, PtrView Ptr) {
bool FinishInitGlobal(InterpState &S) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!Ptr.isBlockPointer())
+ return true;
finishGlobalRecurse(S, Ptr.view());
if (Ptr.canBeInitialized()) {
@@ -3355,13 +3474,16 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC,
return Ptr;
const OpaquePointer &OP = Ptr.asOpaquePointer();
- QualType ArrTy = OP.getSurroundingArray();
- QualType ElemTy = ArrTy;
+ QualType ArrTy = OP.getSurroundingArray().getCanonicalType();
+ QualType ElemTy = OP.getFieldType();
unsigned NumElems = 1;
- if (const ArrayType *AT = ArrTy->getAsArrayTypeUnsafe()) {
- ElemTy = AT->getElementType();
- if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
+
+ if (OP.isArrayElement()) {
+ if (const ConstantArrayType *CAT =
+ S.getASTContext().getAsConstantArrayType(ArrTy))
NumElems = CAT->getZExtSize();
+ } else {
+ ArrTy = ElemTy;
}
if (isa<IncompleteArrayType>(ArrTy)) {
@@ -3373,10 +3495,10 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC,
if (Offset > NumElems) {
if (Op == ArithOp::Add)
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems;
+ << Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
else
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << -Offset << /*non-array*/ !isa<ArrayType>(ArrTy) << NumElems;
+ << -Offset << /*non-array*/ !OP.isArrayElement() << NumElems;
}
if (!validType(ElemTy) || !validType(ArrTy)) {
@@ -3412,6 +3534,38 @@ std::optional<Pointer> addSubOffsetOpaque(InterpState &S, CodePtr OpPC,
return Pointer(OP.withPastEnd(true), NewOffset);
}
+bool virtBaseHelper(InterpState &S, const CXXRecordDecl *Decl,
+ const Pointer &Ptr) {
+ if (Ptr.isOpaquePointer()) {
+ const OpaquePointer &OP = Ptr.asOpaquePointer();
+ if (!OP.getFieldType()->isRecordType()) {
+ S.Stk.push<Pointer>(Ptr);
+ return true;
+ }
+
+ PointerPathEntry *NewPath =
+ S.extendPointerPath(OP.PathLength + 1, OP.Path,
+ PointerPathEntry::base(Decl, /*IsVirtual=*/true));
+
+ S.Stk.push<Pointer>(
+ OP.withPath(NewPath, OP.PathLength + 1,
+ S.getASTContext().getCanonicalTagType(Decl).getTypePtr()),
+ Ptr.getByteOffset());
+ return true;
+ }
+
+ if (!Ptr.isBlockPointer())
+ return false;
+ if (!Ptr.getFieldDesc()->isRecord())
+ return false;
+ Pointer Base = Ptr.stripBaseCasts();
+ const Record::Base *VirtBase = Base.getRecord()->findVirtualBase(Decl);
+ if (!VirtBase)
+ return false;
+ S.Stk.push<Pointer>(Base.atField(VirtBase->Offset));
+ return true;
+}
+
// FIXME: Would be nice to generate this instead of hardcoding it here.
[[maybe_unused]] static constexpr bool OpReturns(Opcode Op) {
return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 61118d77b7ac2..343883872728b 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1494,15 +1494,30 @@ bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) {
const T &LHS = S.Stk.pop<T>();
const Pointer &P = S.Stk.peek<Pointer>();
- ComparisonCategoryResult CmpResult = LHS.compare(RHS);
+ ComparisonCategoryResult CmpResult;
if constexpr (std::is_same_v<T, Pointer>) {
- if (CmpResult == ComparisonCategoryResult::Unordered) {
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
+ if (!Pointer::hasSameBase(LHS, RHS)) {
+ S.FFDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_pointer_comparison_unspecified)
<< LHS.toDiagnosticString(S.getASTContext())
<< RHS.toDiagnosticString(S.getASTContext());
return false;
}
+ std::optional<size_t> LHSOffset =
+ LHS.computeLayoutOffset(S.getASTContext());
+ std::optional<size_t> RHSOffset =
+ RHS.computeLayoutOffset(S.getASTContext());
+ if (!LHSOffset || !RHSOffset)
+ return false;
+
+ if (LHSOffset < RHSOffset)
+ CmpResult = ComparisonCategoryResult::Less;
+ else if (LHSOffset > RHSOffset)
+ CmpResult = ComparisonCategoryResult::Greater;
+ else
+ CmpResult = ComparisonCategoryResult::Equal;
+ } else {
+ CmpResult = LHS.compare(RHS);
}
assert(CmpInfo);
@@ -1673,6 +1688,9 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
if (!CheckRange(S, OpPC, Obj, CSK_Field))
return false;
+ if (!Obj.isBlockPointer())
+ return false;
+
// FIXME(postswitch): The isUnknownSizeArray() check here is only needed
// to keep an invalid sample producing the same diagnostics as the current
// interpreter.
@@ -1696,6 +1714,9 @@ bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
if (!CheckRange(S, OpPC, Obj, CSK_Field))
return false;
+ if (!Obj.isBlockPointer())
+ return false;
+
// FIXME(postswitch): The isUnknownSizeArray() check here is...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/222897
More information about the cfe-commits
mailing list