From d95d8873c5733b18c002b5eb6680d38b2ba7a5a3 Mon Sep 17 00:00:00 2001 From: Kai <450507+neko-kai@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:03:28 +0100 Subject: [PATCH] Fix detection of generic type projections as 'weak' types on Scala 2 when obscured by type alias, also fix /~https://github.com/zio/izumi-reflect/issues/189 --- ...cala => AllPartsStrongTestScala2Jvm.scala} | 15 +++- .../izumi/reflect/ReflectionUtil.scala | 82 ++++++++++--------- .../main/scala-2/izumi/reflect/TagMacro.scala | 4 +- .../reflect/macrortti/LightTypeTagImpl.scala | 12 +-- .../dottyreflection/FullDbInspector.scala | 3 +- .../InheritanceDbInspector.scala | 2 +- .../izumi/reflect/test/DiscoveryModel.scala | 17 ++++ .../reflect/test/TagProgressionTest.scala | 19 ----- .../scala-2/izumi/reflect/test/TagTest.scala | 32 ++++++++ .../izumi/reflect/test/DiscoveryModel.scala | 20 +++++ .../test/CurrentDottySupportExtentTest.scala | 3 - .../reflect/test/SharedLightTypeTagTest.scala | 13 +-- .../test/SharedTagProgressionTest.scala | 29 ++----- .../izumi/reflect/test/SharedTagTest.scala | 35 ++++++-- 14 files changed, 175 insertions(+), 111 deletions(-) rename izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/{LightTypeTagTestJvm.scala => AllPartsStrongTestScala2Jvm.scala} (74%) create mode 100644 izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/DiscoveryModel.scala create mode 100644 izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DiscoveryModel.scala diff --git a/izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/LightTypeTagTestJvm.scala b/izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/AllPartsStrongTestScala2Jvm.scala similarity index 74% rename from izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/LightTypeTagTestJvm.scala rename to izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/AllPartsStrongTestScala2Jvm.scala index 503d8b13..1e8849cc 100644 --- a/izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/LightTypeTagTestJvm.scala +++ b/izumi-reflect/izumi-reflect/.jvm/src/test/scala-2/izumi/reflect/test/AllPartsStrongTestScala2Jvm.scala @@ -19,9 +19,10 @@ package izumi.reflect.test import izumi.reflect.ReflectionUtil +import izumi.reflect.test.DiscoveryModel.{DiscoverableService, DiscoverableServiceImpl, DiscoveryNodeProvider, GetDiscoveryNode} import org.scalatest.wordspec.AnyWordSpec -class LightTypeTagTestJvm extends AnyWordSpec { +class AllPartsStrongTestScala2Jvm extends AnyWordSpec { type FP1[+T] = List[T] type Ap1[+F[+_], +T] = F[T] @@ -53,7 +54,6 @@ class LightTypeTagTestJvm extends AnyWordSpec { .asInstanceOf[scala.reflect.runtime.universe.RefinedTypeApi].decl(scala.reflect.runtime.universe.TypeName("l")) .asType.typeSignature .typeConstructor - println(tpe) val res1 = ReflectionUtil.allPartsStrong(tpe) assert(res1) } @@ -63,4 +63,15 @@ class LightTypeTagTestJvm extends AnyWordSpec { assert(res1) } + "allPartsStrong for TC#DiscoveryNode type projection" in { + def test1[TC <: DiscoverableService]: Boolean = { + ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.weakTypeOf[DiscoveryNodeProvider[GetDiscoveryNode[TC]]]) + } + def test2[TC <: DiscoverableService]: Boolean = { + ReflectionUtil.allPartsStrong(scala.reflect.runtime.universe.weakTypeOf[DiscoveryNodeProvider[TC#DiscoveryNode]]) + } + assert(!test1[DiscoverableServiceImpl]) + assert(!test2[DiscoverableServiceImpl]) + } + } diff --git a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ReflectionUtil.scala b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ReflectionUtil.scala index 4d2140b3..bd5f0faa 100644 --- a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ReflectionUtil.scala +++ b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/ReflectionUtil.scala @@ -45,43 +45,42 @@ private[reflect] object ReflectionUtil { } def allPartsStrong(tpe: Universe#Type): Boolean = { - val selfStrong = isSelfStrong(tpe) - def prefixStrong = { - tpe match { - case t: Universe#TypeRefApi => - allPartsStrong(t.pre.dealias) - case _ => - true - } - } - val dealiased = tpe.dealias - def typeCtorStrong = { - val resType = dealiased.finalResultType - val ctor = resType.typeConstructor - (resType == dealiased) || (ctor == resType) || (ctor == tpe) || - tpe.typeParams.contains(ctor.typeSymbol) || allPartsStrong(ctor) - } - def argsStrong = { - dealiased.finalResultType.typeArgs.forall { - arg => - tpe.typeParams.contains(arg.typeSymbol) || - allPartsStrong(arg) - } - } - def intersectionStructStrong = { - tpe match { - case t: Universe#RefinedTypeApi => - t.parents.forall(allPartsStrong) && - t.decls.toSeq.forall((s: Universe#Symbol) => s.isTerm || allPartsStrong(s.asType.typeSignature.dealias)) - case _ => - true - } - } + allPartsStrong(Set.empty, tpe) + } + + def allPartsStrong(outerTypeParams: Set[Universe#Symbol], tpeRaw: Universe#Type): Boolean = { + val dealiased = tpeRaw.dealias + val selfStrong = isSelfStrong(outerTypeParams, dealiased) || outerTypeParams.contains(dealiased.typeSymbol) + + dealiased match { + case t: Universe#RefinedTypeApi => + t.parents.forall(allPartsStrong(outerTypeParams, _)) && + t.decls.toSeq.forall((s: Universe#Symbol) => s.isTerm || allPartsStrong(outerTypeParams, s.asType.typeSignature.dealias)) + case _ => + val resType = dealiased.finalResultType + if (dealiased.takesTypeArgs && !dealiased.typeParams.forall(outerTypeParams.contains)) { + allPartsStrong(outerTypeParams ++ dealiased.typeParams, resType) + } else { - selfStrong && prefixStrong && typeCtorStrong && argsStrong && intersectionStructStrong + def typeCtorStrong: Boolean = { + val ctor = resType.typeConstructor + (resType == dealiased) || (ctor == dealiased) || (ctor == tpeRaw) || + outerTypeParams.contains(ctor.typeSymbol) || allPartsStrong(outerTypeParams, ctor) + } + + def argsStrong: Boolean = { + resType.typeArgs.forall { + arg => + outerTypeParams.contains(arg.typeSymbol) || allPartsStrong(outerTypeParams, arg) + } + } + + selfStrong && /*prefixStrong &&*/ typeCtorStrong && argsStrong + } + } } - def isSelfStrong(tpe: Universe#Type): Boolean = { + def isSelfStrong(outerTypeParams: Set[Universe#Symbol], tpe: Universe#Type): Boolean = { // FIXME: strengthening check to capture `IntersectionBlockingIO` test case causes StackOverflow during implicit search // def intersectionMembersStrong = { // tpe match { @@ -91,15 +90,24 @@ private[reflect] object ReflectionUtil { // } // } - !(tpe.typeSymbol.isParameter || ( + def prefixStrong: Boolean = { + tpe match { + case t: Universe#TypeRefApi => + allPartsStrong(outerTypeParams, t.pre.dealias) + case _ => + true + } + } + + (prefixStrong && !(tpe.typeSymbol.isParameter || ( // we regard abstract types like T in trait X { type T; Tag[this.T] } - when we are _inside_ the definition template // as 'type parameters' too. So that you could define `implicit def tagForT: Tag[this.T]` and the tag would be resolved // to this implicit correctly, instead of generating a useless `X::this.type::T` tag. tpe.isInstanceOf[Universe#TypeRefApi] && tpe.asInstanceOf[Universe#TypeRefApi].pre.isInstanceOf[Universe#ThisTypeApi] && tpe.typeSymbol.isAbstract && !tpe.typeSymbol.isClass && isNotDealiasedFurther(tpe) - )) /*&& intersectionMembersStrong*/ || - tpe.typeParams.exists { + ))) /*&& intersectionMembersStrong*/ || + tpe.typeParams.exists { // is identity t => t == tpe.typeSymbol || t.typeSignature == tpe.typeSymbol.typeSignature || diff --git a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TagMacro.scala b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TagMacro.scala index c1223448..b738d5c4 100644 --- a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TagMacro.scala +++ b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/TagMacro.scala @@ -282,7 +282,7 @@ class TagMacro(val c: blackbox.Context) { // skip resolution for types in methods/vals (that would need a new runtime constructor, `methodTag`, like `refinedTag` for the case & dealing with method type parameters may be non-trivial) // see: "progression test: can't handle parameters in defs/vals in structural types" symbol.isTerm || - ReflectionUtil.isSelfStrong(symbol.info) + ReflectionUtil.isSelfStrong(Set.empty, symbol.info) } val strongDeclsTpe = internal.refinedType(intersection, originalRefinement.typeSymbol.owner, internal.newScopeWith(strongDecls.toSeq: _*)) @@ -354,7 +354,7 @@ class TagMacro(val c: blackbox.Context) { @inline private[this] final def getCtorKindIfCtorIsTypeParameter(tpe: Type): Option[Kind] = { - if (!ReflectionUtil.isSelfStrong(tpe)) Some(kindOf(tpe)) + if (!ReflectionUtil.isSelfStrong(Set.empty, tpe)) Some(kindOf(tpe)) else None } diff --git a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagImpl.scala b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagImpl.scala index 40df223c..527b2d52 100644 --- a/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagImpl.scala +++ b/izumi-reflect/izumi-reflect/src/main/scala-2/izumi/reflect/macrortti/LightTypeTagImpl.scala @@ -91,7 +91,6 @@ final class LightTypeTagImpl[U <: Universe with Singleton](val u: U, withCache: logger.log(s"Initial mainTpe=$tpe:${tpe.getClass} beforeDealias=$tpe0:${tpe0.getClass}") val lttRef = makeRef(tpe) -// println(s"$tpe0 => $tpe => $lttRef") val allReferenceComponents = allTypeReferences(tpe) @@ -243,14 +242,17 @@ final class LightTypeTagImpl[U <: Universe with Singleton](val u: U, withCache: IzAssert( assertion = { - if (componentsOfPolyTypeResultType.maybeUnbrokenType.nonEmpty) { - componentsOfPolyTypeResultType.intersectionComponents.exists(_.etaExpand.typeParams.nonEmpty) - } else true + if (componentsOfPolyTypeResultType.maybeUnbrokenType.isEmpty) { + !componentsOfPolyTypeResultType.intersectionComponents.exists(_.takesTypeArgs) + } else { + true + } }, clue = { s"""Unexpected intersection contains a PolyType: |tpeRaw0 = $tpeRaw0 |components = ${componentsOfPolyTypeResultType.intersectionComponents.niceList(prefix = "*")} + |takesTypeArgs = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.takesTypeArgs).niceList(prefix = "*")} |etaExpand = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.etaExpand).niceList(prefix = "+")} |tparams = ${componentsOfPolyTypeResultType.intersectionComponents.map(_.etaExpand.typeParams).niceList(prefix = "-")} |""".stripMargin @@ -260,7 +262,7 @@ final class LightTypeTagImpl[U <: Universe with Singleton](val u: U, withCache: componentsOfPolyTypeResultType.intersectionComponents.toSeq.flatMap { component => val componentAsPolyType = component.etaExpand - val tparams = component.etaExpand.typeParams + val tparams = componentAsPolyType.typeParams if (tparams.isEmpty) { Seq.empty diff --git a/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/FullDbInspector.scala b/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/FullDbInspector.scala index 85b2535c..28280dab 100644 --- a/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/FullDbInspector.scala +++ b/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/FullDbInspector.scala @@ -86,7 +86,8 @@ abstract class FullDbInspector(protected val shift: Int) extends InspectorBase { processSymbol(typeRef, selfRef) case paramRef: ParamRef => - processSymbol(paramRef, selfRef) + // do not process type parameters for bases db + Nil case termRef: TermRef => extractBase(termRef, selfRef, false) diff --git a/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InheritanceDbInspector.scala b/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InheritanceDbInspector.scala index e99b9bc3..a2de68b2 100644 --- a/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InheritanceDbInspector.scala +++ b/izumi-reflect/izumi-reflect/src/main/scala-3/izumi/reflect/dottyreflection/InheritanceDbInspector.scala @@ -23,7 +23,7 @@ abstract class InheritanceDbInspector(protected val shift: Int) extends Inspecto val tpe0 = TypeRepr.of[T].dealias val allReferenceComponents = allTypeReferences(tpe0).filter { - case _: ParamRef => false + case _: ParamRef => false // do not process type parameters for inheritance db case _ => true } diff --git a/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/DiscoveryModel.scala b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/DiscoveryModel.scala new file mode 100644 index 00000000..51e69211 --- /dev/null +++ b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/DiscoveryModel.scala @@ -0,0 +1,17 @@ +package izumi.reflect.test + +object DiscoveryModel { + + trait NodeId + trait DiscoverableService { + type DiscoveryNode <: NodeId + } + trait DiscoveryNodeProvider[Id <: NodeId] + trait DiscoverableServiceImpl extends DiscoverableService { + override type DiscoveryNode = NodeIdImpl + } + class NodeIdImpl extends NodeId + + type GetDiscoveryNode[T <: DiscoverableService] = T#DiscoveryNode + +} diff --git a/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagProgressionTest.scala b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagProgressionTest.scala index f6f0fb4b..67df6f5b 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagProgressionTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagProgressionTest.scala @@ -6,25 +6,6 @@ class TagProgressionTest extends SharedTagProgressionTest { "[progression] Tag (Scala 2)" should { - "progression test: we may accidentally materialize tags for type parameters that are prefixes of type projections (Scala 2 specific, generic type projection)" in { - class Path { - type Child - } - val path = new Path - - // A has no tag and the definition of `getTag` should not compile at all. It's a bug that it compiles - def getTag[A <: Path]: Tag[A#Child] = Tag[A#Child] - - val directChildTag = Tag[Path#Child].tag // Path::Child - val indirectChildTag = getTag[path.type].tag // A|::Child - - assertDifferent(indirectChildTag, directChildTag) - assertNotChild(directChildTag, indirectChildTag) - assertNotChild(indirectChildTag, directChildTag) - - assert(indirectChildTag.toString == "A|::Child") - } - "progression test: combine intersection path-dependent intersection types with inner tags doesn't work yet (Scala 2 specific, generic type projection)" in { trait PDT { type T diff --git a/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagTest.scala b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagTest.scala index 1ba6dd83..b925b763 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala-2/izumi/reflect/test/TagTest.scala @@ -20,6 +20,7 @@ package izumi.reflect.test import izumi.reflect._ import izumi.reflect.macrortti._ +import org.scalatest.exceptions.TestFailedException class TagTest extends SharedTagTest { @@ -67,6 +68,37 @@ class TagTest extends SharedTagTest { assertCompiles("HKTag.hktagFromTagMacro[({ type l[F[_]] = { type Arg[C] = F[C] } })#l[Option]]") } + "we no longer accidentally materialize tags for type parameters that are prefixes of type projections (Scala 2 specific, generic type projection)" in { + class Path { + type Child + } + val path = new Path + + // A has no tag and the definition of `getTag` should not compile at all. It's a bug that it compiles + val t = intercept[TestFailedException]( + assertCompiles( + """ + def getTag[A <: Path]: Tag[A#Child] = Tag[A#Child] + """ + ) + ) + assert( + t.getMessage.contains("could not find implicit value") || + t.getMessage.contains("no implicit argument of type") /*Dotty*/ + ) + + def getTag[A <: Path](implicit t: Tag[A#Child]): Tag[A#Child] = Tag[A#Child] + + val directChildTag = Tag[Path#Child].tag // Path::Child + val indirectChildTag = getTag[path.type].tag // A|::Child + + assertDifferent(indirectChildTag, directChildTag) + assertNotChild(directChildTag, indirectChildTag) + assertNotChild(indirectChildTag, directChildTag) + + assertRepr(indirectChildTag, "path::Child") + } + } } diff --git a/izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DiscoveryModel.scala b/izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DiscoveryModel.scala new file mode 100644 index 00000000..eb918125 --- /dev/null +++ b/izumi-reflect/izumi-reflect/src/test/scala-3/izumi/reflect/test/DiscoveryModel.scala @@ -0,0 +1,20 @@ +package izumi.reflect.test + +object DiscoveryModel { + + trait NodeId + trait DiscoverableService { + type DiscoveryNode <: NodeId + } + trait DiscoveryNodeProvider[Id <: NodeId] + trait DiscoverableServiceImpl extends DiscoverableService { + override type DiscoveryNode = NodeIdImpl + } + class NodeIdImpl extends NodeId + + type GetDiscoveryNode[T <: DiscoverableService] <: NodeId = T match { + case GetDiscoveryNodePattern[t] => t + } + type GetDiscoveryNodePattern[Id <: NodeId] = DiscoverableService { type DiscoveryNode = Id } + +} diff --git a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/CurrentDottySupportExtentTest.scala b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/CurrentDottySupportExtentTest.scala index ca280fb7..2a5b92c9 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/CurrentDottySupportExtentTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/CurrentDottySupportExtentTest.scala @@ -65,9 +65,6 @@ trait CurrentDottySupportExtentTest extends TagAssertions { assertChild(tupleTag1, tupleTag0) assertChild(tupleTag3, tupleTag2) -// println(`LTT[_]`[List].debug()) -// println(`LTT[_]`[List[B]].debug()) - assertChild(LTT[List[B]], LTT[Seq[A]]) assertChild(`LTT[_]`[List].combine(LTT[B]), `LTT[_]`[Seq].combine(LTT[A])) } diff --git a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagTest.scala b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagTest.scala index 3047f155..a7b1530f 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedLightTypeTagTest.scala @@ -1,8 +1,7 @@ package izumi.reflect.test -import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, AppliedNamedReference, AppliedReference, Boundaries, FullReference, NameReference, SymName, TypeParam} +import izumi.reflect.macrortti.LightTypeTagRef.{AbstractReference, AppliedNamedReference, Boundaries, FullReference, NameReference, TypeParam} import izumi.reflect.macrortti._ -import izumi.reflect.test.TestModel.W3 import scala.collection.immutable.ListSet import scala.collection.{BitSet, immutable, mutable} @@ -511,11 +510,6 @@ abstract class SharedLightTypeTagTest extends TagAssertions { val alias = LTT[T3[Int, Boolean]] val direct = LTT[W1 with W4[Boolean] with W5[Int]] - println(debugCtor) - println(debugCombined) - println(alias.debug("alias")) - println(direct.debug("direct")) - assert(!debugCtor.contains("")) assert(!debugCtor.contains("")) assert(!debugCtor.contains("- T")) @@ -584,11 +578,6 @@ abstract class SharedLightTypeTagTest extends TagAssertions { val w3 = `LTT[_]`[W3] val w4 = `LTT[_]`[W4] - println(t1.debug("T1[_]")) - println(t2.debug("T2[_]")) - println(w3.debug("W3[_]")) - println(w4.debug("W4[_]")) - assertChild(t1, w3) assertChild(t1, LTT[W1]) assertChild(w4, w3) diff --git a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagProgressionTest.scala b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagProgressionTest.scala index 19b50a59..32000ef0 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagProgressionTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagProgressionTest.scala @@ -4,7 +4,6 @@ import izumi.reflect.macrortti._ import izumi.reflect.test.TestModel._ import izumi.reflect._ import izumi.reflect.test.PlatformSpecific.fromRuntime -import izumi.reflect.test.TestModel.x.SrcContextProcessor import org.scalatest.exceptions.TestFailedException import org.scalatest.wordspec.AnyWordSpec @@ -26,22 +25,6 @@ abstract class SharedTagProgressionTest extends AnyWordSpec with TagAssertions w } } - "progression test: Scala 2, reported in /~https://github.com/zio/izumi-reflect/issues/189, parameterized type alias with intersection produces incorrect output" in { - def elementTag[F[_]: TagK]: Tag[SrcContextProcessor[F]] = Tag[TestModel.x.SrcContextProcessor[F]] - assert(elementTag[CIO].tag == Tag[TestModel.x.SrcContextProcessor[CIO]].tag) - - doesntWorkYetOnScala2 { - type K[F[_]] = Set[TestModel.x.SrcContextProcessor[F]] - assert(TagT[K].tag.combine(TagK[CIO].tag) == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) - - def aliasedTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[K[F]] - assert(aliasedTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) - - def directTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[Set[TestModel.x.SrcContextProcessor[F]]] - assert(directTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) - } - } - "progression test: cannot resolve a higher-kinded type in a higher-kinded tag in a named deeply-nested type lambda on Scala 2" in { val t = Try(intercept[TestFailedException] { assertCompiles( @@ -59,8 +42,8 @@ abstract class SharedTagProgressionTest extends AnyWordSpec with TagAssertions w succeedsOnScala2ButShouldnt { assert(t.isSuccess) assert( - t.get.message.get.contains("could not find implicit value") || - t.get.message.get.contains("diverging implicit") /*2.11*/ + t.get.getMessage.contains("could not find implicit value") || + t.get.getMessage.contains("diverging implicit") /*2.11*/ ) } } @@ -77,10 +60,10 @@ abstract class SharedTagProgressionTest extends AnyWordSpec with TagAssertions w ) } assert( - t.message.get.contains("could not find implicit value") || - t.message.get.contains("diverging implicit") || /*2.11*/ - t.message.get.contains("no implicit argument of type") || /*Dotty*/ - t.message.get.contains("Cannot find implicit Tag") /*Dotty 3.1.3+*/ + t.getMessage.contains("could not find implicit value") || + t.getMessage.contains("diverging implicit") || /*2.11*/ + t.getMessage.contains("no implicit argument of type") || /*Dotty*/ + t.getMessage.contains("Cannot find implicit Tag") /*Dotty 3.1.3+*/ ) } diff --git a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagTest.scala b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagTest.scala index 7abdd09e..fb5e68e7 100644 --- a/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagTest.scala +++ b/izumi-reflect/izumi-reflect/src/test/scala/izumi/reflect/test/SharedTagTest.scala @@ -6,13 +6,13 @@ import izumi.reflect.test.ID._ import izumi.reflect.test.TestModel._ import izumi.reflect.thirdparty.internal.boopickle.PickleImpl import izumi.reflect._ -import izumi.reflect.macrortti.LightTypeTagRef.{FullReference, NameReference, SymName, TypeParam} +import izumi.reflect.test.DiscoveryModel.{DiscoverableService, DiscoverableServiceImpl, DiscoveryNodeProvider, GetDiscoveryNode, NodeIdImpl} +import izumi.reflect.test.TestModel.x.SrcContextProcessor import org.scalatest.Assertions import org.scalatest.exceptions.TestFailedException import org.scalatest.wordspec.AnyWordSpec import scala.annotation.StaticAnnotation -import scala.collection.immutable.List import scala.collection.mutable import scala.util.Try @@ -277,10 +277,6 @@ abstract class SharedTagTest extends AnyWordSpec with XY[String] with TagAsserti val left = tag.tag val right = Tag[H1].tag - println(TagT[ApplePaymentProvider].tag.debug()) - println(left.debug()) - println(right.debug()) - assertChild(left, right) } @@ -977,6 +973,33 @@ abstract class SharedTagTest extends AnyWordSpec with XY[String] with TagAsserti assertDebugSame(t1, t3) } + "support injecting runtime tags in place of type projections from type parameters / match types on type parameters" in { + def tag[TC <: DiscoverableService](implicit tag: Tag[GetDiscoveryNode[TC]]): Tag[DiscoveryNodeProvider[GetDiscoveryNode[TC]]] = { + Tag[DiscoveryNodeProvider[GetDiscoveryNode[TC]]] + } + + val t1 = tag[DiscoverableServiceImpl].tag + val t2 = Tag[DiscoveryNodeProvider[NodeIdImpl]].tag + + assertSameStrict(t1, t2) + + assertDebugSame(t1, t2) + } + + "regression test for: Scala 2, /~https://github.com/zio/izumi-reflect/issues/189, parameterized type alias with intersection produces incorrect output" in { + def elementTag[F[_]: TagK]: Tag[SrcContextProcessor[F]] = Tag[TestModel.x.SrcContextProcessor[F]] + assert(elementTag[CIO].tag == Tag[TestModel.x.SrcContextProcessor[CIO]].tag) + + type K[F[_]] = Set[TestModel.x.SrcContextProcessor[F]] + assert(TagT[K].tag.combine(TagK[CIO].tag) == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) + + def aliasedTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[K[F]] + assert(aliasedTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) + + def directTag[F[_]: TagK]: Tag[Set[SrcContextProcessor[F]]] = Tag[Set[TestModel.x.SrcContextProcessor[F]]] + assert(directTag[CIO].tag == Tag[Set[TestModel.x.SrcContextProcessor[CIO]]].tag) + } + } }