diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 452fa2d898ed..4cb9a232f9ac 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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 @@ -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) && @@ -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 diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index e71416828505..65a42cd42561 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -198,13 +198,18 @@ 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 { @@ -212,14 +217,16 @@ trait TypeOps { this: Context => // TODO: Make standalone object. 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 + case _ => fallback } + case AndType(tp11, tp12) => + mergeRefinedOrApplied(tp11, tp2) & mergeRefinedOrApplied(tp12, tp2) case _ => fail } } diff --git a/tests/pos/i7965.scala b/tests/pos/i7965.scala new file mode 100644 index 000000000000..ee9a9e64a01b --- /dev/null +++ b/tests/pos/i7965.scala @@ -0,0 +1,66 @@ +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)) + +// ------------------------------------------- + +class Outer { + class Elem +} + +abstract class Test2 { + 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) +} \ No newline at end of file