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

Java parser/typer rejects import of Java inner class #7675

Open
scabug opened this issue Jul 17, 2013 · 10 comments
Open

Java parser/typer rejects import of Java inner class #7675

scabug opened this issue Jul 17, 2013 · 10 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Jul 17, 2013

If you have Scala code with an inner class and attempt to use a wildcard import in Java so that you can use that class name unqualified, the Scala compiler will give an error when reading in that Java source.

Here is an example:

// scalapkg/Main.scala
package scalapkg

import javapkg.JavaClass

object Main extends App {
  println(new JavaClass().num)
}

class SomeScala {
  class InnerClass
}
// javapkg/JavaClass.java
package javapkg;

import scalapkg.SomeScala.*;

public class JavaClass {
    public int num = 1;
    InnerClass inner = null;
}

Compile with:

dnadolny@donny-desktop:~/tools/scala-2.10.2/bin$ ./scalac  javapkg/*.java scalapkg/*.scala
javapkg/JavaClass.java:3: error: object SomeScala is not a member of package scalapkg
Note: class SomeScala exists, but it has no companion object.
import scalapkg.SomeScala.*;
       ^
one error found

This is valid Java code, and would work if SomeScala was compiled first, then JavaClass compiled depending on the SomeScala*.class files.

A workaround is using the partially qualified name "SomeScala.Inner" rather than "Inner"

(This came up on the mailing list, https://groups.google.com/forum/#!topic/scala-ide-user/zkGaA_-uig8 )

@scabug
Copy link
Author

scabug commented Jul 17, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7675?orig=1
Reporter: Donny Nadolny (dnadolny)
Affected Versions: 2.10.2
See #3120

@scabug
Copy link
Author

scabug commented Jul 18, 2013

@paulp said:
It's only valid java code because java doesn't know anything about scala. That it would work compiling against the class files is a manifestation of the same thing. But scalac doesn't have to be ignorant of its own language semantics just because javac is. As it says, there is no object SomeScala from which to import SomeScala.*.

@scabug
Copy link
Author

scabug commented Jul 18, 2013

Donny Nadolny (dnadolny) said:
There is no object SomeScala, but in Java that import means you can refer to the inner class unqualified.

Given this Scala:
{code:title=scalapkg/Main.scala}
package scalapkg

import javapkg.JavaClass

object Main extends App {
val someScala = new SomeScala("abc")
new JavaClass().useInner(new someScala.InnerClass)
}

class SomeScala(str: String) {
class InnerClass {
def printStr() = println(str)
}
}

This compiles and runs when passed as source to scala:
{code:title=javapkg/JavaClass.java}
package javapkg;

import scalapkg.SomeScala;

public class JavaClass {
    public void useInner(Object probablyInner) {
    	SomeScala.InnerClass inner = (SomeScala.InnerClass) probablyInner;
    	inner.printStr(); 
    }
}

This would be compiled by java to the exact same .class file as the one above, but it does not compile when passed to scala as source:
{code:title=javapkg/JavaClass.java}
package javapkg;

import scalapkg.SomeScala.*;

public class JavaClass {
public void useInner(Object probablyInner) {
InnerClass inner = InnerClass probablyInner;
inner.printStr();
}
}
{code}

When you put "SomeScala.InnerClass" in the signature for "JavaClass.useInner", instead of Object, then the scala compiler complains (although the Java compiler allows it, and it runs and does what you expect). So maybe this is just a case of scala only rejecting method signatures that it doesn't like, rather than checking the body of the method.

How are you supposed to use a scala inner class from java?

@scabug
Copy link
Author

scabug commented Jul 18, 2013

@paulp said:
"How are you supposed to use a scala inner class from java?"

The question presupposes that you're supposed to. It's sure to be unsound - there are good reasons you can't "new Foo#Bar" in scala - and plenty of scala features are effectively inaccessible from java.

@scabug
Copy link
Author

scabug commented Jul 18, 2013

Donny Nadolny (dnadolny) said:
Maybe I'm missing something, but:
The code isn't trying to do "new Foo#Bar" (which the Java compiler would already reject), it's just using Foo#Bar in a method signature.
Is there a good reason why this scala class works:
{code:title=scala}
class Whatever {
def useInner(inner: SomeScala#InnerClass) = inner.printStr
}

but this java class shouldn't?
{code:title=java}
class JavaClass {
    public void useInner(SomeScala.InnerClass inner) {
        inner.printStr(); 
    }
}

They're equivalent, aren't they? Yet the scala compiler will reject the 2nd one.

I don't understand why using scala inner classes from java is sure to be unsound. The example above should be fine, and the only unsound usage I can think of isn't prevented by the scala compiler:
{code:title=java}
SomeScala a = new SomeScala("a");
InnerClass aa = a.new InnerClass();
SomeScala b = new SomeScala("b");

a.useMyInner(aa); //useMyInner is "def useMyInner(inner: InnerClass) = ???"
b.useMyInner(aa); //invalid if this was scala, but in java it works even when the scala compiler reads in the source
{code}

If you're not convinced, this probably won't convince you but I'll voice my opinion anyway: I would prefer a scala compiler that didn't do extra checks on java code, unless they were just given as warnings. Rejecting java code that would compile with the java compiler means you can't convert your java project to a mixed scala/java project. To anyone who complained that they can use java to do unsound things with scala classes (eg the "b.useMyInner(aa)" example), I think a fair response would be "if you want the safety of the scala compiler, write in scala".

@scabug
Copy link
Author

scabug commented Jul 22, 2013

@retronym said:
If you have mixed Java/Scala sources, scalac has to parse and typecheck everything other than method/field RHSs. The error isn't coming from an "extra check", its just coming from a flawed attempt to reuse Scala's typechecker for Java sources which have slightly different semantics.

In general, if the Java source can be separately compiled, scalac should also be able to jointly compile it.

We've got this code in Typers which looks like it ought to apply to this case. We'll have to investigate why it doesn't.

        if (!reallyExists(sym)) {
          def handleMissing: Tree = {
            def errorTree = missingSelectErrorTree(tree, qual, name)
            def asTypeSelection = (
              if (context.unit.isJava && name.isTypeName) {
                // SI-3120 Java uses the same syntax, A.B, to express selection from the
                // value A and from the type A. We have to try both.
                atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match {
                  case EmptyTree => None
                  case tree1     => Some(typed1(tree1, mode, pt))
                }
              }
              else None
            )

@scabug
Copy link
Author

scabug commented Jul 22, 2013

@paulp said:
I lost track of this, but I had meant to add that I was being reflexively contrary. That is supported by the fact that I participated in the writing of that snippet.

@scabug
Copy link
Author

scabug commented Jul 24, 2013

@retronym said:
I took a stab at allowing this. Didn't get all the way there, but the branch at least shows which spots need to be modified:

https://github.com/retronym/scala/tree/ticket/7675

@scabug
Copy link
Author

scabug commented Feb 13, 2014

@retronym said:
From a ticket marked as a duplicate:

{code}package p1;
public class EnclosingClass {
public class NestedClass {
}
}
import p1.EnclosingClass.NestedClass;
public class ConsumingClass {
NestedClass nc;
}
{code}

This results in:

NestedClass is not a member of _root_.p1.EnclosingClass.

@scabug
Copy link
Author

scabug commented Feb 13, 2014

@retronym said:
See also #3120. I think the fix for this ticket needs to extend the approach taken for that one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants