@@ -658,44 +658,70 @@ object TypeOps:
658
658
* Otherwise, return NoType.
659
659
*/
660
660
private def instantiateToSubType (tp1 : NamedType , tp2 : Type )(using Context ): Type = {
661
- /** expose abstract type references to their bounds or tvars according to variance */
662
- class AbstractTypeMap (maximize : Boolean )(using Context ) extends TypeMap {
663
- def expose (lo : Type , hi : Type ): Type =
664
- if (variance == 0 )
665
- newTypeVar(TypeBounds (lo, hi))
666
- else if (variance == 1 )
667
- if (maximize) hi else lo
668
- else
669
- if (maximize) lo else hi
661
+ // In order for a child type S to qualify as a valid subtype of the parent
662
+ // T, we need to test whether it is possible S <: T. Therefore, we replace
663
+ // type parameters in T with tvars, and see if the subtyping is true.
664
+ val approximateTypeParams = new TypeMap {
665
+ val boundTypeParams = util.HashMap [TypeRef , TypeVar ]()
670
666
671
- def apply (tp : Type ): Type = tp match {
667
+ def apply (tp : Type ): Type = tp.dealias match {
672
668
case _ : MatchType =>
673
669
tp // break cycles
674
670
675
- case tp : TypeRef if isBounds(tp.underlying) =>
676
- val lo = this (tp.info.loBound)
677
- val hi = this (tp.info.hiBound)
678
- // See tests/patmat/gadt.scala tests/patmat/exhausting.scala tests/patmat/t9657.scala
679
- val exposed = expose(lo, hi)
680
- typr.println(s " $tp exposed to =====> $exposed" )
681
- exposed
682
-
683
- case AppliedType (tycon : TypeRef , args) if isBounds(tycon.underlying) =>
684
- val args2 = args.map(this )
685
- val lo = this (tycon.info.loBound).applyIfParameterized(args2)
686
- val hi = this (tycon.info.hiBound).applyIfParameterized(args2)
687
- val exposed = expose(lo, hi)
688
- typr.println(s " $tp exposed to =====> $exposed" )
689
- exposed
671
+ case tp : TypeRef if ! tp.symbol.isClass =>
672
+ def lo = LazyRef (apply(tp.underlying.loBound))
673
+ def hi = LazyRef (apply(tp.underlying.hiBound))
674
+ val lookup = boundTypeParams.lookup(tp)
675
+ if lookup != null then lookup
676
+ else
677
+ val tv = newTypeVar(TypeBounds (lo, hi))
678
+ boundTypeParams(tp) = tv
679
+ // Force lazy ref eagerly using current context
680
+ // Otherwise, the lazy ref will be forced with a unknown context,
681
+ // which causes a problem in tests/patmat/i3645e.scala
682
+ lo.ref
683
+ hi.ref
684
+ tv
685
+ end if
686
+
687
+ case AppliedType (tycon : TypeRef , _) if ! tycon.dealias.typeSymbol.isClass =>
688
+
689
+ // In tests/patmat/i3645g.scala, we need to tell whether it's possible
690
+ // that K1 <: K[Foo]. If yes, we issue a warning; otherwise, no
691
+ // warnings.
692
+ //
693
+ // - K1 <: K[Foo] is possible <==>
694
+ // - K[Int] <: K[Foo] is possible <==>
695
+ // - Int <: Foo is possible <==>
696
+ // - Int <: Module.Foo.Type is possible
697
+ //
698
+ // If we remove this special case, we will encounter the case Int <:
699
+ // X[Y], where X and Y are tvars. The subtype checking will simply
700
+ // return false. But depending on the bounds of X and Y, the subtyping
701
+ // can be true.
702
+ //
703
+ // As a workaround, we approximate higher-kinded type parameters with
704
+ // the value types that can be instantiated from its bounds.
705
+ //
706
+ // Note that `HKTypeLambda.resType` may contain TypeParamRef that are
707
+ // bound in the HKTypeLambda. This is fine, as the TypeComparer will
708
+ // recurse on the bounds of `TypeParamRef`.
709
+ val bounds : TypeBounds = tycon.underlying match {
710
+ case TypeBounds (tl1 : HKTypeLambda , tl2 : HKTypeLambda ) =>
711
+ TypeBounds (tl1.resType, tl2.resType)
712
+ case TypeBounds (tl1 : HKTypeLambda , tp2) =>
713
+ TypeBounds (tl1.resType, tp2)
714
+ case TypeBounds (tp1, tl2 : HKTypeLambda ) =>
715
+ TypeBounds (tp1, tl2.resType)
716
+ }
690
717
691
- case _ =>
718
+ newTypeVar(bounds)
719
+
720
+ case tp =>
692
721
mapOver(tp)
693
722
}
694
723
}
695
724
696
- def minTypeMap (using Context ) = new AbstractTypeMap (maximize = false )
697
- def maxTypeMap (using Context ) = new AbstractTypeMap (maximize = true )
698
-
699
725
// Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C`
700
726
// Note: we need to strip ThisType in `p` recursively.
701
727
//
@@ -721,37 +747,25 @@ object TypeOps:
721
747
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
722
748
val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars)
723
749
724
- val force = new ForceDegree .Value (
725
- tvar =>
726
- ! (ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
727
- (tvar `eq` inferThisMap.prefixTVar), // always instantiate prefix
728
- IfBottom .flip
729
- )
730
-
731
750
// If parent contains a reference to an abstract type, then we should
732
751
// refine subtype checking to eliminate abstract types according to
733
752
// variance. As this logic is only needed in exhaustivity check,
734
753
// we manually patch subtyping check instead of changing TypeComparer.
735
754
// See tests/patmat/i3645b.scala
736
- def parentQualify = tp1.widen.classSymbol.info.parents.exists { parent =>
737
- inContext(ctx.fresh.setNewTyperState()) {
738
- parent.argInfos.nonEmpty && minTypeMap.apply(parent) <:< maxTypeMap.apply(tp2)
739
- }
755
+ def parentQualify (tp1 : Type , tp2 : Type ) = tp1.widen.classSymbol.info.parents.exists { parent =>
756
+ parent.argInfos.nonEmpty && approximateTypeParams(parent) <:< tp2
740
757
}
741
758
742
- if (protoTp1 <:< tp2) {
759
+ def instantiate () : Type = {
743
760
maximizeType(protoTp1, NoSpan , fromScala2x = false )
744
761
wildApprox(protoTp1)
745
762
}
763
+
764
+ if (protoTp1 <:< tp2) instantiate()
746
765
else {
747
- val protoTp2 = maxTypeMap.apply(tp2)
748
- if (protoTp1 <:< protoTp2 || parentQualify)
749
- if (isFullyDefined(AndType (protoTp1, protoTp2), force)) protoTp1
750
- else wildApprox(protoTp1)
751
- else {
752
- typr.println(s " $protoTp1 <:< $protoTp2 = false " )
753
- NoType
754
- }
766
+ val protoTp2 = approximateTypeParams(tp2)
767
+ if (protoTp1 <:< protoTp2 || parentQualify(protoTp1, protoTp2)) instantiate()
768
+ else NoType
755
769
}
756
770
}
757
771
0 commit comments