Skip to content

Commit fbafb72

Browse files
authored
Merge pull request #9116 from dotty-staging/overload-field-meth
Allow overloads between fields and methods for Java code
2 parents 41388ee + 741685d commit fbafb72

File tree

8 files changed

+82
-36
lines changed

8 files changed

+82
-36
lines changed

compiler/src/dotty/tools/dotc/core/Denotations.scala

+33-14
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Periods._
1414
import Flags._
1515
import DenotTransformers._
1616
import Decorators._
17+
import Signature.MatchDegree._
1718
import printing.Texts._
1819
import printing.Printer
1920
import io.AbstractFile
@@ -612,12 +613,19 @@ object Denotations {
612613
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation =
613614
if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation
614615

615-
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = {
616-
val situated = if (site == NoPrefix) this else asSeenFrom(site)
617-
val matches = sig.matchDegree(situated.signature).ordinal >=
618-
(if (relaxed) Signature.ParamMatch else Signature.FullMatch).ordinal
619-
if (matches) this else NoDenotation
620-
}
616+
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation =
617+
val situated = if site == NoPrefix then this else asSeenFrom(site)
618+
val matches = sig.matchDegree(situated.signature) match
619+
case FullMatch =>
620+
true
621+
case MethodNotAMethodMatch =>
622+
// See comment in `matches`
623+
relaxed && !symbol.is(JavaDefined)
624+
case ParamMatch =>
625+
relaxed
626+
case noMatch =>
627+
false
628+
if matches then this else NoDenotation
621629

622630
def matchesImportBound(bound: Type)(implicit ctx: Context): Boolean =
623631
if bound.isRef(defn.NothingClass) then false
@@ -961,15 +969,26 @@ object Denotations {
961969
final def first: SingleDenotation = this
962970
final def last: SingleDenotation = this
963971

964-
final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
972+
final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
965973
val d = signature.matchDegree(other.signature)
966-
(// fast path: signatures are the same and neither denotation is a PolyType
967-
// For polytypes, signatures alone do not tell us enough to be sure about matching.
968-
d == Signature.FullMatch &&
969-
!infoOrCompleter.isInstanceOf[PolyType] && !other.infoOrCompleter.isInstanceOf[PolyType]
970-
||
971-
d != Signature.NoMatch && info.matches(other.info))
972-
}
974+
975+
/** Slower check used if the signatures alone do not tell us enough to be sure about matching */
976+
def slowCheck = info.matches(other.info)
977+
978+
d match
979+
case FullMatch =>
980+
if infoOrCompleter.isInstanceOf[PolyType] || other.infoOrCompleter.isInstanceOf[PolyType] then
981+
slowCheck
982+
else
983+
true
984+
case MethodNotAMethodMatch =>
985+
// Java allows defining both a field and a zero-parameter method with the same name
986+
!ctx.erasedTypes && !(symbol.is(JavaDefined) && other.symbol.is(JavaDefined))
987+
case ParamMatch =>
988+
!ctx.erasedTypes && slowCheck
989+
case noMatch =>
990+
false
991+
end matches
973992

974993
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =
975994
if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation

compiler/src/dotty/tools/dotc/core/Signature.scala

+25-16
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,7 @@ case class Signature(paramsSig: List[ParamSig], resSig: TypeName) {
6060
@tailrec def loop(names1: List[ParamSig], names2: List[ParamSig]): Boolean =
6161
if (names1.isEmpty) names2.isEmpty
6262
else !names2.isEmpty && consistent(names1.head, names2.head) && loop(names1.tail, names2.tail)
63-
if (ctx.erasedTypes && (this == NotAMethod) != (that == NotAMethod))
64-
false // After erasure, we allow fields and parameterless methods with the same name.
65-
// This is needed to allow both a module field and a bridge method for an abstract val.
66-
// Test case is patmatch-classtag.scala
67-
else
68-
loop(this.paramsSig, that.paramsSig)
63+
loop(this.paramsSig, that.paramsSig)
6964
}
7065

7166
/** `that` signature, but keeping all corresponding parts of `this` signature. */
@@ -87,23 +82,27 @@ case class Signature(paramsSig: List[ParamSig], resSig: TypeName) {
8782
/** The degree to which this signature matches `that`.
8883
* If parameter signatures are consistent and result types names match (i.e. they are the same
8984
* or one is a wildcard), the result is `FullMatch`.
90-
* If only the parameter signatures are consistent, the result is `ParamMatch` before erasure and
91-
* `NoMatch` otherwise.
85+
* If only the parameter signatures are consistent, the result is either
86+
* `MethodNotAMethodMatch` (if one side is a method signature and the other isn't),
87+
* or `ParamMatch`.
9288
* If the parameters are inconsistent, the result is always `NoMatch`.
9389
*/
9490
final def matchDegree(that: Signature)(implicit ctx: Context): MatchDegree =
95-
if (consistentParams(that))
96-
if (resSig == that.resSig || isWildcard(resSig) || isWildcard(that.resSig)) FullMatch
97-
else if (!ctx.erasedTypes) ParamMatch
98-
else NoMatch
99-
else NoMatch
91+
if consistentParams(that) then
92+
if resSig == that.resSig || isWildcard(resSig) || isWildcard(that.resSig) then
93+
FullMatch
94+
else if (this == NotAMethod) != (that == NotAMethod) then
95+
MethodNotAMethodMatch
96+
else
97+
ParamMatch
98+
else
99+
NoMatch
100100

101101
/** Does this signature potentially clash with `that` ? */
102102
def clashes(that: Signature)(implicit ctx: Context): Boolean =
103103
matchDegree(that) == FullMatch
104104

105-
/** name.toString == "" or name.toString == "_" */
106-
private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD
105+
private def isWildcard(name: TypeName) = name == tpnme.WILDCARD
107106

108107
/** Construct a signature by prepending the signature names of the given `params`
109108
* to the parameter part of this signature.
@@ -136,7 +135,17 @@ object Signature {
136135
// small values, so the performance hit should be minimal.
137136

138137
enum MatchDegree {
139-
case NoMatch, ParamMatch, FullMatch
138+
/** The signatures are unrelated. */
139+
case NoMatch
140+
/** The parameter signatures are equivalent. */
141+
case ParamMatch
142+
/** Both signatures have no parameters, one is a method and the other isn't.
143+
*
144+
* @see NotAMethod
145+
*/
146+
case MethodNotAMethodMatch
147+
/** The parameter and result type signatures are equivalent. */
148+
case FullMatch
140149
}
141150
export MatchDegree._
142151

compiler/src/dotty/tools/dotc/reporting/messages.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -2015,19 +2015,23 @@ object messages {
20152015
def nameAnd = if (decl.name != previousDecl.name) " name and" else ""
20162016
def details(implicit ctx: Context): String =
20172017
if (decl.isRealMethod && previousDecl.isRealMethod) {
2018+
import Signature.MatchDegree._
2019+
20182020
// compare the signatures when both symbols represent methods
20192021
decl.signature.matchDegree(previousDecl.signature) match {
2020-
case Signature.MatchDegree.NoMatch =>
2022+
case NoMatch =>
20212023
// If the signatures don't match at all at the current phase, then
20222024
// they might match after erasure.
20232025
val elimErasedCtx = ctx.withPhaseNoEarlier(ctx.elimErasedValueTypePhase.next)
20242026
if (elimErasedCtx != ctx)
20252027
details(elimErasedCtx)
20262028
else
20272029
"" // shouldn't be reachable
2028-
case Signature.MatchDegree.ParamMatch =>
2030+
case ParamMatch =>
20292031
"have matching parameter types."
2030-
case Signature.MatchDegree.FullMatch =>
2032+
case MethodNotAMethodMatch =>
2033+
"neither has parameters."
2034+
case FullMatch =>
20312035
i"have the same$nameAnd type after erasure."
20322036
}
20332037
}

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import dotty.tools.dotc.ast.{TreeTypeMap, Trees, tpd, untpd}
66
import dotty.tools.dotc.typer.{Implicits, Typer}
77
import dotty.tools.dotc.core._
88
import dotty.tools.dotc.core.Flags._
9-
import dotty.tools.dotc.core.StdNames.nme
9+
import dotty.tools.dotc.core.StdNames._
1010
import dotty.tools.dotc.core.quoted.PickledQuotes
1111
import dotty.tools.dotc.core.Symbols._
1212
import dotty.tools.dotc.core.Decorators._
@@ -2103,7 +2103,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
21032103
val tpe = tpt.tpe.dropDependentRefinement
21042104
// we checked that this is a plain Function closure, so there will be an apply method with a MethodType
21052105
// and the expected signature based on param types
2106-
val expectedSig = Signature.NotAMethod.prependTermParams(paramTypes, false)
2106+
val expectedSig = Signature(Nil, tpnme.WILDCARD).prependTermParams(paramTypes, false)
21072107
val method = tpt.tpe.member(nme.apply).atSignature(expectedSig)
21082108
if method.symbol.is(Deferred) then
21092109
val methodType = method.info.asInstanceOf[MethodType]

project/Build.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ object Build {
10561056

10571057
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/utils" ** "*.scala").get
10581058
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/junit" ** "*.scala").get
1059-
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/niobuffer" ** (("*.scala": FileFilter) -- "ByteBufferTest.scala")).get
1059+
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/niobuffer" ** "*.scala").get
10601060
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/niocharset" ** (("*.scala": FileFilter) -- "BaseCharsetTest.scala" -- "Latin1Test.scala" -- "USASCIITest.scala" -- "UTF16Test.scala" -- "UTF8Test.scala")).get
10611061
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/scalalib" ** (("*.scala": FileFilter) -- "ArrayBuilderTest.scala" -- "ClassTagTest.scala" -- "EnumerationTest.scala" -- "SymbolTest.scala")).get
10621062
++ (dir / "shared/src/test/require-sam" ** "*.scala").get
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class A {
2+
public String foo = "";
3+
public int foo() { return 1; }
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test {
2+
val a = new A
3+
val b = a.foo
4+
val bs: String = b
5+
val c = a.foo()
6+
val ci: Int = c
7+
}

tests/pos/i7132.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test {
2+
java.nio.ByteBuffer.allocate(1).isReadOnly
3+
}

0 commit comments

Comments
 (0)