Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce bottom store to improve dataflow analysis #167

Open
wants to merge 27 commits into
base: master
Choose a base branch
from

Conversation

d367wang
Copy link

@d367wang d367wang commented Apr 1, 2021

The bottom store has the following properties:

  1. The LUB of a bottom store and a second store is the second store.
  2. A bottom store is immutable once it's created. i.e. It doesn't support insertion, replacement and other forms of updating.
  3. When getting value from a bottom store, it always returns the bottom value (consisted of bottom annotation and the underlying type).
  4. With property 2, we maintain one single instance of bottom store for each type system involved at runtime.

Copy link

@zcai1 zcai1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the changes LGTM! 👍

AnnotationMirror guardBy = ((LockAnnotatedTypeFactory) analysis.getTypeFactory()).GUARDEDBY;
AnnotationMirror lockHeld = ((LockAnnotatedTypeFactory) analysis.getTypeFactory()).LOCKHELD;

return analysis.createAbstractValue(new HashSet<>(Arrays.asList(lockHeld, guardBy)), type);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new HashSet<>(Arrays.asList(lockHeld, guardBy)
Can we replace it with ImmutableSet.of(...)? Or should we store these bottom annotations instead of creating a new set each time this method is invoked?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually there is a final field bottomAnnos in the these store classes that represents the set of bottom annotations. That field is initialized in the constructor with AnnotatedTypeFactory.getBottomAnnotations().

But LockStore is special. We can't use the real bottom annotations, so here the getBottomValue is overriden.

isPolyNullNonNull = false;
isPolyNullNull = false;
this.isPolyNullNull = false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why adding this. on line 49 while line 48 doesn't have this.?


// Is it necessary to consider `x = true ? y : z`
if (parentTree.getKind() == Tree.Kind.CONDITIONAL_EXPRESSION) {
return ((ConditionalExpressionTree) parentTree).getCondition() == tree;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the condition is wrapped by parentheses? For example, (x == true) ? y : z.

return ((ConditionalExpressionTree) parentTree).getCondition() == tree;
}

return parentTree.getKind() == Tree.Kind.IF
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic may work as expected, but I think it's safer to compare with parentTree.getCondition() so we know it indeed is a condition.

int @MinLen(10) [] res = arr;
int @MinLen(4) [] res2 = arr;
// :: error: (assignment.type.incompatible)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes make sense, but they also alter the meaning of this test. Should we write a new test case for the merging of @BottomVal and @MinLen(4)?

@@ -6,7 +6,6 @@ String testWhile1() {
String ans = "x";
while (true) {
if (true) {
// :: error: (return.type.incompatible)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here: we are removing all occurences of // :: error: (return.type.incompatible) in this file, and we need some new test cases to cover issue typetools#548.


class DeadBranch {

Object foo(@Nullable Object myObj, boolean x) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need boolean x?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out! I need to add another case using x as the condition.

@@ -23,10 +22,8 @@ public static void Bottom(int @BottomVal [] arg, int @MinLen(4) [] arg2) {
} else {
arr = arg2;
}
// :: error: (assignment.type.incompatible)
Copy link

@zcai1 zcai1 Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue here: I think we need to test LUB of @BottomVal and @MinLen(4), and LUB of @MinLen(10) and @MinLen(4).

@d367wang
Copy link
Author

d367wang commented Sep 29, 2021

Thank you for the comments @zcai1

public abstract S createBottomStore(boolean sequentialSemantics);

/**
* Get the singleton of the bottom store corresponding to the type.
Copy link
Author

@d367wang d367wang Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... corresponding to the type system

@Override
public S leastUpperBound(S other) {
if (this.isBottom) return other;
if (other != null && other.isBottom) return (S) this;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to test the nullness of other here? The dataflow code is nullness-type-safe guaranteed by NullnessChecker, isn't it?

@@ -27,6 +27,11 @@ public KeyForStore createEmptyStore(boolean sequentialSemantics) {
return new KeyForStore(this, sequentialSemantics);
}

@Override
public KeyForStore createBottomStore(boolean sequentialSemantics) {
return new KeyForStore(this, sequentialSemantics);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just return createEmptyStore(sequentialSemantics)?

Copy link
Author

@d367wang d367wang Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return new KeyForStore(this, sequentialSemantics, true);

"contracts.conditional.postcondition.not.satisfied" // TODO: `if` needs the
// BOTH_TO_THEN treatment that ?: gets.
)
@SuppressWarnings("contracts.conditional.postcondition.not.satisfied")
Copy link
Author

@d367wang d367wang Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment for reason for suppressWarning

add another case return true in if and return false in else

* @return ture if the given tree is a condition
*/
@SuppressWarnings("interning:not.interned")
boolean isCondition(ExpressionTree tree) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private

* @param type the input {@link TypeMirror}
* @return the bottom value
*/
protected V getBottomValue(TypeMirror type) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe createBottomValue?

zcai1 pushed a commit to zcai1/checker-framework that referenced this pull request Feb 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants