Skip to content

Commit 3ae9347

Browse files
committed
Merge pull request #812 from dotty-staging/fix/#806-signature-matching
Fix #806 signature matching
2 parents 7d3a006 + a537ac1 commit 3ae9347

File tree

11 files changed

+59
-75
lines changed

11 files changed

+59
-75
lines changed

src/dotty/tools/dotc/config/Config.scala

-6
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,6 @@ object Config {
7676
*/
7777
final val checkProjections = false
7878

79-
/** When set, use new signature-based matching.
80-
* Advantage of doing so: It's supposed to be faster
81-
* Disadvantage: It might hide inconsistencies, so while debugging it's better to turn it off
82-
*/
83-
final val newMatch = false
84-
8579
/** The recursion depth for showing a summarized string */
8680
final val summarizeDepth = 2
8781

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

+24-19
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ object Denotations {
125125

126126
/** Resolve overloaded denotation to pick the one with the given signature
127127
* when seen from prefix `site`.
128+
* @param relaxed When true, consider only parameter signatures for a match.
128129
*/
129-
def atSignature(sig: Signature, site: Type = NoPrefix)(implicit ctx: Context): SingleDenotation
130+
def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(implicit ctx: Context): SingleDenotation
130131

131132
/** The variant of this denotation that's current in the given context, or
132133
* `NotDefinedHereDenotation` if this denotation does not exist at current phase, but
@@ -221,7 +222,7 @@ object Denotations {
221222
*/
222223
def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation =
223224
if (isOverloaded)
224-
atSignature(targetType.signature, site).matchingDenotation(site, targetType)
225+
atSignature(targetType.signature, site, relaxed = true).matchingDenotation(site, targetType)
225226
else if (exists && !site.memberInfo(symbol).matchesLoosely(targetType))
226227
NoDenotation
227228
else
@@ -268,7 +269,7 @@ object Denotations {
268269
}
269270
case denot1: SingleDenotation =>
270271
if (denot1 eq denot2) denot1
271-
else if (denot1.signature matches denot2.signature) {
272+
else if (denot1.matches(denot2)) {
272273
val info1 = denot1.info
273274
val info2 = denot2.info
274275
val sym1 = denot1.symbol
@@ -282,14 +283,14 @@ object Denotations {
282283
case Nil => true
283284
}
284285
sym1.derivesFrom(sym2) ||
285-
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)
286+
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)
286287
}
287288

288289
/** Preference according to partial pre-order (isConcrete, precedes) */
289290
def preferSym(sym1: Symbol, sym2: Symbol) =
290291
sym1.eq(sym2) ||
291-
sym1.isAsConcrete(sym2) &&
292-
(!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner))
292+
sym1.isAsConcrete(sym2) &&
293+
(!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner))
293294

294295
/** Sym preference provided types also override */
295296
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
@@ -310,8 +311,7 @@ object Denotations {
310311
new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor)
311312
}
312313
}
313-
}
314-
else NoDenotation
314+
} else NoDenotation
315315
}
316316

317317
if (this eq that) this
@@ -333,7 +333,7 @@ object Denotations {
333333
def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
334334

335335
def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation =
336-
if (denot1.signature matches denot2.signature) {
336+
if (denot1.matches(denot2)) {
337337
val sym1 = denot1.symbol
338338
val sym2 = denot2.symbol
339339
val info1 = denot1.info
@@ -396,8 +396,8 @@ object Denotations {
396396
final def validFor = denot1.validFor & denot2.validFor
397397
final def isType = false
398398
final def signature(implicit ctx: Context) = Signature.OverloadedSignature
399-
def atSignature(sig: Signature, site: Type)(implicit ctx: Context): SingleDenotation =
400-
denot1.atSignature(sig, site) orElse denot2.atSignature(sig, site)
399+
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation =
400+
denot1.atSignature(sig, site, relaxed) orElse denot2.atSignature(sig, site, relaxed)
401401
def currentIfExists(implicit ctx: Context): Denotation =
402402
derivedMultiDenotation(denot1.currentIfExists, denot2.currentIfExists)
403403
def current(implicit ctx: Context): Denotation =
@@ -467,9 +467,11 @@ object Denotations {
467467
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation =
468468
if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation
469469

470-
def atSignature(sig: Signature, site: Type)(implicit ctx: Context): SingleDenotation = {
470+
def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = {
471471
val situated = if (site == NoPrefix) this else asSeenFrom(site)
472-
if (sig matches situated.signature) this else NoDenotation
472+
val matches = sig.matchDegree(situated.signature) >=
473+
(if (relaxed) Signature.ParamMatch else Signature.FullMatch)
474+
if (matches) this else NoDenotation
473475
}
474476

475477
// ------ Forming types -------------------------------------------
@@ -778,12 +780,15 @@ object Denotations {
778780
final def last = this
779781
final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
780782
final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
781-
final def containsSig(sig: Signature)(implicit ctx: Context) =
782-
exists && (signature matches sig)
783+
final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
784+
val d = signature.matchDegree(other.signature)
785+
d == Signature.FullMatch ||
786+
d >= Signature.ParamMatch && info.matches(other.info)
787+
}
783788
final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
784789
if (p(this)) this else NoDenotation
785790
final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =
786-
if (denots.exists && denots.containsSig(signature)) NoDenotation else this
791+
if (denots.exists && denots.matches(this)) NoDenotation else this
787792
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =
788793
if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation
789794
else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre)
@@ -872,7 +877,7 @@ object Denotations {
872877
def containsSym(sym: Symbol): Boolean
873878

874879
/** Group contains a denotation with given signature */
875-
def containsSig(sig: Signature)(implicit ctx: Context): Boolean
880+
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean
876881

877882
/** Keep only those denotations in this group which satisfy predicate `p`. */
878883
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation
@@ -933,8 +938,8 @@ object Denotations {
933938
(denots1 toDenot pre) & (denots2 toDenot pre, pre)
934939
def containsSym(sym: Symbol) =
935940
(denots1 containsSym sym) || (denots2 containsSym sym)
936-
def containsSig(sig: Signature)(implicit ctx: Context) =
937-
(denots1 containsSig sig) || (denots2 containsSig sig)
941+
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
942+
denots1.matches(other) || denots2.matches(other)
938943
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation =
939944
derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p)
940945
def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation =

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

+21-5
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,27 @@ 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)
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
45+
46+
/** name.toString == "" or name.toString == "_" */
47+
private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD
3748

3849
/** Construct a signature by prepending the signature names of the given `params`
3950
* to the parameter part of this signature.
@@ -45,6 +56,11 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
4556

4657
object Signature {
4758

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

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

+1-31
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
358358
def compareMethod = tp1 match {
359359
case tp1 @ MethodType(_, formals1) =>
360360
(tp1.signature sameParams tp2.signature) &&
361-
(if (Config.newMatch) subsumeParams(formals1, formals2, tp1.isJava, tp2.isJava)
362-
else matchingParams(formals1, formals2, tp1.isJava, tp2.isJava)) &&
361+
matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
363362
tp1.isImplicit == tp2.isImplicit && // needed?
364363
isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1))
365364
case _ =>
@@ -705,21 +704,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
705704
formals2.isEmpty
706705
}
707706

708-
private def subsumeParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match {
709-
case formal1 :: rest1 =>
710-
formals2 match {
711-
case formal2 :: rest2 =>
712-
(isSubType(formal2, formal1)
713-
|| isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass)
714-
|| isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) &&
715-
subsumeParams(rest1, rest2, isJava1, isJava2)
716-
case nil =>
717-
false
718-
}
719-
case nil =>
720-
formals2.isEmpty
721-
}
722-
723707
/** Do poly types `poly1` and `poly2` have type parameters that
724708
* have the same bounds (after renaming one set to the other)?
725709
*/
@@ -948,13 +932,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
948932
case tp1 @ MethodType(names1, formals1) =>
949933
tp2 match {
950934
case tp2 @ MethodType(names2, formals2)
951-
if Config.newMatch && tp1.signature.sameParams(tp2.signature) &&
952-
tp1.isImplicit == tp2.isImplicit =>
953-
tp1.derivedMethodType(
954-
mergeNames(names1, names2, nme.syntheticParamName),
955-
(formals1 zipWithConserve formals2)(_ | _),
956-
tp1.resultType & tp2.resultType.subst(tp2, tp1))
957-
case tp2 @ MethodType(names2, formals2)
958935
if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
959936
tp1.isImplicit == tp2.isImplicit =>
960937
tp1.derivedMethodType(
@@ -1014,13 +991,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1014991
case tp1 @ MethodType(names1, formals1) =>
1015992
tp2 match {
1016993
case tp2 @ MethodType(names2, formals2)
1017-
if Config.newMatch && tp1.signature.sameParams(tp2.signature) &&
1018-
tp1.isImplicit == tp2.isImplicit =>
1019-
tp1.derivedMethodType(
1020-
mergeNames(names1, names2, nme.syntheticParamName),
1021-
(formals1 zipWithConserve formals2)(_ & _),
1022-
tp1.resultType | tp2.resultType.subst(tp2, tp1))
1023-
case tp2 @ MethodType(names2, formals2)
1024994
if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
1025995
tp1.isImplicit == tp2.isImplicit =>
1026996
tp1.derivedMethodType(

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

+4-6
Original file line numberDiff line numberDiff line change
@@ -647,12 +647,9 @@ object Types {
647647
* (*) when matching with a Java method, we also regard Any and Object as equivalent
648648
* parameter types.
649649
*/
650-
def matches(that: Type)(implicit ctx: Context): Boolean =
651-
if (Config.newMatch) this.signature matches that.signature
652-
else track("matches") {
653-
ctx.typeComparer.matchesType(
654-
this, that, relaxed = !ctx.phase.erasedTypes)
655-
}
650+
def matches(that: Type)(implicit ctx: Context): Boolean = track("matches") {
651+
ctx.typeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes)
652+
}
656653

657654
/** This is the same as `matches` except that it also matches => T with T and
658655
* vice versa.
@@ -1383,6 +1380,7 @@ object Types {
13831380

13841381
lastDenotation = denot
13851382
lastSymbol = denot.symbol
1383+
checkedPeriod = Nowhere
13861384
}
13871385

13881386
private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType =

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ trait Checking {
312312
def checkDecl(decl: Symbol): Unit = {
313313
for (other <- seen(decl.name)) {
314314
typr.println(i"conflict? $decl $other")
315-
if (decl.signature matches other.signature) {
315+
if (decl.matches(other)) {
316316
def doubleDefError(decl: Symbol, other: Symbol): Unit = {
317317
def ofType = if (decl.isType) "" else d": ${other.info}"
318318
def explanation =

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

+1-1
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

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ trait TypeAssigner {
6262
case info: ClassInfo if variance > 0 =>
6363
val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))
6464
def addRefinement(parent: Type, decl: Symbol) = {
65-
val inherited = parentType.findMember(decl.name, info.cls.thisType, Private)
66-
val inheritedInfo = inherited.atSignature(decl.info .signature).info
65+
val inherited =
66+
parentType.findMember(decl.name, info.cls.thisType, Private)
67+
.suchThat(decl.matches(_))
68+
val inheritedInfo = inherited.info
6769
if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info))
6870
typr.echo(
6971
i"add ref $parent $decl --> ",

test/dotc/tests.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class tests extends CompilerTest {
104104
@Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4)
105105
@Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2)
106106
@Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3)
107-
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 12)(allowDoubleBindings)
107+
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 10)(allowDoubleBindings)
108108
@Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2)
109109
@Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2)
110110
@Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3)
@@ -115,7 +115,6 @@ class tests extends CompilerTest {
115115
@Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 11)
116116
@Test def neg_i39 = compileFile(negDir, "i39", xerrors = 2)
117117
@Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 6)
118-
@Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1)
119118
@Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12)
120119

121120
val negTailcallDir = negDir + "tailcall/"

tests/neg/typers.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ object typers {
2222
val z: Int
2323
def z(): String // error: double def
2424

25-
def f(x: Any) = () // error: double def
25+
def f(x: Any) = () // OK!
2626
def f(x: AnyRef): AnyRef
2727

2828
def g(x: Object): Unit
29-
def g[T](x: T): T = x // error: double def
29+
def g[T](x: T): T = x // OK!
3030
}
3131

3232

File renamed without changes.

0 commit comments

Comments
 (0)