================
@@ -4860,6 +4924,404 @@ static CompleteObject findCompleteObject(EvalInfo
&Info, const Expr *E,
return CompleteObject(LVal.getLValueBase(), BaseVal, BaseType);
}
+static const APValue *GetArrayInitializedElt(const APValue &Array,
+ uint64_t Index) {
+ if (!Array.isArray() || Index >= Array.getArraySize())
+ return nullptr;
+ if (Index < Array.getArrayInitializedElts())
+ return &Array.getArrayInitializedElt(Index);
+ return Array.hasArrayFiller() ? &Array.getArrayFiller() : nullptr;
+}
+
+namespace {
+
+/// Combine a per-field result into an aggregate result. Models the lattice
+/// Equal < Unknown < NotEqual, where NotEqual absorbs and Equal is the
+/// identity. Callers may short-circuit on NotEqual.
+APValueEquality joinAPValueEquality(APValueEquality A, APValueEquality B) {
+ if (A == APValueEquality::NotEqual || B == APValueEquality::NotEqual)
+ return APValueEquality::NotEqual;
+ if (A == APValueEquality::Unknown || B == APValueEquality::Unknown)
+ return APValueEquality::Unknown;
+ return APValueEquality::Equal;
+}
+
+APValueEquality fromBoolEquality(bool Eq) {
+ return Eq ? APValueEquality::Equal : APValueEquality::NotEqual;
+}
+
+/// Return true if \p B names a program entity whose address is guaranteed
+/// distinct from any unrelated entity's address.
+bool isUniquelyAddressedBase(APValue::LValueBase B) {
+ if (const auto *VD = B.dyn_cast<const ValueDecl *>()) {
+ if (const auto *Var = dyn_cast<VarDecl>(VD))
+ return Var->hasGlobalStorage() && !Var->isWeak();
+ if (const auto *Fn = dyn_cast<FunctionDecl>(VD))
+ return !Fn->isWeak();
+ // Each LifetimeExtendedTemporaryDecl names a distinct storage instance.
+ if (isa<LifetimeExtendedTemporaryDecl>(VD))
+ return true;
+ return false;
+ }
+ // typeid objects and dynamic allocations are uniquely addressed in the
+ // abstract machine.
+ return B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>();
+}
+
+APValueEquality compareAddrLabelDiff(const APValue &LHS, const APValue &RHS) {
+ if (!LHS.isAddrLabelDiff() || !RHS.isAddrLabelDiff())
+ return APValueEquality::Unknown;
+ // &&label values are not constant - their runtime difference is only
+ // determined if both endpoints refer to the same labels.
+ if (LHS.getAddrLabelDiffLHS() == RHS.getAddrLabelDiffLHS() &&
+ LHS.getAddrLabelDiffRHS() == RHS.getAddrLabelDiffRHS())
+ return APValueEquality::Equal;
+ return APValueEquality::Unknown;
+}
+
+APValueEquality compareLValues(const APValue &LHS, const APValue &RHS,
+ const ASTContext &Ctx) {
+ if (!LHS.isLValue() || !RHS.isLValue())
+ return APValueEquality::Unknown;
+
+ bool LHSIsNull = LHS.isNullPointer();
+ bool RHSIsNull = RHS.isNullPointer();
+ if (LHSIsNull != RHSIsNull)
+ return APValueEquality::NotEqual;
+ if (LHSIsNull)
+ return APValueEquality::Equal;
+
+ APValue::LValueBase LB = LHS.getLValueBase();
+ APValue::LValueBase RB = RHS.getLValueBase();
+
+ if (LB == RB) {
+ // Same program entity, same evaluation instance. The address is fully
+ // determined by the byte offset within the object. One-past-the-end
+ // pointers, however, may compare equal to the start of an adjacent
+ // object, so we can't claim either Equal or NotEqual when only one
+ // side is one-past-the-end.
+ if (LHS.isLValueOnePastTheEnd() != RHS.isLValueOnePastTheEnd())
+ return APValueEquality::Unknown;
+ return fromBoolEquality(LHS.getLValueOffset() == RHS.getLValueOffset());
+ }
+
+ if (std::optional<bool> PotentiallyOverlapping =
+ ArePotentiallyOverlappingStringLiterals(Ctx, LHS, RHS))
+ return *PotentiallyOverlapping ? APValueEquality::Unknown
+ : APValueEquality::NotEqual;
+
+ // Distinct, uniquely-addressed entities have distinct addresses - unless
+ // one of the operands is one-past-the-end, which is permitted to alias
+ // the start of another object.
+ if (isUniquelyAddressedBase(LB) && isUniquelyAddressedBase(RB) &&
+ !LHS.isLValueOnePastTheEnd() && !RHS.isLValueOnePastTheEnd())
+ return APValueEquality::NotEqual;
+
+ return APValueEquality::Unknown;
+}
+
+APValueEquality compareMemberPointers(const APValue &LHS, const APValue &RHS) {
+ if (!LHS.isMemberPointer() || !RHS.isMemberPointer())
+ return APValueEquality::Unknown;
+
+ const ValueDecl *LD = LHS.getMemberPointerDecl();
+ const ValueDecl *RD = RHS.getMemberPointerDecl();
+ if (!LD != !RD)
+ return APValueEquality::NotEqual;
+ if (!LD)
+ return APValueEquality::Equal;
+ if (LD->getCanonicalDecl() != RD->getCanonicalDecl()) {
+ // Distinct weak decls may resolve to the same merged target at link time.
+ if (LD->isWeak() || RD->isWeak())
+ return APValueEquality::Unknown;
+ return APValueEquality::NotEqual;
+ }
+
+ // A member pointer's value is fully determined by its target decl plus
+ // the path of derived-to-base / base-to-derived adjustments.
+ ArrayRef<const CXXRecordDecl *> LP = LHS.getMemberPointerPath();
+ ArrayRef<const CXXRecordDecl *> RP = RHS.getMemberPointerPath();
+ if (LP.size() != RP.size())
+ return APValueEquality::NotEqual;
+ for (auto [LE, RE] : llvm::zip_equal(LP, RP))
+ if (LE->getCanonicalDecl() != RE->getCanonicalDecl())
+ return APValueEquality::NotEqual;
+ return APValueEquality::Equal;
+}
+
+APValueEquality compareArrays(const ArrayType *AT, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx);
+APValueEquality compareRecords(const RecordDecl *RD, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx);
+APValueEquality compareComplex(const APValue &LHS, const APValue &RHS);
+APValueEquality compareVector(const APValue &LHS, const APValue &RHS,
+ QualType EltTy, const ASTContext &Ctx);
+APValueEquality compareMatrix(const MatrixType *MT, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx);
+
+} // namespace
+
+APValueEquality compareAPValues(QualType T, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx) {
+ // The wrapped value is what's observable through an atomic type.
+ if (const auto *AT = T->getAs<AtomicType>())
+ T = AT->getValueType();
+
+ T = T.getCanonicalType();
+
+ // Volatile objects have implementation-defined reads, so even if both
+ // APValues currently hold the same bits we cannot prove the runtime
+ // values would be observably equal.
+ if (T.isVolatileQualified())
+ return APValueEquality::Unknown;
+
+ // A value flagged as "constexpr-unknown" carries no determinate content.
+ if (LHS.allowConstexprUnknown() || RHS.allowConstexprUnknown())
+ return APValueEquality::Unknown;
+
+ if (!LHS.hasValue() || !RHS.hasValue())
+ return APValueEquality::Unknown;
+
+ // std::nullptr_t has exactly one value, so any two operands are equal.
+ if (T->isNullPtrType())
+ return APValueEquality::Equal;
+
+ // Compound and pointer-like types - dispatch before checking kinds because
+ // null pointers and member pointers have distinct APValue representations.
+ if (T->isReferenceType() || T->isPointerType())
+ return compareLValues(LHS, RHS, Ctx);
+ if (T->isMemberPointerType())
+ return compareMemberPointers(LHS, RHS);
+ if (const ArrayType *AT = Ctx.getAsArrayType(T))
+ return compareArrays(AT, LHS, RHS, Ctx);
+ if (const RecordDecl *RD = T->getAsRecordDecl())
+ return compareRecords(RD, LHS, RHS, Ctx);
+ if (T->isAnyComplexType())
+ return compareComplex(LHS, RHS);
+ if (const auto *VT = T->getAs<VectorType>())
+ return compareVector(LHS, RHS, VT->getElementType(), Ctx);
+ if (const auto *MT = T->getAs<MatrixType>())
+ return compareMatrix(MT, LHS, RHS, Ctx);
+
+ // Scalar leaf: both sides must have the same kind.
+ if (LHS.getKind() != RHS.getKind())
+ return APValueEquality::NotEqual;
+
+ if (LHS.isInt())
+ return fromBoolEquality(LHS.getInt() == RHS.getInt());
+
+ // Bitwise comparison: -0.0 / +0.0 and NaN payloads are observable via
+ // signbit / memcmp, so the "indistinguishable" relation is bitwise.
+ if (LHS.isFloat())
+ return fromBoolEquality(LHS.getFloat().bitwiseIsEqual(RHS.getFloat()));
+
+ if (LHS.isFixedPoint())
+ return fromBoolEquality(LHS.getFixedPoint() == RHS.getFixedPoint());
+
+ // &&label values: the runtime address of a label isn't constant, so two
+ // diffs are only provably equal when they share both endpoints.
+ if (LHS.isAddrLabelDiff())
+ return compareAddrLabelDiff(LHS, RHS);
+
+ // Any APValue kind we don't explicitly model is conservatively unknown.
+ return APValueEquality::Unknown;
+}
+
+namespace {
+
+APValueEquality compareArrays(const ArrayType *AT, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx) {
+ if (!LHS.isArray() || !RHS.isArray())
+ return APValueEquality::Unknown;
+ if (LHS.getArraySize() != RHS.getArraySize())
+ return APValueEquality::NotEqual;
+
+ QualType ElementType = AT->getElementType();
+ APValueEquality Result = APValueEquality::Equal;
+ for (uint64_t I = 0, N = LHS.getArraySize(); I != N; ++I) {
+ const APValue *LHSElt = GetArrayInitializedElt(LHS, I);
+ const APValue *RHSElt = GetArrayInitializedElt(RHS, I);
+ if (!LHSElt || !RHSElt)
+ return APValueEquality::Unknown;
+ Result = joinAPValueEquality(
+ Result, compareAPValues(ElementType, *LHSElt, *RHSElt, Ctx));
+ if (Result == APValueEquality::NotEqual)
+ return Result;
+ }
+ return Result;
+}
+
+APValueEquality compareRecords(const RecordDecl *RD, const APValue &LHS,
+ const APValue &RHS, const ASTContext &Ctx) {
+ if (const RecordDecl *Definition = RD->getDefinition())
+ RD = Definition;
+
+ if (RD->isUnion()) {
+ if (!LHS.isUnion() || !RHS.isUnion())
+ return APValueEquality::Unknown;
----------------
cor3ntin wrote:
Can this happen?
https://github.com/llvm/llvm-project/pull/197458
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits