Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,68 @@ abstract class LightTypeTag private[reflect] (
* F[_, _, _].combine(A, B) = F[A, B, _]
* }}}
*/
def combine(args: LightTypeTag*): LightTypeTag = {
val argRefs = args.map(_.ref)
val appliedBases = basesdb ++ basesdb.iterator.collect { // do not remove the unapplied base lambdas after combination (required for inferredLambdaParents in isChild)
case (self: LightTypeTagRef.Lambda, parents) =>
self.combine(argRefs) -> parents.map {
case l: LightTypeTagRef.Lambda =>
l.combine(argRefs)
case nonLambdaParent =>
val context = self.input.zip(argRefs.collect { case a: AbstractReference => a }).toMap
new RuntimeAPI.Rewriter(context).replaceRefs(nonLambdaParent)
private[this] def rewriteBases(
args: Seq[Option[AbstractReference]],
combineRootLambda: LightTypeTagRef.Lambda => AbstractReference
): Map[AbstractReference, Set[AbstractReference]] = {
ref match {
case currentLambda: LightTypeTagRef.Lambda =>
def reachableLambdaBases(todo: List[LightTypeTagRef.Lambda], seen: Set[LightTypeTagRef.Lambda]): Set[LightTypeTagRef.Lambda] = {
todo match {
case head :: tail if seen(head) =>
reachableLambdaBases(tail, seen)
case head :: tail =>
val next = basesdb.get(head).iterator.flatMap(_.iterator).collect {
case l: LightTypeTagRef.Lambda if l.input == currentLambda.input =>
l
}.toList
reachableLambdaBases(next ::: tail, seen + head)
case Nil =>
seen
}
}

val inheritedLambdas = reachableLambdaBases(currentLambda :: Nil, Set.empty)
val context = currentLambda.input.zip(args).collect {
case (param, Some(arg)) =>
param -> arg
}.toMap
val rewriter = new RuntimeAPI.Rewriter(context)

basesdb ++ basesdb.iterator.collect {
case (self: LightTypeTagRef.Lambda, parents) =>
val rewrittenSelf =
if (inheritedLambdas(self)) {
combineRootLambda(self)
} else {
rewriter.replaceRefs(self)
}

val rewrittenParents = parents.map {
case l: LightTypeTagRef.Lambda if inheritedLambdas(l) =>
combineRootLambda(l)
case other =>
rewriter.replaceRefs(other)
}

if (rewrittenSelf != self || rewrittenParents != parents) {
Some(rewrittenSelf -> rewrittenParents)
} else {
None
}
}.flatten

case _ =>
basesdb
}
}

def combine(args: LightTypeTag*): LightTypeTag = {
val argRefs = args.map(_.ref match { case reference: AbstractReference => reference })
val appliedBases = rewriteBases(
args = argRefs.map(Some(_)),
combineRootLambda = _.combine(argRefs)
)

def mergedBasesDB = LightTypeTag.mergeIDBs(appliedBases, args.iterator.map(_.basesdb))

Expand All @@ -132,17 +182,11 @@ abstract class LightTypeTag private[reflect] (
* }}}
*/
def combineNonPos(args: Option[LightTypeTag]*): LightTypeTag = {
val argRefs = args.map(_.map(_.ref))
val appliedBases = basesdb ++ basesdb.iterator.collect { // do not remove the unapplied base lambdas after combination (required for inferredLambdaParents in isChild)
case (self: LightTypeTagRef.Lambda, parents) =>
self.combineNonPos(argRefs) -> parents.map {
case l: LightTypeTagRef.Lambda =>
l.combineNonPos(argRefs)
case nonLambdaParent =>
val context = self.input.zip(argRefs.flatten.collect { case a: AbstractReference => a }).toMap
new RuntimeAPI.Rewriter(context).replaceRefs(nonLambdaParent)
}
}
val argRefs = args.map(_.map(_.ref match { case reference: AbstractReference => reference }))
val appliedBases = rewriteBases(
args = argRefs,
combineRootLambda = _.combineNonPos(argRefs)
)

def mergedBasesDB = LightTypeTag.mergeIDBs(appliedBases, args.iterator.map(_.map(_.basesdb).getOrElse(Map.empty)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ class TagTest extends SharedTagTest {
assert(t[TraitK30, Trait30].tag == Tag[TraitK30[Trait30]].tag)
}

"regression test for https://github.com/zio/izumi-reflect/issues/571: RuntimeAPI.unpack handles abstract type bounds with intersections" in {
trait Scope {
trait X

type SomeType[S] <: S with X

trait RootTrait[P]
trait TestTrait[F[_]] extends RootTrait[SomeType[String]]
}
object Scope extends Scope
import Scope._

def unpackTag[F[_]: TagK] = {
val ref = implicitly[Tag[TestTrait[F]]].tag.ref match {
case reference: LightTypeTagRef.AbstractReference => reference
}
RuntimeAPI.unpack(ref)
}

type Id[A] = A

assert(unpackTag[Id].nonEmpty)
}

"example in 3.0.9 release notes works" in {
def printColType[F[+x] <: Iterable[x]: Tag.auto.T]: LightTypeTag = {
Tag[F[Int]].tag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,45 +71,6 @@ abstract class SharedLightTypeTagProgressionTest extends TagAssertions with TagP
}
}

"progression test: combined intersection lambda tags still contain some junk bases (coming from the unsound same-arity assumption in LightTypeTag#combine)" in {
val tCtor = `LTT[_,_]`[T3]
val combined = tCtor.combine(LTT[Int], LTT[Boolean])
val debugCombined = combined.debug("combined")

val alias = LTT[T3[Int, Boolean]]
val direct = LTT[W1 with W4[Boolean] with W5[Int]]

broken {
assert(!debugCombined.contains("W4[=scala.Int]"))
}
broken {
assert(!debugCombined.contains("W3[=scala.Int]"))
}

broken {
assertDebugSame(combined, alias)
}
broken {
assertDebugSame(combined, direct)
}
}

"progression test: combined lambda tags still contain some junk bases (coming from the unsound same-arity assumption in LightTypeTag#combine)" in {
val curriedApplied = `LTT[_,_]`[Right].combine(LTT[Throwable]).combine(LTT[Unit])
val debug1 = curriedApplied.debug()

assertSame(curriedApplied, LTT[Right[Throwable, Unit]])

assert(debug1.contains(": scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug1.contains("- scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug1.contains("* scala.Product"))
assert(debug1.contains("- λ %1 → scala.util.Right[+java.lang.Throwable,+1]"))
assert(debug1.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
broken {
assert(!debug1.contains("λ %1 → scala.util.Right[+scala.Unit,+1]"))
}
}

"progression test: Dotty fails to `support methods with type parameters in structural refinements`" in {
trait X { def x[A](a: A): A }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,8 +663,12 @@ abstract class SharedLightTypeTagTest extends TagAssertions {
assert(!debugCombined.contains("W4[=B]"))
assert(!debugCombined.contains("W3[=B]"))
assert(!debugCombined.contains("W5[=A]"))
assert(!debugCombined.contains("W4[=scala.Int]"))
assert(!debugCombined.contains("W3[=scala.Int]"))
assert(debugCombined.contains("W5[=scala.Int]"))

assertSame(combined, alias)
assertSame(combined, direct)
assertDebugSame(alias, direct)
}

Expand Down Expand Up @@ -725,15 +729,20 @@ abstract class SharedLightTypeTagTest extends TagAssertions {
val oneArgApplied = `LTT[_,_]`[Right].combine(LTT[Throwable]).combine(LTT[Unit])
val debug5 = oneArgApplied.debug()

assertSame(oneArgApplied, LTT[Right[Throwable, Unit]])

assert(!debug5.contains("package::Right"))
assert(!debug5.contains("<refinement>"))
assert(!debug5.contains("<none>"))
assert(!debug5.contains("scala.package.A"))
assert(!debug5.contains("scala.package.B"))
assert(!debug5.contains("+scala.Nothing"))
assert(debug5.contains(": scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug5.contains("- scala.util.Right[+java.lang.Throwable,+scala.Unit]"))
assert(debug5.contains("* scala.Product"))
assert(debug5.contains("- λ %1 → scala.util.Right[+java.lang.Throwable,+1]"))
assert(debug5.contains("- λ %0,%1 → scala.util.Right[+0,+1]"))
assert(!debug5.contains("λ %1 → scala.util.Right[+scala.Unit,+1]"))
}

"No degenerate lambdas (regression test https://github.com/zio/izumi-reflect/issues/345)" in {
Expand Down
Loading