Skip to content

Commit f6efd99

Browse files
committed
Fix appliedTo and typeParams, and higher kinded subtyping tests
Add existential type elimination for HKApply
1 parent 3490e01 commit f6efd99

File tree

3 files changed

+196
-65
lines changed

3 files changed

+196
-65
lines changed

src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -245,16 +245,22 @@ class TypeApplications(val self: Type) extends AnyVal {
245245
self match {
246246
case self: ClassInfo =>
247247
self.cls.typeParams
248+
case self: TypeLambda =>
249+
self.typeParams
248250
case self: TypeRef =>
249251
val tsym = self.symbol
250-
if (tsym.isClass) tsym.typeParams else tsym.info.typeParams
252+
if (tsym.isClass) tsym.typeParams
253+
else if (!tsym.isCompleting) tsym.info.typeParams
254+
else Nil
251255
case self: RefinedType =>
252256
val precedingParams = self.parent.typeParams.filterNot(_.memberName == self.refinedName)
253257
if (self.isTypeParam) precedingParams :+ self else precedingParams
254258
case self: RecType =>
255259
self.parent.typeParams
256-
case self: SingletonType =>
260+
case _: HKApply | _: SingletonType =>
257261
Nil
262+
case self: WildcardType =>
263+
self.optBounds.typeParams
258264
case self: TypeProxy =>
259265
self.underlying.typeParams
260266
case _ =>
@@ -342,7 +348,7 @@ class TypeApplications(val self: Type) extends AnyVal {
342348
case self: TypeLambda => true
343349
case self: HKApply => false
344350
case self: SingletonType => false
345-
case self: TypeVar => self.origin.isHK
351+
case self: TypeVar => self.origin.isHK // discrepancy with typeParams, why?
346352
case self: WildcardType => self.optBounds.isHK
347353
case self: TypeProxy => self.underlying.isHK
348354
case _ => false
@@ -439,7 +445,11 @@ class TypeApplications(val self: Type) extends AnyVal {
439445
*/
440446
def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = {
441447
def expand(tp: Type) =
442-
if (Config.newHK) TypeLambda.fromSymbols(tparams, tp)
448+
if (Config.newHK)
449+
TypeLambda(
450+
tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.variance))(
451+
tl => tparams.map(tparam => tl.lifted(tparams, tparam.info).bounds),
452+
tl => tl.lifted(tparams, tp))
443453
else
444454
TypeLambdaOLD(
445455
tparams.map(tparam =>
@@ -579,6 +589,10 @@ class TypeApplications(val self: Type) extends AnyVal {
579589
self.EtaExpand(self.typeParamSymbols)
580590
}
581591

592+
/** If self is not higher-kinded, eta expand it. */
593+
def ensureHK(implicit ctx: Context): Type =
594+
if (isHK) self else EtaExpansion(self)
595+
582596
/** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */
583597
def etaExpandIfHK(bound: Type)(implicit ctx: Context): Type = {
584598
val hkParams = bound.hkTypeParams
@@ -687,8 +701,10 @@ class TypeApplications(val self: Type) extends AnyVal {
687701
}
688702
}
689703
substHkArgs(body)
690-
case self1 =>
691-
self1.safeDealias.appliedTo(args, typeParams)
704+
case self1: WildcardType =>
705+
self1
706+
case _ =>
707+
self.safeDealias.appliedTo(args, typeParams)
692708
}
693709
}
694710

@@ -712,18 +728,31 @@ class TypeApplications(val self: Type) extends AnyVal {
712728
case nil => t
713729
}
714730
assert(args.nonEmpty)
715-
if (Config.newHK && self.isHK) AppliedType(self, args)
716-
else matchParams(self, typParams, args) match {
717-
case refined @ RefinedType(_, pname, _) if !Config.newHK && pname.isHkArgNameOLD =>
718-
refined.betaReduceOLD
719-
case refined =>
720-
refined
731+
self.stripTypeVar match {
732+
case self: TypeLambda if !args.exists(_.isInstanceOf[TypeBounds]) =>
733+
self.instantiate(args)
734+
case self: AndOrType =>
735+
self.derivedAndOrType(self.tp1.appliedTo(args), self.tp2.appliedTo(args))
736+
case self: LazyRef =>
737+
LazyRef(() => self.ref.appliedTo(args, typParams))
738+
case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] =>
739+
HKApply(self, args)
740+
case _ =>
741+
matchParams(self, typParams, args) match {
742+
case refined @ RefinedType(_, pname, _) if !Config.newHK && pname.isHkArgNameOLD =>
743+
refined.betaReduceOLD
744+
case refined =>
745+
refined
746+
}
721747
}
722748
}
723749

724750
final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil)
725751
final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil)
726752

753+
final def applyIfParameterized(args: List[Type])(implicit ctx: Context): Type =
754+
if (typeParams.nonEmpty) appliedTo(args) else self
755+
727756
/** A cycle-safe version of `appliedTo` where computing type parameters do not force
728757
* the typeconstructor. Instead, if the type constructor is completing, we make
729758
* up hk type parameters matching the arguments. This is needed when unpickling
@@ -733,7 +762,7 @@ class TypeApplications(val self: Type) extends AnyVal {
733762
if (Config.newHK)
734763
self match {
735764
case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting =>
736-
AppliedType(self, args)
765+
HKApply(self, args)
737766
case _ =>
738767
appliedTo(args, typeParams)
739768
}

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

Lines changed: 146 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -390,40 +390,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
390390
isSubType(fixRecs(tp1stable, tp1stable.widenExpr), tp2.parent.substRecThis(tp2, tp1stable))
391391
}
392392
case tp2 @ HKApply(tycon2, args2) =>
393-
def compareHkApply(tycon2: Type): Boolean = tycon2 match {
394-
case tycon2: TypeVar => compareHkApply(tycon2.underlying)
395-
case param2: PolyParam if canConstrain(param2) =>
396-
val tparams2 = tycon2.typeParams
397-
398-
def tyconOK(tycon1a: Type) =
399-
variancesConform(tycon1a.typeParams, tparams2) && {
400-
if (ctx.mode.is(Mode.TypevarsMissContext)) isSubType(tp1, tycon1a.appliedTo(args2))
401-
else tryInstantiate(param2, tycon1a) && isSubType(tp1, tp2)
402-
}
403-
404-
tp1 match {
405-
case tp1 @ HKApply(tycon1, _) =>
406-
tyconOK(tycon1) || isSubType(tp1.upperBound, tp2)
407-
case _ if tp1.widenDealias.typeSymbol.isClass =>
408-
val classBounds = tp2.classSymbols
409-
def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match {
410-
case bc :: bcs1 =>
411-
classBounds.exists(bc.derivesFrom) && tyconOK(tp1.baseTypeRef(bc)) ||
412-
liftToBase(bcs1)
413-
case _ =>
414-
false
415-
}
416-
liftToBase(tp1.baseClasses)
417-
case tp1: TypeProxy =>
418-
isSubType(tp1.underlying, tp2)
419-
case _ =>
420-
false
421-
}
422-
case _ =>
423-
// TODO handle lower bounds of hk params here
424-
false
425-
}
426-
compareHkApply(tycon2) || fourthTry(tp1, tp2)
393+
compareHkApply2(tp1, tp2, tycon2, args2)
427394
case tp2 @ TypeLambda(tparams2, body2) =>
428395
def compareHkLambda = tp1.stripTypeVar match {
429396
case tp1 @ TypeLambda(tparams1, body1) =>
@@ -452,7 +419,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
452419
return isSubType(tp1, OrType(tp21, tp221)) && isSubType(tp1, OrType(tp21, tp222))
453420
case _ =>
454421
}
455-
eitherIsSubType(tp1, tp21, tp1, tp22) || fourthTry(tp1, tp2)
422+
either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2)
456423
case tp2 @ MethodType(_, formals2) =>
457424
def compareMethod = tp1 match {
458425
case tp1 @ MethodType(_, formals1) =>
@@ -555,14 +522,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
555522
case tp1: RecType =>
556523
isNewSubType(tp1.parent, tp2)
557524
case HKApply(tycon1, args1) =>
558-
tp2 match {
559-
case AppliedType(tycon2, args2) =>
560-
assert(!tycon2.isHK) // this should have been handled by thirdTry
561-
isSubType(tycon1, EtaExpansion(tycon2)) &&
562-
isSubArgs(args1, args2, tycon2.typeParams)
563-
case _ =>
564-
false
565-
}
525+
compareHkApply1(tp1, tycon1, args1, tp2)
566526
case EtaExpansion(tycon1) =>
567527
isSubType(tycon1, tp2)
568528
case AndType(tp11, tp12) =>
@@ -581,7 +541,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
581541
return isSubType(AndType(tp11, tp121), tp2) && isSubType(AndType(tp11, tp122), tp2)
582542
case _ =>
583543
}
584-
eitherIsSubType(tp11, tp2, tp12, tp2)
544+
either(isSubType(tp11, tp2), isSubType(tp12, tp2))
585545
case JavaArrayType(elem1) =>
586546
def compareJavaArray = tp2 match {
587547
case JavaArrayType(elem2) => isSubType(elem1, elem2)
@@ -595,6 +555,128 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
595555
false
596556
}
597557

558+
/** Subtype test for the hk application `tp2 = tycon2[args2]`.
559+
*/
560+
def compareHkApply2(tp1: Type, tp2: Type, tycon2: Type, args2: List[Type]): Boolean = {
561+
val tparams = tycon2.typeParams
562+
563+
def isMatchingApply(tp1: Type): Boolean = tp1 match {
564+
case HKApply(tycon1, args1) =>
565+
tycon1 match {
566+
case tycon1: PolyParam =>
567+
(tycon1 == tycon2 ||
568+
canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) &&
569+
isSubArgs(args1, args2, tparams)
570+
case tycon1: TypeRef =>
571+
tycon2 match {
572+
case tycon2: TypeRef if tycon1.symbol == tycon2.symbol =>
573+
isSubType(tycon1.prefix, tycon2.prefix) &&
574+
isSubArgs(args1, args2, tparams)
575+
case _ =>
576+
false
577+
}
578+
case tycon1: TypeVar =>
579+
isMatchingApply(tycon1.underlying)
580+
case tycon1: AnnotatedType =>
581+
isMatchingApply(tycon1.underlying)
582+
case _ =>
583+
false
584+
}
585+
case _ =>
586+
false
587+
}
588+
589+
/** `param2` can be instantiated to the type constructor of the LHS
590+
* or to the type constructor of one of the LHS base class instances
591+
* and the resulting type application is a supertype of `tp1`,
592+
* or fallback to fourthTry.
593+
*/
594+
def canInstantiate(param2: PolyParam): Boolean = {
595+
596+
/** `param2` can be instantiated to `tycon1a`.
597+
* and the resulting type application is a supertype of `tp1`.
598+
*/
599+
def tyconOK(tycon1a: Type) =
600+
variancesConform(tycon1a.typeParams, tparams) && {
601+
(ctx.mode.is(Mode.TypevarsMissContext) ||
602+
tryInstantiate(param2, tycon1a.ensureHK)) &&
603+
isSubType(tp1, tycon1a.appliedTo(args2))
604+
}
605+
606+
tp1.widen match {
607+
case tp1w @ HKApply(tycon1, _) =>
608+
tyconOK(tycon1)
609+
case tp1w =>
610+
tp1w.typeSymbol.isClass && {
611+
val classBounds = tycon2.classSymbols
612+
def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match {
613+
case bc :: bcs1 =>
614+
classBounds.exists(bc.derivesFrom) && tyconOK(tp1w.baseTypeRef(bc)) ||
615+
liftToBase(bcs1)
616+
case _ =>
617+
false
618+
}
619+
liftToBase(tp1w.baseClasses)
620+
} ||
621+
fourthTry(tp1, tp2)
622+
}
623+
}
624+
625+
/** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`.
626+
* Let `app2 = tp2` where the type constructor of `tp2` is replaced by
627+
* `tycon2bounds.lo`.
628+
* If both bounds are the same, continue with `tp1 <:< app2`.
629+
* otherwise continue with either
630+
*
631+
* tp1 <:< tp2 using fourthTry (this might instantiate params in tp1)
632+
* tp1 <:< app2 using isSubType (this might instantiate params in tp2)
633+
*/
634+
def compareLower(tycon2bounds: TypeBounds): Boolean = {
635+
val app2 = tycon2bounds.lo.applyIfParameterized(args2)
636+
if (tycon2bounds.lo eq tycon2bounds.hi) isSubType(tp1, app2)
637+
else either(fourthTry(tp1, tp2), isSubType(tp1, app2))
638+
}
639+
640+
tycon2 match {
641+
case param2: PolyParam =>
642+
isMatchingApply(tp1) || {
643+
if (canConstrain(param2)) canInstantiate(param2)
644+
else compareLower(bounds(param2))
645+
}
646+
case tycon2: TypeRef =>
647+
isMatchingApply(tp1) ||
648+
compareLower(tycon2.info.bounds)
649+
case tycon2: TypeVar =>
650+
isSubType(tp1, tycon2.underlying.appliedTo(args2))
651+
case tycon2: AnnotatedType =>
652+
compareHkApply2(tp1, tp2, tycon2.underlying, args2)
653+
case _ =>
654+
false
655+
}
656+
}
657+
658+
/** Subtype test for the hk application `tp1 = tycon1[args1]`.
659+
*/
660+
def compareHkApply1(tp1: Type, tycon1: Type, args1: List[Type], tp2: Type): Boolean =
661+
tycon1 match {
662+
case param1: PolyParam =>
663+
def canInstantiate = tp2 match {
664+
case AppliedType(tycon2, args2) =>
665+
tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams)
666+
case _ =>
667+
false
668+
}
669+
canConstrain(param1) && canInstantiate ||
670+
isSubType(bounds(param1).hi.applyIfParameterized(args1), tp2)
671+
case tycon1: TypeProxy =>
672+
isSubType(tycon1.underlying.applyIfParameterized(args1), tp2)
673+
case _ =>
674+
false
675+
}
676+
677+
/** Subtype test for corresponding arguments in `args1`, `args2` according to
678+
* variances in type parameters `tparams`.
679+
*/
598680
def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[MemberBinding]): Boolean =
599681
if (args1.isEmpty) args2.isEmpty
600682
else args2.nonEmpty && {
@@ -678,7 +760,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
678760
val hkTypeParams = param.typeParams
679761
subtyping.println(i"classBounds = ${app.classSymbols}")
680762
subtyping.println(i"base classes = ${other.baseClasses}")
681-
subtyping.println(i"type params = $hkTypeParams")
763+
subtyping.println(i"type params = $hkTypeParams, ${app.classSymbol}")
682764
if (inOrder) unifyWith(other)
683765
else testLifted(other, app, hkTypeParams, unifyWith)
684766
case _ =>
@@ -775,8 +857,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
775857
/** The symbol referred to in the refinement of `rt` */
776858
private def refinedSymbol(rt: RefinedType) = rt.parent.member(rt.refinedName).symbol
777859

778-
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
779-
* to keep the constraint as wide as possible. Specifically, if
860+
/** Returns true iff the result of evaluating either `op1` or `op2` is true,
861+
* trying at the same time to keep the constraint as wide as possible.
862+
* E.g, if
780863
*
781864
* tp11 <:< tp12 = true with post-constraint c1
782865
* tp12 <:< tp22 = true with post-constraint c2
@@ -803,15 +886,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
803886
* Here, each precondition leads to a different constraint, and neither of
804887
* the two post-constraints subsumes the other.
805888
*/
806-
private def eitherIsSubType(tp11: Type, tp21: Type, tp12: Type, tp22: Type) = {
889+
private def either(op1: => Boolean, op2: => Boolean): Boolean = {
807890
val preConstraint = constraint
808-
isSubType(tp11, tp21) && {
891+
op1 && {
809892
val leftConstraint = constraint
810893
constraint = preConstraint
811-
if (!(isSubType(tp12, tp22) && subsumes(leftConstraint, constraint, preConstraint)))
894+
if (!(op2 && subsumes(leftConstraint, constraint, preConstraint)))
812895
constraint = leftConstraint
813896
true
814-
} || isSubType(tp12, tp22)
897+
} || op2
815898
}
816899

817900
/** Like tp1 <:< tp2, but returns false immediately if we know that
@@ -1533,12 +1616,23 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
15331616
}
15341617

15351618
override def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean =
1536-
traceIndented(s"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound $frozenConstraint") {
1619+
traceIndented(i"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound $frozenConstraint") {
15371620
super.addConstraint(param, bound, fromBelow)
15381621
}
15391622

15401623
override def copyIn(ctx: Context) = new ExplainingTypeComparer(ctx)
15411624

1625+
override def compareHkApply2(tp1: Type, tp2: Type, tycon2: Type, args2: List[Type]): Boolean = {
1626+
def addendum = tycon2 match {
1627+
case param2: PolyParam =>
1628+
i": it's a polyparam with entry ${ctx.typerState.constraint.entry(param2)}"
1629+
case _ =>
1630+
}
1631+
traceIndented(i"compareHkApply $tp1, $tp2, $addendum") {
1632+
super.compareHkApply2(tp1, tp2, tycon2, args2)
1633+
}
1634+
}
1635+
15421636
override def compareHkApplyOLD(app: RefinedType, other: Type, inOrder: Boolean) =
15431637
if (app.isHKApplyOLD)
15441638
traceIndented(i"compareHkApply $app, $other, $inOrder, ${app.normalizeHkApplyOLD}") {

0 commit comments

Comments
 (0)