Skip to content

Commit

Permalink
Specificity of overloaded constructor of generic class (#8458)
Browse files Browse the repository at this point in the history
Specificity of overloaded constructor of generic class
  • Loading branch information
lrytz committed Oct 10, 2019
2 parents 5892178 + bf9fff4 commit fedee8a
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
28 changes: 25 additions & 3 deletions src/compiler/scala/tools/nsc/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,9 @@ trait Infer extends Checkable {
|| isProperSubClassOrObject(sym1.safeOwner, sym2.owner)
)

// Note that this doesn't consider undetparams -- any type params in `ftpe1/2` need to be bound by their type (i.e. in a PolyType)
// since constructors of poly classes do not have their own polytype in their infos, this must be fixed up
// before calling this method (see memberTypeForSpecificity)
def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type, sym1: Symbol, sym2: Symbol): Boolean = {
// ftpe1 / ftpe2 are OverloadedTypes (possibly with one single alternative) if they
// denote the type of an "apply" member method (see "followApply")
Expand Down Expand Up @@ -1338,6 +1341,25 @@ trait Infer extends Checkable {

/* -- Overload Resolution ---------------------------------------------- */

/** Adjust polymorphic class's constructor info to be polymorphic as well
*
* Normal polymorphic methods have a PolyType as their info, but a constructor reuses the type params of the class.
* We wrap them in a PolyType here so that we get consistent behavior in determining specificity.
*
* @param pre
* @param sym must not be overloaded!
* @return `pre memberType sym`, unless `sym`` is a polymorphic class's constructor,
* in which case a `PolyType` is wrapped around the ctor's info
*/
private def memberTypeForSpecificity(pre: Type, sym: Symbol) = {
val tparsToAdd = if (sym.isConstructor) sym.owner.info.typeParams else Nil

if (tparsToAdd.isEmpty) pre memberType sym
// Need to make sure tparsToAdd are owned by sym (the constructor), and not the class (`sym.owner`).
// Otherwise, asSeenFrom will rewrite them to the corresponding symbols in `pre` (the new this type for `sym.owner`).
else createFromClonedSymbolsAtOwner(tparsToAdd, sym, sym.info)(PolyType(_, _)).asSeenFrom(pre, sym.owner)
}

/** Assign `tree` the symbol and type of the alternative which
* matches prototype `pt`, if it exists.
* If several alternatives match `pt`, take parameterless one.
Expand All @@ -1350,8 +1372,8 @@ trait Infer extends Checkable {
val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
val alts1 = if (alts0.isEmpty) alts else alts0
val bests = bestAlternatives(alts1) { (sym1, sym2) =>
val tp1 = pre memberType sym1
val tp2 = pre memberType sym2
val tp1 = memberTypeForSpecificity(pre, sym1)
val tp2 = memberTypeForSpecificity(pre, sym2)

( (tp2 eq ErrorType)
|| isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
Expand Down Expand Up @@ -1463,7 +1485,7 @@ trait Infer extends Checkable {
case tp => tp
}

private def followType(sym: Symbol) = followApply(pre memberType sym)
private def followType(sym: Symbol) = followApply(memberTypeForSpecificity(pre, sym))
// separate method to help the inliner
private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors }
private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
Expand Down
15 changes: 15 additions & 0 deletions test/files/pos/t11755.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Map[K]
class HashMap[K] extends Map[K]
class IdentityBox[+A]

class IdentityHashMap[K](inner: HashMap[IdentityBox[K]]) extends Map[K] {
def this(initialMap: Map[_ <: K]) = this(new HashMap[IdentityBox[K]]())

def bla[K](inner: HashMap[IdentityBox[K]]): IdentityHashMap[K] = ???
def bla[K](initialMap: Map[_ <: K]): IdentityHashMap[K] = bla(new HashMap[IdentityBox[K]]())

new IdentityHashMap(??? : HashMap[IdentityBox[K]])
bla(??? :HashMap[IdentityBox[K]])
}

// isApplicable true : (inner: HashMap[IdentityBox[K]])IdentityHashMap[K] to List(HashMap[IdentityBox[K]]) for ? under List()

0 comments on commit fedee8a

Please sign in to comment.