@@ -658,44 +658,70 @@ object TypeOps:
658658 * Otherwise, return NoType.
659659 */
660660 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 ]()
670666
671- def apply (tp : Type ): Type = tp match {
667+ def apply (tp : Type ): Type = tp.dealias match {
672668 case _ : MatchType =>
673669 tp // break cycles
674670
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+ }
690717
691- case _ =>
718+ newTypeVar(bounds)
719+
720+ case tp =>
692721 mapOver(tp)
693722 }
694723 }
695724
696- def minTypeMap (using Context ) = new AbstractTypeMap (maximize = false )
697- def maxTypeMap (using Context ) = new AbstractTypeMap (maximize = true )
698-
699725 // Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C`
700726 // Note: we need to strip ThisType in `p` recursively.
701727 //
@@ -721,37 +747,25 @@ object TypeOps:
721747 val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
722748 val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars)
723749
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-
731750 // If parent contains a reference to an abstract type, then we should
732751 // refine subtype checking to eliminate abstract types according to
733752 // variance. As this logic is only needed in exhaustivity check,
734753 // we manually patch subtyping check instead of changing TypeComparer.
735754 // 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
740757 }
741758
742- if (protoTp1 <:< tp2) {
759+ def instantiate () : Type = {
743760 maximizeType(protoTp1, NoSpan , fromScala2x = false )
744761 wildApprox(protoTp1)
745762 }
763+
764+ if (protoTp1 <:< tp2) instantiate()
746765 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
755769 }
756770 }
757771
0 commit comments