Skip to content

Commit 25431a9

Browse files
committed
Refinements to signature matching
1) Matching after erasure also takes wildcards into account (before it didn't). 2) Combine all signature matching operations into a single matchDegree method.
1 parent ee4e2e0 commit 25431a9

File tree

3 files changed

+62
-57
lines changed

3 files changed

+62
-57
lines changed

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

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -269,52 +269,49 @@ object Denotations {
269269
}
270270
case denot1: SingleDenotation =>
271271
if (denot1 eq denot2) denot1
272-
else {
272+
else if (denot1.matches(denot2)) {
273273
val info1 = denot1.info
274274
val info2 = denot2.info
275-
if (denot1.signature.matches(denot2.signature) &&
276-
denot1.info.matches(denot2.info)) {
277-
val sym1 = denot1.symbol
278-
val sym2 = denot2.symbol
279-
val sym2Accessible = sym2.isAccessibleFrom(pre)
280-
281-
/** Does `sym1` come before `sym2` in the linearization of `pre`? */
282-
def precedes(sym1: Symbol, sym2: Symbol) = {
283-
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
284-
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
285-
case Nil => true
286-
}
287-
sym1.derivesFrom(sym2) ||
288-
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)
275+
val sym1 = denot1.symbol
276+
val sym2 = denot2.symbol
277+
val sym2Accessible = sym2.isAccessibleFrom(pre)
278+
279+
/** Does `sym1` come before `sym2` in the linearization of `pre`? */
280+
def precedes(sym1: Symbol, sym2: Symbol) = {
281+
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
282+
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
283+
case Nil => true
289284
}
285+
sym1.derivesFrom(sym2) ||
286+
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)
287+
}
290288

291-
/** Preference according to partial pre-order (isConcrete, precedes) */
292-
def preferSym(sym1: Symbol, sym2: Symbol) =
293-
sym1.eq(sym2) ||
294-
sym1.isAsConcrete(sym2) &&
295-
(!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner))
289+
/** Preference according to partial pre-order (isConcrete, precedes) */
290+
def preferSym(sym1: Symbol, sym2: Symbol) =
291+
sym1.eq(sym2) ||
292+
sym1.isAsConcrete(sym2) &&
293+
(!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner))
296294

297-
/** Sym preference provided types also override */
298-
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
299-
preferSym(sym1, sym2) && info1.overrides(info2)
295+
/** Sym preference provided types also override */
296+
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
297+
preferSym(sym1, sym2) && info1.overrides(info2)
300298

301-
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
299+
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
300+
else {
301+
val sym1Accessible = sym1.isAccessibleFrom(pre)
302+
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
303+
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
304+
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
302305
else {
303-
val sym1Accessible = sym1.isAccessibleFrom(pre)
304-
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
305-
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
306-
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
307-
else {
308-
val sym =
309-
if (!sym1.exists) sym2
310-
else if (!sym2.exists) sym1
311-
else if (preferSym(sym2, sym1)) sym2
312-
else sym1
313-
new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor)
314-
}
306+
val sym =
307+
if (!sym1.exists) sym2
308+
else if (!sym2.exists) sym1
309+
else if (preferSym(sym2, sym1)) sym2
310+
else sym1
311+
new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor)
315312
}
316-
} else NoDenotation
317-
}
313+
}
314+
} else NoDenotation
318315
}
319316

320317
if (this eq that) this
@@ -472,14 +469,16 @@ object Denotations {
472469

473470
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = {
474471
val situated = if (site == NoPrefix) this else asSeenFrom(site)
475-
val matches =
476-
if (relaxed) sig.matches(situated.signature)
477-
else sig.matchesFully(situated.signature)
472+
val matches = sig.matchDegree(situated.signature) >=
473+
(if (relaxed) Signature.ParamMatch else Signature.FullMatch)
478474
if (matches) this else NoDenotation
479475
}
480476

481-
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
482-
signature.matches(other.signature) && info.matches(other.info)
477+
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
478+
val d = signature.matchDegree(other.signature)
479+
d == Signature.FullMatch ||
480+
d >= Signature.ParamMatch && info.matches(other.info)
481+
}
483482

484483
// ------ Forming types -------------------------------------------
485484

@@ -788,7 +787,7 @@ object Denotations {
788787
final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
789788
final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
790789
final def containsSig(sig: Signature)(implicit ctx: Context) =
791-
exists && (signature matches sig)
790+
exists && signature.matchDegree(sig) >= Signature.ParamMatch
792791
final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
793792
if (p(this)) this else NoDenotation
794793
final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,24 @@ import TypeErasure.sigName
2424
* The signatures of non-method types are always `NotAMethod`.
2525
*/
2626
case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
27+
import Signature._
2728

2829
/** Does this signature coincide with that signature on their parameter parts? */
2930
final def sameParams(that: Signature): Boolean = this.paramsSig == that.paramsSig
3031

31-
/** The meaning of `matches` depends on the phase. If types are not erased,
32-
* it means `sameParams`. Once types are erased, it means `==`, comparing parameter as
33-
* well as result type parts.
32+
/** The degree to which this signature matches `that`.
33+
* If both parameter and result type names match (i.e. they are the same
34+
* or one is a wildcard), the result is `FullMatch`.
35+
* If only the parameter names match, the result is `ParamMatch` before erasure and
36+
* `NoMatch` otherwise.
37+
* If the parameters do not match, the result is always `NoMatch`.
3438
*/
35-
final def matches(that: Signature)(implicit ctx: Context) =
36-
if (ctx.erasedTypes) equals(that) else sameParams(that)
37-
38-
/** A signature matches fully another if it has the same parameter type names
39-
* and either one of the result type names is a wildcard or both agree.
40-
*/
41-
final def matchesFully(that: Signature)(implicit ctx: Context) =
42-
this.paramsSig == that.paramsSig &&
43-
(isWildcard(this.resSig) || isWildcard(that.resSig) || this.resSig == that.resSig)
39+
final def matchDegree(that: Signature)(implicit ctx: Context): MatchDegree =
40+
if (sameParams(that))
41+
if (resSig == that.resSig || isWildcard(resSig) || isWildcard(that.resSig)) FullMatch
42+
else if (!ctx.erasedTypes) ParamMatch
43+
else NoMatch
44+
else NoMatch
4445

4546
/** name.toString == "" or name.toString == "_" */
4647
private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD
@@ -55,6 +56,11 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
5556

5657
object Signature {
5758

59+
type MatchDegree = Int
60+
val NoMatch = 0
61+
val ParamMatch = 1
62+
val FullMatch = 2
63+
5864
/** The signature of everything that's not a method, i.e. that has
5965
* a type different from PolyType, MethodType, or ExprType.
6066
*/

src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ object RefChecks {
369369
clazz.info.nonPrivateMember(sym.name).hasAltWith { alt =>
370370
alt.symbol.is(JavaDefined, butNot = Deferred) &&
371371
!sym.owner.derivesFrom(alt.symbol.owner) &&
372-
alt.signature.matches(sym.signature)
372+
alt.matches(sym)
373373
}
374374
}
375375

0 commit comments

Comments
 (0)