Skip to content

Commit e7c208e

Browse files
fix generic case class issue for Scala 3 by disabling cache for abstract type params
1 parent 22dfa6e commit e7c208e

File tree

5 files changed

+37
-38
lines changed

5 files changed

+37
-38
lines changed

modules/scala-api/src/main/scala-3/org/apache/flinkx/api/LowPrioImplicits.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ private[api] trait LowPrioImplicits extends TaggedDerivation[TypeInformation]:
2626
classTag: ClassTag[T],
2727
typeTag: TypeTag[T]
2828
): Typeclass[T] =
29-
val cacheKey = typeName(ctx.typeInfo)
30-
println(cacheKey)
31-
cache.get(cacheKey) match
29+
val useCache = typeTag.isCachable
30+
val cacheKey = typeTag.toString
31+
Option(useCache).filter(_ == true).flatMap(_ => cache.get(cacheKey)) match
3232
case Some(cached) =>
3333
cached.asInstanceOf[TypeInformation[T]]
3434

@@ -48,16 +48,16 @@ private[api] trait LowPrioImplicits extends TaggedDerivation[TypeInformation]:
4848
fieldNames = ctx.params.map(_.label),
4949
ser = serializer
5050
).asInstanceOf[TypeInformation[T]]
51-
cache.put(cacheKey, ti)
51+
if useCache then cache.put(cacheKey, ti)
5252
ti
5353

5454
override def split[T](ctx: SealedTrait[Typeclass, T])(using
5555
classTag: ClassTag[T],
5656
typeTag: TypeTag[T]
5757
): Typeclass[T] =
58-
val cacheKey = typeName(ctx.typeInfo)
59-
println(cacheKey)
60-
cache.get(cacheKey) match
58+
val useCache = typeTag.isCachable
59+
val cacheKey = typeTag.toString
60+
Option(useCache).filter(_ == true).flatMap(_ => cache.get(cacheKey)) match
6161
case Some(cached) =>
6262
cached.asInstanceOf[TypeInformation[T]]
6363

@@ -68,14 +68,11 @@ private[api] trait LowPrioImplicits extends TaggedDerivation[TypeInformation]:
6868
)
6969
val clazz = classTag.runtimeClass.asInstanceOf[Class[T]]
7070
val ti = new CoproductTypeInformation[T](clazz, serializer)
71-
cache.put(cacheKey, ti)
71+
if useCache then cache.put(cacheKey, ti)
7272
ti
7373

7474
final inline implicit def deriveTypeInformation[T](implicit
7575
m: Mirror.Of[T],
7676
classTag: ClassTag[T],
7777
typeTag: TypeTag[T]
7878
): TypeInformation[T] = derived
79-
80-
private def typeName(ti: TypeInfo): String =
81-
s"${ti.full}[${ti.typeParams.map(typeName).mkString(",")}]"

modules/scala-api/src/main/scala-3/org/apache/flinkx/api/TypeTag.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import scala.quoted.*
66
trait TypeTag[A]:
77
// Is the type a module, i.e. is it a case object?
88
def isModule: Boolean
9+
def isCachable: Boolean
10+
def toString: String
911

1012
object TypeTag:
1113
def apply[A: TypeTag]: TypeTag[A] = summon

modules/scala-api/src/main/scala-3/org/apache/flinkx/api/TypeTagMacro.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ object TypeTagMacro:
77
def gen[A: Type](using q: Quotes): Expr[TypeTag[A]] =
88
import q.reflect.*
99

10-
val A = TypeRepr.of[A]
11-
val symA = A.typeSymbol
12-
val flagsA = symA.flags
13-
val isModuleExpr = Expr(flagsA.is(Flags.Module))
10+
val A = TypeRepr.of[A]
11+
val symA = A.typeSymbol
12+
val flagsA = symA.flags
13+
val isModuleExpr = Expr(flagsA.is(Flags.Module))
14+
val isCachableExpr = Expr(A match {
15+
case a: AppliedType => !a.args.exists { t => t.typeSymbol.isAbstractType }
16+
case _ => true
17+
})
18+
val toStringExpr = Expr(A.show)
1419

1520
'{
1621
new TypeTag[A]:
1722
override lazy val isModule: Boolean = ${ isModuleExpr }
23+
override lazy val isCachable: Boolean = ${ isCachableExpr }
24+
override lazy val toString: String = ${ toStringExpr }
1825
}

modules/scala-api/src/test/scala-2/org/apache/flinkx/api/GenericCaseClassScala2Test.scala

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,35 @@ class GenericCaseClassScala2Test extends AnyFlatSpec with should.Matchers {
1515

1616
"Both TypeInformation of Animal Basket" should "have their respective TypeInformation of Animal" in {
1717
typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[Cat](classOf[Cat])
18-
// This second call is failing without the fix: TypeInformation of DogBasket hold a wrong TypeInformation of Cat
1918
typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[Dog](classOf[Dog])
2019
}
2120

2221
def typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[A <: Animal: TypeTag: TypeInformation](
2322
aClass: Class[A]
2423
): Unit = {
25-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat[] => OK
24+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat => OK
2625
val catInfo: TypeInformation[Cat] = implicitly[TypeInformation[Cat]]
27-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Dog[] => OK
26+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Dog => OK
2827
val dogInfo: TypeInformation[Dog] = implicitly[TypeInformation[Dog]]
29-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat[] or Dog[] => OK
28+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat or Dog => OK
3029
val aInfo: TypeInformation[A] = implicitly[TypeInformation[A]]
31-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat[]] => OK
30+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat] => OK
3231
val catBasketInfo: TypeInformation[Basket[Cat]] = implicitly[TypeInformation[Basket[Cat]]]
33-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Dog[]] => OK
32+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Dog] => OK
3433
val dogBasketInfo: TypeInformation[Basket[Dog]] = implicitly[TypeInformation[Basket[Dog]]]
35-
// without the fix: cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal.A[]] => issue
36-
// with the fix: cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat] or Dog => OK
34+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat] or Dog => OK
3735
val aBasketInfo: TypeInformation[Basket[A]] = implicitly[TypeInformation[Basket[A]]]
3836

3937
if (classOf[Cat].isAssignableFrom(aClass)) {
4038
aInfo should be theSameInstanceAs catInfo
41-
aBasketInfo should be theSameInstanceAs catBasketInfo // Fails without the fix => cache miss
39+
aBasketInfo should be theSameInstanceAs catBasketInfo
4240
}
4341
if (classOf[Dog].isAssignableFrom(aClass)) {
4442
aInfo should be theSameInstanceAs dogInfo
45-
aBasketInfo should be theSameInstanceAs dogBasketInfo // Fails without the fix => cache miss
43+
aBasketInfo should be theSameInstanceAs dogBasketInfo
4644
}
4745
catBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs catInfo
4846
dogBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs dogInfo
49-
50-
// This check is failing on second call without the fix: TypeInformation of DogBasket hold a wrong TypeInformation of Cat
5147
aBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs aInfo
5248
}
5349

modules/scala-api/src/test/scala-3/org/apache/flinkx/api/GenericCaseClassScala3Test.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,35 @@ class GenericCaseClassScala3Test extends AnyFlatSpec with should.Matchers {
1212

1313
"Both TypeInformation of Animal Basket" should "have their respective TypeInformation of Animal" in {
1414
typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[Cat](classOf[Cat])
15-
// This second call is failing: TypeInformation of DogBasket hold a wrong TypeInformation of Cat
1615
typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[Dog](classOf[Dog])
1716
}
1817

1918
def typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal[A <: Animal: TypeTag: TypeInformation](
2019
aClass: Class[A]
2120
): Unit = {
22-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat[] => OK
21+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat => OK
2322
val catInfo: TypeInformation[Cat] = implicitly[TypeInformation[Cat]]
24-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Dog[] => OK
23+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Dog => OK
2524
val dogInfo: TypeInformation[Dog] = implicitly[TypeInformation[Dog]]
26-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat[] or Dog[] => OK
25+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Cat or Dog => OK
2726
val aInfo: TypeInformation[A] = implicitly[TypeInformation[A]]
28-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat[]] => OK
27+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Cat] => OK
2928
val catBasketInfo: TypeInformation[Basket[Cat]] = implicitly[TypeInformation[Basket[Cat]]]
30-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Dog[]] => OK
29+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.Dog] => OK
3130
val dogBasketInfo: TypeInformation[Basket[Dog]] = implicitly[TypeInformation[Basket[Dog]]]
32-
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[org.apache.flinkx.api.GenericCaseClassTest.typeInformationOfAnimalBasketShouldHaveATypeInformationOfAnimal.A[]] => issue
31+
// cacheKey=org.apache.flinkx.api.GenericCaseClassTest.Basket[A] => not cached
3332
val aBasketInfo: TypeInformation[Basket[A]] = implicitly[TypeInformation[Basket[A]]]
3433

3534
if (classOf[Cat].isAssignableFrom(aClass)) {
3635
aInfo should be theSameInstanceAs catInfo
37-
// aBasketInfo should be theSameInstanceAs catBasketInfo // Fails => cache miss
36+
// aBasketInfo should be theSameInstanceAs catBasketInfo // Fails => aBasketInfo is not cached
3837
}
3938
if (classOf[Dog].isAssignableFrom(aClass)) {
4039
aInfo should be theSameInstanceAs dogInfo
41-
// aBasketInfo should be theSameInstanceAs dogBasketInfo // Fails => cache miss
40+
// aBasketInfo should be theSameInstanceAs dogBasketInfo // Fails => aBasketInfo is not cached
4241
}
4342
catBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs catInfo
4443
dogBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs dogInfo
45-
46-
// This check is failing on second call: TypeInformation of DogBasket hold a wrong TypeInformation of Cat
4744
aBasketInfo.asInstanceOf[ProductTypeInformation[A]].getFieldTypes()(0) should be theSameInstanceAs aInfo
4845
}
4946

0 commit comments

Comments
 (0)