From 9af598d442697529292fb0307a19c84bebb284c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Sat, 31 Dec 2022 15:29:22 +0100 Subject: [PATCH] Subtyping for TypeLambda's. --- .../src/main/scala/tastyquery/Subtyping.scala | 18 +++++++++++++++ .../scala/tastyquery/SubtypingSuite.scala | 22 +++++++++++++++++++ .../main/scala/subtyping/TypesFromTASTy.scala | 2 ++ 3 files changed, 42 insertions(+) diff --git a/tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala b/tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala index d3dcd062..7689f120 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Subtyping.scala @@ -143,6 +143,16 @@ private[tastyquery] object Subtyping: case tp2: AppliedType => compareAppliedType2(tp1, tp2) + case tp2: TypeLambda => + tp1 match + case tp1: TypeLambda => + // TODO Check bounds and variances + tp1.paramRefs.lengthCompare(tp2.paramRefs) == 0 + && isSubtype(tp1.resultType, tp2.appliedTo(tp1.paramRefs)) + case _ => + // TODO? Eta-expand polymorphic type constructors? + level4(tp1, tp2) + case tp2: OrType => isSubtype(tp1, tp2.first) || isSubtype(tp1, tp2.second) || level4(tp1, tp2) @@ -269,6 +279,14 @@ private[tastyquery] object Subtyping: comparePaths || proceedWithWidenedType + case tp1: TypeLambda => + tp2 match + case tp2: TypeLambda => + false // this case is already handled in level3 + case _ => + tp2.typeParams.lengthCompare(tp1.paramRefs) == 0 + && isSubtype(tp1.resultType, tp2.appliedTo(tp1.paramRefs)) + case tp1: AndType => // TODO Try and simplify first isSubtype(tp1.first, tp2) || isSubtype(tp1.second, tp2) diff --git a/tasty-query/shared/src/test/scala/tastyquery/SubtypingSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/SubtypingSuite.scala index b6359f25..ce404691 100644 --- a/tasty-query/shared/src/test/scala/tastyquery/SubtypingSuite.scala +++ b/tasty-query/shared/src/test/scala/tastyquery/SubtypingSuite.scala @@ -105,6 +105,9 @@ class SubtypingSuite extends UnrestrictedUnpicklingSuite: def findTypesFromTASTyNamed(name: String)(using Context): Type = ctx.findTopLevelClass("subtyping.TypesFromTASTy").findDecl(termName(name)).declaredType + def findTypesFromTASTyNamed(name: TypeName)(using Context): Type = + ctx.findTopLevelClass("subtyping.TypesFromTASTy").findDecl(name).asInstanceOf[TypeMemberSymbol].aliasedType + testWithContext("same-monomorphic-class") { assertEquiv(defn.IntType, defn.IntClass.typeRef).withRef[Int, scala.Int] assertEquiv(defn.IntType, defn.IntClass.newTypeRef) @@ -560,4 +563,23 @@ class SubtypingSuite extends UnrestrictedUnpicklingSuite: .withRef[mutable.Seq[? <: Option[Any]], mutable.Seq[? <: Product]] } + testWithContext("type-lambdas") { + val Function1Class = ctx.findTopLevelClass("scala.Function1") + def fun1Type(argType: Type, resultType: Type): Type = + AppliedType(Function1Class.typeRef, argType :: resultType :: Nil) + + def makeTToTTypeLambda() = TypeLambda(typeName("T") :: Nil)( + _ => List(defn.NothingAnyBounds), + tl => fun1Type(tl.paramRefs(0), tl.paramRefs(0)) + ) + val tToTTypeLambda = makeTToTTypeLambda() + + val fromTasty = findTypesFromTASTyNamed(typeName("TToTType")) + + type TToTType = [T] => T => T + + assertEquiv(tToTTypeLambda, makeTToTTypeLambda()).withRef[TToTType, TToTType] + assertEquiv(tToTTypeLambda, fromTasty) + } + end SubtypingSuite diff --git a/test-sources/src/main/scala/subtyping/TypesFromTASTy.scala b/test-sources/src/main/scala/subtyping/TypesFromTASTy.scala index 564b80ba..7bc3cb19 100644 --- a/test-sources/src/main/scala/subtyping/TypesFromTASTy.scala +++ b/test-sources/src/main/scala/subtyping/TypesFromTASTy.scala @@ -7,4 +7,6 @@ class TypesFromTASTy: val orType: Int | String = 1 val andType: Product & Serializable = Nil + + type TToTType[T] = T => T end TypesFromTASTy