From: Jordy Rose Date: Mon, 28 Jun 2010 08:26:15 +0000 (+0000) Subject: Pointer comparisons (and pointer-pointer subtraction). Basically filling in SimpleSVa... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=eac4a00e1d93aa963903031ed76425c231f0f0b9;p=clang Pointer comparisons (and pointer-pointer subtraction). Basically filling in SimpleSValuator::EvalBinOpLL(). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106992 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Checker/PathSensitive/SValuator.h b/include/clang/Checker/PathSensitive/SValuator.h index 9beb8cb086..5e3ba04a20 100644 --- a/include/clang/Checker/PathSensitive/SValuator.h +++ b/include/clang/Checker/PathSensitive/SValuator.h @@ -47,8 +47,8 @@ public: virtual SVal EvalBinOpNN(const GRState *state, BinaryOperator::Opcode Op, NonLoc lhs, NonLoc rhs, QualType resultTy) = 0; - virtual SVal EvalBinOpLL(BinaryOperator::Opcode Op, Loc lhs, Loc rhs, - QualType resultTy) = 0; + virtual SVal EvalBinOpLL(const GRState *state, BinaryOperator::Opcode Op, + Loc lhs, Loc rhs, QualType resultTy) = 0; virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode Op, Loc lhs, NonLoc rhs, QualType resultTy) = 0; diff --git a/lib/Checker/RegionStore.cpp b/lib/Checker/RegionStore.cpp index 6b5e5e33f2..336d3925ad 100644 --- a/lib/Checker/RegionStore.cpp +++ b/lib/Checker/RegionStore.cpp @@ -867,6 +867,19 @@ SVal RegionStoreManager::EvalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, if (!isa(L)) return UnknownVal(); + // Special case for zero RHS. + if (R.isZeroConstant()) { + switch (Op) { + default: + // Handle it normally. + break; + case BinaryOperator::Add: + case BinaryOperator::Sub: + // FIXME: does this need to be casted to match resultTy? + return L; + } + } + const MemRegion* MR = cast(L).getRegion(); const ElementRegion *ER = 0; diff --git a/lib/Checker/SValuator.cpp b/lib/Checker/SValuator.cpp index 542fc1b107..a7e15fc3fd 100644 --- a/lib/Checker/SValuator.cpp +++ b/lib/Checker/SValuator.cpp @@ -29,15 +29,15 @@ SVal SValuator::EvalBinOp(const GRState *ST, BinaryOperator::Opcode Op, if (isa(L)) { if (isa(R)) - return EvalBinOpLL(Op, cast(L), cast(R), T); + return EvalBinOpLL(ST, Op, cast(L), cast(R), T); return EvalBinOpLN(ST, Op, cast(L), cast(R), T); } if (isa(R)) { - // Support pointer arithmetic where the increment/decrement operand - // is on the left and the pointer on the right. - assert(Op == BinaryOperator::Add || Op == BinaryOperator::Sub); + // Support pointer arithmetic where the addend is on the left + // and the pointer on the right. + assert(Op == BinaryOperator::Add); // Commute the operands. return EvalBinOpLN(ST, Op, cast(R), cast(L), T); diff --git a/lib/Checker/SimpleSValuator.cpp b/lib/Checker/SimpleSValuator.cpp index bf539defd4..0f4fe07bb7 100644 --- a/lib/Checker/SimpleSValuator.cpp +++ b/lib/Checker/SimpleSValuator.cpp @@ -30,8 +30,8 @@ public: virtual SVal EvalComplement(NonLoc val); virtual SVal EvalBinOpNN(const GRState *state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy); - virtual SVal EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, - QualType resultTy); + virtual SVal EvalBinOpLL(const GRState *state, BinaryOperator::Opcode op, + Loc lhs, Loc rhs, QualType resultTy); virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy); @@ -173,45 +173,18 @@ static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { } } -// Equality operators for Locs. -// FIXME: All this logic will be revamped when we have MemRegion::getLocation() -// implemented. - -static SVal EvalEquality(ValueManager &ValMgr, Loc lhs, Loc rhs, bool isEqual, - QualType resultTy) { - - switch (lhs.getSubKind()) { - default: - assert(false && "EQ/NE not implemented for this Loc."); - return UnknownVal(); - - case loc::ConcreteIntKind: { - if (SymbolRef rSym = rhs.getAsSymbol()) - return ValMgr.makeNonLoc(rSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast(lhs).getValue(), - resultTy); - break; - } - case loc::MemRegionKind: { - if (SymbolRef lSym = lhs.getAsLocSymbol()) { - if (isa(rhs)) { - return ValMgr.makeNonLoc(lSym, - isEqual ? BinaryOperator::EQ - : BinaryOperator::NE, - cast(rhs).getValue(), - resultTy); - } - } - break; - } - - case loc::GotoLabelKind: - break; +static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { + switch (op) { + default: + assert(false && "Invalid opcode."); + case BinaryOperator::LT: return BinaryOperator::GT; + case BinaryOperator::GT: return BinaryOperator::LT; + case BinaryOperator::LE: return BinaryOperator::GE; + case BinaryOperator::GE: return BinaryOperator::LE; + case BinaryOperator::EQ: + case BinaryOperator::NE: + return op; } - - return ValMgr.makeTruthVal(isEqual ? lhs == rhs : lhs != rhs, resultTy); } SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS, @@ -322,7 +295,8 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, Loc lhsL = cast(lhs).getLoc(); switch (rhs.getSubKind()) { case nonloc::LocAsIntegerKind: - return EvalBinOpLL(op, lhsL, cast(rhs).getLoc(), + return EvalBinOpLL(state, op, lhsL, + cast(rhs).getLoc(), resultTy); case nonloc::ConcreteIntKind: { // Transform the integer into a location and compare. @@ -330,7 +304,7 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, llvm::APSInt i = cast(rhs).getValue(); i.setIsUnsigned(true); i.extOrTrunc(Ctx.getTypeSize(Ctx.VoidPtrTy)); - return EvalBinOpLL(op, lhsL, ValMgr.makeLoc(i), resultTy); + return EvalBinOpLL(state, op, lhsL, ValMgr.makeLoc(i), resultTy); } default: switch (op) { @@ -451,10 +425,12 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, lhs = tmp; switch (op) { - case BinaryOperator::LT: op = BinaryOperator::GT; continue; - case BinaryOperator::GT: op = BinaryOperator::LT; continue; - case BinaryOperator::LE: op = BinaryOperator::GE; continue; - case BinaryOperator::GE: op = BinaryOperator::LE; continue; + case BinaryOperator::LT: + case BinaryOperator::GT: + case BinaryOperator::LE: + case BinaryOperator::GE: + op = ReverseComparison(op); + continue; case BinaryOperator::EQ: case BinaryOperator::NE: case BinaryOperator::Add: @@ -519,21 +495,296 @@ SVal SimpleSValuator::EvalBinOpNN(const GRState *state, } } -SVal SimpleSValuator::EvalBinOpLL(BinaryOperator::Opcode op, Loc lhs, Loc rhs, +// FIXME: all this logic will change if/when we have MemRegion::getLocation(). +SVal SimpleSValuator::EvalBinOpLL(const GRState *state, + BinaryOperator::Opcode op, + Loc lhs, Loc rhs, QualType resultTy) { - switch (op) { + // Only comparisons and subtractions are valid operations on two pointers. + // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15]. + assert(BinaryOperator::isComparisonOp(op) || op == BinaryOperator::Sub); + + // Special cases for when both sides are identical. + if (lhs == rhs) { + switch (op) { default: + assert(false && "Unimplemented operation for two identical values"); return UnknownVal(); + case BinaryOperator::Sub: + return ValMgr.makeZeroVal(resultTy); case BinaryOperator::EQ: + case BinaryOperator::LE: + case BinaryOperator::GE: + return ValMgr.makeTruthVal(true, resultTy); case BinaryOperator::NE: - return EvalEquality(ValMgr, lhs, rhs, op == BinaryOperator::EQ, resultTy); case BinaryOperator::LT: case BinaryOperator::GT: - // FIXME: Generalize. For now, just handle the trivial case where - // the two locations are identical. - if (lhs == rhs) + return ValMgr.makeTruthVal(false, resultTy); + } + } + + switch (lhs.getSubKind()) { + default: + assert(false && "Ordering not implemented for this Loc."); + return UnknownVal(); + + case loc::GotoLabelKind: + // The only thing we know about labels is that they're non-null. + if (rhs.isZeroConstant()) { + switch (op) { + default: + break; + case BinaryOperator::Sub: + return EvalCastL(lhs, resultTy); + case BinaryOperator::EQ: + case BinaryOperator::LE: + case BinaryOperator::LT: + return ValMgr.makeTruthVal(false, resultTy); + case BinaryOperator::NE: + case BinaryOperator::GT: + case BinaryOperator::GE: + return ValMgr.makeTruthVal(true, resultTy); + } + } + // There may be two labels for the same location, and a function region may + // have the same address as a label at the start of the function (depending + // on the ABI). + // FIXME: we can probably do a comparison against other MemRegions, though. + // FIXME: is there a way to tell if two labels refer to the same location? + return UnknownVal(); + + case loc::ConcreteIntKind: { + // If one of the operands is a symbol and the other is a constant, + // build an expression for use by the constraint manager. + if (SymbolRef rSym = rhs.getAsLocSymbol()) { + // We can only build expressions with symbols on the left, + // so we need a reversible operator. + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + + const llvm::APSInt &lVal = cast(lhs).getValue(); + return ValMgr.makeNonLoc(rSym, ReverseComparison(op), lVal, resultTy); + } + + // If both operands are constants, just perform the operation. + if (loc::ConcreteInt *rInt = dyn_cast(&rhs)) { + BasicValueFactory &BVF = ValMgr.getBasicValueFactory(); + SVal ResultVal = cast(lhs).EvalBinOp(BVF, op, *rInt); + if (Loc *Result = dyn_cast(&ResultVal)) + return EvalCastL(*Result, resultTy); + else + return UnknownVal(); + } + + // Special case comparisons against NULL. + // This must come after the test if the RHS is a symbol, which is used to + // build constraints. The address of any non-symbolic region is guaranteed + // to be non-NULL, as is any label. + assert(isa(rhs) || isa(rhs)); + if (lhs.isZeroConstant()) { + switch (op) { + default: + break; + case BinaryOperator::EQ: + case BinaryOperator::GT: + case BinaryOperator::GE: + return ValMgr.makeTruthVal(false, resultTy); + case BinaryOperator::NE: + case BinaryOperator::LT: + case BinaryOperator::LE: + return ValMgr.makeTruthVal(true, resultTy); + } + } + + // Comparing an arbitrary integer to a region or label address is + // completely unknowable. + return UnknownVal(); + } + case loc::MemRegionKind: { + if (loc::ConcreteInt *rInt = dyn_cast(&rhs)) { + // If one of the operands is a symbol and the other is a constant, + // build an expression for use by the constraint manager. + if (SymbolRef lSym = lhs.getAsLocSymbol()) + return MakeSymIntVal(lSym, op, rInt->getValue(), resultTy); + + // Special case comparisons to NULL. + // This must come after the test if the LHS is a symbol, which is used to + // build constraints. The address of any non-symbolic region is guaranteed + // to be non-NULL. + if (rInt->isZeroConstant()) { + switch (op) { + default: + break; + case BinaryOperator::Sub: + return EvalCastL(lhs, resultTy); + case BinaryOperator::EQ: + case BinaryOperator::LT: + case BinaryOperator::LE: + return ValMgr.makeTruthVal(false, resultTy); + case BinaryOperator::NE: + case BinaryOperator::GT: + case BinaryOperator::GE: + return ValMgr.makeTruthVal(true, resultTy); + } + } + + // Comparing a region to an arbitrary integer is completely unknowable. + return UnknownVal(); + } + + // Get both values as regions, if possible. + const MemRegion *LeftMR = lhs.getAsRegion(); + assert(LeftMR && "MemRegionKind SVal doesn't have a region!"); + + const MemRegion *RightMR = rhs.getAsRegion(); + if (!RightMR) + // The RHS is probably a label, which in theory could address a region. + // FIXME: we can probably make a more useful statement about non-code + // regions, though. + return UnknownVal(); + + // If both values wrap regions, see if they're from different base regions. + const MemRegion *LeftBase = LeftMR->getBaseRegion(); + const MemRegion *RightBase = RightMR->getBaseRegion(); + if (LeftBase != RightBase && + !isa(LeftBase) && !isa(RightBase)) { + switch (op) { + default: + return UnknownVal(); + case BinaryOperator::EQ: return ValMgr.makeTruthVal(false, resultTy); + case BinaryOperator::NE: + return ValMgr.makeTruthVal(true, resultTy); + } + } + + // The two regions are from the same base region. See if they're both a + // type of region we know how to compare. + + // FIXME: If/when there is a getAsRawOffset() for FieldRegions, this + // ElementRegion path and the FieldRegion path below should be unified. + if (const ElementRegion *LeftER = dyn_cast(LeftMR)) { + // First see if the right region is also an ElementRegion. + const ElementRegion *RightER = dyn_cast(RightMR); + if (!RightER) + return UnknownVal(); + + // Next, see if the two ERs have the same super-region and matching types. + // FIXME: This should do something useful even if the types don't match, + // though if both indexes are constant the RegionRawOffset path will + // give the correct answer. + if (LeftER->getSuperRegion() == RightER->getSuperRegion() && + LeftER->getElementType() == RightER->getElementType()) { + // Get the left index and cast it to the correct type. + // If the index is unknown or undefined, bail out here. + SVal LeftIndexVal = LeftER->getIndex(); + NonLoc *LeftIndex = dyn_cast(&LeftIndexVal); + if (!LeftIndex) + return UnknownVal(); + LeftIndexVal = EvalCastNL(*LeftIndex, resultTy); + LeftIndex = dyn_cast(&LeftIndexVal); + if (!LeftIndex) + return UnknownVal(); + + // Do the same for the right index. + SVal RightIndexVal = RightER->getIndex(); + NonLoc *RightIndex = dyn_cast(&RightIndexVal); + if (!RightIndex) + return UnknownVal(); + RightIndexVal = EvalCastNL(*RightIndex, resultTy); + RightIndex = dyn_cast(&RightIndexVal); + if (!RightIndex) + return UnknownVal(); + + // Actually perform the operation. + // EvalBinOpNN expects the two indexes to already be the right type. + return EvalBinOpNN(state, op, *LeftIndex, *RightIndex, resultTy); + } + + // If the element indexes aren't comparable, see if the raw offsets are. + RegionRawOffset LeftOffset = LeftER->getAsRawOffset(); + RegionRawOffset RightOffset = RightER->getAsRawOffset(); + + if (LeftOffset.getRegion() != NULL && + LeftOffset.getRegion() == RightOffset.getRegion()) { + int64_t left = LeftOffset.getByteOffset(); + int64_t right = RightOffset.getByteOffset(); + + switch (op) { + default: + return UnknownVal(); + case BinaryOperator::LT: + return ValMgr.makeTruthVal(left < right, resultTy); + case BinaryOperator::GT: + return ValMgr.makeTruthVal(left > right, resultTy); + case BinaryOperator::LE: + return ValMgr.makeTruthVal(left <= right, resultTy); + case BinaryOperator::GE: + return ValMgr.makeTruthVal(left >= right, resultTy); + case BinaryOperator::EQ: + return ValMgr.makeTruthVal(left == right, resultTy); + case BinaryOperator::NE: + return ValMgr.makeTruthVal(left != right, resultTy); + } + } + + // If we get here, we have no way of comparing the ElementRegions. return UnknownVal(); + } + + // See if both regions are fields of the same structure. + // FIXME: This doesn't handle nesting, inheritance, or Objective-C ivars. + if (const FieldRegion *LeftFR = dyn_cast(LeftMR)) { + // Only comparisons are meaningful here! + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + + // First see if the right region is also a FieldRegion. + const FieldRegion *RightFR = dyn_cast(RightMR); + if (!RightFR) + return UnknownVal(); + + // Next, see if the two FRs have the same super-region. + // FIXME: This doesn't handle casts yet, and simply stripping the casts + // doesn't help. + if (LeftFR->getSuperRegion() != RightFR->getSuperRegion()) + return UnknownVal(); + + const FieldDecl *LeftFD = LeftFR->getDecl(); + const FieldDecl *RightFD = RightFR->getDecl(); + const RecordDecl *RD = LeftFD->getParent(); + + // Make sure the two FRs are from the same kind of record. Just in case! + // FIXME: This is probably where inheritance would be a problem. + if (RD != RightFD->getParent()) + return UnknownVal(); + + // We know for sure that the two fields are not the same, since that + // would have given us the same SVal. + if (op == BinaryOperator::EQ) + return ValMgr.makeTruthVal(false, resultTy); + if (op == BinaryOperator::NE) + return ValMgr.makeTruthVal(true, resultTy); + + // Iterate through the fields and see which one comes first. + // [C99 6.7.2.1.13] "Within a structure object, the non-bit-field + // members and the units in which bit-fields reside have addresses that + // increase in the order in which they are declared." + bool leftFirst = (op == BinaryOperator::LT || op == BinaryOperator::LE); + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I!=E; ++I) { + if (*I == LeftFD) + return ValMgr.makeTruthVal(leftFirst, resultTy); + if (*I == RightFD) + return ValMgr.makeTruthVal(!leftFirst, resultTy); + } + + assert(false && "Fields not found in parent record's definition"); + } + + // If we get here, we have no way of comparing the regions. + return UnknownVal(); + } } } @@ -545,7 +796,7 @@ SVal SimpleSValuator::EvalBinOpLN(const GRState *state, // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 // can generate comparisons that trigger this code. // FIXME: Are all locations guaranteed to have pointer width? - if (BinaryOperator::isEqualityOp(op)) { + if (BinaryOperator::isComparisonOp(op)) { if (nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs)) { const llvm::APSInt *x = &rhsInt->getValue(); ASTContext &ctx = ValMgr.getContext(); @@ -554,7 +805,7 @@ SVal SimpleSValuator::EvalBinOpLN(const GRState *state, if (x->isSigned()) x = &ValMgr.getBasicValueFactory().getValue(*x, true); - return EvalBinOpLL(op, lhs, loc::ConcreteInt(*x), resultTy); + return EvalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy); } } } diff --git a/test/Analysis/constant-folding.c b/test/Analysis/constant-folding.c index 5b0d80fd39..6ed2b390cf 100644 --- a/test/Analysis/constant-folding.c +++ b/test/Analysis/constant-folding.c @@ -59,3 +59,14 @@ void testAsymmetricIntSymOperations (int a) { if ((((unsigned)(~0)) >> ((unsigned) a)) != ((unsigned)(~0))) WARN; // expected-warning{{}} } + +void testLocations (char *a) { + char *b = a; + if (!(b==a)) WARN; + if (!(b>=a)) WARN; + if (!(b<=a)) WARN; + if (b!=a) WARN; + if (b>a) WARN; + if (b 0)) + WARN; // no-warning + if (!(&&start >= 0)) + WARN; // no-warning + if (!(&&start - 0)) + WARN; // no-warning + + // LHS is a non-symbolic value, RHS is NULL + if (&a == 0) + WARN; // no-warning + if (&a < 0) + WARN; // no-warning + if (&a <= 0) + WARN; // no-warning + if (!(&a != 0)) + WARN; // no-warning + if (!(&a > 0)) + WARN; // no-warning + if (!(&a >= 0)) + WARN; // no-warning + + if (!(&a - 0)) // expected-warning{{Pointer arithmetic done on non-array variables}} + WARN; // no-warning + + // LHS is NULL, RHS is non-symbolic + // The same code is used for labels and non-symbolic values. + if (0 == &a) + WARN; // no-warning + if (0 > &a) + WARN; // no-warning + if (0 >= &a) + WARN; // no-warning + if (!(0 != &a)) + WARN; // no-warning + if (!(0 < &a)) + WARN; // no-warning + if (!(0 <= &a)) + WARN; // no-warning + + // LHS is a symbolic value, RHS is NULL + if (a == 0) + WARN; // expected-warning{{}} + if (a < 0) + WARN; // no-warning + if (a <= 0) + WARN; // expected-warning{{}} + if (!(a != 0)) + WARN; // expected-warning{{}} + if (!(a > 0)) + WARN; // expected-warning{{}} + if (!(a >= 0)) + WARN; // no-warning + if (!(a - 0)) + WARN; // expected-warning{{}} + + // LHS is NULL, RHS is a symbolic value + if (0 == a) + WARN; // expected-warning{{}} + if (0 > a) + WARN; // no-warning + if (0 >= a) + WARN; // expected-warning{{}} + if (!(0 != a)) + WARN; // expected-warning{{}} + if (!(0 < a)) + WARN; // expected-warning{{}} + if (!(0 <= a)) + WARN; // no-warning +} + +void const_locs() { + char *a = (char*)0x1000; + char *b = (char*)0x1100; +start: + if (a==b) + WARN; // no-warning + if (!(a!=b)) + WARN; // no-warning + if (a>b) + WARN; // no-warning + if (b=b) + WARN; // no-warning + if (b<=a) + WARN; // no-warning + if (b-a != 0x100) + WARN; // no-warning + + if (&&start == a) + WARN; // expected-warning{{}} + if (a == &&start) + WARN; // expected-warning{{}} + if (&a == (char**)a) + WARN; // expected-warning{{}} + if ((char**)a == &a) + WARN; // expected-warning{{}} +} + +void array_matching_types() { + int array[10]; + int *a = &array[2]; + int *b = &array[5]; + + if (a==b) + WARN; // no-warning + if (!(a!=b)) + WARN; // no-warning + if (a>b) + WARN; // no-warning + if (b=b) + WARN; // no-warning + if (b<=a) + WARN; // no-warning + if ((b-a) == 0) + WARN; // no-warning +} + +// This takes a different code path than array_matching_types() +void array_different_types() { + int array[10]; + int *a = &array[2]; + char *b = (char*)&array[5]; + + if (a==b) // expected-warning{{comparison of distinct pointer types}} + WARN; // no-warning + if (!(a!=b)) // expected-warning{{comparison of distinct pointer types}} + WARN; // no-warning + if (a>b) // expected-warning{{comparison of distinct pointer types}} + WARN; // no-warning + if (b=b) // expected-warning{{comparison of distinct pointer types}} + WARN; // no-warning + if (b<=a) // expected-warning{{comparison of distinct pointer types}} + WARN; // no-warning +} + +struct test { int x; int y; }; +void struct_fields() { + struct test a, b; + + if (&a.x == &a.y) + WARN; // no-warning + if (!(&a.x != &a.y)) + WARN; // no-warning + if (&a.x > &a.y) + WARN; // no-warning + if (&a.y < &a.x) + WARN; // no-warning + if (&a.x >= &a.y) + WARN; // no-warning + if (&a.y <= &a.x) + WARN; // no-warning + + if (&a.x == &b.x) + WARN; // no-warning + if (!(&a.x != &b.x)) + WARN; // no-warning + if (&a.x > &b.x) + WARN; // expected-warning{{}} + if (&b.x < &a.x) + WARN; // expected-warning{{}} + if (&a.x >= &b.x) + WARN; // expected-warning{{}} + if (&b.x <= &a.x) + WARN; // expected-warning{{}} +} + +void mixed_region_types() { + struct test s; + int array[2]; + void *a = &array, *b = &s; + + if (&a == &b) + WARN; // no-warning + if (!(&a != &b)) + WARN; // no-warning + if (&a > &b) + WARN; // expected-warning{{}} + if (&b < &a) + WARN; // expected-warning{{}} + if (&a >= &b) + WARN; // expected-warning{{}} + if (&b <= &a) + WARN; // expected-warning{{}} +} + +void symbolic_region(int *p) { + int a; + + if (&a == p) + WARN; // expected-warning{{}} + if (&a != p) + WARN; // expected-warning{{}} + if (&a > p) + WARN; // expected-warning{{}} + if (&a < p) + WARN; // expected-warning{{}} + if (&a >= p) + WARN; // expected-warning{{}} + if (&a <= p) + WARN; // expected-warning{{}} +}