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

GH-37286: [Java] Start adding nullability/nullness annotations #37723

Merged
merged 25 commits into from
Jan 9, 2024

Conversation

davisusanibar
Copy link
Contributor

@davisusanibar davisusanibar commented Sep 14, 2023

Rationale for this change

Closes: #37286

What changes are included in this PR?

Initial support for:

  • Use the Checker Framework to enhances Java’s type system to make it more powerful and useful. Planning to start with Nullness Checker

Are these changes tested?

These are the activities involved on this PR:

  • Configure the Checker Framework
  • Treat checker errors as warnings initially
  • Applying Nullness Checker annotation as needed: @nonnull / @nullable
  • Check if building timer increases after this checker is added
  • Fixes for code review

Are there any user-facing changes?

Yes

@github-actions
Copy link

Thanks for opening a pull request!

If this is not a minor PR. Could you open an issue for this pull request on GitHub? https://github.com/apache/arrow/issues/new/choose

Opening GitHub issues ahead of time contributes to the Openness of the Apache Arrow project.

Then could you also rename the pull request title in the following format?

GH-${GITHUB_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}

or

MINOR: [${COMPONENT}] ${SUMMARY}

In the case of PARQUET issues on JIRA the title also supports:

PARQUET-${JIRA_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}

See also:

@davisusanibar davisusanibar changed the title Null ability impl GH-37286: [Java] Start adding nullability/nullness annotations Sep 14, 2023
@github-actions
Copy link

⚠️ GitHub issue #37286 has been automatically assigned in GitHub to PR creator.

@davisusanibar
Copy link
Contributor Author

Hello @lidavidm, could you please provide me with an initial review of how I intend to integrate Nullness evaluation into Java modules (please consider only: arrow-memory-core initially)?

I have these 02 doubts:

  • There are a lots of warning message if we are planning to include Nullness Checker ... then is needed to define an initial scope that could be:
    1. Define what modules need to be analyzed by Nullness Checker, or
    2. Define what classes need to be analyzed by Nullness Checker.
  • The building time of Java modules has doubled since Nullness Checker has been added.

Could you please help me with these two doubts so that I know how to proceed?

Thank you in advance

@lidavidm
Copy link
Member

For (1), what are some examples of the warnings? Ideally, we'd only enable it one module at a time.

For (2), can we enable it only in a separate profile, and have that be run in CI? That way it does not affect the 'normal' builds.

java/pom.xml Outdated
@@ -44,8 +44,11 @@
<forkCount>2</forkCount>
<checkstyle.failOnViolation>true</checkstyle.failOnViolation>
<errorprone.javac.version>9+181-r4173-1</errorprone.javac.version>
<error_prone_core.version>2.16</error_prone_core.version>
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
<pinjdk8.errorprone.core.version>2.10.0</pinjdk8.errorprone.core.version>
Copy link
Member

Choose a reason for hiding this comment

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

More reason to drop JDK8 ASAP.

java/pom.xml Outdated
with other annotation processors.
See https://github.com/jbosstools/m2e-apt/issues/62 for details
-->
<id>error-prone-checkerframework-jdk8</id>
Copy link
Member

Choose a reason for hiding this comment

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

IMO, we don't bother enabling this for JDK8. In fact, I'd be OK with dropping error-prone for JDK8 as well, and only enabling these checks only on the latest LTS.

@@ -1033,7 +1034,7 @@ public void getBytes(long index, OutputStream out, int length) throws IOExceptio
// copy length bytes of data from this ArrowBuf starting at
// address addr(index) into the tmp byte array starting at index 0
byte[] tmp = new byte[length];
MemoryUtil.UNSAFE.copyMemory(null, addr(index), tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, length);
Copy link
Member

Choose a reason for hiding this comment

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

nit: is it possible to make the UNSAFE constant private to avoid other problems like this? (We can do that in a separate ticket.)

@@ -154,7 +157,7 @@ private static String createErrorMsg(final BufferAllocator allocator, final long
}
}

public static boolean isDebug() {
public static @Nullable boolean isDebug() {
Copy link
Member

Choose a reason for hiding this comment

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

This seems wrong: this is a primitive and should never be null.

@@ -42,7 +43,7 @@ abstract class BaseAllocator extends Accountant implements BufferAllocator {

public static final String DEBUG_ALLOCATOR = "arrow.memory.debug.allocator";
public static final int DEBUG_LOG_LENGTH = 6;
public static final boolean DEBUG;
public static final @Nullable boolean DEBUG;
Copy link
Member

Choose a reason for hiding this comment

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

This also seems wrong.

@@ -64,15 +65,16 @@ abstract class BaseAllocator extends Accountant implements BufferAllocator {
// Package exposed for sharing between AllocatorManger and BaseAllocator objects
private final String name;
private final RootAllocator root;
private final Object DEBUG_LOCK = DEBUG ? new Object() : null;

private final @Nullable Object DEBUG_LOCK = DEBUG ? new Object() : null;
Copy link
Member

Choose a reason for hiding this comment

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

IMO, there is no harm in always initializing this.

@@ -183,6 +186,7 @@ public ArrowBuf getEmpty() {
* we have a new ledger
* associated with this allocator.
*/
@SuppressWarnings({"nullness:locking.nullable", "nullness:argument"})
Copy link
Member

Choose a reason for hiding this comment

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

What are the warnings here?

@@ -81,7 +82,8 @@ public ArrowBufPointer(ArrowBuf buf, long offset, long length) {
* @param length the length off set of the memory region pointed to.
* @param hasher the hasher used to calculate the hash code.
*/
public ArrowBufPointer(ArrowBuf buf, long offset, long length, ArrowBufHasher hasher) {
@SuppressWarnings("initialization.fields.uninitialized")
Copy link
Member

Choose a reason for hiding this comment

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

Can we initialize fields instead of suppressing the warnings?

@@ -33,7 +35,7 @@
public class MemoryUtil {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MemoryUtil.class);

private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
private static final @Nullable Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
Copy link
Member

Choose a reason for hiding this comment

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

Does Checker Framework have anything for static initializers?

Copy link
Contributor

Choose a reason for hiding this comment

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

@github-actions github-actions bot added awaiting changes Awaiting changes and removed awaiting review Awaiting review labels Sep 19, 2023
@davisusanibar
Copy link
Contributor Author

For (1), what are some examples of the warnings? Ideally, we'd only enable it one module at a time.

For (2), can we enable it only in a separate profile, and have that be run in CI? That way it does not affect the 'normal' builds.

(1) You could see warning messages here, search for [WARNING] /arrow/java/ inside of {2_AMD64 Debian 9 Java JDK 11 Maven 3.6.2.txt, ...}

(2) Ok, let me add that inside a custom profile only for CI

@lidavidm
Copy link
Member

(1) let's only run the checker on memory-core to start with

@lidavidm
Copy link
Member

For https://github.com/apache/arrow/tree/main/java/format/src/main/java/org/apache/arrow/flatbuf

let's add a package-info.java with a @DefaultQualifiers annotation to mark everything in the package as nullable-by-default

@github-actions github-actions bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Oct 2, 2023
Copy link
Contributor

@danepitkin danepitkin left a comment

Choose a reason for hiding this comment

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

Thanks for opening up this PR @davisusanibar!

I see there are a lot of Suppressed warnings. Are they truly all needed? (I haven't looked into all of them). I would like to see zero suppressed warnings if possible. Otherwise, the checker doesn't feel as useful.

java/pom.xml Outdated
@@ -46,6 +46,7 @@
<errorprone.javac.version>9+181-r4173-1</errorprone.javac.version>
<error_prone_core.version>2.16</error_prone_core.version>
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
<checker.framework.version>3.38.0</checker.framework.version>
Copy link
Contributor

Choose a reason for hiding this comment

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

v3.39.0 just released a few days ago, which adds additional support for Java 21 compilation

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we try v3.39.0? It might be worth rebasing so that the Java 21 CI job can run to see if its required.

@@ -29,6 +29,7 @@ public class StackTrace {
/**
* Constructor. Captures the current stack trace.
*/
@SuppressWarnings("nullness:assignment") //incompatible types in assignment
Copy link
Contributor

@danepitkin danepitkin Oct 5, 2023

Choose a reason for hiding this comment

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

Can you expand on why this is suppressed? nullness:assignment warning makes me think we can just check the length of the stack trace to make sure we are assigning valid entries.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you expand on why this is suppressed? nullness:assignment warning makes me think we can just check the length of the stack trace to make sure we are assigning valid entries.

It is not necessary, I will update it.

@@ -286,7 +288,7 @@ public void setLimit(long newLimit) {
*
* @return Currently allocate memory in bytes.
*/
public long getAllocatedMemory() {
public long getAllocatedMemory(Accountant this) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, is this needed? It's not used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, is this needed? It's not used.

Not needed, I am going to delete that

@davisusanibar
Copy link
Contributor Author

Thanks for opening up this PR @davisusanibar!

I see there are a lot of Suppressed warnings. Are they truly all needed? (I haven't looked into all of them). I would like to see zero suppressed warnings if possible. Otherwise, the checker doesn't feel as useful.

Hi @danepitkin yes this is possible to suppress some warnings such as @SuppressWarnings("nullness:dereference.of.nullable") adding more if/else logic.

Let me add that logic explicitly.

@github-actions github-actions bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jan 8, 2024
Comment on lines 77 to 79
if (parent == null) {
throw new IllegalArgumentException(String.valueOf("Accountant(parent) must not be null"));
}
Copy link
Member

Choose a reason for hiding this comment

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

Why do you use String.valueOf?

Suggested change
if (parent == null) {
throw new IllegalArgumentException(String.valueOf("Accountant(parent) must not be null"));
}
Preconditions.checkArgument(parent != null, "parent must not be null");

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I mention at #37723 (comment) (1st option) if I added Preconditions.xxx then that finished also with errors, then, is needed to add inline throw validation.

Finished OK:

final BufferLedger oldLedger = map.remove(allocator);
if (oldLedger == null) {
  throw new IllegalArgumentException(String.valueOf("BufferLedger(oldLedger) must not be null"));
}
BufferAllocator oldAllocator = oldLedger.getAllocator();

Finished with errors:

final BufferLedger oldLedger = map.remove(allocator);
Preconditions.checkArgument(oldLedger != null, "BufferLedger(oldLedger) must not be null");
BufferAllocator oldAllocator = oldLedger.getAllocator();

Error message:
error: [dereference.of.nullable] dereference of possibly-null reference oldLedger

Copy link
Member

Choose a reason for hiding this comment

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

You need to annotate the Preconditions methods so that Checker Framework recognizes them...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

@@ -48,8 +49,9 @@ public abstract class AllocationManager {
// This is mostly a semantic constraint on the API user: if the reference count reaches 0 in the owningLedger, then
// there are not supposed to be any references through other allocators. In practice, this doesn't do anything
// as the implementation just forces ownership to be transferred to one of the other extant references.
private volatile BufferLedger owningLedger;
private volatile @Nullable BufferLedger owningLedger;
Copy link
Member

Choose a reason for hiding this comment

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

@davisusanibar can you review this?

private final RoundingPolicy roundingPolicy;
private final AllocationManager.Factory allocationManagerFactory;
private final AllocationManager.@NonNull Factory allocationManagerFactory;
Copy link
Member

Choose a reason for hiding this comment

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

@davisusanibar can you follow up here? File an issue?

* @param eval The assertion to be evaluated at compile time
*/
@AssertMethod
public static void assertMethod(boolean eval) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't see any change.

@github-actions github-actions bot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jan 8, 2024
@github-actions github-actions bot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Jan 8, 2024
@github-actions github-actions bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Jan 8, 2024
@lidavidm
Copy link
Member

lidavidm commented Jan 8, 2024

@github-actions crossbow submit java

@github-actions github-actions bot added awaiting merge Awaiting merge and removed awaiting change review Awaiting change review labels Jan 8, 2024
Copy link

github-actions bot commented Jan 8, 2024

Revision: f92f376

Submitted crossbow builds: ursacomputing/crossbow @ actions-b35c84940e

Task Status
java-jars GitHub Actions
verify-rc-source-java-linux-almalinux-8-amd64 GitHub Actions
verify-rc-source-java-linux-conda-latest-amd64 GitHub Actions
verify-rc-source-java-linux-ubuntu-20.04-amd64 GitHub Actions
verify-rc-source-java-linux-ubuntu-22.04-amd64 GitHub Actions
verify-rc-source-java-macos-amd64 GitHub Actions

@lidavidm lidavidm merged commit ccc79e9 into apache:main Jan 9, 2024
17 checks passed
@lidavidm lidavidm removed the awaiting merge Awaiting merge label Jan 9, 2024
Copy link

After merging your PR, Conbench analyzed the 5 benchmarking runs that have been run so far on merge-commit ccc79e9.

There were no benchmark performance regressions. 🎉

The full Conbench report has more details. It also includes information about 3 possible false positives for unstable benchmarks that are known to sometimes produce them.

clayburn pushed a commit to clayburn/arrow that referenced this pull request Jan 23, 2024
…pache#37723)

### Rationale for this change

Closes: apache#37286

### What changes are included in this PR?

Initial support for: 

- Use the Checker Framework to enhances Java’s type system to make it more powerful and useful. Planning to start with [Nullness Checker](https://checkerframework.org/manual/#nullness-checker)

### Are these changes tested?

These are the activities involved on this PR:

- [x] Configure the Checker Framework
- [x] Treat checker errors as warnings initially 
- [x] Applying Nullness Checker annotation as needed: @ NonNull / @ Nullable
- [x] Check if building timer increases after this checker is added
- [x] Fixes for code review

### Are there any user-facing changes?

Yes
* Closes: apache#37286

Lead-authored-by: david dali susanibar arce <davi.sarces@gmail.com>
Co-authored-by: David Susanibar Arce <dsusanibar@Voltron.local>
Co-authored-by: David Susanibar Arce <davi.sarces@gmail.com>
Signed-off-by: David Li <li.davidm96@gmail.com>
dgreiss pushed a commit to dgreiss/arrow that referenced this pull request Feb 19, 2024
…pache#37723)

### Rationale for this change

Closes: apache#37286

### What changes are included in this PR?

Initial support for: 

- Use the Checker Framework to enhances Java’s type system to make it more powerful and useful. Planning to start with [Nullness Checker](https://checkerframework.org/manual/#nullness-checker)

### Are these changes tested?

These are the activities involved on this PR:

- [x] Configure the Checker Framework
- [x] Treat checker errors as warnings initially 
- [x] Applying Nullness Checker annotation as needed: @ NonNull / @ Nullable
- [x] Check if building timer increases after this checker is added
- [x] Fixes for code review

### Are there any user-facing changes?

Yes
* Closes: apache#37286

Lead-authored-by: david dali susanibar arce <davi.sarces@gmail.com>
Co-authored-by: David Susanibar Arce <dsusanibar@Voltron.local>
Co-authored-by: David Susanibar Arce <davi.sarces@gmail.com>
Signed-off-by: David Li <li.davidm96@gmail.com>
zanmato1984 pushed a commit to zanmato1984/arrow that referenced this pull request Feb 28, 2024
…pache#37723)

### Rationale for this change

Closes: apache#37286

### What changes are included in this PR?

Initial support for: 

- Use the Checker Framework to enhances Java’s type system to make it more powerful and useful. Planning to start with [Nullness Checker](https://checkerframework.org/manual/#nullness-checker)

### Are these changes tested?

These are the activities involved on this PR:

- [x] Configure the Checker Framework
- [x] Treat checker errors as warnings initially 
- [x] Applying Nullness Checker annotation as needed: @ NonNull / @ Nullable
- [x] Check if building timer increases after this checker is added
- [x] Fixes for code review

### Are there any user-facing changes?

Yes
* Closes: apache#37286

Lead-authored-by: david dali susanibar arce <davi.sarces@gmail.com>
Co-authored-by: David Susanibar Arce <dsusanibar@Voltron.local>
Co-authored-by: David Susanibar Arce <davi.sarces@gmail.com>
Signed-off-by: David Li <li.davidm96@gmail.com>
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.

[Java] Start adding JSpecify nullability/nullness annotations
4 participants