From 8805dd4f821e06a688fcf492b61033fe0992e752 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 26 Jun 2016 16:57:38 +0200 Subject: When comparing types revert eta-expansion as needed The problem is that some existential types read from Java (and Scala as well? not sure) appear as naked typerefs. They consequently get expanded via eta expansion to type lambdas. This commit compensates for this by collapsing an eta expansion if this can make a subtype tests succeed or a union or intersection be legal. Also, take hk types into account for liftToClasses Needs to special-treat TypeLambda and HKApply since otherwise we risk creating malformed And-types. --- src/dotty/tools/dotc/core/ConstraintHandling.scala | 11 +++-- src/dotty/tools/dotc/core/TypeComparer.scala | 48 +++++++++++++++++----- src/dotty/tools/dotc/typer/Implicits.scala | 8 ++++ 3 files changed, 53 insertions(+), 14 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 66767d58a..44b6abe12 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -6,6 +6,7 @@ import Types._, Contexts._, Symbols._ import Decorators._ import config.Config import config.Printers._ +import TypeApplications.EtaExpansion import collection.mutable /** Methods for adding constraints and solving them. @@ -239,8 +240,6 @@ trait ConstraintHandling { def addParamBound(bound: PolyParam) = if (fromBelow) addLess(bound, param) else addLess(param, bound) - assert(param.isHK == bound.isHK, s"$param / $bound / $fromBelow") - /** Drop all constrained parameters that occur at the toplevel in `bound` and * handle them by `addLess` calls. * The preconditions make sure that such parameters occur only @@ -297,7 +296,13 @@ trait ConstraintHandling { case bound: PolyParam if constraint contains bound => addParamBound(bound) case _ => - val pbound = prune(bound) + var pbound = prune(bound) + if (pbound.isHK && !param.isHK) { + param match { + case EtaExpansion(tycon) if tycon.symbol.isClass => pbound = tycon + case _ => + } + } pbound.exists && ( if (fromBelow) addLowerBound(param, pbound) else addUpperBound(param, pbound)) } diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 12e9e638a..e6cd0a0df 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -392,12 +392,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp2 @ HKApply(tycon2, args2) => compareHkApply2(tp1, tp2, tycon2, args2) case tp2 @ TypeLambda(tparams2, body2) => - def compareHkLambda = tp1.stripTypeVar match { + def compareHkLambda: Boolean = tp1.stripTypeVar match { case tp1 @ TypeLambda(tparams1, body1) => // Don't compare bounds of lambdas, or t2994 will fail // The issue is that, logically, bounds should compare contravariantly, // so the bounds checking should look like this: - // + // // tparams1.corresponds(tparams2)((tparam1, tparam2) => // isSubType(tparam2.memberBounds.subst(tp2, tp1), tparam1.memberBounds)) // @@ -408,6 +408,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // bounds of * types are checked. variancesConform(tparams1, tparams2) && isSubType(body1, body2.subst(tp2, tp1)) case _ => + if (!tp1.isHK) { + tp2 match { + case EtaExpansion(tycon2) if tycon2.symbol.isClass => + return isSubType(tp1, tycon2) + case _ => + } + } fourthTry(tp1, tp2) } compareHkLambda @@ -1269,7 +1276,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val t2 = distributeAnd(tp2, tp1) if (t2.exists) t2 else if (erased) erasedGlb(tp1, tp2, isJava = false) - else liftIfHK(tp1, tp2, AndType(_, _)) + else liftIfHK(tp1, tp2, AndType(_, _), _ & _) } } @@ -1293,7 +1300,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val t2 = distributeOr(tp2, tp1) if (t2.exists) t2 else if (erased) erasedLub(tp1, tp2) - else liftIfHK(tp1, tp2, OrType(_, _)) + else liftIfHK(tp1, tp2, OrType(_, _), _ | _) } } @@ -1314,11 +1321,24 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * allowing both interpretations. A possible remedy is to be somehow stricter * in where we allow which interpretation. */ - private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type) = { + private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type, original: (Type, Type) => Type) = { val tparams1 = tp1.typeParams val tparams2 = tp2.typeParams - if (tparams1.isEmpty || tparams2.isEmpty) op(tp1, tp2) - else if (tparams1.length != tparams2.length) mergeConflict(tp1, tp2) + if (!Config.newHK && tparams1.isEmpty || tparams2.isEmpty) op(tp1, tp2) + else if (Config.newHK && tparams1.isEmpty) + if (tparams2.isEmpty) op(tp1, tp2) + else tp2 match { + case EtaExpansion(tycon2) if tycon2.symbol.isClass => original(tp1, tycon2) // TODO: Roll isClass into EtaExpansion? + case _ => mergeConflict(tp1, tp2) + } + else if (Config.newHK && tparams2.isEmpty) { + tp1 match { + case EtaExpansion(tycon1) if tycon1.symbol.isClass => original(tycon1, tp2) + case _ => mergeConflict(tp1, tp2) + } + } + else if (!Config.newHK && (tparams1.isEmpty || tparams2.isEmpty)) op(tp1, tp2) + else if (!Config.newHK && tparams1.length != tparams2.length) mergeConflict(tp1, tp2) else if (Config.newHK) { val numArgs = tparams1.length def argRefs(tl: GenericType) = List.range(0, numArgs).map(PolyParam(tl, _)) @@ -1330,7 +1350,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tl.lifted(tparams1, tparam1.memberBoundsAsSeenFrom(tp1)).bounds & tl.lifted(tparams2, tparam2.memberBoundsAsSeenFrom(tp2)).bounds), resultTypeExp = tl => - op(tl.lifted(tparams1, tp1).appliedTo(argRefs(tl)), + original(tl.lifted(tparams1, tp1).appliedTo(argRefs(tl)), tl.lifted(tparams2, tp2).appliedTo(argRefs(tl)))) } else { @@ -1389,12 +1409,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { mergeNames(names1, names2, nme.syntheticParamName), formals1, tp1.resultType & tp2.resultType.subst(tp2, tp1)) case _ => + tp2 match { + case tp2 @ MethodType(names2, formals2) => + println( + TypeComparer.explained(implicit ctx => isSameType(formals1.head, formals2.head))) + case _ => + } mergeConflict(tp1, tp2) } - case tp1: GenericType => + case tp1: PolyType => tp2 match { - case tp2: GenericType if matchingTypeParams(tp1, tp2) => - tp1.derivedGenericType( + case tp2: PolyType if matchingTypeParams(tp1, tp2) => + tp1.derivedPolyType( mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), tp1.paramBounds, tp1.resultType & tp2.resultType.subst(tp2, tp1)) case _ => diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index a5246cf6b..d3f9fd777 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -292,6 +292,14 @@ trait ImplicitRunInfo { self: RunInfo => (lead /: tp.classSymbols)(joinClass) case tp: TypeVar => apply(tp.underlying) + case tp: HKApply => + def applyArg(arg: Type) = arg match { + case TypeBounds(lo, hi) => AndType.make(lo, hi) + case _ => arg + } + (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg))) + case tp: TypeLambda => + apply(tp.resType) case _ => mapOver(tp) } -- cgit v1.2.3