Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8351448

Inconsistent propagation of type annotations through inference

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • None
    • tools

      The following program shows that retrieving the inferred type of a method invocation through the Compiler Tree API includes type annotations on the returned type for only one of the two examples.

      I wondered if the inconsistency was a bug. In general my understanding is that there's no guarantee that type annotations are preserved for derived or inferred types, and are only present on types that are annotated in source, but it seemed surprising the two cases were handled differently.

      Stepping through in a debugger shows the annotations are removed from the second example in two places:

      * The call to baseType() turns the annotated String into an unannotated type (but does not remove annotations from the annotated type Test<String> in the first example): https://github.com/openjdk/jdk/blob/76e0f30b15efda86cbb974bbc764ac3fb3f0f054/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L2146

      * stripMetadataIfNeeded removes annotations from String (but not Test<String>) in the Tree API before the types are returned: https://github.com/openjdk/jdk/blob/76e0f30b15efda86cbb974bbc764ac3fb3f0f054/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTrees.java#L718

      ```
      import com.sun.source.tree.MethodInvocationTree;
      import com.sun.source.util.JavacTask;
      import com.sun.source.util.TaskEvent;
      import com.sun.source.util.TaskListener;
      import com.sun.source.util.TreePathScanner;
      import com.sun.source.util.Trees;
      import java.io.IOException;
      import java.nio.charset.StandardCharsets;
      import java.util.Locale;
      import javax.lang.model.type.TypeMirror;
      import javax.tools.JavaCompiler;
      import javax.tools.StandardJavaFileManager;
      import javax.tools.ToolProvider;

      public final class Z {

        public static void main(final String[] args) throws IOException {
          final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
          StandardJavaFileManager fileManager =
              compiler.getStandardFileManager(null, Locale.ENGLISH, StandardCharsets.UTF_8);
          final JavacTask task =
              (JavacTask)
                  compiler.getTask(null, null, null, null, null, fileManager.getJavaFileObjects(args));
          task.addTaskListener(
              new TaskListener() {
                @Override
                public void finished(TaskEvent e) {
                  if (e.getKind() == TaskEvent.Kind.ANALYZE) {
                    new TreePathScanner<Void, Void>() {
                      @Override
                      public Void visitMethodInvocation(MethodInvocationTree tree, Void unused) {
                        TypeMirror type = Trees.instance(task).getTypeMirror(getCurrentPath());
                        System.err.printf("Tree: %s, Type: %s\n", tree, type);
                        return super.visitMethodInvocation(tree, unused);
                      }
                    }.scan(e.getCompilationUnit(), null);
                  }
                }
              });
          task.analyze();
        }
      }
      ```

      ```
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;

      abstract class Test<T> {
        @Target(ElementType.TYPE_USE)
        @Retention(RetentionPolicy.RUNTIME)
        @interface TA {}

        abstract <U> U f();
        abstract <U> Test<U> g();

        public void testMethod() {
          Test<@TA String> f = f();
          Test<@TA String> g = g();
        }
      }
      ```

      ```
      $ java Z Test.java
      ...
      Tree: f(), Type: Test<java.lang.@Test.TA String>
      Tree: g(), Type: Test<java.lang.String>
      ```

            Unassigned Unassigned
            cushon Liam Miller-Cushon
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: