@@ -27,6 +27,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
27
27
28
28
private [this ] var needsGc = false
29
29
30
+ /** True iff a compared type `tp1` */
31
+ private [this ] var loIsPrecise = true
32
+ private [this ] var hiIsPrecise = true
33
+ val newScheme = true
34
+
30
35
/** Is a subtype check in progress? In that case we may not
31
36
* permanently instantiate type variables, because the corresponding
32
37
* constraint might still be retracted and the instantiation should
@@ -100,13 +105,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
100
105
def topLevelSubType (tp1 : Type , tp2 : Type ): Boolean = {
101
106
if (tp2 eq NoType ) return false
102
107
if ((tp2 eq tp1) || (tp2 eq WildcardType )) return true
103
- try isSubType (tp1, tp2)
108
+ try isSubTypePart (tp1, tp2)
104
109
finally
105
110
if (Config .checkConstraintsSatisfiable)
106
111
assert(isSatisfiable, constraint.show)
107
112
}
108
113
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")
110
117
if (tp2 eq NoType ) false
111
118
else if (tp1 eq tp2) true
112
119
else {
@@ -175,6 +182,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
175
182
}
176
183
}
177
184
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
+
178
208
private def firstTry (tp1 : Type , tp2 : Type ): Boolean = tp2 match {
179
209
case tp2 : NamedType =>
180
210
def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
@@ -208,13 +238,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
208
238
if ((sym1 ne NoSymbol ) && (sym1 eq sym2))
209
239
ctx.erasedTypes ||
210
240
sym1.isStaticOwner ||
211
- isSubType (tp1.prefix, tp2.prefix) ||
241
+ isSubTypePart (tp1.prefix, tp2.prefix) ||
212
242
thirdTryNamed(tp1, tp2)
213
243
else
214
244
( (tp1.name eq tp2.name)
215
245
&& tp1.isMemberRef
216
246
&& tp2.isMemberRef
217
- && isSubType (tp1.prefix, tp2.prefix)
247
+ && isSubTypePart (tp1.prefix, tp2.prefix)
218
248
&& tp1.signature == tp2.signature
219
249
&& ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
220
250
) ||
@@ -270,7 +300,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
270
300
}
271
301
compareSuper
272
302
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
274
304
case OrType (tp21, tp22) =>
275
305
if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21)
276
306
else secondTry(tp1, tp2)
@@ -293,6 +323,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
293
323
secondTry(tp1, tp2)
294
324
}
295
325
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
+
296
333
private def secondTry (tp1 : Type , tp2 : Type ): Boolean = tp1 match {
297
334
case tp1 : NamedType =>
298
335
tp1.info match {
@@ -314,7 +351,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
314
351
def compareTypeParamRef =
315
352
ctx.mode.is(Mode .TypevarsMissContext ) ||
316
353
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
318
355
else thirdTry(tp1, tp2)
319
356
}
320
357
compareTypeParamRef
@@ -379,8 +416,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
379
416
GADTusage (tp2.symbol)
380
417
}
381
418
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)
384
421
385
422
case _ =>
386
423
val cls2 = tp2.symbol
@@ -393,7 +430,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
393
430
if (cls2.is(JavaDefined ))
394
431
// If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
395
432
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)
397
435
}
398
436
if (cls2 == defn.SingletonClass && tp1.isStable) return true
399
437
}
@@ -420,7 +458,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
420
458
if (frozenConstraint) isSubType(tp1, bounds(tp2).lo)
421
459
else isSubTypeWhenFrozen(tp1, tp2)
422
460
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 )
424
462
else fourthTry(tp1, tp2)
425
463
}
426
464
}
@@ -481,14 +519,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
481
519
def boundsOK =
482
520
ctx.scala2Mode ||
483
521
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))
485
523
val saved = comparedTypeLambdas
486
524
comparedTypeLambdas += tp1
487
525
comparedTypeLambdas += tp2
488
526
try
489
527
variancesConform(tp1.typeParams, tp2.typeParams) &&
490
528
boundsOK &&
491
- isSubType (tp1.resType, tp2.resType.subst(tp2, tp1))
529
+ isSubTypePart (tp1.resType, tp2.resType.subst(tp2, tp1))
492
530
finally comparedTypeLambdas = saved
493
531
case _ =>
494
532
if (tp1.isHK) {
@@ -535,7 +573,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
535
573
(tp1.signature consistentParams tp2.signature) &&
536
574
matchingParams(tp1, tp2) &&
537
575
(! tp2.isImplicitMethod || tp1.isImplicitMethod) &&
538
- isSubType (tp1.resultType, tp2.resultType.subst(tp2, tp1))
576
+ isSubTypePart (tp1.resultType, tp2.resultType.subst(tp2, tp1))
539
577
case _ =>
540
578
false
541
579
}
@@ -548,15 +586,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
548
586
// as members of the same type. And it seems most logical to take
549
587
// ()T <:< => T, since everything one can do with a => T one can
550
588
// 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)
553
591
}
554
592
compareExpr
555
593
case tp2 @ TypeBounds (lo2, hi2) =>
556
594
def compareTypeBounds = tp1 match {
557
595
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))
560
598
case tp1 : ClassInfo =>
561
599
tp2 contains tp1
562
600
case _ =>
@@ -566,7 +604,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
566
604
case ClassInfo (pre2, cls2, _, _, _) =>
567
605
def compareClassInfo = tp1 match {
568
606
case ClassInfo (pre1, cls1, _, _, _) =>
569
- (cls1 eq cls2) && isSubType (pre1, pre2)
607
+ (cls1 eq cls2) && isSubTypePart (pre1, pre2)
570
608
case _ =>
571
609
false
572
610
}
@@ -586,7 +624,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
586
624
narrowGADTBounds(tp1, tp2, isUpper = true )) &&
587
625
GADTusage (tp1.symbol)
588
626
}
589
- isSubType (hi1, tp2) || compareGADT
627
+ isSubApproxLo (hi1, tp2) || compareGADT
590
628
case _ =>
591
629
def isNullable (tp : Type ): Boolean = tp.widenDealias match {
592
630
case tp : TypeRef => tp.symbol.isNullableClass
@@ -627,7 +665,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
627
665
case EtaExpansion (tycon1) => isSubType(tycon1, tp2)
628
666
case _ => tp2 match {
629
667
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))
631
669
}
632
670
}
633
671
compareHKLambda
@@ -654,7 +692,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
654
692
either(isSubType(tp11, tp2), isSubType(tp12, tp2))
655
693
case JavaArrayType (elem1) =>
656
694
def compareJavaArray = tp2 match {
657
- case JavaArrayType (elem2) => isSubType (elem1, elem2)
695
+ case JavaArrayType (elem2) => isSubTypePart (elem1, elem2)
658
696
case _ => tp2 isRef ObjectClass
659
697
}
660
698
compareJavaArray
@@ -685,7 +723,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
685
723
case tycon1 : TypeRef =>
686
724
tycon2.dealias match {
687
725
case tycon2 : TypeRef if tycon1.symbol == tycon2.symbol =>
688
- isSubType (tycon1.prefix, tycon2.prefix) &&
726
+ isSubTypePart (tycon1.prefix, tycon2.prefix) &&
689
727
isSubArgs(args1, args2, tp1, tparams)
690
728
case _ =>
691
729
false
@@ -766,7 +804,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
766
804
* @param tyconLo The type constructor's lower approximation.
767
805
*/
768
806
def fallback (tyconLo : Type ) =
769
- either(fourthTry(tp1, tp2), isSubType (tp1, tyconLo.applyIfParameterized(args2)))
807
+ either(fourthTry(tp1, tp2), isSubApproxHi (tp1, tyconLo.applyIfParameterized(args2)))
770
808
771
809
/** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`.
772
810
* Let `app2 = tp2` where the type constructor of `tp2` is replaced by
@@ -779,9 +817,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
779
817
*/
780
818
def compareLower (tycon2bounds : TypeBounds , tyconIsTypeRef : Boolean ): Boolean =
781
819
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))
785
822
else
786
823
fallback(tycon2bounds.lo)
787
824
@@ -797,7 +834,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
797
834
compareLower(info2, tyconIsTypeRef = true )
798
835
case info2 : ClassInfo =>
799
836
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)
801
840
else fourthTry(tp1, tp2)
802
841
case _ =>
803
842
fourthTry(tp1, tp2)
@@ -824,7 +863,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
824
863
false
825
864
}
826
865
canConstrain(param1) && canInstantiate ||
827
- isSubType (bounds(param1).hi.applyIfParameterized(args1), tp2)
866
+ isSubApproxLo (bounds(param1).hi.applyIfParameterized(args1), tp2)
828
867
case tycon1 : TypeRef if tycon1.symbol.isClass =>
829
868
false
830
869
case tycon1 : TypeProxy =>
@@ -858,8 +897,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
858
897
case arg1 : TypeBounds =>
859
898
compareCaptured(arg1, arg2)
860
899
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))
863
902
}
864
903
}
865
904
@@ -962,7 +1001,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
962
1001
if (isCovered(tp1) && isCovered(tp2)) {
963
1002
// println(s"useless subtype: $tp1 <:< $tp2")
964
1003
false
965
- } else isSubType (tp1, tp2)
1004
+ } else isSubApproxLo (tp1, tp2)
966
1005
967
1006
/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
968
1007
* the normalized type of the refinement `tp2`?
@@ -991,15 +1030,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
991
1030
tp2.refinedInfo match {
992
1031
case rinfo2 : TypeBounds =>
993
1032
val ref1 = tp1.widenExpr.select(name)
994
- isSubType (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
1033
+ isSubTypePart (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
995
1034
case _ =>
996
1035
false
997
1036
}
998
1037
case _ => false
999
1038
}
1000
1039
1001
1040
def qualifies (m : SingleDenotation ) =
1002
- isSubType (m.info, rinfo2) || matchAbstractTypeMember(m.info)
1041
+ isSubTypePart (m.info, rinfo2) || matchAbstractTypeMember(m.info)
1003
1042
1004
1043
tp1.member(name) match { // inlined hasAltWith for performance
1005
1044
case mbr : SingleDenotation => qualifies(mbr)
@@ -1039,7 +1078,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1039
1078
*/
1040
1079
private def isSubRefinements (tp1 : RefinedType , tp2 : RefinedType , limit : Type ): Boolean = {
1041
1080
def hasSubRefinement (tp1 : RefinedType , refine2 : Type ): Boolean = {
1042
- isSubType (tp1.refinedInfo, refine2) || {
1081
+ isSubTypePart (tp1.refinedInfo, refine2) || {
1043
1082
// last effort: try to adapt variances of higher-kinded types if this is sound.
1044
1083
// TODO: Move this to eta-expansion?
1045
1084
val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
@@ -1079,7 +1118,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1079
1118
* case of a GADT bounded typeref, we should narrow with `tp2` instead of its lower bound.
1080
1119
*/
1081
1120
private def isCappable (tp : Type ): Boolean = tp match {
1082
- case tp : TypeRef => ctx.gadt.bounds.contains(tp.symbol)
1083
1121
case tp : TypeParamRef => constraint contains tp
1084
1122
case tp : TypeProxy => isCappable(tp.underlying)
1085
1123
case tp : AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2)
@@ -1090,8 +1128,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1090
1128
* `bound` as an upper or lower bound (which depends on `isUpper`).
1091
1129
* Test that the resulting bounds are still satisfiable.
1092
1130
*/
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 && {
1095
1134
val tparam = tr.symbol
1096
1135
gadts.println(i " narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) " above" else " below" } to $bound ${bound.toString} ${bound.isRef(tparam)}" )
1097
1136
if (bound.isRef(tparam)) false
@@ -1100,10 +1139,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1100
1139
val newBounds =
1101
1140
if (isUpper) TypeBounds (oldBounds.lo, oldBounds.hi & bound)
1102
1141
else TypeBounds (oldBounds.lo | bound, oldBounds.hi)
1103
- isSubType (newBounds.lo, newBounds.hi) &&
1142
+ isSubTypePart (newBounds.lo, newBounds.hi) &&
1104
1143
{ ctx.gadt.setBounds(tparam, newBounds); true }
1105
1144
}
1106
1145
}
1146
+ }
1107
1147
1108
1148
// Tests around `matches`
1109
1149
0 commit comments