Skip to content

Commit 7dc5235

Browse files
committed
reorganise construction of refined mirror types,
record constraints against the formal.
1 parent 9dbe2d3 commit 7dc5235

File tree

4 files changed

+127
-14
lines changed

4 files changed

+127
-14
lines changed

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

+23-8
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
241241
* MirroredLabel = <label> }
242242
* }
243243
*/
244-
private def mirrorCore(parentClass: ClassSymbol, monoType: Type, mirroredType: Type, label: Name, formal: Type)(using Context) =
245-
formal & parentClass.typeRef
244+
private def mirrorCore(parentClass: ClassSymbol, monoType: Type, mirroredType: Type, label: Name)(using Context) =
245+
parentClass.typeRef
246246
.refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType))
247247
.refinedWith(tpnme.MirroredType, TypeAlias(mirroredType))
248248
.refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString))))
@@ -267,6 +267,13 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
267267
then report.error(
268268
em"$name mismatch, expected: $expected, found: $actual.", ctx.source.atSpan(span))
269269

270+
extension (formal: Type)
271+
/** `tp := op; tp <:< formal; formal & tp` */
272+
private def constrained_&(op: Context ?=> Type)(using Context): Type =
273+
val tp = op
274+
tp <:< formal
275+
formal & tp
276+
270277
private def mkMirroredMonoType(mirroredType: HKTypeLambda)(using Context): Type =
271278
val monoMap = new TypeMap:
272279
def apply(t: Type) = t match
@@ -311,10 +318,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
311318
val elemsLabels = TypeOps.nestedPairs(elemLabels)
312319
checkRefinement(formal, tpnme.MirroredElemTypes, elemsType, span)
313320
checkRefinement(formal, tpnme.MirroredElemLabels, elemsLabels, span)
314-
val mirrorType =
315-
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name, formal)
321+
val mirrorType = formal.constrained_& {
322+
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name)
316323
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
317324
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels))
325+
}
318326
val mirrorRef =
319327
if (genAnonyousMirror(cls)) anonymousMirror(monoType, ExtendsProductMirror, span)
320328
else companionPath(mirroredType, span)
@@ -329,11 +337,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
329337
val module = mirroredType.termSymbol
330338
val modulePath = pathFor(mirroredType).withSpan(span)
331339
if module.info.classSymbol.is(Scala2x) then
332-
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, module.name, formal)
340+
val mirrorType = formal.constrained_& {
341+
mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, module.name)
342+
}
333343
val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, modulePath :: Nil)
334344
mirrorRef.cast(mirrorType)
335345
else
336-
val mirrorType = mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, module.name, formal)
346+
val mirrorType = formal.constrained_& {
347+
mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, module.name)
348+
}
337349
modulePath.cast(mirrorType)
338350
else
339351
val cls = mirroredType.classSymbol
@@ -404,9 +416,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
404416
(mirroredType, elems)
405417

406418
val mirrorType =
407-
mirrorCore(defn.Mirror_SumClass, monoType, mirroredType, cls.name, formal)
419+
val labels = TypeOps.nestedPairs(elemLabels)
420+
formal.constrained_& {
421+
mirrorCore(defn.Mirror_SumClass, monoType, mirroredType, cls.name)
408422
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
409-
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
423+
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(labels))
424+
}
410425
val mirrorRef =
411426
if useCompanion then companionPath(mirroredType, span)
412427
else anonymousMirror(monoType, ExtendsSumMirror, span)

tests/neg/7380a.scala

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import scala.deriving.Mirror
2+
3+
object Lib {
4+
5+
trait HasFields[T]
6+
trait HasFieldsOrNone[T]
7+
8+
given mirHasFields[T, ElemLabels <: NonEmptyTuple](using
9+
mir: Mirror.ProductOf[T] { type MirroredElemLabels = ElemLabels }
10+
): HasFields[T]()
11+
12+
given mirHasFieldsOrNone[T, ElemLabels <: Tuple](using
13+
mir: Mirror.ProductOf[T] { type MirroredElemLabels = ElemLabels }
14+
): HasFieldsOrNone[T]()
15+
16+
}
17+
18+
object Test {
19+
20+
Lib.mirHasFields[(Int, String), ("_1", "_2", "_3")] // error
21+
22+
summon[Lib.HasFields[(Int, String)]] // ok
23+
24+
case class NoFields()
25+
26+
summon[Lib.HasFields[NoFields]] // error
27+
summon[Lib.HasFieldsOrNone[NoFields]] // ok
28+
29+
}

tests/run-macros/i7987.check

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
scala.deriving.Mirror {
2-
type MirroredType >: scala.Some[scala.Int] <: scala.Some[scala.Int]
3-
type MirroredMonoType >: scala.Some[scala.Int] <: scala.Some[scala.Int]
4-
type MirroredElemTypes >: scala.Nothing <: scala.Tuple
5-
} & scala.deriving.Mirror.Product {
1+
scala.deriving.Mirror.Product {
62
type MirroredMonoType >: scala.Some[scala.Int] <: scala.Some[scala.Int]
73
type MirroredType >: scala.Some[scala.Int] <: scala.Some[scala.Int]
84
type MirroredLabel >: "Some" <: "Some"
9-
} {
105
type MirroredElemTypes >: scala.*:[scala.Int, scala.Tuple$package.EmptyTuple] <: scala.*:[scala.Int, scala.Tuple$package.EmptyTuple]
116
type MirroredElemLabels >: scala.*:["value", scala.Tuple$package.EmptyTuple] <: scala.*:["value", scala.Tuple$package.EmptyTuple]
127
}

tests/run/i14150.scala

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import scala.deriving.Mirror
2+
import scala.util.NotGiven
3+
import scala.compiletime.constValue
4+
5+
trait GetConstValue[T] {
6+
type Out
7+
def get : Out
8+
}
9+
10+
object GetConstValue {
11+
type Aux[T, O] = GetConstValue[T] { type Out = O }
12+
13+
inline given value[T <: Singleton](
14+
using
15+
ev : NotGiven[T <:< Tuple],
16+
) : GetConstValue.Aux[T, T] = {
17+
val out = constValue[T]
18+
19+
new GetConstValue[T] {
20+
type Out = T
21+
def get : Out = out
22+
}
23+
}
24+
25+
given empty : GetConstValue[EmptyTuple] with {
26+
type Out = EmptyTuple
27+
def get : Out = EmptyTuple
28+
}
29+
30+
given nonEmpty[H, HRes, Tail <: Tuple, TRes <: Tuple](
31+
using
32+
head : GetConstValue.Aux[H, HRes],
33+
tail : GetConstValue.Aux[Tail, TRes],
34+
) : GetConstValue[H *: Tail] with {
35+
type Out = HRes *: TRes
36+
37+
def get : Out = head.get *: tail.get
38+
}
39+
}
40+
41+
trait MirrorNamesDeriver[T] {
42+
type Derived <: Tuple
43+
def derive : Derived
44+
}
45+
46+
object MirrorNamesDeriver {
47+
given mirDeriver[T, ElemLabels <: NonEmptyTuple](
48+
using
49+
mir: Mirror.SumOf[T] { type MirroredElemLabels = ElemLabels },
50+
ev : GetConstValue.Aux[ElemLabels, ElemLabels],
51+
): MirrorNamesDeriver[T] with {
52+
type Derived = ElemLabels
53+
54+
def derive: ElemLabels = ev.get
55+
}
56+
57+
def derive[T](using d : MirrorNamesDeriver[T]) : d.Derived = d.derive
58+
}
59+
60+
sealed trait SuperT
61+
final case class SubT1(int: Int) extends SuperT
62+
final case class SubT2(str: String, dbl : Double, bool : Boolean) extends SuperT
63+
64+
@main def Test =
65+
66+
// Works when type parameters are set explicitly
67+
val successfulLabels = MirrorNamesDeriver.mirDeriver[SuperT, ("SubT1", "SubT2")].derive
68+
println(successfulLabels)
69+
assert(successfulLabels == ("SubT1", "SubT2"))
70+
71+
// Fails when type parameters are inferred
72+
val failedLabels = MirrorNamesDeriver.derive[SuperT]
73+
println(successfulLabels)
74+
assert(failedLabels == ("SubT1", "SubT2"))

0 commit comments

Comments
 (0)