diff options
author | Martin Odersky <odersky@gmail.com> | 2017-03-19 12:44:27 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2017-04-06 13:15:29 +0200 |
commit | 8d33ca7460493427055daaecca53c66127772831 (patch) | |
tree | 2c8f539d138a1374dde36c58ffb2f1d4c0841dfa /compiler/src/dotty/tools/dotc/core | |
parent | 15317555c94f613f266d7b0fb0a75b0b6ed2da6d (diff) | |
download | dotty-8d33ca7460493427055daaecca53c66127772831.tar.gz dotty-8d33ca7460493427055daaecca53c66127772831.tar.bz2 dotty-8d33ca7460493427055daaecca53c66127772831.zip |
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.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core')
6 files changed, 70 insertions, 112 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9360cabb1..b70fcb093 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -150,7 +150,7 @@ class Definitions { private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { - val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) + val tparamNames = PolyType.syntheticParamNames(typeParamCount) val tparamInfos = tparamNames map (_ => TypeBounds.empty) val ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn) enterMethod(cls, name, ptype, flags) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 7e552eda9..7341b96af 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -252,13 +252,12 @@ object Denotations { else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging } - /** Merge two lists of names. If names in corresponding positions match, keep them, + /** Merge parameter names of lambda types. If names in corresponding positions match, keep them, * otherwise generate new synthetic names. */ - def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = { - for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped) - yield if (name1 == name2) name1 else syntheticName(idx) - }.toList + private def mergeParamNames(tp1: LambdaType, tp2: LambdaType): List[tp1.ThisName] = + (for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped) + yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(idx)).toList /** Form a denotation by conjoining with denotation `that`. * @@ -308,27 +307,17 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp1 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType if isTerm => + case tp1: MethodOrPoly => tp2 match { - case tp2: MethodType if ctx.typeComparer.matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => tp1.derivedLambdaType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramInfos, + mergeParamNames(tp1, tp2), tp1.paramInfos, infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) case _ => mergeConflict(tp1, tp2) } - case tp1: PolyType if isTerm => - tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedLambdaType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramInfos, - infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) - case _: MethodicType => - mergeConflict(tp1, tp2) - } case _ => tp1 & tp2 } @@ -471,23 +460,14 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp2 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType => - tp2 match { - case tp2: MethodType - if ctx.typeComparer.matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedLambdaType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramInfos, tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case _ => - mergeConflict(tp1, tp2) - } - case tp1: PolyType => + case tp1: MethodOrPoly => tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => tp1.derivedLambdaType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramInfos, tp1.resultType | tp2.resultType.subst(tp2, tp1)) + mergeParamNames(tp1, tp2), tp1.paramInfos, + tp1.resultType | tp2.resultType.subst(tp2, tp1)) case _ => mergeConflict(tp1, tp2) } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 30fa03537..e7928fd09 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -722,9 +722,6 @@ object StdNames { case _ => termName("_" + j) } - def syntheticParamNames(num: Int): List[TermName] = - (0 until num).map(syntheticParamName)(breakOut) - def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName = LOCALDUMMY_PREFIX ++ clazz.name ++ ">" @@ -747,9 +744,6 @@ object StdNames { def syntheticTypeParamName(i: Int): TypeName = "X" + i - def syntheticTypeParamNames(num: Int): List[TypeName] = - (0 until num).map(syntheticTypeParamName)(breakOut) - final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 0ce61de95..56acbd9d6 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1146,8 +1146,7 @@ object SymDenotations { case tp: NamedType => hasSkolems(tp.prefix) case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) case tp: RecType => hasSkolems(tp.parent) - case tp: TypeLambda => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: MethodType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) + case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) case tp: ExprType => hasSkolems(tp.resType) case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 131c31d6b..173520a10 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -428,9 +428,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareRec case tp2 @ HKApply(tycon2, args2) => compareHkApply2(tp1, tp2, tycon2, args2) - case tp2: TypeLambda => + case tp2: HKTypeLambda => def compareTypeLambda: Boolean = tp1.stripTypeVar match { - case tp1: TypeLambda if tp1.isInstanceOf[PolyType] == tp2.isInstanceOf[PolyType] => + case tp1: TypeLambda => /* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail * The issue is that, logically, bounds should compare contravariantly, * but that would invalidate a pattern exploited in t2994: @@ -484,12 +484,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2) - case tp2: MethodType => + case tp2: MethodOrPoly => def compareMethod = tp1 match { - case tp1: MethodType => + case tp1: MethodOrPoly => (tp1.signature consistentParams tp2.signature) && - matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) && - (tp1.isImplicit == tp2.isImplicit) && + matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => false @@ -1021,7 +1021,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: MethodType => // implicitness is ignored when matching - matchingParams(tp1.paramInfos, tp2.paramInfos, tp1.isJava, tp2.isJava) && + matchingParams(tp1, tp2) && matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case tp2 => relaxed && tp1.paramNames.isEmpty && @@ -1031,7 +1031,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: PolyType => sameLength(tp1.paramNames, tp2.paramNames) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case _ => false } @@ -1047,28 +1047,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ - def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - (isSameTypeWhenFrozen(formal1, formal2) - || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) - || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && - matchingParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - - /** Do generic types `poly1` and `poly2` have type parameters that - * have the same bounds (after renaming one set to the other)? + /** Do lambda types `lam1` and `lam2` have parameters that have the same types + * and the same implicit status? (after renaming one set to the other) */ - def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean = - (poly1.paramInfos corresponds poly2.paramInfos)((b1, b2) => - isSameType(b1, b2.subst(poly2, poly1))) + def matchingParams(lam1: MethodOrPoly, lam2: MethodOrPoly): Boolean = { + /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ + def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match { + case formal1 :: rest1 => + formals2 match { + case formal2 :: rest2 => + val formal2a = if (lam2.isParamDependent) formal2.subst(lam2, lam1) else formal2 + (isSameTypeWhenFrozen(formal1, formal2a) + || lam1.isJava && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) + || lam2.isJava && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && + loop(rest1, rest2) + case nil => + false + } + case nil => + formals2.isEmpty + } + loop(lam1.paramInfos, lam2.paramInfos) + } // Type equality =:= @@ -1281,7 +1281,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { original(tp1.appliedTo(tp1.typeParams.map(_.paramInfoAsSeenFrom(tp1))), tp2) else HKTypeLambda( - paramNames = (tpnme.syntheticTypeParamNames(tparams1.length), tparams1, tparams2) + paramNames = (HKTypeLambda.syntheticParamNames(tparams1.length), tparams1, tparams2) .zipped.map((pname, tparam1, tparam2) => pname.withVariance((tparam1.paramVariance + tparam2.paramVariance) / 2)))( paramInfosExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 1855e3376..dd314c589 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -215,17 +215,15 @@ object Types { /** Is this the type of a method that has a repeated parameter type as * last parameter type? */ - def isVarArgsMethod(implicit ctx: Context): Boolean = this match { - case tp: PolyType => tp.resultType.isVarArgsMethod + def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match { case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam case _ => false } /** Is this the type of a method with a leading empty parameter list? */ - def isNullaryMethod(implicit ctx: Context): Boolean = this match { + def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match { case MethodType(Nil) => true - case tp: PolyType => tp.resultType.isNullaryMethod case _ => false } @@ -2355,7 +2353,11 @@ object Types { override def resultType(implicit ctx: Context) = resType + def isJava: Boolean = false + def isImplicit = false + def isDependent(implicit ctx: Context): Boolean + def isParamDependent(implicit ctx: Context): Boolean final def isTermLambda = paramNames.head.isTermName final def isTypeLambda = paramNames.head.isTypeName @@ -2369,7 +2371,7 @@ object Types { if (isDependent) resultType.substParams(this, argTypes) else resultType - protected def companion: LambdaTypeCompanion[ThisName, PInfo, This] + def companion: LambdaTypeCompanion[ThisName, PInfo, This] /** The type `[tparams := paramRefs] tp`, where `tparams` can be * either a list of type parameter symbols or a list of lambda parameters @@ -2519,11 +2521,6 @@ object Types { type This = MethodType - protected def companion: MethodTypeCompanion - - def isJava = false - def isImplicit = false - val paramInfos = paramInfosExp(this) val resType = resultTypeExp(this) assert(resType.exists) @@ -2554,7 +2551,12 @@ object Types { } abstract class LambdaTypeCompanion[N <: Name, PInfo <: Type, LT <: LambdaType] { - def syntheticParamNames(n: Int): List[N] + def syntheticParamName(n: Int): N + + @sharable private val memoizedNames = new mutable.HashMap[Int, List[N]] + def syntheticParamNames(n: Int): List[N] = synchronized { + memoizedNames.getOrElseUpdate(n, (0 until n).map(syntheticParamName).toList) + } def apply(paramNames: List[N])(paramInfosExp: LT => List[PInfo], resultTypeExp: LT => Type)(implicit ctx: Context): LT def apply(paramNames: List[N], paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT = @@ -2576,12 +2578,12 @@ object Types { abstract class TermLambdaCompanion[LT <: TermLambda] extends LambdaTypeCompanion[TermName, Type, LT] { - def syntheticParamNames(n: Int) = nme.syntheticParamNames(n) + def syntheticParamName(n: Int) = nme.syntheticParamName(n) } abstract class TypeLambdaCompanion[LT <: TypeLambda] extends LambdaTypeCompanion[TypeName, TypeBounds, LT] { - def syntheticParamNames(n: Int) = tpnme.syntheticTypeParamNames(n) + def syntheticParamName(n: Int) = tpnme.syntheticTypeParamName(n) } abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] { @@ -2653,6 +2655,7 @@ object Types { type This <: TypeLambda def isDependent(implicit ctx: Context): Boolean = true + def isParamDependent(implicit ctx: Context): Boolean = true def newParamRef(n: Int) = TypeParamRef(this, n) @@ -3422,8 +3425,7 @@ object Types { object SAMType { def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match { case tp: ClassInfo => - def zeroParams(tp: Type): Boolean = tp match { - case pt: PolyType => zeroParams(pt.resultType) + def zeroParams(tp: Type): Boolean = tp.stripPoly match { case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType] case et: ExprType => true case _ => false @@ -3541,27 +3543,18 @@ object Types { variance = -variance derivedTypeBounds(tp, lo1, this(tp.hi)) - case tp: MethodType => - def mapOverMethod = { + case tp: LambdaType => + def mapOverLambda = { variance = -variance - val ptypes1 = tp.paramInfos mapConserve this + val ptypes1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[tp.PInfo]] variance = -variance derivedLambdaType(tp)(ptypes1, this(tp.resultType)) } - mapOverMethod + mapOverLambda case tp: ExprType => derivedExprType(tp, this(tp.resultType)) - case tp: TypeLambda => - def mapOverPoly = { - variance = -variance - val bounds1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[TypeBounds]] - variance = -variance - derivedLambdaType(tp)(bounds1, this(tp.resultType)) - } - mapOverPoly - case tp: RecType => derivedRecType(tp, this(tp.parent)) @@ -3769,7 +3762,7 @@ object Types { this(y, hi) } - case tp: MethodType => + case tp: LambdaType => variance = -variance val y = foldOver(x, tp.paramInfos) variance = -variance @@ -3778,12 +3771,6 @@ object Types { case ExprType(restpe) => this(x, restpe) - case tp: TypeLambda => - variance = -variance - val y = foldOver(x, tp.paramInfos) - variance = -variance - this(y, tp.resultType) - case tp: RecType => this(x, tp.parent) @@ -3884,9 +3871,7 @@ object Types { apply(x, tp.tref) case tp: ConstantType => apply(x, tp.underlying) - case tp: TermParamRef => - apply(x, tp.underlying) - case tp: TypeParamRef => + case tp: ParamRef => apply(x, tp.underlying) case _ => foldOver(x, tp) |