Skip to content

Commit

Permalink
Merge pull request #231 from sjrd/subtyping-wildcard
Browse files Browse the repository at this point in the history
Handle wildcard types in type arguments in subtyping.
  • Loading branch information
bishabosha authored Dec 20, 2022
2 parents b74e039 + 1b04d40 commit 032ebf8
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 8 deletions.
24 changes: 16 additions & 8 deletions tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private[tastyquery] object Subtyping:
val tycon1Sym = tycon1.symbol
val tycon2Sym = tycon2.symbol
if tycon1Sym == tycon2Sym && isSubprefix(tycon1.prefix, tycon2.prefix) then
isSubArgs(tp1.args, tp2.args, tp1, tparams)
isSubArgs(tp1.args, tp2.args, tparams)
else false
case _ =>
false
Expand Down Expand Up @@ -210,15 +210,23 @@ private[tastyquery] object Subtyping:
false
end compareAppliedType2

private def isSubArgs(args1: List[Type], args2: List[Type], tp1: AppliedType, tparams2: List[TypeParamInfo])(
using Context
): Boolean =
private def isSubArgs(args1: List[Type], args2: List[Type], tparams2: List[TypeParamInfo])(using Context): Boolean =
def isSubArg(arg1: Type, arg2: Type, tparam2: TypeParamInfo): Boolean =
val variance = tparam2.paramVariance
variance.sign match
case 1 => isSubtype(arg1, arg2)
case -1 => isSubtype(arg2, arg1)
case 0 => isSubtype(arg1, arg2) && isSubtype(arg2, arg1)

arg2 match
case arg2: WildcardTypeBounds =>
arg2.bounds.contains(arg1)
case _ =>
arg1 match
case arg1: WildcardTypeBounds =>
// TODO? Capture conversion
false
case _ =>
variance.sign match
case 1 => isSubtype(arg1, arg2)
case -1 => isSubtype(arg2, arg1)
case 0 => isSameType(arg1, arg2)
end isSubArg

if args1.sizeCompare(args2) != 0 || args2.sizeCompare(tparams2) != 0 then
Expand Down
7 changes: 7 additions & 0 deletions tasty-query/shared/src/main/scala/tastyquery/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,13 @@ object Types {
private[tastyquery] def derivedTypeBounds(low: Type, high: Type): TypeBounds =
if ((low eq this.low) && (high eq this.high)) this
else RealTypeBounds(low, high)

final def contains(tp: Type)(using Context): Boolean = tp match
case tp: WildcardTypeBounds =>
low.isSubtype(tp.bounds.low) && tp.bounds.high.isSubtype(high)
case _ =>
low.isSubtype(tp) && tp.isSubtype(high)
end contains
}

final case class RealTypeBounds(override val low: Type, override val high: Type) extends TypeBounds(low, high):
Expand Down
23 changes: 23 additions & 0 deletions tasty-query/shared/src/test/scala/tastyquery/SubtypingSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class SubtypingSuite extends UnrestrictedUnpicklingSuite:
def ListClass(using Context): ClassSymbol =
ctx.findTopLevelClass("scala.collection.immutable.List")

def OptionClass(using Context): ClassSymbol =
ctx.findTopLevelClass("scala.Option")

def PredefModuleClass(using Context): ClassSymbol =
ctx.findTopLevelModuleClass("scala.Predef")

Expand All @@ -90,6 +93,9 @@ class SubtypingSuite extends UnrestrictedUnpicklingSuite:
def listOf(tpe: Type)(using Context): Type =
ListClass.typeRef.appliedTo(tpe)

def optionOf(tpe: Type)(using Context): Type =
OptionClass.typeRef.appliedTo(tpe)

def genSeqOf(tpe: Type)(using Context): Type =
ctx.findTopLevelClass("scala.collection.Seq").typeRef.appliedTo(tpe)

Expand Down Expand Up @@ -537,4 +543,21 @@ class SubtypingSuite extends UnrestrictedUnpicklingSuite:
).withRef[(=> JString) => JString, (=> Int) => JString]
}

testWithContext("wildcard-type-bounds") {
val seqOfWildcardProduct = mutableSeqOf(WildcardTypeBounds(RealTypeBounds(defn.NothingType, ProductClass.typeRef)))
val seqOfWildcardList = mutableSeqOf(WildcardTypeBounds(RealTypeBounds(defn.NothingType, listOf(defn.AnyType))))
val seqOfWildcardOption = mutableSeqOf(WildcardTypeBounds(RealTypeBounds(defn.NothingType, optionOf(defn.AnyType))))

assertEquiv(
seqOfWildcardProduct,
mutableSeqOf(WildcardTypeBounds(RealTypeBounds(defn.NothingType, ProductClass.typeRef)))
).withRef[mutable.Seq[? <: Product], mutable.Seq[? <: Product]]

assertNeitherSubtype(seqOfWildcardProduct, seqOfWildcardList)
.withRef[mutable.Seq[? <: Product], mutable.Seq[? <: List[Any]]]

assertStrictSubtype(seqOfWildcardOption, seqOfWildcardProduct)
.withRef[mutable.Seq[? <: Option[Any]], mutable.Seq[? <: Product]]
}

end SubtypingSuite

0 comments on commit 032ebf8

Please sign in to comment.