typetools / typetools/checker-framework

Method references and initialization

Open
#1,412 3 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement False Positive (false warning or imprecision) Java8
Dominant language
Java
Stars
1.1k
Forks
440
Avg merge
1d 12h
Merged PRs (30d)
134

Description

Take this small method reference example:

interface Dump<T> {
    void dump(T p);
}

class Demo {
    Dump<String> ds = this::print;
    void print(String s) {}
}

The Nullness Checker raises an error on the initialization of ds, because this is under initialization:

Error: [methodref.receiver.bound.invalid] @UnderInitialization(java.lang.Object.class) @NonNull Demo is not a valid receiver for @NonNull void print(@Initialized @NonNull Demo this, @Initialized @NonNull String p0) in Demo; attempting to use an incompatible receiver type
  found : @UnderInitialization(java.lang.Object.class) @NonNull Demo
  required: @Initialized @NonNull Demo

(Note: when using master, I get a Invalid format string error instead. I didn't immediately see what is wrong with the format string or invocation. https://github.com/typetools/checker-framework/commit/dd7bc5917cb2f4d6647eef7cc63ff647715d6f8e touched the key last.)

The error is valid, because if someone extends the class like this:

interface Dump<T> {
    void dump(T p);
}

class Demo {
    Dump<String> ds = this::print;
    void print(String s) {}
    
    Demo() {
        ds.dump("init");
    }
    void bar(String s) {
        ds.dump(s);
    }
}

public class Sub extends Demo {
    Object o = new Object();
    void print(String s) {
        o.toString();
    }
    public static void main(String[] args) {
        new Sub();
    }
}

one will get a NullPointerException:

Exception in thread "main" java.lang.NullPointerException
	at Sub.print(Sub.java:20)
	at Demo.<init>(Sub.java:10)
	at Sub.<init>(Sub.java:17)
	at Sub.main(Sub.java:23)

However, lots of code wants to set method references in field initializers, but won't use them until construction is done.
We should see whether we can find a sound way to allow the field initialization and allow the use in methods with fully-initialized receivers, but then forbid the use of that field in the constructor.
In our concrete example, I would want to allow the ds initialization and the use in bar, but forbid the invocation in the Demo constructor.
We would probably need some way to propagate method reference receiver initialization requirements.

Issue #904 is probably related, as we want similar sound but usable behavior for inner/anonymous classes.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the Java examples with the Nullness Checker and inspect the method-reference receiver handling related to commit dd7bc5917cb2f4d6647eef7cc63ff647715d6f8e. Compare the desired behavior with issue #904 and determine whether receiver initialization requirements can be propagated soundly. Done means field initialization and use in bar are accepted while invocation from the Demo constructor is rejected.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.