Skip to content

Fix #9392: Allow overriding a Java method when a same-named field is present #9412

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

Conversation

smarter
Copy link
Member

@smarter smarter commented Jul 22, 2020

In #9126, Java fields were marked effectively finals to prevent false
overrides. This poses a problem when a Java field and method of the same
name are present, because a Scala val or def will match both of these
according to Denotation#matches. We could tweak matches further to not
consider the Java field to match any Scala definition, but that might
introduce new problems (we couldn't trust FullMatch anymore and would
need to check the info of any Java symbol which might cause cycles, we
would also probably need to tweak overloading resolution to avoid
ambiguity when trying to decide whether to select the Java field or the
Scala override of the Java method).

This commit takes a different approach: we allow a val or def to match a
Java field, but we tweak the logic that checks whether the override
modifier is correctly used to disallow definitions that only match a
Java field (thus preventing tests/neg/i9115 from compiling), while
letting through definitions that both match a Java field and something
else (thus making tests/pos/i9392 compile).

This is all a bit messy but I can't think of a better solution which
wouldn't be significantly more complicated.

…d is present

In scala#9126, Java fields were marked effectively finals to prevent false
overrides. This poses a problem when a Java field and method of the same
name are present, because a Scala val or def will match both of these
according to Denotation#matches. We could tweak `matches` further to not
consider the Java field to match any Scala definition, but that might
introduce new problems (we couldn't trust `FullMatch` anymore and would
need to check the info of any Java symbol which might cause cycles, we
would also probably need to tweak overloading resolution to avoid
ambiguity when trying to decide whether to select the Java field or the
Scala override of the Java method).

This commit takes a different approach: we allow a val or def to match a
Java field, but we tweak the logic that checks whether the `override`
modifier is correctly used to disallow definitions that _only_ match a
Java field (thus preventing tests/neg/i9115 from compiling), while
letting through definitions that both match a Java field and something
else (thus making tests/pos/i9392 compile).

This is all a bit messy but I can't think of a better solution which
wouldn't be significantly more complicated.
@ohze
Copy link

ohze commented Jul 23, 2020

...according to Denotation#matches. We could tweak matches further to not
consider the Java field to match any Scala definition, but ...

Note that #9395 don't tweak Denotation#matches but OverridingPairs.Cursor#matches

Note2. When debugging compilation of tests/pos/i9392/J.java tests/pos/i9392/S.scala (the files here), I found that:

@smarter
Copy link
Member Author

smarter commented Jul 23, 2020

@ohze The problem with your PR (besides the code duplication it introduces) is that the override def i in S2 in this PR doesn't compile with it (unlike in Scala 2).

In this PR's dotc, the overriding pairs contains both S.i() -> J.i() and S.i() -> J.i pairs.

Yes, it's a bit weird, but as explained in the commit message, I can't think of a better approach that doesn't introduce other problems.

@odersky
Copy link
Contributor

odersky commented Jul 27, 2020

My first reaction would be that OverridingPairs#matches and Denotation#matches should be the same, just for basic sanity.

Copy link
Contributor

@odersky odersky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this. It seems wrong that a Java or Scala method is allowed to override a Java field. That's simply not the right semantics. So maybe it's better to tweak matches after all?

@odersky odersky assigned smarter and unassigned odersky Jul 27, 2020
@smarter
Copy link
Member Author

smarter commented Jul 27, 2020

So maybe it's better to tweak matches after all?

The issue is that when we have something like:

package pkg;

public class J {
    int i = 0;
    public int i() { return 1; }
}
class S2 extends pkg.J {
  override def i: Int = 2
}

We want i to override the public i() method, but comparing their signatures gives us a FullMatch. So either we need to always check if one fhe two symbols in a FullMatch happens to be a Java field (is(JavaDefined, butNot = Method)) everywhere we rely on matchDegree which might hurt performance, or we somehow need to change Signature so that Java fields don't have the same signature as zero-param defs and vals (currently NotAMethod), but I don't see a way to do that (for java methods, we keep track of the fact they come from Java in the type, but for fields there's no way to know they come from Java by the time we're computing signatures)

@smarter
Copy link
Member Author

smarter commented Jul 27, 2020

So either we need to always check if one fhe two symbols in a FullMatch happens to be a Java field (is(JavaDefined, butNot = Method)) everywhere we rely on matchDegree which might hurt performance

I don't even know if we can do that in Denotation#atSignature, because in there we only have one symbol, so how can we know if a Java field is a valid match or not?

smarter added a commit to dotty-staging/dotty that referenced this pull request Jul 27, 2020
…d is present

In scala#9126, Java fields were marked effectively finals to prevent false
overrides. This poses a problem when a Java field and method of the same
name are present, because a Scala val or def will match both of these
according to Denotation#matches. This commit changes `matches` to
prevent Java field from matching any Scala method, but we can't prevent
them from matching Scala definitions with NotAMethod signature without
introducing more complexity, so the following still doesn't compile
unlike Scala 2:

```java
package pkg;

public class J {
    int i = 0;
    public int i() { return 1; }
}
```

```scala
class S2 extends pkg.J {
  override def i: Int = 2 // error
}
```

I have an alternative fix at scala#9412 which doesn't have this issue but is
more hacky.
@smarter
Copy link
Member Author

smarter commented Jul 27, 2020

We want i to override the public i() method

If we're OK with giving up on this requirement, I have an alternative fix which doesn't involve having OverridingPairs#matches and Denotation#matches do different things: #9452

smarter added a commit to smarter/dotty that referenced this pull request Aug 1, 2020
…d is present

In scala#9126, Java fields were marked effectively finals to prevent false
overrides. This poses a problem when a Java field and method of the same
name are present, because a Scala val or def will match both of these
according to Denotation#matches. This commit changes `matches` to
prevent Java field from matching any Scala method, but we can't prevent
them from matching Scala definitions with NotAMethod signature without
introducing more complexity, so the following still doesn't compile
unlike Scala 2:

```java
package pkg;

public class J {
    int i = 0;
    public int i() { return 1; }
}
```

```scala
class S2 extends pkg.J {
  override def i: Int = 2 // error
}
```

I have an alternative fix at scala#9412 which doesn't have this issue but is
more hacky.
@smarter
Copy link
Member Author

smarter commented Aug 3, 2020

Superceded by #9452

@smarter smarter closed this Aug 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants