Skip to content

Commit 83ab6c7

Browse files
authored
Merge pull request #7992 from dotty-staging/fix-#7965
Fix #7965: Deal with AndTypes in joins
2 parents 10c7668 + 91cf80d commit 83ab6c7

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
417417
if (tp11.stripTypeVar eq tp12.stripTypeVar) recur(tp11, tp2)
418418
else thirdTry
419419
case tp1 @ OrType(tp11, tp12) =>
420+
420421
def joinOK = tp2.dealiasKeepRefiningAnnots match {
421422
case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass =>
422423
// 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
427428
case _ =>
428429
false
429430
}
431+
432+
def containsAnd(tp: Type): Boolean = tp.dealiasKeepRefiningAnnots match
433+
case tp: AndType => true
434+
case OrType(tp1, tp2) => containsAnd(tp1) || containsAnd(tp2)
435+
case _ => false
436+
430437
def widenOK =
431438
(tp2.widenSingletons eq tp2) &&
432439
(tp1.widenSingletons ne tp1) &&
@@ -435,7 +442,15 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
435442
if (tp2.atoms.nonEmpty && canCompare(tp2.atoms))
436443
tp1.atoms.nonEmpty && tp1.atoms.subsetOf(tp2.atoms)
437444
else
438-
widenOK || joinOK || recur(tp11, tp2) && recur(tp12, tp2)
445+
widenOK
446+
|| joinOK
447+
|| recur(tp11, tp2) && recur(tp12, tp2)
448+
|| containsAnd(tp1) && recur(tp1.join, tp2)
449+
// An & on the left side loses information. Compensate by also trying the join.
450+
// This is less ad-hoc than it looks since we produce joins in type inference,
451+
// and then need to check that they are indeed supertypes of the original types
452+
// under -Ycheck. Test case is i7965.scala.
453+
439454
case tp1: MatchType =>
440455
val reduced = tp1.reduced
441456
if (reduced.exists) recur(reduced, tp2) else thirdTry

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,28 +198,35 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
198198

199199
def mergeRefinedOrApplied(tp1: Type, tp2: Type): Type = {
200200
def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2")
201+
def fallback = tp2 match
202+
case AndType(tp21, tp22) =>
203+
mergeRefinedOrApplied(tp1, tp21) & mergeRefinedOrApplied(tp1, tp22)
204+
case _ =>
205+
fail
201206
tp1 match {
202207
case tp1 @ RefinedType(parent1, name1, rinfo1) =>
203208
tp2 match {
204209
case RefinedType(parent2, `name1`, rinfo2) =>
205210
tp1.derivedRefinedType(
206211
mergeRefinedOrApplied(parent1, parent2), name1, rinfo1 | rinfo2)
207-
case _ => fail
212+
case _ => fallback
208213
}
209214
case tp1 @ AppliedType(tycon1, args1) =>
210215
tp2 match {
211216
case AppliedType(tycon2, args2) =>
212217
tp1.derivedAppliedType(
213218
mergeRefinedOrApplied(tycon1, tycon2),
214219
ctx.typeComparer.lubArgs(args1, args2, tycon1.typeParams))
215-
case _ => fail
220+
case _ => fallback
216221
}
217222
case tp1 @ TypeRef(pre1, _) =>
218223
tp2 match {
219224
case tp2 @ TypeRef(pre2, _) if tp1.name eq tp2.name =>
220225
tp1.derivedSelect(pre1 | pre2)
221-
case _ => fail
226+
case _ => fallback
222227
}
228+
case AndType(tp11, tp12) =>
229+
mergeRefinedOrApplied(tp11, tp2) & mergeRefinedOrApplied(tp12, tp2)
223230
case _ => fail
224231
}
225232
}

tests/pos/i7965.scala

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
class Has[A]
2+
trait X
3+
trait Y
4+
trait Z
5+
6+
abstract class Test {
7+
def x: Has[X] | (Has[Y] & Has[Z])
8+
val y: Has[? >: (X & Y) | (X & Z) <: (X | Y) & (X | Z)] = x
9+
10+
def foo[T <: Has[_]](has: T): T = has
11+
foo(x)
12+
}
13+
14+
// -------------------------------------------
15+
16+
trait ZLayer[-RIn, +E, +ROut <: Has[_]] {
17+
def >>>[E1 >: E, ROut2 <: Has[_]](that: ZLayer[ROut, E1, ROut2]): ZLayer[RIn, E1, ROut2]
18+
def ++[E1 >: E, RIn2, ROut1 >: ROut <: Has[_], ROut2 <: Has[_]](that: ZLayer[RIn2, E1, ROut2]): ZLayer[RIn with RIn2, E1, ROut1 with ROut2]
19+
}
20+
object ZLayer {
21+
type NoDeps[+E, +B <: Has[_]] = ZLayer[Any, E, B]
22+
}
23+
24+
type ServiceA = Has[ServiceA.Service]
25+
object ServiceA {
26+
trait Service
27+
val live: ZLayer.NoDeps[Nothing, ServiceA] = ???
28+
}
29+
30+
type ServiceB = Has[ServiceB.Service]
31+
object ServiceB {
32+
trait Service
33+
val live: ZLayer.NoDeps[Nothing, ServiceB] = ???
34+
}
35+
36+
type ServiceC = Has[ServiceC.Service]
37+
object ServiceC {
38+
trait Service
39+
val live: ZLayer.NoDeps[Nothing, ServiceC] = ???
40+
}
41+
42+
type ServiceD = Has[ServiceD.Service]
43+
object ServiceD {
44+
trait Service
45+
val live: ZLayer.NoDeps[ServiceC, ServiceD with ServiceC] = ???
46+
}
47+
48+
val combined =
49+
ServiceA.live >>>
50+
(ServiceB.live ++ (ServiceC.live >>> ServiceD.live))
51+
52+
// -------------------------------------------
53+
54+
class Outer {
55+
class Elem
56+
}
57+
58+
abstract class Test2 {
59+
val o1: Outer = ???
60+
val o2: Outer = ???
61+
val o3: Outer = ???
62+
63+
val x: o1.Elem | (o2.Elem & o3.Elem)
64+
def foo[T <: Outer#Elem](has: T): T = ???
65+
foo(x)
66+
}

0 commit comments

Comments
 (0)