typetools / typetools/checker-framework

Unsoundness in MustCallChecker (and RLC) for functional interfaces

Open
#6,823 8 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

ResourceLeakChecker
Dominant language
Java
Stars
1.1k
Forks
440
Avg merge
1d 12h
Merged PRs (30d)
134

Description

This example creates a Closeable functional interface and makes new instances of it in a few sneaky ways:

  • normal inner class
  • inline inner class
  • anonymous class
  • lambda expression
  • method reference
// Test that the correct type is assigned to instantiations of functional
// interfaces.

import java.io.Closeable;
import org.checkerframework.checker.mustcall.qual.*;

public abstract class FunctionalInterfaces {

  @FunctionalInterface
  public interface Actor extends Closeable {
    void act();

    @Override
    default void close() {
    }
  }

  public static class ActorImpl implements Actor {
    @Override
    public void act() {
    }
  }

  public abstract void run(@MustCall({}) Actor a);

  public static void method() {
  }

  public void normalConstruction() {

    // :: error: (assignment)
    @MustCall({}) Actor a = new ActorImpl();

  }

  public void inlineClass() {

    class ActorImplInline implements Actor {
      @Override
      public void act() {
      }
    }

    // :: error: (assignment)
    @MustCall({}) Actor a = new ActorImplInline();

  }

  public void anonymousClass() {

    // :: error: (assignment)
    @MustCall({}) Actor a = new Actor() {
      public void act() {
      }
    };

  }

  public void lambda() {

    // :: error: (assignment)
    @MustCall({}) Actor a = () -> {
    };

  }

  public void methodRef() {

    // :: error: (assignment)
    @MustCall({}) Actor a = FunctionalInterfaces::method;

  }

}

I would expect all 5 cases would report an error, but only the first two do:

checker/tests/mustcall/FunctionalInterfaces.java:32: error: [assignment] incompatible types in assignment.
    @MustCall({}) Actor a = new ActorImpl();
                            ^
  found   : @MustCall("close") ActorImpl
  required: @MustCall Actor
checker/tests/mustcall/FunctionalInterfaces.java:45: error: [assignment] incompatible types in assignment.
    @MustCall({}) Actor a = new ActorImplInline();
                            ^
  found   : @MustCall FunctionalInterfaces.@MustCall("close") ActorImplInline
  required: @MustCall Actor
2 errors

I did my best to debug this one, but the problem actually seems to be in the "framework" part of the Checker Framework, and not in the Must Call Checker specifically---although I'm not quite knowledgeable enough to know for sure. It seems like new ClassName() is always assigned the correct type @MustCall({"close"}), but other ways of constructing an instance of the class do not get the right inherited annotations.

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 with checker/tests/mustcall/FunctionalInterfaces.java and run the reproducer, comparing normal, inline, anonymous, lambda, and method-reference construction. Trace how the framework and MustCallChecker/RLC assign inherited annotations; done means all five cases report the expected assignment error.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.