Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
else thirdTry
case tp1 @ OrType(tp11, tp12) =>

def joinOK = tp2.dealiasKeepRefiningAnnots match {
case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass =>
// If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a
Expand All @@ -427,6 +428,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
case _ =>
false
}

def containsAnd(tp: Type): Boolean = tp.dealiasKeepRefiningAnnots match
case tp: AndType => true
case OrType(tp1, tp2) => containsAnd(tp1) || containsAnd(tp2)
case _ => false

def widenOK =
(tp2.widenSingletons eq tp2) &&
(tp1.widenSingletons ne tp1) &&
Expand All @@ -435,7 +442,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
if (tp2.atoms.nonEmpty && canCompare(tp2.atoms))
tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms)
else
widenOK || joinOK || recur(tp11, tp2) && recur(tp12, tp2)
widenOK
|| joinOK
|| recur(tp11, tp2) && recur(tp12, tp2)
|| containsAnd(tp1) && recur(tp1.join, tp2)
// An & on the left side loses information. Compensate by also trying the join.
// This is less ad-hoc than it looks since we produce joins in type inference,
// and then need to check that they are indeed supertypes of the original types
// under -Ycheck. Test case is i7965.scala.

case tp1: MatchType =>
val reduced = tp1.reduced
if (reduced.exists) recur(reduced, tp2) else thirdTry
Expand Down
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,28 +198,35 @@ trait TypeOps { this: Context => // TODO: Make standalone object.

def mergeRefinedOrApplied(tp1: Type, tp2: Type): Type = {
def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2")
def fallback = tp2 match
case AndType(tp21, tp22) =>
mergeRefinedOrApplied(tp1, tp21) & mergeRefinedOrApplied(tp1, tp22)
case _ =>
fail
tp1 match {
case tp1 @ RefinedType(parent1, name1, rinfo1) =>
tp2 match {
case RefinedType(parent2, `name1`, rinfo2) =>
tp1.derivedRefinedType(
mergeRefinedOrApplied(parent1, parent2), name1, rinfo1 | rinfo2)
case _ => fail
case _ => fallback
}
case tp1 @ AppliedType(tycon1, args1) =>
tp2 match {
case AppliedType(tycon2, args2) =>
tp1.derivedAppliedType(
mergeRefinedOrApplied(tycon1, tycon2),
ctx.typeComparer.lubArgs(args1, args2, tycon1.typeParams))
case _ => fail
case _ => fallback
}
case tp1 @ TypeRef(pre1, _) =>
tp2 match {
case tp2 @ TypeRef(pre2, _) if tp1.name eq tp2.name =>
tp1.derivedSelect(pre1 | pre2)
case _ => fail
Copy link
Member

@smarter smarter Jan 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback is also necessary here, otherwise the following would crash:

class Outer { class Elem } class Test { val o1: Outer = ??? val o2: Outer = ??? val o3: Outer = ??? val x: o1.Elem | (o2.Elem & o3.Elem) def foo[T <: Outer#Elem](has: T): T = ??? foo(x) }
}
case AndType(tp11, tp12) =>
mergeRefinedOrApplied(tp11, tp2) & mergeRefinedOrApplied(tp12, tp2)
case _ => fail
}
}
Expand Down
48 changes: 48 additions & 0 deletions tests/pos/i7965.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
class Has[A]
trait X
trait Y
trait Z

abstract class Test {
def x: Has[X] | (Has[Y] & Has[Z])
val y: Has[? >: (X & Y) | (X & Z) <: (X | Y) & (X | Z)] = x

def foo[T <: Has[_]](has: T): T = has
foo(x)
}

trait ZLayer[-RIn, +E, +ROut <: Has[_]] {
def >>>[E1 >: E, ROut2 <: Has[_]](that: ZLayer[ROut, E1, ROut2]): ZLayer[RIn, E1, ROut2]
def ++[E1 >: E, RIn2, ROut1 >: ROut <: Has[_], ROut2 <: Has[_]](that: ZLayer[RIn2, E1, ROut2]): ZLayer[RIn with RIn2, E1, ROut1 with ROut2]
}
object ZLayer {
type NoDeps[+E, +B <: Has[_]] = ZLayer[Any, E, B]
}

type ServiceA = Has[ServiceA.Service]
object ServiceA {
trait Service
val live: ZLayer.NoDeps[Nothing, ServiceA] = ???
}

type ServiceB = Has[ServiceB.Service]
object ServiceB {
trait Service
val live: ZLayer.NoDeps[Nothing, ServiceB] = ???
}

type ServiceC = Has[ServiceC.Service]
object ServiceC {
trait Service
val live: ZLayer.NoDeps[Nothing, ServiceC] = ???
}

type ServiceD = Has[ServiceD.Service]
object ServiceD {
trait Service
val live: ZLayer.NoDeps[ServiceC, ServiceD with ServiceC] = ???
}

val combined =
ServiceA.live >>>
(ServiceB.live ++ (ServiceC.live >>> ServiceD.live))