@@ -27,6 +27,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
2727
2828 private [this ] var needsGc = false
2929
30+ /** True iff a compared type `tp1` */
31+ private [this ] var loIsPrecise = true
32+ private [this ] var hiIsPrecise = true
33+ val newScheme = true
34+
3035 /** Is a subtype check in progress? In that case we may not
3136 * permanently instantiate type variables, because the corresponding
3237 * constraint might still be retracted and the instantiation should
@@ -100,13 +105,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
100105 def topLevelSubType (tp1 : Type , tp2 : Type ): Boolean = {
101106 if (tp2 eq NoType ) return false
102107 if ((tp2 eq tp1) || (tp2 eq WildcardType )) return true
103- try isSubType (tp1, tp2)
108+ try isSubTypePart (tp1, tp2)
104109 finally
105110 if (Config .checkConstraintsSatisfiable)
106111 assert(isSatisfiable, constraint.show)
107112 }
108113
109- protected def isSubType (tp1 : Type , tp2 : Type ): Boolean = trace(s " isSubType ${traceInfo(tp1, tp2)}" , subtyping) {
114+ protected def isSubType (tp1 : Type , tp2 : Type ): Boolean = trace(s " isSubType ${traceInfo(tp1, tp2)} $loIsPrecise $hiIsPrecise" , subtyping) {
115+ // assert(s"isSubType ${traceInfo(tp1, tp2)} $loIsPrecise $hiIsPrecise" !=
116+ // "isSubType String <:< E false true")
110117 if (tp2 eq NoType ) false
111118 else if (tp1 eq tp2) true
112119 else {
@@ -175,6 +182,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
175182 }
176183 }
177184
185+ private def isSubApproxLo (tp1 : Type , tp2 : Type ) = {
186+ val saved = loIsPrecise
187+ loIsPrecise = false
188+ try isSubType(tp1, tp2) finally loIsPrecise = saved
189+ }
190+
191+ private def isSubApproxHi (tp1 : Type , tp2 : Type ) = {
192+ val saved = hiIsPrecise
193+ hiIsPrecise = false
194+ try isSubType(tp1, tp2) finally hiIsPrecise = saved
195+ }
196+
197+ private def isSubTypePart (tp1 : Type , tp2 : Type ) = {
198+ val savedHi = hiIsPrecise
199+ val savedLo = loIsPrecise
200+ hiIsPrecise = true
201+ loIsPrecise = true
202+ try isSubType(tp1, tp2) finally {
203+ hiIsPrecise = savedHi
204+ loIsPrecise = savedLo
205+ }
206+ }
207+
178208 private def firstTry (tp1 : Type , tp2 : Type ): Boolean = tp2 match {
179209 case tp2 : NamedType =>
180210 def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
@@ -208,13 +238,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
208238 if ((sym1 ne NoSymbol ) && (sym1 eq sym2))
209239 ctx.erasedTypes ||
210240 sym1.isStaticOwner ||
211- isSubType (tp1.prefix, tp2.prefix) ||
241+ isSubTypePart (tp1.prefix, tp2.prefix) ||
212242 thirdTryNamed(tp1, tp2)
213243 else
214244 ( (tp1.name eq tp2.name)
215245 && tp1.isMemberRef
216246 && tp2.isMemberRef
217- && isSubType (tp1.prefix, tp2.prefix)
247+ && isSubTypePart (tp1.prefix, tp2.prefix)
218248 && tp1.signature == tp2.signature
219249 && ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
220250 ) ||
@@ -270,7 +300,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
270300 }
271301 compareSuper
272302 case AndType (tp21, tp22) =>
273- isSubType(tp1, tp21) && isSubType(tp1, tp22)
303+ isSubType(tp1, tp21) && isSubType(tp1, tp22) // no isSubApprox, as the two calls together maintain all information
274304 case OrType (tp21, tp22) =>
275305 if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21)
276306 else secondTry(tp1, tp2)
@@ -293,6 +323,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
293323 secondTry(tp1, tp2)
294324 }
295325
326+ def testConstrain (tp1 : Type , tp2 : Type , isUpper : Boolean ): Boolean =
327+ ! newScheme ||
328+ (if (isUpper) hiIsPrecise else loIsPrecise) || {
329+ println(i " missing constraint $tp1 with $tp2, isUpper = $isUpper" )
330+ false
331+ }
332+
296333 private def secondTry (tp1 : Type , tp2 : Type ): Boolean = tp1 match {
297334 case tp1 : NamedType =>
298335 tp1.info match {
@@ -314,7 +351,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
314351 def compareTypeParamRef =
315352 ctx.mode.is(Mode .TypevarsMissContext ) ||
316353 isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || {
317- if (canConstrain(tp1)) addConstraint(tp1, tp2, fromBelow = false ) && flagNothingBound
354+ if (canConstrain(tp1) && testConstrain(tp1, tp2, isUpper = true ) ) addConstraint(tp1, tp2, fromBelow = false ) && flagNothingBound
318355 else thirdTry(tp1, tp2)
319356 }
320357 compareTypeParamRef
@@ -379,8 +416,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
379416 GADTusage (tp2.symbol)
380417 }
381418 val tryLowerFirst = frozenConstraint || ! isCappable(tp1)
382- if (tryLowerFirst) isSubType (tp1, lo2) || compareGADT || fourthTry(tp1, tp2)
383- else compareGADT || fourthTry(tp1, tp2) || isSubType (tp1, lo2)
419+ if (tryLowerFirst) isSubApproxHi (tp1, lo2) || compareGADT || fourthTry(tp1, tp2)
420+ else compareGADT || fourthTry(tp1, tp2) || isSubApproxHi (tp1, lo2)
384421
385422 case _ =>
386423 val cls2 = tp2.symbol
@@ -393,7 +430,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
393430 if (cls2.is(JavaDefined ))
394431 // If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
395432 return base.typeSymbol == cls2
396- if (base ne tp1) return isSubType(base, tp2)
433+ if (base ne tp1)
434+ return if (tp1.isRef(cls2)) isSubType(base, tp2) else isSubApproxLo(base, tp2)
397435 }
398436 if (cls2 == defn.SingletonClass && tp1.isStable) return true
399437 }
@@ -420,7 +458,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
420458 if (frozenConstraint) isSubType(tp1, bounds(tp2).lo)
421459 else isSubTypeWhenFrozen(tp1, tp2)
422460 alwaysTrue || {
423- if (canConstrain(tp2)) addConstraint(tp2, tp1.widenExpr, fromBelow = true )
461+ if (canConstrain(tp2) && testConstrain(tp2, tp1.widenExpr, isUpper = false ) ) addConstraint(tp2, tp1.widenExpr, fromBelow = true )
424462 else fourthTry(tp1, tp2)
425463 }
426464 }
@@ -481,14 +519,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
481519 def boundsOK =
482520 ctx.scala2Mode ||
483521 tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) =>
484- isSubType (tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
522+ isSubTypePart (tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
485523 val saved = comparedTypeLambdas
486524 comparedTypeLambdas += tp1
487525 comparedTypeLambdas += tp2
488526 try
489527 variancesConform(tp1.typeParams, tp2.typeParams) &&
490528 boundsOK &&
491- isSubType (tp1.resType, tp2.resType.subst(tp2, tp1))
529+ isSubTypePart (tp1.resType, tp2.resType.subst(tp2, tp1))
492530 finally comparedTypeLambdas = saved
493531 case _ =>
494532 if (tp1.isHK) {
@@ -535,7 +573,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
535573 (tp1.signature consistentParams tp2.signature) &&
536574 matchingParams(tp1, tp2) &&
537575 (! tp2.isImplicitMethod || tp1.isImplicitMethod) &&
538- isSubType (tp1.resultType, tp2.resultType.subst(tp2, tp1))
576+ isSubTypePart (tp1.resultType, tp2.resultType.subst(tp2, tp1))
539577 case _ =>
540578 false
541579 }
@@ -548,15 +586,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
548586 // as members of the same type. And it seems most logical to take
549587 // ()T <:< => T, since everything one can do with a => T one can
550588 // also do with a ()T by automatic () insertion.
551- case tp1 @ MethodType (Nil ) => isSubType (tp1.resultType, restpe2)
552- case _ => isSubType (tp1.widenExpr, restpe2)
589+ case tp1 @ MethodType (Nil ) => isSubTypePart (tp1.resultType, restpe2)
590+ case _ => isSubTypePart (tp1.widenExpr, restpe2)
553591 }
554592 compareExpr
555593 case tp2 @ TypeBounds (lo2, hi2) =>
556594 def compareTypeBounds = tp1 match {
557595 case tp1 @ TypeBounds (lo1, hi1) =>
558- ((lo2 eq NothingType ) || isSubType (lo2, lo1)) &&
559- ((hi2 eq AnyType ) || isSubType (hi1, hi2))
596+ ((lo2 eq NothingType ) || isSubTypePart (lo2, lo1)) &&
597+ ((hi2 eq AnyType ) || isSubTypePart (hi1, hi2))
560598 case tp1 : ClassInfo =>
561599 tp2 contains tp1
562600 case _ =>
@@ -566,7 +604,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
566604 case ClassInfo (pre2, cls2, _, _, _) =>
567605 def compareClassInfo = tp1 match {
568606 case ClassInfo (pre1, cls1, _, _, _) =>
569- (cls1 eq cls2) && isSubType (pre1, pre2)
607+ (cls1 eq cls2) && isSubTypePart (pre1, pre2)
570608 case _ =>
571609 false
572610 }
@@ -586,7 +624,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
586624 narrowGADTBounds(tp1, tp2, isUpper = true )) &&
587625 GADTusage (tp1.symbol)
588626 }
589- isSubType (hi1, tp2) || compareGADT
627+ isSubApproxLo (hi1, tp2) || compareGADT
590628 case _ =>
591629 def isNullable (tp : Type ): Boolean = tp.widenDealias match {
592630 case tp : TypeRef => tp.symbol.isNullableClass
@@ -627,7 +665,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
627665 case EtaExpansion (tycon1) => isSubType(tycon1, tp2)
628666 case _ => tp2 match {
629667 case tp2 : HKTypeLambda => false // this case was covered in thirdTry
630- case _ => tp2.isHK && isSubType (tp1.resultType, tp2.appliedTo(tp1.paramRefs))
668+ case _ => tp2.isHK && isSubTypePart (tp1.resultType, tp2.appliedTo(tp1.paramRefs))
631669 }
632670 }
633671 compareHKLambda
@@ -654,7 +692,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
654692 either(isSubType(tp11, tp2), isSubType(tp12, tp2))
655693 case JavaArrayType (elem1) =>
656694 def compareJavaArray = tp2 match {
657- case JavaArrayType (elem2) => isSubType (elem1, elem2)
695+ case JavaArrayType (elem2) => isSubTypePart (elem1, elem2)
658696 case _ => tp2 isRef ObjectClass
659697 }
660698 compareJavaArray
@@ -685,7 +723,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
685723 case tycon1 : TypeRef =>
686724 tycon2.dealias match {
687725 case tycon2 : TypeRef if tycon1.symbol == tycon2.symbol =>
688- isSubType (tycon1.prefix, tycon2.prefix) &&
726+ isSubTypePart (tycon1.prefix, tycon2.prefix) &&
689727 isSubArgs(args1, args2, tp1, tparams)
690728 case _ =>
691729 false
@@ -766,7 +804,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
766804 * @param tyconLo The type constructor's lower approximation.
767805 */
768806 def fallback (tyconLo : Type ) =
769- either(fourthTry(tp1, tp2), isSubType (tp1, tyconLo.applyIfParameterized(args2)))
807+ either(fourthTry(tp1, tp2), isSubApproxHi (tp1, tyconLo.applyIfParameterized(args2)))
770808
771809 /** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`.
772810 * Let `app2 = tp2` where the type constructor of `tp2` is replaced by
@@ -779,9 +817,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
779817 */
780818 def compareLower (tycon2bounds : TypeBounds , tyconIsTypeRef : Boolean ): Boolean =
781819 if (tycon2bounds.lo eq tycon2bounds.hi)
782- isSubType(tp1,
783- if (tyconIsTypeRef) tp2.superType
784- else tycon2bounds.lo.applyIfParameterized(args2))
820+ if (tyconIsTypeRef) isSubType(tp1, tp2.superType)
821+ else isSubApproxHi(tp1, tycon2bounds.lo.applyIfParameterized(args2))
785822 else
786823 fallback(tycon2bounds.lo)
787824
@@ -797,7 +834,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
797834 compareLower(info2, tyconIsTypeRef = true )
798835 case info2 : ClassInfo =>
799836 val base = tp1.baseType(info2.cls)
800- if (base.exists && base.ne(tp1)) isSubType(base, tp2)
837+ if (base.exists && base.ne(tp1))
838+ if (tp1.isRef(info2.cls)) isSubType(base, tp2)
839+ else isSubApproxLo(base, tp2)
801840 else fourthTry(tp1, tp2)
802841 case _ =>
803842 fourthTry(tp1, tp2)
@@ -824,7 +863,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
824863 false
825864 }
826865 canConstrain(param1) && canInstantiate ||
827- isSubType (bounds(param1).hi.applyIfParameterized(args1), tp2)
866+ isSubApproxLo (bounds(param1).hi.applyIfParameterized(args1), tp2)
828867 case tycon1 : TypeRef if tycon1.symbol.isClass =>
829868 false
830869 case tycon1 : TypeProxy =>
@@ -858,8 +897,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
858897 case arg1 : TypeBounds =>
859898 compareCaptured(arg1, arg2)
860899 case _ =>
861- (v > 0 || isSubType (arg2, arg1)) &&
862- (v < 0 || isSubType (arg1, arg2))
900+ (v > 0 || isSubTypePart (arg2, arg1)) &&
901+ (v < 0 || isSubTypePart (arg1, arg2))
863902 }
864903 }
865904
@@ -962,7 +1001,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
9621001 if (isCovered(tp1) && isCovered(tp2)) {
9631002 // println(s"useless subtype: $tp1 <:< $tp2")
9641003 false
965- } else isSubType (tp1, tp2)
1004+ } else isSubApproxLo (tp1, tp2)
9661005
9671006 /** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
9681007 * the normalized type of the refinement `tp2`?
@@ -991,15 +1030,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
9911030 tp2.refinedInfo match {
9921031 case rinfo2 : TypeBounds =>
9931032 val ref1 = tp1.widenExpr.select(name)
994- isSubType (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
1033+ isSubTypePart (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
9951034 case _ =>
9961035 false
9971036 }
9981037 case _ => false
9991038 }
10001039
10011040 def qualifies (m : SingleDenotation ) =
1002- isSubType (m.info, rinfo2) || matchAbstractTypeMember(m.info)
1041+ isSubTypePart (m.info, rinfo2) || matchAbstractTypeMember(m.info)
10031042
10041043 tp1.member(name) match { // inlined hasAltWith for performance
10051044 case mbr : SingleDenotation => qualifies(mbr)
@@ -1039,7 +1078,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10391078 */
10401079 private def isSubRefinements (tp1 : RefinedType , tp2 : RefinedType , limit : Type ): Boolean = {
10411080 def hasSubRefinement (tp1 : RefinedType , refine2 : Type ): Boolean = {
1042- isSubType (tp1.refinedInfo, refine2) || {
1081+ isSubTypePart (tp1.refinedInfo, refine2) || {
10431082 // last effort: try to adapt variances of higher-kinded types if this is sound.
10441083 // TODO: Move this to eta-expansion?
10451084 val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
@@ -1079,7 +1118,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10791118 * case of a GADT bounded typeref, we should narrow with `tp2` instead of its lower bound.
10801119 */
10811120 private def isCappable (tp : Type ): Boolean = tp match {
1082- case tp : TypeRef => ctx.gadt.bounds.contains(tp.symbol)
10831121 case tp : TypeParamRef => constraint contains tp
10841122 case tp : TypeProxy => isCappable(tp.underlying)
10851123 case tp : AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2)
@@ -1090,8 +1128,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10901128 * `bound` as an upper or lower bound (which depends on `isUpper`).
10911129 * Test that the resulting bounds are still satisfiable.
10921130 */
1093- private def narrowGADTBounds (tr : NamedType , bound : Type , isUpper : Boolean ): Boolean =
1094- ctx.mode.is(Mode .GADTflexible ) && ! frozenConstraint && {
1131+ private def narrowGADTBounds (tr : NamedType , bound : Type , isUpper : Boolean ): Boolean = {
1132+ val boundIsPrecise = if (isUpper) hiIsPrecise else loIsPrecise
1133+ ctx.mode.is(Mode .GADTflexible ) && ! frozenConstraint && boundIsPrecise && {
10951134 val tparam = tr.symbol
10961135 gadts.println(i " narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) " above" else " below" } to $bound ${bound.toString} ${bound.isRef(tparam)}" )
10971136 if (bound.isRef(tparam)) false
@@ -1100,10 +1139,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
11001139 val newBounds =
11011140 if (isUpper) TypeBounds (oldBounds.lo, oldBounds.hi & bound)
11021141 else TypeBounds (oldBounds.lo | bound, oldBounds.hi)
1103- isSubType (newBounds.lo, newBounds.hi) &&
1142+ isSubTypePart (newBounds.lo, newBounds.hi) &&
11041143 { ctx.gadt.setBounds(tparam, newBounds); true }
11051144 }
11061145 }
1146+ }
11071147
11081148 // Tests around `matches`
11091149
0 commit comments