From 9c8e247d8900f3fb6f497bc5e269b68f6bc967b7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 17:29:53 +0200 Subject: [PATCH 1/8] Matching denotations should take infos into account Whenchecking whether two denotations match it is not enough to look at the signatures. The signatures might match (on the parameters) but the actual parametre types might be different. The change always tests infos after signatures, effectively turning the signature test into a pre-filter. --- src/dotty/tools/dotc/core/Denotations.scala | 77 ++++++++++--------- src/dotty/tools/dotc/typer/Checking.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 +- test/dotc/tests.scala | 3 +- tests/neg/typers.scala | 4 +- 5 files changed, 47 insertions(+), 42 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 16a151e89ddd..cd46918cf0e6 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -268,50 +268,52 @@ object Denotations { } case denot1: SingleDenotation => if (denot1 eq denot2) denot1 - else if (denot1.signature matches denot2.signature) { + else { val info1 = denot1.info val info2 = denot2.info - val sym1 = denot1.symbol - val sym2 = denot2.symbol - val sym2Accessible = sym2.isAccessibleFrom(pre) - - /** Does `sym1` come before `sym2` in the linearization of `pre`? */ - def precedes(sym1: Symbol, sym2: Symbol) = { - def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) - case Nil => true + if (denot1.signature.matches(denot2.signature) && + denot1.info.matches(denot2.info)) { + val sym1 = denot1.symbol + val sym2 = denot2.symbol + val sym2Accessible = sym2.isAccessibleFrom(pre) + + /** Does `sym1` come before `sym2` in the linearization of `pre`? */ + def precedes(sym1: Symbol, sym2: Symbol) = { + def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) + case Nil => true + } + sym1.derivesFrom(sym2) || + !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) } - sym1.derivesFrom(sym2) || - !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) - } - /** Preference according to partial pre-order (isConcrete, precedes) */ - def preferSym(sym1: Symbol, sym2: Symbol) = - sym1.eq(sym2) || - sym1.isAsConcrete(sym2) && - (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) + /** Preference according to partial pre-order (isConcrete, precedes) */ + def preferSym(sym1: Symbol, sym2: Symbol) = + sym1.eq(sym2) || + sym1.isAsConcrete(sym2) && + (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) - /** Sym preference provided types also override */ - def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = - preferSym(sym1, sym2) && info1.overrides(info2) + /** Sym preference provided types also override */ + def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = + preferSym(sym1, sym2) && info1.overrides(info2) - if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 - else { - val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 - else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 - else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 + if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 else { - val sym = - if (!sym1.exists) sym2 - else if (!sym2.exists) sym1 - else if (preferSym(sym2, sym1)) sym2 - else sym1 - new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) + val sym1Accessible = sym1.isAccessibleFrom(pre) + if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 + else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 + else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 + else { + val sym = + if (!sym1.exists) sym2 + else if (!sym2.exists) sym1 + else if (preferSym(sym2, sym1)) sym2 + else sym1 + new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) + } } - } + } else NoDenotation } - else NoDenotation } if (this eq that) this @@ -333,7 +335,7 @@ object Denotations { def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = { def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation = - if (denot1.signature matches denot2.signature) { + if (denot1.matches(denot2)) { val sym1 = denot1.symbol val sym2 = denot2.symbol val info1 = denot1.info @@ -472,6 +474,9 @@ object Denotations { if (sig matches situated.signature) this else NoDenotation } + def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = + signature.matches(other.signature) && info.matches(other.info) + // ------ Forming types ------------------------------------------- /** The TypeRef representing this type denotation at its original location. */ diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 8376dd4e9619..9092523dbb55 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -312,7 +312,7 @@ trait Checking { def checkDecl(decl: Symbol): Unit = { for (other <- seen(decl.name)) { typr.println(i"conflict? $decl $other") - if (decl.signature matches other.signature) { + if (decl.matches(other)) { def doubleDefError(decl: Symbol, other: Symbol): Unit = { def ofType = if (decl.isType) "" else d": ${other.info}" def explanation = diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1ae0bec0c2fa..3247592c3c89 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -63,7 +63,8 @@ trait TypeAssigner { val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) def addRefinement(parent: Type, decl: Symbol) = { val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) - val inheritedInfo = inherited.atSignature(decl.info .signature).info + val inheritedInfo = inherited.atSignature(decl.info.signature).info + // @smarter atSignature probably wrong now; we are now missing out on types that refine the result type if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) typr.echo( i"add ref $parent $decl --> ", diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 403a99a190b3..2b5b86be18e6 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -104,7 +104,7 @@ class tests extends CompilerTest { @Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4) @Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2) @Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3) - @Test def neg_typers() = compileFile(negDir, "typers", xerrors = 12)(allowDoubleBindings) + @Test def neg_typers() = compileFile(negDir, "typers", xerrors = 10)(allowDoubleBindings) @Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2) @Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2) @Test def neg_templateParents() = compileFile(negDir, "templateParents", xerrors = 3) @@ -115,7 +115,6 @@ class tests extends CompilerTest { @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 11) @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 2) @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 6) - @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) @Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12) val negTailcallDir = negDir + "tailcall/" diff --git a/tests/neg/typers.scala b/tests/neg/typers.scala index b5bd1fa2c129..9fcc63e38562 100644 --- a/tests/neg/typers.scala +++ b/tests/neg/typers.scala @@ -22,11 +22,11 @@ object typers { val z: Int def z(): String // error: double def - def f(x: Any) = () // error: double def + def f(x: Any) = () // OK! def f(x: AnyRef): AnyRef def g(x: Object): Unit - def g[T](x: T): T = x // error: double def + def g[T](x: T): T = x // OK! } From 5b07f556ebb1e87d5b5c87c6375fa33c29c9a72d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 17:33:09 +0200 Subject: [PATCH 2/8] Eliminate Config.newMatch option With the new approach to matching it is no longer sound. We always have to match infos anyway to be sure. --- src/dotty/tools/dotc/config/Config.scala | 6 ---- src/dotty/tools/dotc/core/TypeComparer.scala | 32 +------------------- src/dotty/tools/dotc/core/Types.scala | 9 ++---- 3 files changed, 4 insertions(+), 43 deletions(-) diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index d66d1ecdb898..7e3615416b85 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -76,12 +76,6 @@ object Config { */ final val checkProjections = false - /** When set, use new signature-based matching. - * Advantage of doing so: It's supposed to be faster - * Disadvantage: It might hide inconsistencies, so while debugging it's better to turn it off - */ - final val newMatch = false - /** The recursion depth for showing a summarized string */ final val summarizeDepth = 2 diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index fce803c56866..e3e5f3960eec 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -358,8 +358,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def compareMethod = tp1 match { case tp1 @ MethodType(_, formals1) => (tp1.signature sameParams tp2.signature) && - (if (Config.newMatch) subsumeParams(formals1, formals2, tp1.isJava, tp2.isJava) - else matchingParams(formals1, formals2, tp1.isJava, tp2.isJava)) && + matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && tp1.isImplicit == tp2.isImplicit && // needed? isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => @@ -705,21 +704,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { formals2.isEmpty } - private def subsumeParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - (isSubType(formal2, formal1) - || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) - || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && - subsumeParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - /** Do poly types `poly1` and `poly2` have type parameters that * have the same bounds (after renaming one set to the other)? */ @@ -948,13 +932,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1 @ MethodType(names1, formals1) => tp2 match { case tp2 @ MethodType(names2, formals2) - if Config.newMatch && tp1.signature.sameParams(tp2.signature) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - (formals1 zipWithConserve formals2)(_ | _), - tp1.resultType & tp2.resultType.subst(tp2, tp1)) - case tp2 @ MethodType(names2, formals2) if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && tp1.isImplicit == tp2.isImplicit => tp1.derivedMethodType( @@ -1014,13 +991,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1 @ MethodType(names1, formals1) => tp2 match { case tp2 @ MethodType(names2, formals2) - if Config.newMatch && tp1.signature.sameParams(tp2.signature) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - (formals1 zipWithConserve formals2)(_ & _), - tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case tp2 @ MethodType(names2, formals2) if matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && tp1.isImplicit == tp2.isImplicit => tp1.derivedMethodType( diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index e545066af221..c35b9a35a34c 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -647,12 +647,9 @@ object Types { * (*) when matching with a Java method, we also regard Any and Object as equivalent * parameter types. */ - def matches(that: Type)(implicit ctx: Context): Boolean = - if (Config.newMatch) this.signature matches that.signature - else track("matches") { - ctx.typeComparer.matchesType( - this, that, relaxed = !ctx.phase.erasedTypes) - } + def matches(that: Type)(implicit ctx: Context): Boolean = track("matches") { + ctx.typeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes) + } /** This is the same as `matches` except that it also matches => T with T and * vice versa. From 2d9dcecc5757e4bd8659e78a94119c61ebc81a14 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 18:21:15 +0200 Subject: [PATCH 3/8] Fix problem with avoid. When determining what to refine we should not rely only on signatures but we need full denotation matching. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 3247592c3c89..ba8d44110b10 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -62,9 +62,10 @@ trait TypeAssigner { case info: ClassInfo if variance > 0 => val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) def addRefinement(parent: Type, decl: Symbol) = { - val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) - val inheritedInfo = inherited.atSignature(decl.info.signature).info - // @smarter atSignature probably wrong now; we are now missing out on types that refine the result type + val inherited = + parentType.findMember(decl.name, info.cls.thisType, Private) + .suchThat(decl.matches(_)) + val inheritedInfo = inherited.info if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) typr.echo( i"add ref $parent $decl --> ", From a336ec0a700c0902c74d5318ab47929bc28c5413 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 19:15:56 +0200 Subject: [PATCH 4/8] Refine atSignature atSignature should also check result type names, except - if one of the result is a wildcard - a boolean flag relaxed is explicitly set --- src/dotty/tools/dotc/core/Denotations.scala | 16 ++++++++++------ src/dotty/tools/dotc/core/Signature.scala | 10 ++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index cd46918cf0e6..3bab19ea9ffa 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -125,8 +125,9 @@ object Denotations { /** Resolve overloaded denotation to pick the one with the given signature * when seen from prefix `site`. + * @param relaxed When true, consider only parameter signatures for a match. */ - def atSignature(sig: Signature, site: Type = NoPrefix)(implicit ctx: Context): SingleDenotation + def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(implicit ctx: Context): SingleDenotation /** The variant of this denotation that's current in the given context, or * `NotDefinedHereDenotation` if this denotation does not exist at current phase, but @@ -221,7 +222,7 @@ object Denotations { */ def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = if (isOverloaded) - atSignature(targetType.signature, site).matchingDenotation(site, targetType) + atSignature(targetType.signature, site, relaxed = true).matchingDenotation(site, targetType) else if (exists && !site.memberInfo(symbol).matchesLoosely(targetType)) NoDenotation else @@ -398,8 +399,8 @@ object Denotations { final def validFor = denot1.validFor & denot2.validFor final def isType = false final def signature(implicit ctx: Context) = Signature.OverloadedSignature - def atSignature(sig: Signature, site: Type)(implicit ctx: Context): SingleDenotation = - denot1.atSignature(sig, site) orElse denot2.atSignature(sig, site) + def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = + denot1.atSignature(sig, site, relaxed) orElse denot2.atSignature(sig, site, relaxed) def currentIfExists(implicit ctx: Context): Denotation = derivedMultiDenotation(denot1.currentIfExists, denot2.currentIfExists) def current(implicit ctx: Context): Denotation = @@ -469,9 +470,12 @@ object Denotations { def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation - def atSignature(sig: Signature, site: Type)(implicit ctx: Context): SingleDenotation = { + def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = { val situated = if (site == NoPrefix) this else asSeenFrom(site) - if (sig matches situated.signature) this else NoDenotation + val matches = + if (relaxed) sig.matches(situated.signature) + else sig.matchesFully(situated.signature) + if (matches) this else NoDenotation } def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala index 4d83a796329e..317f45e65b31 100644 --- a/src/dotty/tools/dotc/core/Signature.scala +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -35,6 +35,16 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) { final def matches(that: Signature)(implicit ctx: Context) = if (ctx.erasedTypes) equals(that) else sameParams(that) + /** A signature matches fully another if it has the same parameter type names + * and either one of the result type names is a wildcard or both agree. + */ + final def matchesFully(that: Signature)(implicit ctx: Context) = + this.paramsSig == that.paramsSig && + (isWildcard(this.resSig) || isWildcard(that.resSig) || this.resSig == that.resSig) + + /** name.toString == "" or name.toString == "_" */ + private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD + /** Construct a signature by prepending the signature names of the given `params` * to the parameter part of this signature. */ From 48a1f089dd34a4554e98bb1edcc08eb7ebaee384 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Sep 2015 19:18:12 +0200 Subject: [PATCH 5/8] Move test to pos A test that checked for errors on overloading now succeeds with the new rules. --- tests/{neg => pos}/t0273.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{neg => pos}/t0273.scala (100%) diff --git a/tests/neg/t0273.scala b/tests/pos/t0273.scala similarity index 100% rename from tests/neg/t0273.scala rename to tests/pos/t0273.scala From ee4e2e0178d0db6494f2a971f5a5b9d3c8f732db Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 30 Sep 2015 13:52:38 +0200 Subject: [PATCH 6/8] Fix a long sleeper bug When setting the denotation we did not change the checked period. This can lead to a situation where a denotation is set to NoDenotation, yet the checked period is something else. This means in effect the denotation will vanish at the checked period. This bug caused the junit test failure about "non-member exception" for sourceFile in DottyBackendInterface. --- src/dotty/tools/dotc/core/Types.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index c35b9a35a34c..bce57ae23757 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1380,6 +1380,7 @@ object Types { lastDenotation = denot lastSymbol = denot.symbol + checkedPeriod = Nowhere } private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType = From 25431a96c849c878c577e7449d9f8eeec9f94328 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 30 Sep 2015 14:39:59 +0200 Subject: [PATCH 7/8] 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. --- src/dotty/tools/dotc/core/Denotations.scala | 87 ++++++++++----------- src/dotty/tools/dotc/core/Signature.scala | 30 ++++--- src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- 3 files changed, 62 insertions(+), 57 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 3bab19ea9ffa..232206652b28 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -269,52 +269,49 @@ object Denotations { } case denot1: SingleDenotation => if (denot1 eq denot2) denot1 - else { + else if (denot1.matches(denot2)) { val info1 = denot1.info val info2 = denot2.info - if (denot1.signature.matches(denot2.signature) && - denot1.info.matches(denot2.info)) { - val sym1 = denot1.symbol - val sym2 = denot2.symbol - val sym2Accessible = sym2.isAccessibleFrom(pre) - - /** Does `sym1` come before `sym2` in the linearization of `pre`? */ - def precedes(sym1: Symbol, sym2: Symbol) = { - def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) - case Nil => true - } - sym1.derivesFrom(sym2) || - !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) + val sym1 = denot1.symbol + val sym2 = denot2.symbol + val sym2Accessible = sym2.isAccessibleFrom(pre) + + /** Does `sym1` come before `sym2` in the linearization of `pre`? */ + def precedes(sym1: Symbol, sym2: Symbol) = { + def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) + case Nil => true } + sym1.derivesFrom(sym2) || + !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) + } - /** Preference according to partial pre-order (isConcrete, precedes) */ - def preferSym(sym1: Symbol, sym2: Symbol) = - sym1.eq(sym2) || - sym1.isAsConcrete(sym2) && - (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) + /** Preference according to partial pre-order (isConcrete, precedes) */ + def preferSym(sym1: Symbol, sym2: Symbol) = + sym1.eq(sym2) || + sym1.isAsConcrete(sym2) && + (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) - /** Sym preference provided types also override */ - def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = - preferSym(sym1, sym2) && info1.overrides(info2) + /** Sym preference provided types also override */ + def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = + preferSym(sym1, sym2) && info1.overrides(info2) - if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 + if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 + else { + val sym1Accessible = sym1.isAccessibleFrom(pre) + if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 + else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 + else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 else { - val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 - else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 - else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 - else { - val sym = - if (!sym1.exists) sym2 - else if (!sym2.exists) sym1 - else if (preferSym(sym2, sym1)) sym2 - else sym1 - new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) - } + val sym = + if (!sym1.exists) sym2 + else if (!sym2.exists) sym1 + else if (preferSym(sym2, sym1)) sym2 + else sym1 + new JointRefDenotation(sym, info1 & info2, denot1.validFor & denot2.validFor) } - } else NoDenotation - } + } + } else NoDenotation } if (this eq that) this @@ -472,14 +469,16 @@ object Denotations { def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = { val situated = if (site == NoPrefix) this else asSeenFrom(site) - val matches = - if (relaxed) sig.matches(situated.signature) - else sig.matchesFully(situated.signature) + val matches = sig.matchDegree(situated.signature) >= + (if (relaxed) Signature.ParamMatch else Signature.FullMatch) if (matches) this else NoDenotation } - def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = - signature.matches(other.signature) && info.matches(other.info) + def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = { + val d = signature.matchDegree(other.signature) + d == Signature.FullMatch || + d >= Signature.ParamMatch && info.matches(other.info) + } // ------ Forming types ------------------------------------------- @@ -788,7 +787,7 @@ object Denotations { final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) final def containsSig(sig: Signature)(implicit ctx: Context) = - exists && (signature matches sig) + exists && signature.matchDegree(sig) >= Signature.ParamMatch final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation = if (p(this)) this else NoDenotation final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation = diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala index 317f45e65b31..54771bae5101 100644 --- a/src/dotty/tools/dotc/core/Signature.scala +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -24,23 +24,24 @@ import TypeErasure.sigName * The signatures of non-method types are always `NotAMethod`. */ case class Signature(paramsSig: List[TypeName], resSig: TypeName) { + import Signature._ /** Does this signature coincide with that signature on their parameter parts? */ final def sameParams(that: Signature): Boolean = this.paramsSig == that.paramsSig - /** The meaning of `matches` depends on the phase. If types are not erased, - * it means `sameParams`. Once types are erased, it means `==`, comparing parameter as - * well as result type parts. + /** The degree to which this signature matches `that`. + * If both parameter and result type names match (i.e. they are the same + * or one is a wildcard), the result is `FullMatch`. + * If only the parameter names match, the result is `ParamMatch` before erasure and + * `NoMatch` otherwise. + * If the parameters do not match, the result is always `NoMatch`. */ - final def matches(that: Signature)(implicit ctx: Context) = - if (ctx.erasedTypes) equals(that) else sameParams(that) - - /** A signature matches fully another if it has the same parameter type names - * and either one of the result type names is a wildcard or both agree. - */ - final def matchesFully(that: Signature)(implicit ctx: Context) = - this.paramsSig == that.paramsSig && - (isWildcard(this.resSig) || isWildcard(that.resSig) || this.resSig == that.resSig) + final def matchDegree(that: Signature)(implicit ctx: Context): MatchDegree = + if (sameParams(that)) + if (resSig == that.resSig || isWildcard(resSig) || isWildcard(that.resSig)) FullMatch + else if (!ctx.erasedTypes) ParamMatch + else NoMatch + else NoMatch /** name.toString == "" or name.toString == "_" */ private def isWildcard(name: TypeName) = name.isEmpty || name == tpnme.WILDCARD @@ -55,6 +56,11 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) { object Signature { + type MatchDegree = Int + val NoMatch = 0 + val ParamMatch = 1 + val FullMatch = 2 + /** The signature of everything that's not a method, i.e. that has * a type different from PolyType, MethodType, or ExprType. */ diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 71fba158871c..ae854ed9a245 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -369,7 +369,7 @@ object RefChecks { clazz.info.nonPrivateMember(sym.name).hasAltWith { alt => alt.symbol.is(JavaDefined, butNot = Deferred) && !sym.owner.derivesFrom(alt.symbol.owner) && - alt.signature.matches(sym.signature) + alt.matches(sym) } } From a537ac19fb5623115384da7dd40ba0f7dce17479 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 30 Sep 2015 15:01:36 +0200 Subject: [PATCH 8/8] Replace `containsSig` with `matches`. containsSig still used param-only matching, which is incorrect in the new system, because different overloaded methods may have the same parameter signature. --- src/dotty/tools/dotc/core/Denotations.scala | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 232206652b28..fff4478037a4 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -474,12 +474,6 @@ object Denotations { if (matches) this else NoDenotation } - def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = { - val d = signature.matchDegree(other.signature) - d == Signature.FullMatch || - d >= Signature.ParamMatch && info.matches(other.info) - } - // ------ Forming types ------------------------------------------- /** The TypeRef representing this type denotation at its original location. */ @@ -786,12 +780,15 @@ object Denotations { final def last = this final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) - final def containsSig(sig: Signature)(implicit ctx: Context) = - exists && signature.matchDegree(sig) >= Signature.ParamMatch + final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = { + val d = signature.matchDegree(other.signature) + d == Signature.FullMatch || + d >= Signature.ParamMatch && info.matches(other.info) + } final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation = if (p(this)) this else NoDenotation final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation = - if (denots.exists && denots.containsSig(signature)) NoDenotation else this + if (denots.exists && denots.matches(this)) NoDenotation else this def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation = if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre) @@ -880,7 +877,7 @@ object Denotations { def containsSym(sym: Symbol): Boolean /** Group contains a denotation with given signature */ - def containsSig(sig: Signature)(implicit ctx: Context): Boolean + def matches(other: SingleDenotation)(implicit ctx: Context): Boolean /** Keep only those denotations in this group which satisfy predicate `p`. */ def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation @@ -941,8 +938,8 @@ object Denotations { (denots1 toDenot pre) & (denots2 toDenot pre, pre) def containsSym(sym: Symbol) = (denots1 containsSym sym) || (denots2 containsSym sym) - def containsSig(sig: Signature)(implicit ctx: Context) = - (denots1 containsSig sig) || (denots2 containsSig sig) + def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = + denots1.matches(other) || denots2.matches(other) def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation = derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p) def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation =