@@ -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 {
@@ -159,6 +166,29 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
159
166
}
160
167
}
161
168
169
+ private def isSubApproxLo (tp1 : Type , tp2 : Type ) = {
170
+ val saved = loIsPrecise
171
+ loIsPrecise = false
172
+ try isSubType(tp1, tp2) finally loIsPrecise = saved
173
+ }
174
+
175
+ private def isSubApproxHi (tp1 : Type , tp2 : Type ) = {
176
+ val saved = hiIsPrecise
177
+ hiIsPrecise = false
178
+ try isSubType(tp1, tp2) finally hiIsPrecise = saved
179
+ }
180
+
181
+ private def isSubTypePart (tp1 : Type , tp2 : Type ) = {
182
+ val savedHi = hiIsPrecise
183
+ val savedLo = loIsPrecise
184
+ hiIsPrecise = true
185
+ loIsPrecise = true
186
+ try isSubType(tp1, tp2) finally {
187
+ hiIsPrecise = savedHi
188
+ loIsPrecise = savedLo
189
+ }
190
+ }
191
+
162
192
private def firstTry (tp1 : Type , tp2 : Type ): Boolean = tp2 match {
163
193
case tp2 : NamedType =>
164
194
def compareNamed (tp1 : Type , tp2 : NamedType ): Boolean = {
@@ -192,13 +222,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
192
222
if ((sym1 ne NoSymbol ) && (sym1 eq sym2))
193
223
ctx.erasedTypes ||
194
224
sym1.isStaticOwner ||
195
- isSubType (tp1.prefix, tp2.prefix) ||
225
+ isSubTypePart (tp1.prefix, tp2.prefix) ||
196
226
thirdTryNamed(tp1, tp2)
197
227
else
198
228
( (tp1.name eq tp2.name)
199
229
&& tp1.isMemberRef
200
230
&& tp2.isMemberRef
201
- && isSubType (tp1.prefix, tp2.prefix)
231
+ && isSubTypePart (tp1.prefix, tp2.prefix)
202
232
&& tp1.signature == tp2.signature
203
233
&& ! (sym1.isClass && sym2.isClass) // class types don't subtype each other
204
234
) ||
@@ -254,7 +284,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
254
284
}
255
285
compareSuper
256
286
case AndType (tp21, tp22) =>
257
- isSubType(tp1, tp21) && isSubType(tp1, tp22)
287
+ isSubType(tp1, tp21) && isSubType(tp1, tp22) // no isSubApprox, as the two calls together maintain all information
258
288
case OrType (tp21, tp22) =>
259
289
if (tp21.stripTypeVar eq tp22.stripTypeVar) isSubType(tp1, tp21)
260
290
else secondTry(tp1, tp2)
@@ -277,6 +307,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
277
307
secondTry(tp1, tp2)
278
308
}
279
309
310
+ def testConstrain (tp1 : Type , tp2 : Type , isUpper : Boolean ): Boolean =
311
+ ! newScheme ||
312
+ (if (isUpper) hiIsPrecise else loIsPrecise) || {
313
+ println(i " missing constraint $tp1 with $tp2, isUpper = $isUpper" )
314
+ false
315
+ }
316
+
280
317
private def secondTry (tp1 : Type , tp2 : Type ): Boolean = tp1 match {
281
318
case tp1 : NamedType =>
282
319
tp1.info match {
@@ -298,7 +335,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
298
335
def compareTypeParamRef =
299
336
ctx.mode.is(Mode .TypevarsMissContext ) ||
300
337
isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || {
301
- if (canConstrain(tp1)) addConstraint(tp1, tp2, fromBelow = false ) && flagNothingBound
338
+ if (canConstrain(tp1) && testConstrain(tp1, tp2, isUpper = true ) ) addConstraint(tp1, tp2, fromBelow = false ) && flagNothingBound
302
339
else thirdTry(tp1, tp2)
303
340
}
304
341
compareTypeParamRef
@@ -363,8 +400,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
363
400
GADTusage (tp2.symbol)
364
401
}
365
402
val tryLowerFirst = frozenConstraint || ! isCappable(tp1)
366
- if (tryLowerFirst) isSubType (tp1, lo2) || compareGADT || fourthTry(tp1, tp2)
367
- else compareGADT || fourthTry(tp1, tp2) || isSubType (tp1, lo2)
403
+ if (tryLowerFirst) isSubApproxHi (tp1, lo2) || compareGADT || fourthTry(tp1, tp2)
404
+ else compareGADT || fourthTry(tp1, tp2) || isSubApproxHi (tp1, lo2)
368
405
369
406
case _ =>
370
407
val cls2 = tp2.symbol
@@ -377,7 +414,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
377
414
if (cls2.is(JavaDefined ))
378
415
// If `cls2` is parameterized, we are seeing a raw type, so we need to compare only the symbol
379
416
return base.typeSymbol == cls2
380
- if (base ne tp1) return isSubType(base, tp2)
417
+ if (base ne tp1)
418
+ return if (tp1.isRef(cls2)) isSubType(base, tp2) else isSubApproxLo(base, tp2)
381
419
}
382
420
if (cls2 == defn.SingletonClass && tp1.isStable) return true
383
421
}
@@ -404,7 +442,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
404
442
if (frozenConstraint) isSubType(tp1, bounds(tp2).lo)
405
443
else isSubTypeWhenFrozen(tp1, tp2)
406
444
alwaysTrue || {
407
- if (canConstrain(tp2)) addConstraint(tp2, tp1.widenExpr, fromBelow = true )
445
+ if (canConstrain(tp2) && testConstrain(tp2, tp1.widenExpr, isUpper = false ) ) addConstraint(tp2, tp1.widenExpr, fromBelow = true )
408
446
else fourthTry(tp1, tp2)
409
447
}
410
448
}
@@ -465,14 +503,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
465
503
def boundsOK =
466
504
ctx.scala2Mode ||
467
505
tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) =>
468
- isSubType (tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
506
+ isSubTypePart (tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
469
507
val saved = comparedTypeLambdas
470
508
comparedTypeLambdas += tp1
471
509
comparedTypeLambdas += tp2
472
510
try
473
511
variancesConform(tp1.typeParams, tp2.typeParams) &&
474
512
boundsOK &&
475
- isSubType (tp1.resType, tp2.resType.subst(tp2, tp1))
513
+ isSubTypePart (tp1.resType, tp2.resType.subst(tp2, tp1))
476
514
finally comparedTypeLambdas = saved
477
515
case _ =>
478
516
if (tp1.isHK) {
@@ -519,7 +557,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
519
557
(tp1.signature consistentParams tp2.signature) &&
520
558
matchingParams(tp1, tp2) &&
521
559
(! tp2.isImplicitMethod || tp1.isImplicitMethod) &&
522
- isSubType (tp1.resultType, tp2.resultType.subst(tp2, tp1))
560
+ isSubTypePart (tp1.resultType, tp2.resultType.subst(tp2, tp1))
523
561
case _ =>
524
562
false
525
563
}
@@ -532,15 +570,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
532
570
// as members of the same type. And it seems most logical to take
533
571
// ()T <:< => T, since everything one can do with a => T one can
534
572
// also do with a ()T by automatic () insertion.
535
- case tp1 @ MethodType (Nil ) => isSubType (tp1.resultType, restpe2)
536
- case _ => isSubType (tp1.widenExpr, restpe2)
573
+ case tp1 @ MethodType (Nil ) => isSubTypePart (tp1.resultType, restpe2)
574
+ case _ => isSubTypePart (tp1.widenExpr, restpe2)
537
575
}
538
576
compareExpr
539
577
case tp2 @ TypeBounds (lo2, hi2) =>
540
578
def compareTypeBounds = tp1 match {
541
579
case tp1 @ TypeBounds (lo1, hi1) =>
542
- ((lo2 eq NothingType ) || isSubType (lo2, lo1)) &&
543
- ((hi2 eq AnyType ) || isSubType (hi1, hi2))
580
+ ((lo2 eq NothingType ) || isSubTypePart (lo2, lo1)) &&
581
+ ((hi2 eq AnyType ) || isSubTypePart (hi1, hi2))
544
582
case tp1 : ClassInfo =>
545
583
tp2 contains tp1
546
584
case _ =>
@@ -550,7 +588,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
550
588
case ClassInfo (pre2, cls2, _, _, _) =>
551
589
def compareClassInfo = tp1 match {
552
590
case ClassInfo (pre1, cls1, _, _, _) =>
553
- (cls1 eq cls2) && isSubType (pre1, pre2)
591
+ (cls1 eq cls2) && isSubTypePart (pre1, pre2)
554
592
case _ =>
555
593
false
556
594
}
@@ -570,7 +608,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
570
608
narrowGADTBounds(tp1, tp2, isUpper = true )) &&
571
609
GADTusage (tp1.symbol)
572
610
}
573
- isSubType (hi1, tp2) || compareGADT
611
+ isSubApproxLo (hi1, tp2) || compareGADT
574
612
case _ =>
575
613
def isNullable (tp : Type ): Boolean = tp.widenDealias match {
576
614
case tp : TypeRef => tp.symbol.isNullableClass
@@ -611,7 +649,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
611
649
case EtaExpansion (tycon1) => isSubType(tycon1, tp2)
612
650
case _ => tp2 match {
613
651
case tp2 : HKTypeLambda => false // this case was covered in thirdTry
614
- case _ => tp2.isHK && isSubType (tp1.resultType, tp2.appliedTo(tp1.paramRefs))
652
+ case _ => tp2.isHK && isSubTypePart (tp1.resultType, tp2.appliedTo(tp1.paramRefs))
615
653
}
616
654
}
617
655
compareHKLambda
@@ -638,7 +676,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
638
676
either(isSubType(tp11, tp2), isSubType(tp12, tp2))
639
677
case JavaArrayType (elem1) =>
640
678
def compareJavaArray = tp2 match {
641
- case JavaArrayType (elem2) => isSubType (elem1, elem2)
679
+ case JavaArrayType (elem2) => isSubTypePart (elem1, elem2)
642
680
case _ => tp2 isRef ObjectClass
643
681
}
644
682
compareJavaArray
@@ -669,7 +707,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
669
707
case tycon1 : TypeRef =>
670
708
tycon2.dealias match {
671
709
case tycon2 : TypeRef if tycon1.symbol == tycon2.symbol =>
672
- isSubType (tycon1.prefix, tycon2.prefix) &&
710
+ isSubTypePart (tycon1.prefix, tycon2.prefix) &&
673
711
isSubArgs(args1, args2, tp1, tparams)
674
712
case _ =>
675
713
false
@@ -750,7 +788,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
750
788
* @param tyconLo The type constructor's lower approximation.
751
789
*/
752
790
def fallback (tyconLo : Type ) =
753
- either(fourthTry(tp1, tp2), isSubType (tp1, tyconLo.applyIfParameterized(args2)))
791
+ either(fourthTry(tp1, tp2), isSubApproxHi (tp1, tyconLo.applyIfParameterized(args2)))
754
792
755
793
/** Let `tycon2bounds` be the bounds of the RHS type constructor `tycon2`.
756
794
* Let `app2 = tp2` where the type constructor of `tp2` is replaced by
@@ -763,9 +801,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
763
801
*/
764
802
def compareLower (tycon2bounds : TypeBounds , tyconIsTypeRef : Boolean ): Boolean =
765
803
if (tycon2bounds.lo eq tycon2bounds.hi)
766
- isSubType(tp1,
767
- if (tyconIsTypeRef) tp2.superType
768
- else tycon2bounds.lo.applyIfParameterized(args2))
804
+ if (tyconIsTypeRef) isSubType(tp1, tp2.superType)
805
+ else isSubApproxHi(tp1, tycon2bounds.lo.applyIfParameterized(args2))
769
806
else
770
807
fallback(tycon2bounds.lo)
771
808
@@ -781,7 +818,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
781
818
compareLower(info2, tyconIsTypeRef = true )
782
819
case info2 : ClassInfo =>
783
820
val base = tp1.baseType(info2.cls)
784
- if (base.exists && base.ne(tp1)) isSubType(base, tp2)
821
+ if (base.exists && base.ne(tp1))
822
+ if (tp1.isRef(info2.cls)) isSubType(base, tp2)
823
+ else isSubApproxLo(base, tp2)
785
824
else fourthTry(tp1, tp2)
786
825
case _ =>
787
826
fourthTry(tp1, tp2)
@@ -808,7 +847,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
808
847
false
809
848
}
810
849
canConstrain(param1) && canInstantiate ||
811
- isSubType (bounds(param1).hi.applyIfParameterized(args1), tp2)
850
+ isSubApproxLo (bounds(param1).hi.applyIfParameterized(args1), tp2)
812
851
case tycon1 : TypeRef if tycon1.symbol.isClass =>
813
852
false
814
853
case tycon1 : TypeProxy =>
@@ -842,8 +881,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
842
881
case arg1 : TypeBounds =>
843
882
compareCaptured(arg1, arg2)
844
883
case _ =>
845
- (v > 0 || isSubType (arg2, arg1)) &&
846
- (v < 0 || isSubType (arg1, arg2))
884
+ (v > 0 || isSubTypePart (arg2, arg1)) &&
885
+ (v < 0 || isSubTypePart (arg1, arg2))
847
886
}
848
887
}
849
888
@@ -946,7 +985,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
946
985
if (isCovered(tp1) && isCovered(tp2)) {
947
986
// println(s"useless subtype: $tp1 <:< $tp2")
948
987
false
949
- } else isSubType (tp1, tp2)
988
+ } else isSubApproxLo (tp1, tp2)
950
989
951
990
/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
952
991
* the normalized type of the refinement `tp2`?
@@ -975,15 +1014,15 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
975
1014
tp2.refinedInfo match {
976
1015
case rinfo2 : TypeBounds =>
977
1016
val ref1 = tp1.widenExpr.select(name)
978
- isSubType (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
1017
+ isSubTypePart (rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi)
979
1018
case _ =>
980
1019
false
981
1020
}
982
1021
case _ => false
983
1022
}
984
1023
985
1024
def qualifies (m : SingleDenotation ) =
986
- isSubType (m.info, rinfo2) || matchAbstractTypeMember(m.info)
1025
+ isSubTypePart (m.info, rinfo2) || matchAbstractTypeMember(m.info)
987
1026
988
1027
tp1.member(name) match { // inlined hasAltWith for performance
989
1028
case mbr : SingleDenotation => qualifies(mbr)
@@ -1023,7 +1062,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1023
1062
*/
1024
1063
private def isSubRefinements (tp1 : RefinedType , tp2 : RefinedType , limit : Type ): Boolean = {
1025
1064
def hasSubRefinement (tp1 : RefinedType , refine2 : Type ): Boolean = {
1026
- isSubType (tp1.refinedInfo, refine2) || {
1065
+ isSubTypePart (tp1.refinedInfo, refine2) || {
1027
1066
// last effort: try to adapt variances of higher-kinded types if this is sound.
1028
1067
// TODO: Move this to eta-expansion?
1029
1068
val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info)
@@ -1063,7 +1102,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1063
1102
* case of a GADT bounded typeref, we should narrow with `tp2` instead of its lower bound.
1064
1103
*/
1065
1104
private def isCappable (tp : Type ): Boolean = tp match {
1066
- case tp : TypeRef => ctx.gadt.bounds.contains(tp.symbol)
1067
1105
case tp : TypeParamRef => constraint contains tp
1068
1106
case tp : TypeProxy => isCappable(tp.underlying)
1069
1107
case tp : AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2)
@@ -1074,8 +1112,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1074
1112
* `bound` as an upper or lower bound (which depends on `isUpper`).
1075
1113
* Test that the resulting bounds are still satisfiable.
1076
1114
*/
1077
- private def narrowGADTBounds (tr : NamedType , bound : Type , isUpper : Boolean ): Boolean =
1078
- ctx.mode.is(Mode .GADTflexible ) && ! frozenConstraint && {
1115
+ private def narrowGADTBounds (tr : NamedType , bound : Type , isUpper : Boolean ): Boolean = {
1116
+ val boundIsPrecise = if (isUpper) hiIsPrecise else loIsPrecise
1117
+ ctx.mode.is(Mode .GADTflexible ) && ! frozenConstraint && boundIsPrecise && {
1079
1118
val tparam = tr.symbol
1080
1119
gadts.println(i " narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) " above" else " below" } to $bound ${bound.toString} ${bound.isRef(tparam)}" )
1081
1120
if (bound.isRef(tparam)) false
@@ -1084,10 +1123,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
1084
1123
val newBounds =
1085
1124
if (isUpper) TypeBounds (oldBounds.lo, oldBounds.hi & bound)
1086
1125
else TypeBounds (oldBounds.lo | bound, oldBounds.hi)
1087
- isSubType (newBounds.lo, newBounds.hi) &&
1126
+ isSubTypePart (newBounds.lo, newBounds.hi) &&
1088
1127
{ ctx.gadt.setBounds(tparam, newBounds); true }
1089
1128
}
1090
1129
}
1130
+ }
1091
1131
1092
1132
// Tests around `matches`
1093
1133
0 commit comments