Bug 298821 - Java compiler wrongly reports type visibility error
Summary: Java compiler wrongly reports type visibility error
Status: VERIFIED NOT_ECLIPSE
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.5.1   Edit
Hardware: PC Windows XP
: P3 major (vote)
Target Milestone: 3.6 M5   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-01-05 03:32 EST by fantaman CLA
Modified: 2010-01-25 14:43 EST (History)
2 users (show)

See Also:


Attachments
Screenshot of Eclipse Java Editor (4.21 KB, image/png)
2010-01-05 04:01 EST, fantaman CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description fantaman CLA 2010-01-05 03:32:07 EST
Suppose you have 2 Java files like these: 

====== Api.java =======
public class Api {

    private static class PrivateClass<T> {

        @Override
        public String toString() {
            return "PrivateClass";
        }

        public T produce() {
            return (T)this;
        }
    }

    public static class PublicClass extends PrivateClass<PublicClass> {

        @Override
        public String toString() {
            return "PublicClass";
        }
    }

    public static void consume(PrivateClass<?> someClass) {
        System.out.println("consume(" + someClass + ")");
    }
}

====== ApiClient.java =======
public class ApiClient {

    public static void main(String[] args) {
        Api.consume(new Api.PublicClass().produce());
    }
}

============
then Eclipse marks the "Api.consume..." line red with the error message "The type Api.PrivateClass<T> is not visible" and suggests "Change visibility of 'produce()' to 'default'" as a quick fix. 
Aside from the obvious bug that an already public method cannot be made more visible by setting it to package scope, the problem seems to be that PrivateClass is not visible to ApiClient: if I change the visibility of PrivateClass to default/protected/public (depending on whether Api and ApiClient reside in the same package or not), the error disappears.
But there should be no error at all: I can compile the code on the command shell with an ordinary JDK (1.6 for the matter, but it should be the same on 1.5) and it runs fine (the output is "consume(PublicClass)"). 

Eclipe 3.3 had no problem with this code. I've already tried to switch all 'Error'-Settings in the "Compiler > Errors/Warnings" configuration to 'Warning', but the problem persists.

-- Configuration Details --
Product: Eclipse 1.2.1.20090812-1036 (org.eclipse.epp.package.jee.product)
Installed Features:
 org.eclipse.jdt 3.5.1.r351_v20090810-0600-7r88FEoFI0WTo6Az-1qFRHm37ChJ
Comment 1 fantaman CLA 2010-01-05 04:01:14 EST
Created attachment 155313 [details]
Screenshot of Eclipse Java Editor
Comment 2 Srikanth Sankaran CLA 2010-01-07 00:22:43 EST
Hello,

This is a bug in javac and eclipse is indeed behaving correctly here.
As you can see from the disassembled version of the code for ApiClient.main(String[]): (compiled with jdk6)

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   new     #2; //class Api$PublicClass
   3:   dup
   4:   invokespecial   #3; //Method Api$PublicClass."<init>":()V
   7:   invokevirtual   #4; //Method Api$PublicClass.produce:()Ljava/lang/Object
;
   10:  checkcast       #5; //class Api$PrivateClass
   13:  invokestatic    #6; //Method Api.consume:(LApi$PrivateClass;)V
   16:  return
  LineNumberTable:
   line 31: 0
   line 32: 16

the line    
10:  checkcast       #5; //class Api$PrivateClass

attempts to illegally refer to the inaccessible type Api$PrivateClass.

See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6495506
The fix is delivered only on JDK7 though.

See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=209153

When I compile the program with JDK7 (b77) I get 

C:\jtests>C:\jdk-7-ea-bin-b77-windows-i586-03_dec_2009\jdk7\bin\javac -Xlint:unc
hecked -sourcepath c:\jtests Api.java
Api.java:11: warning: [unchecked] unchecked cast
            return (T)this;
                      ^
  required: T
  found:    PrivateClass<T>
  where T is a type-variable:
    T extends Object declared in class PrivateClass
An exception has occurred in the compiler (1.7.0-ea). Please file a bug at the J
ava Developer Connection (http://java.sun.com/webapps/bugreport)  after checking
 the Bug Parade for duplicates. Include your program and the following diagnosti
c in your report.  Thank you.
java.lang.NullPointerException
        at com.sun.tools.javac.comp.Resolve.isAccessible(Resolve.java:147)
        at com.sun.tools.javac.comp.Resolve.isAccessible(Resolve.java:190)
        at com.sun.tools.javac.comp.Resolve$AccessError.getDiagnostic(Resolve.ja
va:1908)
        at com.sun.tools.javac.comp.Resolve.logResolveError(Resolve.java:1590)
        at com.sun.tools.javac.comp.Resolve.logAccessError(Resolve.java:1581)
        at com.sun.tools.javac.comp.TransTypes.cast(TransTypes.java:113)
        at com.sun.tools.javac.comp.TransTypes.coerce(TransTypes.java:129)
        at com.sun.tools.javac.comp.TransTypes.retype(TransTypes.java:163)
        at com.sun.tools.javac.comp.TransTypes.visitApply(TransTypes.java:593)
        at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java
:1299)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.comp.TransTypes.translate(TransTypes.java:422)
        at com.sun.tools.javac.comp.TransTypes.translateArgs(TransTypes.java:193
)
        at com.sun.tools.javac.comp.TransTypes.visitApply(TransTypes.java:590)
        at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java
:1299)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.comp.TransTypes.translate(TransTypes.java:422)
        at com.sun.tools.javac.comp.TransTypes.visitExec(TransTypes.java:556)
        at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.j
ava:1145)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.jav
a:160)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:768)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.comp.TransTypes.translate(TransTypes.java:422)
        at com.sun.tools.javac.comp.TransTypes.visitMethodDef(TransTypes.java:45
9)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:655)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitClassDef(TreeTranslator.
java:134)
        at com.sun.tools.javac.comp.TransTypes.translateClass(TransTypes.java:77
7)
        at com.sun.tools.javac.comp.TransTypes.visitClassDef(TransTypes.java:445
)
        at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:584)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java
:58)
        at com.sun.tools.javac.comp.TransTypes.translate(TransTypes.java:422)
        at com.sun.tools.javac.comp.TransTypes.translateTopLevelClass(TransTypes
.java:802)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1319)

        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1215)

        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:851)

        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:810)
        at com.sun.tools.javac.main.Main.compile(Main.java:400)
        at com.sun.tools.javac.main.Main.compile(Main.java:318)
        at com.sun.tools.javac.main.Main.compile(Main.java:309)
        at com.sun.tools.javac.Main.compile(Main.java:82)
        at com.sun.tools.javac.Main.main(Main.java:67)

---------------------

but we can reasonably conclude from the stack frames:

        at com.sun.tools.javac.comp.Resolve$AccessError.getDiagnostic(Resolve.ja
va:1908)
        at com.sun.tools.javac.comp.Resolve.logResolveError(Resolve.java:1590)
        at com.sun.tools.javac.comp.Resolve.logAccessError(Resolve.java:1581)
        at com.sun.tools.javac.comp.TransTypes.cast(TransTypes.java:113)

that JDK7 would have rejected this code if it weren't for the NPE.


If the body of ApiClient#Main were to be:

    public static void main(String[] args) {
        Api.consume((Api.PrivateClass) new Api.PublicClass().produce());  
    }

JDK6 as well as JDK5 would have straightaway rejected the code, but the
net effect is the same: the compiler has to generate a cast to the private
type  at a place where that type is not accessible (since after erasure
produce() returns java.lang.Object and what consume expects is the
PrivateClass the compiler must insert a checkcast opcode)
Comment 3 Olivier Thomann CLA 2010-01-25 14:43:13 EST
Verified for 3.6M5 using I20100125-0800