Skip to content

Scala's java interop disallows access to Scala nested class from Java #10490

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

Closed
jvican opened this issue Sep 1, 2017 · 2 comments
Closed

Scala's java interop disallows access to Scala nested class from Java #10490

jvican opened this issue Sep 1, 2017 · 2 comments

Comments

@jvican
Copy link
Member

jvican commented Sep 1, 2017

Imagine you're compiling in mixed mode and you have a cyclic dependency between Java and Scala code. In particular, Java wants to access a nested class defined in an Scala class. This is often necessary when you want to define some field or method as static. Let's assume they're defined in the same package java.interop.bug.

package java.interop.bug;
public class StaticsHolder {
  // Foo is defined in Scala
  public static final Foo.Bar bar = new Bar();
}
package java.interop.bug

class Foo {
  class Bar
}

class PerformantPrinter {
  // just a small example to illustrate the Scala -> Java dep
  println(StaticsHolder.bar)
}

If you compile this in mixed mode, first scalac will run. Scalac will parse the java signature and will give the following compile error:

not found: value Foo
      public static final Foo.Bar bar = new Bar();
                                  ^

This happens because Scalac assumes that Bar is defined in a companion object (value) named Foo. This assumption is incorrect because it doesn't take into account that Bar could also be a nested class defined in Foo.

I think this should be easy to fix, but I may be wrong. I've run into this issue twice: now, hacking on statistics in Scalac; and in the past while hacking on Zinc's Java API, which is also compiled in mixed mode.

@jvican
Copy link
Member Author

jvican commented Sep 2, 2017

Related #3120. I'm trying to fix this issue.

jvican added a commit to scalacenter/scala that referenced this issue Sep 2, 2017
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).

However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:

1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.

By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.

To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.

To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.

Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).
jvican added a commit to scalacenter/scala that referenced this issue Sep 2, 2017
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).

However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:

1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.

By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.

To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.

To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.

Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).
jvican added a commit to scalacenter/scala that referenced this issue Sep 25, 2017
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).

However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:

1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.

By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.

To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.

To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.

Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).

Fixes scala/bug#10490.
jvican added a commit to scalacenter/scala that referenced this issue Sep 25, 2017
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).

However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:

1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.

By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.

To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.

To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.

Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).

Fixes scala/bug#10490.
bishabosha pushed a commit to scalacenter/scala that referenced this issue Jun 26, 2020
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).

However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:

1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.

By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.

To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.

To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.

Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).

Fixes scala/bug#10490.
@SethTisue SethTisue added this to the Backlog milestone Jun 8, 2021
@som-snytt
Copy link

The associated PR was superseded scala/scala#7671

@SethTisue SethTisue modified the milestones: Backlog, 2.12.9 May 17, 2023
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

4 participants