Skip to content

Commit 1cbaf7e

Browse files
committed
remove AndType branch in productMirror
remove case: OrType is no longer supported
1 parent 84732a5 commit 1cbaf7e

File tree

4 files changed

+126
-52
lines changed

4 files changed

+126
-52
lines changed

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

-5
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,6 @@ object TypeUtils {
8787
* of this type, while keeping the same prefix.
8888
*/
8989
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
9590
case AndType(tp1, tp2) =>
9691
val c1 = tp1.classSymbol
9792
val c2 = tp2.classSymbol

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

+82-47
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,72 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
276276
case t => mapOver(t)
277277
monoMap(mirroredType.resultType)
278278

279-
private def productMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors =
279+
private[Synthesizer] enum MirrorSource:
280+
case ClassSymbol(cls: Symbol)
281+
case Singleton(tref: TermRef)
282+
283+
def isSub(that: MirrorSource)(using Context): Boolean =
284+
(this, that) match
285+
case (Singleton(tref), ClassSymbol(cls)) => tref.classSymbol.isSubClass(cls)
286+
case (ClassSymbol(cls1), ClassSymbol(cls2)) => cls1.isSubClass(cls2)
287+
case _ => false // includes that a class is never sub of a singleton
288+
289+
def debug(using Context): String = this match
290+
case ClassSymbol(cls) => i"$cls"
291+
case Singleton(tref) => i"${tref.termSymbol}"
292+
293+
object MirrorSource:
294+
def cls(cls: Symbol): MirrorSource = ClassSymbol(cls)
295+
def singleton(tref: TermRef): MirrorSource = Singleton(tref)
296+
297+
/** widen TermRef to see if they are an alias to an enum singleton or case object */
298+
private def isEnumOrCaseObjectRef(tp: Type)(using Context): Boolean = tp match
299+
case tp: TermRef =>
300+
val sym = tp.termSymbol
280301

281-
def whyNotAcceptableType(tp: Type, cls: Symbol): String = tp match
302+
sym.isEnumCase
303+
|| (sym.isClass && sym.isAllOf(Case | Module))
304+
|| (!tp.isOverloaded && isEnumOrCaseObjectRef(tp.underlying.widenExpr))
305+
case _ => false
306+
307+
/** A customised version of `Types.classSymbol`, specialised for mirror generation. */
308+
def reduce(mirroredType: Type)(using Context): Either[String, MirrorSource] = mirroredType match
309+
case tp: TypeRef =>
310+
val sym = tp.symbol
311+
if sym.isClass then // direct ref to a class, not an alias
312+
if sym.isAllOf(Case | Module) then
313+
Right(MirrorSource.singleton(sym.sourceModule.reachableTermRef)) // correct widened module ref
314+
else
315+
Right(MirrorSource.cls(sym))
316+
else
317+
reduce(tp.superType)
318+
case tp: TermRef if isEnumOrCaseObjectRef(tp) =>
319+
Right(MirrorSource.singleton(tp))
282320
case tp: HKTypeLambda if tp.resultType.isInstanceOf[HKTypeLambda] =>
283-
i"its subpart `$tp` is not a supported kind (either `*` or `* -> *`)"
284-
case tp: TypeProxy => whyNotAcceptableType(tp.underlying, cls)
285-
case OrType(tp1, tp2) => i"its subpart `$tp` is a top-level union type."
286-
case _ =>
287-
if tp.classSymbol eq cls then ""
288-
else i"a subpart reduces to the more precise ${tp.classSymbol}, expected $cls"
321+
Left(i"its subpart `$tp` is not a supported kind (either `*` or `* -> *`)")
322+
case tp: TypeProxy =>
323+
reduce(tp.underlying)
324+
case tp: ClassInfo =>
325+
Right(MirrorSource.cls(tp.cls))
326+
case tp @ AndType(l, r) =>
327+
for
328+
lsrc <- reduce(l)
329+
rsrc <- reduce(r)
330+
res <- locally {
331+
if lsrc.isSub(rsrc) then Right(lsrc)
332+
else if rsrc.isSub(lsrc) then Right(rsrc)
333+
else Left(i"its subpart `$tp` is an intersection of unrelated symbols ${lsrc.debug} and ${rsrc.debug}.")
334+
}
335+
yield
336+
res
337+
case tp: OrType =>
338+
Left(i"its subpart `$tp` is a top-level union type.")
339+
case tp =>
340+
Left(i"its subpart `$tp` is an unsupported type.")
341+
342+
end MirrorSource
343+
344+
private def productMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors =
289345

290346
def makeProductMirror(cls: Symbol): TreeWithErrors =
291347
val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal))
@@ -309,55 +365,34 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
309365
withNoErrors(mirrorRef.cast(mirrorType))
310366
end makeProductMirror
311367

312-
/** widen TermRef to see if they are an alias to an enum singleton */
313-
def isEnumSingletonRef(tp: Type)(using Context): Boolean = tp match
314-
case tp: TermRef =>
315-
val sym = tp.termSymbol
316-
sym.isEnumCase || (!tp.isOverloaded && isEnumSingletonRef(tp.underlying.widenExpr))
317-
case _ => false
318-
319-
mirroredType match
320-
case AndType(tp1, tp2) =>
321-
orElse(productMirror(tp1, formal, span), productMirror(tp2, formal, span))
322-
case _ =>
323-
val cls = mirroredType.classSymbol
324-
if isEnumSingletonRef(mirroredType) || cls.isAllOf(Case | Module) then
325-
val (singleton, singletonRef) =
326-
if mirroredType.termSymbol.exists then (mirroredType.termSymbol, mirroredType)
327-
else (cls.sourceModule, cls.sourceModule.reachableTermRef)
328-
val singletonPath = pathFor(singletonRef).withSpan(span)
329-
if singleton.info.classSymbol.is(Scala2x) then // could be Scala 3 alias of Scala 2 case object.
330-
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name, formal)
368+
MirrorSource.reduce(mirroredType) match
369+
case Right(msrc) => msrc match
370+
case MirrorSource.Singleton(tref) =>
371+
val singleton = tref.termSymbol
372+
val singletonPath = pathFor(tref).withSpan(span)
373+
if tref.classSymbol.is(Scala2x) then // could be Scala 3 alias of Scala 2 case object.
374+
val mirrorType =
375+
mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name, formal)
331376
val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, singletonPath :: Nil)
332377
withNoErrors(mirrorRef.cast(mirrorType))
333378
else
334379
val mirrorType = mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, singleton.name, formal)
335380
withNoErrors(singletonPath.cast(mirrorType))
336-
else
337-
val acceptableMsg = whyNotAcceptableType(mirroredType, cls)
338-
if acceptableMsg.isEmpty then
339-
if cls.isGenericProduct then makeProductMirror(cls)
340-
else withErrors(i"$cls is not a generic product because ${cls.whyNotGenericProduct}")
341-
else withErrors(i"type `$mirroredType` is not a generic product because $acceptableMsg")
381+
case MirrorSource.ClassSymbol(cls) =>
382+
if cls.isGenericProduct then makeProductMirror(cls)
383+
else withErrors(i"$cls is not a generic product because ${cls.whyNotGenericProduct}")
384+
case Left(msg) =>
385+
withErrors(i"type `$mirroredType` is not a generic product because $msg")
342386
end productMirror
343387

344388
private def sumMirror(mirroredType: Type, formal: Type, span: Span)(using Context): TreeWithErrors =
345389

346-
val cls = mirroredType.classSymbol
347-
val clsIsGenericSum = cls.isGenericSum
348-
349-
def whyNotAcceptableType(tp: Type): String = tp match
350-
case tp: TermRef => i"its subpart `$tp` is a term reference"
351-
case tp: HKTypeLambda if tp.resultType.isInstanceOf[HKTypeLambda] =>
352-
i"its subpart `$tp` is not a supported kind (either `*` or `* -> *`)"
353-
case tp: TypeProxy => whyNotAcceptableType(tp.underlying)
354-
case OrType(tp1, tp2) => i"its subpart `$tp` is a top-level union type."
355-
case _ =>
356-
if tp.classSymbol eq cls then ""
357-
else i"a subpart reduces to the more precise ${tp.classSymbol}, expected $cls"
358-
390+
val (acceptableMsg, cls) = MirrorSource.reduce(mirroredType) match
391+
case Right(MirrorSource.Singleton(tp)) => (i"its subpart `$tp` is a term reference", NoSymbol)
392+
case Right(MirrorSource.ClassSymbol(cls)) => ("", cls)
393+
case Left(msg) => (msg, NoSymbol)
359394

360-
val acceptableMsg = whyNotAcceptableType(mirroredType)
395+
val clsIsGenericSum = cls.isGenericSum
361396

362397
if acceptableMsg.isEmpty && clsIsGenericSum then
363398
val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString)))
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:11:56 --------------------------------------------------------------
2+
11 |val testA = summon[Mirror.ProductOf[Cns[Int] & Sm[Int]]] // error: unreleated
3+
| ^
4+
|No given instance of type deriving.Mirror.ProductOf[Cns[Int] & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Cns[Int] & Sm[Int]]: type `Cns[Int] & Sm[Int]` is not a generic product because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated symbols class Cns and class Sm.
5+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:12:56 --------------------------------------------------------------
6+
12 |val testB = summon[Mirror.ProductOf[Sm[Int] & Cns[Int]]] // error: unreleated
7+
| ^
8+
|No given instance of type deriving.Mirror.ProductOf[Sm[Int] & Cns[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Sm[Int] & Cns[Int]]: type `Sm[Int] & Cns[Int]` is not a generic product because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated symbols class Sm and class Cns.
9+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:13:49 --------------------------------------------------------------
10+
13 |val testC = summon[Mirror.Of[Cns[Int] & Sm[Int]]] // error: unreleated
11+
| ^
12+
|No given instance of type deriving.Mirror.Of[Cns[Int] & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Cns[Int] & Sm[Int]]:
13+
| * type `Cns[Int] & Sm[Int]` is not a generic product because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated symbols class Cns and class Sm.
14+
| * type `Cns[Int] & Sm[Int]` is not a generic sum because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated symbols class Cns and class Sm.
15+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:14:49 --------------------------------------------------------------
16+
14 |val testD = summon[Mirror.Of[Sm[Int] & Cns[Int]]] // error: unreleated
17+
| ^
18+
|No given instance of type deriving.Mirror.Of[Sm[Int] & Cns[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Sm[Int] & Cns[Int]]:
19+
| * type `Sm[Int] & Cns[Int]` is not a generic product because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated symbols class Sm and class Cns.
20+
| * type `Sm[Int] & Cns[Int]` is not a generic sum because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated symbols class Sm and class Cns.
21+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:15:55 --------------------------------------------------------------
22+
15 |val testE = summon[Mirror.ProductOf[Sm[Int] & Nn.type]] // error: unreleated
23+
| ^
24+
|No given instance of type deriving.Mirror.ProductOf[Sm[Int] & Nn.type] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Sm[Int] & Nn.type]: type `Sm[Int] & Nn.type` is not a generic product because its subpart `Sm[Int] & Nn.type` is an intersection of unrelated symbols class Sm and object Nn.
25+
-- Error: tests/neg/mirror-synthesis-errors-b.scala:16:55 --------------------------------------------------------------
26+
16 |val testF = summon[Mirror.ProductOf[Nn.type & Sm[Int]]] // error: unreleated
27+
| ^
28+
|No given instance of type deriving.Mirror.ProductOf[Nn.type & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Nn.type & Sm[Int]]: type `Nn.type & Sm[Int]` is not a generic product because its subpart `Nn.type & Sm[Int]` is an intersection of unrelated symbols object Nn and class Sm.
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.deriving.Mirror
2+
3+
sealed trait Lst[+A] // AKA: scala.collection.immutable.List
4+
case class Cns[+A](head: A, tail: Lst[A]) extends Lst[A]
5+
case object Nl extends Lst[Nothing]
6+
7+
sealed trait Opt[+A] // AKA: scala.Option
8+
case class Sm[+A](value: A) extends Opt[A]
9+
case object Nn extends Opt[Nothing]
10+
11+
val testA = summon[Mirror.ProductOf[Cns[Int] & Sm[Int]]] // error: unreleated
12+
val testB = summon[Mirror.ProductOf[Sm[Int] & Cns[Int]]] // error: unreleated
13+
val testC = summon[Mirror.Of[Cns[Int] & Sm[Int]]] // error: unreleated
14+
val testD = summon[Mirror.Of[Sm[Int] & Cns[Int]]] // error: unreleated
15+
val testE = summon[Mirror.ProductOf[Sm[Int] & Nn.type]] // error: unreleated
16+
val testF = summon[Mirror.ProductOf[Nn.type & Sm[Int]]] // error: unreleated

0 commit comments

Comments
 (0)