Skip to content

Commit 617e2e7

Browse files
committed
Merge MethodType and PolyType functionality where possible
Two benefits: (1) less code. (2) finding subtle bugs about parameter dependent method types. By merging with PolyTypes we are forced to take parameter dependencies into account.
1 parent 3b9e5b6 commit 617e2e7

12 files changed

+78
-130
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class Definitions {
150150

151151
private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int,
152152
resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = {
153-
val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount)
153+
val tparamNames = PolyType.syntheticParamNames(typeParamCount)
154154
val tparamInfos = tparamNames map (_ => TypeBounds.empty)
155155
val ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn)
156156
enterMethod(cls, name, ptype, flags)

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

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,12 @@ object Denotations {
252252
else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging
253253
}
254254

255-
/** Merge two lists of names. If names in corresponding positions match, keep them,
255+
/** Merge parameter names of lambda types. If names in corresponding positions match, keep them,
256256
* otherwise generate new synthetic names.
257257
*/
258-
def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = {
259-
for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped)
260-
yield if (name1 == name2) name1 else syntheticName(idx)
261-
}.toList
258+
private def mergeParamNames(tp1: LambdaType, tp2: LambdaType): List[tp1.ThisName] =
259+
(for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped)
260+
yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(idx)).toList
262261

263262
/** Form a denotation by conjoining with denotation `that`.
264263
*
@@ -308,27 +307,17 @@ object Denotations {
308307
case tp2: TypeBounds if tp2 contains tp1 => tp1
309308
case _ => mergeConflict(tp1, tp2)
310309
}
311-
case tp1: MethodType if isTerm =>
310+
case tp1: MethodOrPoly =>
312311
tp2 match {
313-
case tp2: MethodType if ctx.typeComparer.matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) &&
314-
tp1.isImplicit == tp2.isImplicit =>
312+
case tp2: MethodOrPoly
313+
if ctx.typeComparer.matchingParams(tp1, tp2) &&
314+
tp1.isImplicit == tp2.isImplicit =>
315315
tp1.derivedLambdaType(
316-
mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName),
317-
tp1.paramInfos,
316+
mergeParamNames(tp1, tp2), tp1.paramInfos,
318317
infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
319318
case _ =>
320319
mergeConflict(tp1, tp2)
321320
}
322-
case tp1: PolyType if isTerm =>
323-
tp2 match {
324-
case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
325-
tp1.derivedLambdaType(
326-
mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
327-
tp1.paramInfos,
328-
infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
329-
case _: MethodicType =>
330-
mergeConflict(tp1, tp2)
331-
}
332321
case _ =>
333322
tp1 & tp2
334323
}
@@ -471,23 +460,14 @@ object Denotations {
471460
case tp2: TypeBounds if tp2 contains tp1 => tp2
472461
case _ => mergeConflict(tp1, tp2)
473462
}
474-
case tp1: MethodType =>
475-
tp2 match {
476-
case tp2: MethodType
477-
if ctx.typeComparer.matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) &&
478-
tp1.isImplicit == tp2.isImplicit =>
479-
tp1.derivedLambdaType(
480-
mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName),
481-
tp1.paramInfos, tp1.resultType | tp2.resultType.subst(tp2, tp1))
482-
case _ =>
483-
mergeConflict(tp1, tp2)
484-
}
485-
case tp1: PolyType =>
463+
case tp1: MethodOrPoly =>
486464
tp2 match {
487-
case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
465+
case tp2: MethodOrPoly
466+
if ctx.typeComparer.matchingParams(tp1, tp2) &&
467+
tp1.isImplicit == tp2.isImplicit =>
488468
tp1.derivedLambdaType(
489-
mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
490-
tp1.paramInfos, tp1.resultType | tp2.resultType.subst(tp2, tp1))
469+
mergeParamNames(tp1, tp2), tp1.paramInfos,
470+
tp1.resultType | tp2.resultType.subst(tp2, tp1))
491471
case _ =>
492472
mergeConflict(tp1, tp2)
493473
}

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,6 @@ object StdNames {
718718
case _ => termName("_" + j)
719719
}
720720

721-
def syntheticParamNames(num: Int): List[TermName] =
722-
(0 until num).map(syntheticParamName)(breakOut)
723-
724721
def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName =
725722
LOCALDUMMY_PREFIX ++ clazz.name ++ ">"
726723

@@ -743,9 +740,6 @@ object StdNames {
743740

744741
def syntheticTypeParamName(i: Int): TypeName = "X" + i
745742

746-
def syntheticTypeParamNames(num: Int): List[TypeName] =
747-
(0 until num).map(syntheticTypeParamName)(breakOut)
748-
749743
final val Conforms = encode("<:<")
750744

751745
final val Uninstantiated: TypeName = "?$"

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,8 +1141,7 @@ object SymDenotations {
11411141
case tp: NamedType => hasSkolems(tp.prefix)
11421142
case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo)
11431143
case tp: RecType => hasSkolems(tp.parent)
1144-
case tp: TypeLambda => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
1145-
case tp: MethodType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
1144+
case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
11461145
case tp: ExprType => hasSkolems(tp.resType)
11471146
case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems)
11481147
case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)

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

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -428,9 +428,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
428428
compareRec
429429
case tp2 @ HKApply(tycon2, args2) =>
430430
compareHkApply2(tp1, tp2, tycon2, args2)
431-
case tp2: TypeLambda =>
431+
case tp2: HKTypeLambda =>
432432
def compareTypeLambda: Boolean = tp1.stripTypeVar match {
433-
case tp1: TypeLambda if tp1.isInstanceOf[PolyType] == tp2.isInstanceOf[PolyType] =>
433+
case tp1: TypeLambda =>
434434
/* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail
435435
* The issue is that, logically, bounds should compare contravariantly,
436436
* but that would invalidate a pattern exploited in t2994:
@@ -484,12 +484,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
484484
case _ =>
485485
}
486486
either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2)
487-
case tp2: MethodType =>
487+
case tp2: MethodOrPoly =>
488488
def compareMethod = tp1 match {
489-
case tp1: MethodType =>
489+
case tp1: MethodOrPoly =>
490490
(tp1.signature consistentParams tp2.signature) &&
491-
matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) &&
492-
(tp1.isImplicit == tp2.isImplicit) &&
491+
matchingParams(tp1, tp2) &&
492+
tp1.isImplicit == tp2.isImplicit &&
493493
isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1))
494494
case _ =>
495495
false
@@ -1021,7 +1021,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10211021
tp2.widen match {
10221022
case tp2: MethodType =>
10231023
// implicitness is ignored when matching
1024-
matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) &&
1024+
matchingParams(tp1, tp2) &&
10251025
matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed)
10261026
case tp2 =>
10271027
relaxed && tp1.paramNames.isEmpty &&
@@ -1031,7 +1031,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10311031
tp2.widen match {
10321032
case tp2: PolyType =>
10331033
sameLength(tp1.paramNames, tp2.paramNames) &&
1034-
matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed)
1034+
matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed)
10351035
case _ =>
10361036
false
10371037
}
@@ -1047,28 +1047,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10471047
}
10481048
}
10491049

1050-
/** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */
1051-
def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match {
1052-
case formal1 :: rest1 =>
1053-
formals2 match {
1054-
case formal2 :: rest2 =>
1055-
(isSameTypeWhenFrozen(formal1, formal2)
1056-
|| isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass)
1057-
|| isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) &&
1058-
matchingParams(rest1, rest2, isJava1, isJava2)
1059-
case nil =>
1060-
false
1061-
}
1062-
case nil =>
1063-
formals2.isEmpty
1064-
}
1065-
1066-
/** Do generic types `poly1` and `poly2` have type parameters that
1067-
* have the same bounds (after renaming one set to the other)?
1050+
/** Do lambda types `lam1` and `lam2` have parameters that have the same types
1051+
* and the same implicit status? (after renaming one set to the other)
10681052
*/
1069-
def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean =
1070-
(poly1.paramInfos corresponds poly2.paramInfos)((b1, b2) =>
1071-
isSameType(b1, b2.subst(poly2, poly1)))
1053+
def matchingParams(lam1: MethodOrPoly, lam2: MethodOrPoly): Boolean = {
1054+
/** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */
1055+
def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match {
1056+
case formal1 :: rest1 =>
1057+
formals2 match {
1058+
case formal2 :: rest2 =>
1059+
val formal2a = if (lam2.isParamDependent) formal2.subst(lam2, lam1) else formal2
1060+
(isSameTypeWhenFrozen(formal1, formal2a)
1061+
|| lam1.isJava && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass)
1062+
|| lam2.isJava && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) &&
1063+
loop(rest1, rest2)
1064+
case nil =>
1065+
false
1066+
}
1067+
case nil =>
1068+
formals2.isEmpty
1069+
}
1070+
loop(lam1.paramInfos, lam2.paramInfos)
1071+
}
10721072

10731073
// Type equality =:=
10741074

@@ -1279,7 +1279,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12791279
original(tp1.appliedTo(tp1.typeParams.map(_.paramInfoAsSeenFrom(tp1))), tp2)
12801280
else
12811281
HKTypeLambda(
1282-
paramNames = (tpnme.syntheticTypeParamNames(tparams1.length), tparams1, tparams2)
1282+
paramNames = (HKTypeLambda.syntheticParamNames(tparams1.length), tparams1, tparams2)
12831283
.zipped.map((pname, tparam1, tparam2) =>
12841284
pname.withVariance((tparam1.paramVariance + tparam2.paramVariance) / 2)))(
12851285
paramInfosExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) =>

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

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -215,17 +215,15 @@ object Types {
215215
/** Is this the type of a method that has a repeated parameter type as
216216
* last parameter type?
217217
*/
218-
def isVarArgsMethod(implicit ctx: Context): Boolean = this match {
219-
case tp: PolyType => tp.resultType.isVarArgsMethod
218+
def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match {
220219
case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam
221220
case _ => false
222221
}
223222

224223
/** Is this the type of a method with a leading empty parameter list?
225224
*/
226-
def isNullaryMethod(implicit ctx: Context): Boolean = this match {
225+
def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match {
227226
case MethodType(Nil) => true
228-
case tp: PolyType => tp.resultType.isNullaryMethod
229227
case _ => false
230228
}
231229

@@ -2351,7 +2349,11 @@ object Types {
23512349

23522350
override def resultType(implicit ctx: Context) = resType
23532351

2352+
def isJava: Boolean = false
2353+
def isImplicit = false
2354+
23542355
def isDependent(implicit ctx: Context): Boolean
2356+
def isParamDependent(implicit ctx: Context): Boolean
23552357

23562358
final def isTermLambda = paramNames.head.isTermName
23572359
final def isTypeLambda = paramNames.head.isTypeName
@@ -2365,7 +2367,7 @@ object Types {
23652367
if (isDependent) resultType.substParams(this, argTypes)
23662368
else resultType
23672369

2368-
protected def companion: LambdaTypeCompanion[ThisName, PInfo, This]
2370+
def companion: LambdaTypeCompanion[ThisName, PInfo, This]
23692371

23702372
/** The type `[tparams := paramRefs] tp`, where `tparams` can be
23712373
* either a list of type parameter symbols or a list of lambda parameters
@@ -2515,11 +2517,6 @@ object Types {
25152517

25162518
type This = MethodType
25172519

2518-
protected def companion: MethodTypeCompanion
2519-
2520-
def isJava = false
2521-
def isImplicit = false
2522-
25232520
val paramInfos = paramInfosExp(this)
25242521
val resType = resultTypeExp(this)
25252522
assert(resType.exists)
@@ -2550,7 +2547,12 @@ object Types {
25502547
}
25512548

25522549
abstract class LambdaTypeCompanion[N <: Name, PInfo <: Type, LT <: LambdaType] {
2553-
def syntheticParamNames(n: Int): List[N]
2550+
def syntheticParamName(n: Int): N
2551+
2552+
@sharable private val memoizedNames = new mutable.HashMap[Int, List[N]]
2553+
def syntheticParamNames(n: Int): List[N] = synchronized {
2554+
memoizedNames.getOrElseUpdate(n, (0 until n).map(syntheticParamName).toList)
2555+
}
25542556

25552557
def apply(paramNames: List[N])(paramInfosExp: LT => List[PInfo], resultTypeExp: LT => Type)(implicit ctx: Context): LT
25562558
def apply(paramNames: List[N], paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT =
@@ -2572,12 +2574,12 @@ object Types {
25722574

25732575
abstract class TermLambdaCompanion[LT <: TermLambda]
25742576
extends LambdaTypeCompanion[TermName, Type, LT] {
2575-
def syntheticParamNames(n: Int) = nme.syntheticParamNames(n)
2577+
def syntheticParamName(n: Int) = nme.syntheticParamName(n)
25762578
}
25772579

25782580
abstract class TypeLambdaCompanion[LT <: TypeLambda]
25792581
extends LambdaTypeCompanion[TypeName, TypeBounds, LT] {
2580-
def syntheticParamNames(n: Int) = tpnme.syntheticTypeParamNames(n)
2582+
def syntheticParamName(n: Int) = tpnme.syntheticTypeParamName(n)
25812583
}
25822584

25832585
abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] {
@@ -2649,6 +2651,7 @@ object Types {
26492651
type This <: TypeLambda
26502652

26512653
def isDependent(implicit ctx: Context): Boolean = true
2654+
def isParamDependent(implicit ctx: Context): Boolean = true
26522655

26532656
def newParamRef(n: Int) = TypeParamRef(this, n)
26542657

@@ -3418,8 +3421,7 @@ object Types {
34183421
object SAMType {
34193422
def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match {
34203423
case tp: ClassInfo =>
3421-
def zeroParams(tp: Type): Boolean = tp match {
3422-
case pt: PolyType => zeroParams(pt.resultType)
3424+
def zeroParams(tp: Type): Boolean = tp.stripPoly match {
34233425
case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType]
34243426
case et: ExprType => true
34253427
case _ => false
@@ -3537,27 +3539,18 @@ object Types {
35373539
variance = -variance
35383540
derivedTypeBounds(tp, lo1, this(tp.hi))
35393541

3540-
case tp: MethodType =>
3541-
def mapOverMethod = {
3542+
case tp: LambdaType =>
3543+
def mapOverLambda = {
35423544
variance = -variance
3543-
val ptypes1 = tp.paramInfos mapConserve this
3545+
val ptypes1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[tp.PInfo]]
35443546
variance = -variance
35453547
derivedLambdaType(tp)(ptypes1, this(tp.resultType))
35463548
}
3547-
mapOverMethod
3549+
mapOverLambda
35483550

35493551
case tp: ExprType =>
35503552
derivedExprType(tp, this(tp.resultType))
35513553

3552-
case tp: TypeLambda =>
3553-
def mapOverPoly = {
3554-
variance = -variance
3555-
val bounds1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[TypeBounds]]
3556-
variance = -variance
3557-
derivedLambdaType(tp)(bounds1, this(tp.resultType))
3558-
}
3559-
mapOverPoly
3560-
35613554
case tp: RecType =>
35623555
derivedRecType(tp, this(tp.parent))
35633556

@@ -3765,7 +3758,7 @@ object Types {
37653758
this(y, hi)
37663759
}
37673760

3768-
case tp: MethodType =>
3761+
case tp: LambdaType =>
37693762
variance = -variance
37703763
val y = foldOver(x, tp.paramInfos)
37713764
variance = -variance
@@ -3774,12 +3767,6 @@ object Types {
37743767
case ExprType(restpe) =>
37753768
this(x, restpe)
37763769

3777-
case tp: TypeLambda =>
3778-
variance = -variance
3779-
val y = foldOver(x, tp.paramInfos)
3780-
variance = -variance
3781-
this(y, tp.resultType)
3782-
37833770
case tp: RecType =>
37843771
this(x, tp.parent)
37853772

@@ -3880,9 +3867,7 @@ object Types {
38803867
apply(x, tp.tref)
38813868
case tp: ConstantType =>
38823869
apply(x, tp.underlying)
3883-
case tp: TermParamRef =>
3884-
apply(x, tp.underlying)
3885-
case tp: TypeParamRef =>
3870+
case tp: ParamRef =>
38863871
apply(x, tp.underlying)
38873872
case _ =>
38883873
foldOver(x, tp)

0 commit comments

Comments
 (0)