Skip to content

Commit bf9fff4

Browse files
committed
Specificity of overloaded constructor of generic class
There are two phases to picking a winning overload for the constructor of a polymorphic class, but the type params are not handled consistently: - isApplicable: we put the class's type params in undetparams - isAsSpecific: the constructor's info is a method type, the class's type params appear unbound. The second one is a problem because specificity should be able to infer different type args for those parameters, as it's doing a bunch of `isApplicable` calls itself to score the overloads. To fix this, we push the class's poly type down to the constructor's info. One caveat: we have to clone the class type params and use the constructor as their owner, otherwise ASF will rewrite them based on the prefix. (`PolyType(tpars, res).ASF(pre, clazz)` means references in `res` to type params owned by `clazz` will be rewritten to use prefix `pre`, but the resulting poly type's type params will not be adjusted.)
1 parent 238a16a commit bf9fff4

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

src/compiler/scala/tools/nsc/typechecker/Infer.scala

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,9 @@ trait Infer extends Checkable {
941941
|| isProperSubClassOrObject(sym1.safeOwner, sym2.owner)
942942
)
943943

944+
// 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)
945+
// since constructors of poly classes do not have their own polytype in their infos, this must be fixed up
946+
// before calling this method (see memberTypeForSpecificity)
944947
def isStrictlyMoreSpecific(ftpe1: Type, ftpe2: Type, sym1: Symbol, sym2: Symbol): Boolean = {
945948
// ftpe1 / ftpe2 are OverloadedTypes (possibly with one single alternative) if they
946949
// denote the type of an "apply" member method (see "followApply")
@@ -1338,6 +1341,25 @@ trait Infer extends Checkable {
13381341

13391342
/* -- Overload Resolution ---------------------------------------------- */
13401343

1344+
/** Adjust polymorphic class's constructor info to be polymorphic as well
1345+
*
1346+
* Normal polymorphic methods have a PolyType as their info, but a constructor reuses the type params of the class.
1347+
* We wrap them in a PolyType here so that we get consistent behavior in determining specificity.
1348+
*
1349+
* @param pre
1350+
* @param sym must not be overloaded!
1351+
* @return `pre memberType sym`, unless `sym`` is a polymorphic class's constructor,
1352+
* in which case a `PolyType` is wrapped around the ctor's info
1353+
*/
1354+
private def memberTypeForSpecificity(pre: Type, sym: Symbol) = {
1355+
val tparsToAdd = if (sym.isConstructor) sym.owner.info.typeParams else Nil
1356+
1357+
if (tparsToAdd.isEmpty) pre memberType sym
1358+
// Need to make sure tparsToAdd are owned by sym (the constructor), and not the class (`sym.owner`).
1359+
// Otherwise, asSeenFrom will rewrite them to the corresponding symbols in `pre` (the new this type for `sym.owner`).
1360+
else createFromClonedSymbolsAtOwner(tparsToAdd, sym, sym.info)(PolyType(_, _)).asSeenFrom(pre, sym.owner)
1361+
}
1362+
13411363
/** Assign `tree` the symbol and type of the alternative which
13421364
* matches prototype `pt`, if it exists.
13431365
* If several alternatives match `pt`, take parameterless one.
@@ -1350,8 +1372,8 @@ trait Infer extends Checkable {
13501372
val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
13511373
val alts1 = if (alts0.isEmpty) alts else alts0
13521374
val bests = bestAlternatives(alts1) { (sym1, sym2) =>
1353-
val tp1 = pre memberType sym1
1354-
val tp2 = pre memberType sym2
1375+
val tp1 = memberTypeForSpecificity(pre, sym1)
1376+
val tp2 = memberTypeForSpecificity(pre, sym2)
13551377

13561378
( (tp2 eq ErrorType)
13571379
|| isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
@@ -1463,7 +1485,7 @@ trait Infer extends Checkable {
14631485
case tp => tp
14641486
}
14651487

1466-
private def followType(sym: Symbol) = followApply(pre memberType sym)
1488+
private def followType(sym: Symbol) = followApply(memberTypeForSpecificity(pre, sym))
14671489
// separate method to help the inliner
14681490
private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors }
14691491
private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)

test/files/pos/t11755.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait Map[K]
2+
class HashMap[K] extends Map[K]
3+
class IdentityBox[+A]
4+
5+
class IdentityHashMap[K](inner: HashMap[IdentityBox[K]]) extends Map[K] {
6+
def this(initialMap: Map[_ <: K]) = this(new HashMap[IdentityBox[K]]())
7+
8+
def bla[K](inner: HashMap[IdentityBox[K]]): IdentityHashMap[K] = ???
9+
def bla[K](initialMap: Map[_ <: K]): IdentityHashMap[K] = bla(new HashMap[IdentityBox[K]]())
10+
11+
new IdentityHashMap(??? : HashMap[IdentityBox[K]])
12+
bla(??? :HashMap[IdentityBox[K]])
13+
}
14+
15+
// isApplicable true : (inner: HashMap[IdentityBox[K]])IdentityHashMap[K] to List(HashMap[IdentityBox[K]]) for ? under List()

0 commit comments

Comments
 (0)