Skip to content

Commit 548886a

Browse files
committed
fix 14823: handle unions in companion path
1 parent b636633 commit 548886a

File tree

5 files changed

+76
-24
lines changed

5 files changed

+76
-24
lines changed

compiler/src/dotty/tools/dotc/transform/TypeUtils.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import Contexts._
99
import Symbols._
1010
import Names.Name
1111

12+
import dotty.tools.dotc.core.Decorators.*
13+
1214
object TypeUtils {
1315
/** A decorator that provides methods on types
1416
* that are needed in the transformer pipeline.
@@ -84,11 +86,16 @@ object TypeUtils {
8486
/** The TermRef referring to the companion of the underlying class reference
8587
* of this type, while keeping the same prefix.
8688
*/
87-
def companionRef(using Context): TermRef = self match {
89+
def mirrorCompanionRef(using Context): TermRef = self match {
90+
case OrType(tp1, tp2) =>
91+
val r1 = tp1.mirrorCompanionRef
92+
val r2 = tp2.mirrorCompanionRef
93+
assert(r1.symbol == r2.symbol, em"mirrorCompanionRef mismatch for $self: $r1, $r2 did not have the same symbol")
94+
r1
8895
case self @ TypeRef(prefix, _) if self.symbol.isClass =>
8996
prefix.select(self.symbol.companionModule).asInstanceOf[TermRef]
9097
case self: TypeProxy =>
91-
self.underlying.companionRef
98+
self.underlying.mirrorCompanionRef
9299
}
93100

94101
/** Is this type a methodic type that takes implicit parameters (both old and new) at some point? */

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
249249

250250
/** A path referencing the companion of class type `clsType` */
251251
private def companionPath(clsType: Type, span: Span)(using Context) =
252-
val ref = pathFor(clsType.companionRef)
252+
val ref = pathFor(clsType.mirrorCompanionRef)
253253
assert(ref.symbol.is(Module) && (clsType.classSymbol.is(ModuleClass) || (ref.symbol.companionClass == clsType.classSymbol)))
254254
ref.withSpan(span)
255255

@@ -275,6 +275,35 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
275275
monoMap(mirroredType.resultType)
276276

277277
private def productMirror(mirroredType: Type, formal: Type, span: Span)(using Context): Tree =
278+
279+
/** do all parts match the class symbol? */
280+
def acceptable(tp: Type, cls: Symbol): Boolean = tp match
281+
case tp: TypeProxy => acceptable(tp.underlying, cls)
282+
case OrType(tp1, tp2) => acceptable(tp1, cls) && acceptable(tp2, cls)
283+
case _ => tp.classSymbol eq cls
284+
285+
def makeProductMirror(cls: Symbol): Tree =
286+
val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal))
287+
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
288+
val nestedPairs = TypeOps.nestedPairs(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
289+
val (monoType, elemsType) = mirroredType match
290+
case mirroredType: HKTypeLambda =>
291+
(mkMirroredMonoType(mirroredType), mirroredType.derivedLambdaType(resType = nestedPairs))
292+
case _ =>
293+
(mirroredType, nestedPairs)
294+
val elemsLabels = TypeOps.nestedPairs(elemLabels)
295+
checkRefinement(formal, tpnme.MirroredElemTypes, elemsType, span)
296+
checkRefinement(formal, tpnme.MirroredElemLabels, elemsLabels, span)
297+
val mirrorType =
298+
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name, formal)
299+
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
300+
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels))
301+
val mirrorRef =
302+
if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span)
303+
else companionPath(mirroredType, span)
304+
mirrorRef.cast(mirrorType)
305+
end makeProductMirror
306+
278307
mirroredType match
279308
case AndType(tp1, tp2) =>
280309
productMirror(tp1, formal, span).orElse(productMirror(tp2, formal, span))
@@ -289,28 +318,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
289318
else
290319
val mirrorType = mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, module.name, formal)
291320
modulePath.cast(mirrorType)
292-
else if mirroredType.classSymbol.isGenericProduct then
321+
else
293322
val cls = mirroredType.classSymbol
294-
val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal))
295-
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
296-
val nestedPairs = TypeOps.nestedPairs(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
297-
val (monoType, elemsType) = mirroredType match
298-
case mirroredType: HKTypeLambda =>
299-
(mkMirroredMonoType(mirroredType), mirroredType.derivedLambdaType(resType = nestedPairs))
300-
case _ =>
301-
(mirroredType, nestedPairs)
302-
val elemsLabels = TypeOps.nestedPairs(elemLabels)
303-
checkRefinement(formal, tpnme.MirroredElemTypes, elemsType, span)
304-
checkRefinement(formal, tpnme.MirroredElemLabels, elemsLabels, span)
305-
val mirrorType =
306-
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name, formal)
307-
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
308-
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels))
309-
val mirrorRef =
310-
if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span)
311-
else companionPath(mirroredType, span)
312-
mirrorRef.cast(mirrorType)
313-
else EmptyTree
323+
if acceptable(mirroredType, cls) && cls.isGenericProduct then makeProductMirror(cls)
324+
else EmptyTree
314325
end productMirror
315326

316327
private def sumMirror(mirroredType: Type, formal: Type, span: Span)(using Context): Tree =

tests/neg/i14823.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg/i14823.scala:8:50 ----------------------------------------------------------------------------------
2+
8 |val baz = summon[Mirror.Of[SubA[Int] | SubB[Int]]] // error
3+
| ^
4+
|no given instance of type deriving.Mirror.Of[SubA[Int] | SubB[Int]] was found for parameter x of method summon in object Predef

tests/neg/i14823.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import deriving.Mirror
2+
3+
case class Cov[+T]()
4+
5+
class SubA[+T]() extends Cov[T]
6+
class SubB[+T]() extends Cov[T]
7+
8+
val baz = summon[Mirror.Of[SubA[Int] | SubB[Int]]] // error
9+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
// this should fail because:
11+
// 1) SubA and SubB are not individually product types
12+
// 2) SubA and SubB are different classes

tests/pos/i14823.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import deriving.Mirror
2+
3+
object MirrorK1:
4+
type Of[F[_]] = Mirror { type MirroredType[A] = F[A] }
5+
6+
sealed trait Box[T]
7+
object Box
8+
9+
case class Child[T]() extends Box[T]
10+
11+
sealed abstract class Foo[T]
12+
object Foo {
13+
case class A[T]() extends Foo[T]
14+
}
15+
16+
val foo = summon[Mirror.Of[Box[Int] | Box[Int]]]
17+
val bar = summon[MirrorK1.Of[[X] =>> Box[Int] | Box[Int]]]
18+
def baz = summon[deriving.Mirror.Of[Foo[String] | Foo[String]]]

0 commit comments

Comments
 (0)