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

First Phase of RLC inference #5870

Merged
merged 149 commits into from
Oct 26, 2023
Merged

Conversation

Nargeshdb
Copy link
Contributor

@Nargeshdb Nargeshdb commented May 15, 2023

This PR infers:

  1. @Owning annotation for owning fields and parameters.
  2. @EnsuresCalledMethods for methods that dispose owning fields' obligation
  3. @InheritableMustCall on the class declaration

@Nargeshdb Nargeshdb requested a review from kelloggm May 15, 2023 17:08
Copy link
Member

@mernst mernst left a comment

Choose a reason for hiding this comment

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

CI is failing. Could you at least make the misc job succeed?

Copy link
Contributor

@kelloggm kelloggm left a comment

Choose a reason for hiding this comment

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

This review only provides high-level feedback on the interactions with the rest of the Checker Framework (i.e., I didn't review MustCallInferenceLogic).

Nargeshdb

This comment was marked as duplicate.

@kelloggm kelloggm self-requested a review May 30, 2023 16:35
@Nargeshdb Nargeshdb requested review from msridhar and removed request for kelloggm May 30, 2023 16:35
Copy link
Contributor

@kelloggm kelloggm left a comment

Choose a reason for hiding this comment

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

I did a full review of all code not in MustCallInferenceLogic, but I only reviewed the documentation of methods/fields in that class.

analysis
.checker
.getUpstreamCheckerNames()
.contains("org.checkerframework.checker.resourceleak.ResourceLeakChecker");
Copy link
Contributor

Choose a reason for hiding this comment

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

Putting this in CFAbstractTransfer seems like too much coupling, to me. If you're going to use this implementation strategy (which I'd prefer we replace; see my comment on the first use of the isResourceLeakCheckerEnabled above), this logic should be in some Resource Leak Checker class (maybe as a static method that takes a BaseTypeChecker?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This flag is used in MustCallTransfer and CalledMethodsTransfer, so I think it should be defined within these two checkers rather than in some Resource Leak Checker class, am I right?

@Nargeshdb Nargeshdb requested a review from kelloggm June 26, 2023 16:46
@@ -113,6 +113,9 @@ public abstract class CFAbstractTransfer<
/** Indicates that the whole-program inference is on. */
private final boolean infer;

/** Indicates that the resource leak checker is enabled. */
private final boolean enableWPIForRLC;
Copy link
Contributor

Choose a reason for hiding this comment

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

I still don't think CFAbstractTransfer is the appropriate place for this flag. I agree that it's the LUB of MustCallTransfer and CalledMethodsTransfer, but this class should only contain code that's germane to all CF checkers.

Maybe create an interface with this functionality instead that those two classes can implement?

Copy link
Contributor

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

This is looking great! I have one (hopefully last!) comment.

msridhar
msridhar previously approved these changes Oct 14, 2023
Copy link
Contributor

@msridhar msridhar left a comment

Choose a reason for hiding this comment

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

LGTM!

@mernst mernst assigned kelloggm and unassigned Nargeshdb Oct 14, 2023
docs/manual/resource-leak-checker.tex Outdated Show resolved Hide resolved
Comment on lines 434 to 438
if (getWholeProgramInference() != null
&& elt.getKind() == ElementKind.PARAMETER
&& getDeclAnnotation(elt, MustCallAlias.class) != null) {
type.replaceAnnotation(BOTTOM);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't appear that the comment that you promised here was actually added to the code.


/**
* Adds the node to the disposedFields set if it is a field and its must-call obligation is
* satisfied by the given method call. If so, it will be given an @Owning annotation later.
Copy link
Contributor

Choose a reason for hiding this comment

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

If so, it will be given an @Owning annotation later.

I don't think this is guaranteed to be true, based on the conversation related to disposedFields and its Javadoc, above. Please make sure this is consistent, and that the Javadoc does not make false claims.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think if we want to be consistent with the algorithm, when we see somewhere in the code, must-call obligation of a field is satisfied, then the field has to be owning. Since disposedFields does not monotonically increase, I think we need to add it to the owningField set too.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need to add it to the owningField set too

If you're saying that there is a bug in the implementation (which I think you are), then we should write a test case for this situation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was unable to reproduce a test case because the test infrastructure for WPI can only run for one iteration. I suspect that this issue arises when the @ECM annotation on the finalizer method is inferred during the second iteration.

{
  Foo f;

  void overwriteFoo() { 
     f.a();
     f = new Foo();
  }

  finalizer() {
     closeFoo(f);
  }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I was unable to reproduce a test case because the test infrastructure for WPI can only run for one iteration

This should not prevent you from writing a test case: if you want to test what happens on the second iteration, the input should have the annotations produced by the first iteration. This process works inductively, so it is possible to test WPI behavior on any iteration using the one-iteration test infrastructure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't able to reproduce this issue because in the test infrastructure, the final result (after inference) should be type checked. If a class has an owning field, it also needs to have a finalizer method m that disposes of the field and an @InheritableMustCall('m') annotation on the class declaration. This contradicts the scenario I described

Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't able to reproduce this issue because in the test infrastructure, the final result (after inference) should be type checked

It's possible to avoid this limitation in one of the following ways:

  • use an @SuppressWarnings annotation to ignore the other warning that you don't care about testing
  • use the same mechanism in the build.gradle file that is used by the ExpectedErrors.java test case for the AinferTestChecker: effectively, disable the removal of the expected warnings.

Of these two, I think in this situation, you should strongly prefer the former.

If you're not able to get this working, rather than a text-only response, can you include the full test case in your next comment on this thread so that I can try to get it working myself?

}

/**
* Computes @Owning annotations for the arguments or receiver of a call.
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it ever make sense for a receiver to be @Owning? I don't think so (and I don't think we considered this case in our formalism at all): receivers are an object of the class that's currently being considered, so I'm not sure what an "owning receiver" means - maybe that the method with that receiver should be a must-call method of the class?

Regardless, I'm concerned that we have code for it at all, since it doesn't make sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

@kelloggm this is referring to the actual parameter passed in the receiver position at a call site, not this inside a method. Does that help?

Copy link
Contributor

Choose a reason for hiding this comment

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

thanks for the clarification. Yes, this helps. I think we need to distinguish "receiver" (i.e., the self object this of when discussing an instance method of a class) from the "receiver of a method invocation" (i.e., the actual argument passed in the receiver position at a call site). Maybe "receiver argument" would work? I think my confusion here could have been avoided if there was a comment noting this distinction at the first point that the receiver of the method invocation is mentioned here (although to be fair I probably should have figured this out myself from the code...)

* @param invocation the method or constructor invocation
*/
private void computeOwningFromInvocation(Set<Obligation> obligations, Node invocation) {
if (methodElt == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

A brief comment to explain this (seemingly-unrelated) check here would be helpful. methodElt is a field of the class and might be unrelated to invocation, right? So why does it need to be checked here?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not actually dereferenced in the body of this method...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is used in addOwningToParam. I can't recall if the null value here has caused any NPE in real benchmarks. Even if it does, I agree that here is not the best place to check this. I will remove it from here.

Copy link
Contributor

@kelloggm kelloggm left a comment

Choose a reason for hiding this comment

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

I am happy with the current state of this PR and I believe it is ready to merge.

@mernst, please review my changes in 29cfb1f to a method that you wrote. I think it contained a bug, which I fixed: the intention seemed to be to print the actual elements of the collection, not to print a field of the current instance repeatedly. I also made the method static to avoid that problem in the future.

@mernst
Copy link
Member

mernst commented Oct 25, 2023

Oops! Martin, thanks for fixing my bug.

@kelloggm kelloggm enabled auto-merge (squash) October 26, 2023 14:47
@kelloggm kelloggm merged commit d282234 into typetools:master Oct 26, 2023
29 checks passed
wmdietl pushed a commit to eisop/checker-framework that referenced this pull request Nov 22, 2023
Co-authored-by: Michael Ernst <mernst@cs.washington.edu>
Co-authored-by: Manu Sridharan <msridhar@gmail.com>
Co-authored-by: Martin Kellogg <martin.kellogg@njit.edu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants