This is an archive of the discontinued LLVM Phabricator instance.

[Analyzer][solver] Handle adjustments in constraint assignor remainder
ClosedPublic

Authored by martong on Oct 22 2021, 1:18 AM.

Details

Summary

We can reuse the "adjustment" handling logic in the higher level
of the solver by calling assumeSymRel.

(Actually, this shows us that there is no point in separating the
RangeConstraintManager from the RangedConstraintManager, that
separation is in fact artificial. A follow-up NFC patch might
address this.)

Diff Detail

Event Timeline

martong created this revision.Oct 22 2021, 1:18 AM
martong requested review of this revision.Oct 22 2021, 1:18 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 22 2021, 1:18 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
This revision is now accepted and ready to land.Oct 25 2021, 2:09 AM
ASDenysPetrov added inline comments.Oct 25 2021, 4:45 AM
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

What I see, you're still trying to avoid using State->assume, which I recommend in a parent revision, but coming closer using its guts.

martong marked an inline comment as done.Oct 25 2021, 7:12 AM
martong added inline comments.
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

So, it would look like this:

State = State->assume(Builder.makeSymbolVal(LHS).castAs<nonloc::SymbolVal>(), true);

The main reason why we cannot use State->assume is that it boils down to RangedConstraintManager::assumeSym that has a specific logic for the boolean assumption. I.e. the operator is being negated in a case:

if (BinaryOperator::isComparisonOp(op) && op != BO_Cmp) {
  if (!Assumption)
    op = BinaryOperator::negateComparisonOp(op);

  return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS());
}

You can try it for yourself, and see that the test case added in this patch will not pass if we were to use State->assume. Essentially, we have to evade the special "bool" logic, and the closest we can get is using assumeSymRel.

Besides that, by using State->assume we would have a superfluous conversion chain Symbol->SVal->Symbol until we reach assumeSymRel.

ASDenysPetrov added inline comments.Oct 25 2021, 12:01 PM
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

You can try it for yourself, and see that the test case added in this patch will not pass if we were to use State->assume.

I can't confirm. Your test case passed when I replaced with State = State->assume(Builder.makeSymbolVal(LHS).castAs<nonloc::SymbolVal>(), true);.

specific logic for the boolean assumption. I.e. the operator is being negated in a case:

That just simplifies the expression, say, you want to find whether x > 5 is false, than the Solver finds for you whether
x <= 5 is true, which is an equivalent.

Essentially, we have to evade the special "bool" logic

There is no problem with bool logic. It's an equivalent of SVal != 0 when true, and SVal == 0 when false. Nothing more.

All in all I see the problem to use assume. Not because of this function itself, but because you do it incorrect by getting an SVal from LHS with makeSymbolVal. We should get it with State->getSVal which needs LocationContext as a second parameter. And that's the challenge, to pass LocationContext here, since RangedConstraintManager doesn't use it, at least for now.

martong marked an inline comment as done.Oct 25 2021, 2:33 PM
martong added inline comments.
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

I can't confirm. Your test case passed when I replaced with State = State->assume(Builder.makeSymbolVal(LHS).castAs<nonloc::SymbolVal>(), true);.

Actually, since the last time I tried with State->assume we merged D110913. If you revert the changes of that patch you'll see that this test case indeed fails. However, here is a slightly modified case that fails even on the current llvm/main branch with State->assume but passes with assumeSymRel:

void rem_constant_adj(int x, int y) {
  if ((x + y + 1) % 3 == 0) // (x + y + 1) % 3 != 0 -> x + y + 1 != 0 -> x != -1
    return;
  clang_analyzer_eval(x + y + 1 != 0); // expected-warning{{TRUE}}
  clang_analyzer_eval(x + y != -1);    // expected-warning{{TRUE}}
  (void)(x * y); // keep the constraints alive.
}

The only change is that we have x + y instead of x.

Now, the explanation for the original test case when we have (x + 1) % 3: When we ask the value of x != -1 then assumeDual evaluates the TRUE case which is feasible and then it tries to evaluate the FALSE case, so it queries x == -1 is true. However, this kicks in the simplification, which simplifies the previous constraint of x+1, [not 0] to -1 + 1, [not 0] which is a contradiction, thus an infeasible state is returned.
When we have x + y in the test case, then simplification cannot simplify x + y + 1, thus the situation is different.

So, the main problem with State->assume is that it does not compute the adjustment. I.e. when we have x + 1 as LHS then assumeSym(LHS) calls into assumeSymUnsupported and that does not compute the adjustment. The only functions that compute the adjustment are assumeSymRel and assumeSymInclusiveRange.

martong added inline comments.Oct 25 2021, 2:44 PM
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

BTW, if D103317 was merged, then I'd have to find another test case that fails here, but I think I could find one. Anyway, the point is that we need a function here, that handles the adjustments.

ASDenysPetrov added inline comments.Oct 25 2021, 3:20 PM
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

So, the main problem with State->assume is that it does not compute the adjustment. I.e. when we have x + 1 as LHS then assumeSym(LHS) calls into assumeSymUnsupported and that does not compute the adjustment. The only functions that compute the adjustment are assumeSymRel and assumeSymInclusiveRange.

Wow, I've just seen a possible solution in the game of words. If assumeSym(LHS) leads to assumeSymUnsupported, then we just need to support it, namely, make it go to assumeSymRel. IMO it's worth to investigate such a chance.

steakhal added inline comments.Oct 26 2021, 1:59 AM
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

+1, Thank you for pushing this @ASDenysPetrov

martong marked 2 inline comments as done.Oct 26 2021, 9:13 AM
martong added inline comments.
clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
1618–1619

So, the main problem with State->assume is that it does not compute the adjustment. I.e. when we have x + 1 as LHS then assumeSym(LHS) calls into assumeSymUnsupported and that does not compute the adjustment. The only functions that compute the adjustment are assumeSymRel and assumeSymInclusiveRange.

Wow, I've just seen a possible solution in the game of words. If assumeSym(LHS) leads to assumeSymUnsupported, then we just need to support it, namely, make it go to assumeSymRel. IMO it's worth to investigate such a chance.

Okay, that could be a good alternative, good idea!

martong updated this revision to Diff 382362.Oct 26 2021, 9:14 AM
  • Handle adjustment in assumeSym for non-comparison ops.
martong updated this revision to Diff 382367.Oct 26 2021, 9:22 AM
  • Remove superflous change.
ASDenysPetrov added inline comments.Oct 26 2021, 9:32 AM
clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
46–49
52
ASDenysPetrov requested changes to this revision.Oct 26 2021, 9:48 AM
ASDenysPetrov added inline comments.
clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
44–50

Actually what you are trying to do here is already inside assumeSymUnsupported and it will work for all SymIntExpr.
Please, proveide a test case which works with assumeSymRel and doesn't with asume from the previous change. That is what we are trying to fix here.

This revision now requires changes to proceed.Oct 26 2021, 9:48 AM
martong marked 4 inline comments as done.Oct 27 2021, 2:54 AM
martong added inline comments.
clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
44–50

Thanks for suggesting the use of the ternary op, the code is way more elegant that way!

Actually what you are trying to do here is already inside assumeSymUnsupported and it will work for all SymIntExpr.

I see your point and I acknowledge that this new code resembles to the one in assumeSymUnsupported. However, there is a subtle but very important difference. In assumeSymUnsupported we use assumeSymNE/EQ, but here I use assumeSymRel. And assumeSymRel does compute the adjustment, but assumeSymNE/EQ do not. Also, it makes sense to compute the adjustment only in case of SymIntExprs, thus this could not be done in assumeSymUnsupported.

Please, proveide a test case which works with assumeSymRel and doesn't with asume from the previous change. That is what we are trying to fix here.

You are right, it desires a test case, so I just added the one I had pasted here previously (with (x + y + 1) % 3). If you comment-out the modifications here in assumSym then that test case will fail.

46–49
52

Yep, good point.

martong updated this revision to Diff 382581.Oct 27 2021, 2:54 AM
martong marked 3 inline comments as done.
  • Add new test case
  • Adapt style changes
ASDenysPetrov accepted this revision.Oct 27 2021, 3:51 AM
ASDenysPetrov added inline comments.
clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
44–50

However, there is a subtle but very important difference.

Now I see the difference. OK, then. Your solution is sensible.

This revision is now accepted and ready to land.Oct 27 2021, 3:51 AM
steakhal accepted this revision.Oct 27 2021, 4:17 AM
steakhal added inline comments.
clang/test/Analysis/constraint-assignor.c
7

Expect an int parameter.

martong marked an inline comment as done.Oct 27 2021, 8:14 AM

@ASDenysPetrov @steakhal Thanks for the assiduous reviews!

This revision was landed with ongoing or failed builds.Oct 27 2021, 8:15 AM
This revision was automatically updated to reflect the committed changes.