@@ -842,13 +842,27 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
842842 val refs1 = tp1.captureSet
843843 try
844844 if refs1.isAlwaysEmpty then recur(tp1, parent2)
845- else subCaptures(refs1, refs2, frozenConstraint).isOK
846- && sameBoxed(tp1, tp2, refs1)
847- && (recur(tp1.widen.stripCapturing, parent2)
848- || tp1.isInstanceOf [SingletonType ] && recur(tp1, parent2)
849- // this alternative is needed in case the right hand side is a
850- // capturing type that contains the lhs as an alternative of a union type.
851- )
845+ else
846+ // The singletonOK branch is because we sometimes have a larger capture set in a singleton
847+ // than in its underlying type. An example is `f: () -> () ->{x} T`, which might be
848+ // the type of a closure. In that case the capture set of `f.type` is `{x}` but the
849+ // capture set of the underlying type is `{}`. So without the `singletonOK` test, a singleton
850+ // might not be a subtype of its underlying type. Examples where this arises is
851+ // capt-capibility.scala and function-combinators.scala
852+ val singletonOK = tp1 match
853+ case tp1 : SingletonType
854+ if subCaptures(tp1.underlying.captureSet, refs2, frozen = true ).isOK =>
855+ recur(tp1.widen, tp2)
856+ case _ =>
857+ false
858+ singletonOK
859+ || subCaptures(refs1, refs2, frozenConstraint).isOK
860+ && sameBoxed(tp1, tp2, refs1)
861+ && (recur(tp1.widen.stripCapturing, parent2)
862+ || tp1.isInstanceOf [SingletonType ] && recur(tp1, parent2)
863+ // this alternative is needed in case the right hand side is a
864+ // capturing type that contains the lhs as an alternative of a union type.
865+ )
852866 catch case ex : AssertionError =>
853867 println(i " assertion failed while compare captured $tp1 <:< $tp2" )
854868 throw ex
0 commit comments