@@ -223,16 +223,19 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
223
223
/** Create an anonymous class `new Object { type MirroredMonoType = ... }`
224
224
* and mark it with given attachment so that it is made into a mirror at PostTyper.
225
225
*/
226
- private def anonymousMirror (monoType : Type , attachment : Property .StickyKey [Unit ], span : Span )(using Context ) =
226
+ private def anonymousMirror (monoType : Type , attachment : Property .StickyKey [Unit ], tupleArity : Option [ Int ], span : Span )(using Context ) =
227
227
if ctx.isAfterTyper then ctx.compilationUnit.needsMirrorSupport = true
228
228
val monoTypeDef = untpd.TypeDef (tpnme.MirroredMonoType , untpd.TypeTree (monoType))
229
- val newImpl = untpd.Template (
229
+ var newImpl = untpd.Template (
230
230
constr = untpd.emptyConstructor,
231
231
parents = untpd.TypeTree (defn.ObjectType ) :: Nil ,
232
232
derived = Nil ,
233
233
self = EmptyValDef ,
234
234
body = monoTypeDef :: Nil
235
235
).withAttachment(attachment, ())
236
+ tupleArity.foreach { n =>
237
+ newImpl = newImpl.withAttachment(GenericTupleArity , n)
238
+ }
236
239
typer.typed(untpd.New (newImpl).withSpan(span))
237
240
238
241
/** The mirror type
@@ -279,6 +282,17 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
279
282
private [Synthesizer ] enum MirrorSource :
280
283
case ClassSymbol (cls : Symbol )
281
284
case Singleton (src : Symbol , tref : TermRef )
285
+ case GenericTuple (tps : List [Type ])
286
+
287
+ /** Tests that both sides are tuples of the same arity */
288
+ infix def sameTuple (that : MirrorSource )(using Context ): Boolean =
289
+ def arity (msrc : MirrorSource ): Int = msrc match
290
+ case GenericTuple (tps) => tps.size
291
+ case ClassSymbol (cls) if defn.isTupleClass(cls) => cls.typeParams.size // tested in tests/pos/i13859.scala
292
+ case _ => - 1
293
+ def equivalent (n : Int , m : Int ) =
294
+ n == m && n > 0
295
+ equivalent(arity(this ), arity(that))
282
296
283
297
/** A comparison that chooses the most specific MirrorSource, this is guided by what is necessary for
284
298
* `Mirror.Product.fromProduct`. i.e. its result type should be compatible with the erasure of `mirroredType`.
@@ -289,10 +303,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
289
303
case (ClassSymbol (cls1), ClassSymbol (cls2)) => cls1.isSubClass(cls2)
290
304
case (Singleton (src1, _), Singleton (src2, _)) => src1 eq src2
291
305
case (_ : ClassSymbol , _ : Singleton ) => false
306
+ case _ => this sameTuple that
292
307
293
308
def show (using Context ): String = this match
294
309
case ClassSymbol (cls) => i " $cls"
295
310
case Singleton (src, _) => i " $src"
311
+ case GenericTuple (tps) =>
312
+ val arity = tps.size
313
+ if arity <= Definitions .MaxTupleArity then s " class Tuple $arity"
314
+ else s " trait Tuple { def size: $arity } "
296
315
297
316
private [Synthesizer ] object MirrorSource :
298
317
@@ -332,6 +351,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
332
351
reduce(tp.underlying)
333
352
case tp : HKTypeLambda if tp.resultType.isInstanceOf [HKTypeLambda ] =>
334
353
Left (i " its subpart ` $tp` is not a supported kind (either `*` or `* -> *`) " )
354
+ case tp @ AppliedType (tref : TypeRef , _)
355
+ if tref.symbol == defn.PairClass
356
+ && tp.tupleArity(relaxEmptyTuple = true ) > 0 =>
357
+ // avoid type aliases for tuples
358
+ Right (MirrorSource .GenericTuple (tp.tupleElementTypes))
335
359
case tp : TypeProxy =>
336
360
reduce(tp.underlying)
337
361
case tp @ AndType (l, r) =>
@@ -354,10 +378,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
354
378
355
379
private def productMirror (mirroredType : Type , formal : Type , span : Span )(using Context ): TreeWithErrors =
356
380
357
- def makeProductMirror (cls : Symbol ): TreeWithErrors =
381
+ def makeProductMirror (cls : Symbol , tps : Option [ List [ Type ]] ): TreeWithErrors =
358
382
val accessors = cls.caseAccessors.filterNot(_.isAllOf(PrivateLocal ))
359
383
val elemLabels = accessors.map(acc => ConstantType (Constant (acc.name.toString)))
360
- val nestedPairs = TypeOps .nestedPairs(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
384
+ val nestedPairs =
385
+ val elems = tps.getOrElse(accessors.map(mirroredType.resultType.memberInfo(_).widenExpr))
386
+ TypeOps .nestedPairs(elems)
361
387
val (monoType, elemsType) = mirroredType match
362
388
case mirroredType : HKTypeLambda =>
363
389
(mkMirroredMonoType(mirroredType), mirroredType.derivedLambdaType(resType = nestedPairs))
@@ -372,7 +398,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
372
398
.refinedWith(tpnme.MirroredElemLabels , TypeAlias (elemsLabels))
373
399
val mirrorRef =
374
400
if cls.useCompanionAsProductMirror then companionPath(mirroredType, span)
375
- else anonymousMirror(monoType, ExtendsProductMirror , span)
401
+ else anonymousMirror(monoType, ExtendsProductMirror , tps.map(_.size), span)
376
402
withNoErrors(mirrorRef.cast(mirrorType))
377
403
end makeProductMirror
378
404
@@ -389,8 +415,15 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
389
415
else
390
416
val mirrorType = mirrorCore(defn.Mirror_SingletonClass , mirroredType, mirroredType, singleton.name, formal)
391
417
withNoErrors(singletonPath.cast(mirrorType))
418
+ case MirrorSource .GenericTuple (tps) =>
419
+ val maxArity = Definitions .MaxTupleArity
420
+ val arity = tps.size
421
+ if tps.size <= maxArity then makeProductMirror(defn.TupleType (arity).nn.classSymbol, Some (tps))
422
+ else
423
+ val reason = s " it reduces to tuple with arity $arity, expected arity <= $maxArity"
424
+ withErrors(i " ${defn.PairClass } is not a generic product because $reason" )
392
425
case MirrorSource .ClassSymbol (cls) =>
393
- if cls.isGenericProduct then makeProductMirror(cls)
426
+ if cls.isGenericProduct then makeProductMirror(cls, None )
394
427
else withErrors(i " $cls is not a generic product because ${cls.whyNotGenericProduct}" )
395
428
case Left (msg) =>
396
429
withErrors(i " type ` $mirroredType` is not a generic product because $msg" )
@@ -401,6 +434,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
401
434
val (acceptableMsg, cls) = MirrorSource .reduce(mirroredType) match
402
435
case Right (MirrorSource .Singleton (_, tp)) => (i " its subpart ` $tp` is a term reference " , NoSymbol )
403
436
case Right (MirrorSource .ClassSymbol (cls)) => (" " , cls)
437
+ case Right (MirrorSource .GenericTuple (tps)) =>
438
+ val arity = tps.size
439
+ val cls = if arity <= Definitions .MaxTupleArity then defn.TupleType (arity).nn.classSymbol else defn.PairClass
440
+ (" " , cls)
404
441
case Left (msg) => (msg, NoSymbol )
405
442
406
443
val clsIsGenericSum = cls.isGenericSum
@@ -457,7 +494,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
457
494
.refinedWith(tpnme.MirroredElemLabels , TypeAlias (TypeOps .nestedPairs(elemLabels)))
458
495
val mirrorRef =
459
496
if cls.useCompanionAsSumMirror then companionPath(mirroredType, span)
460
- else anonymousMirror(monoType, ExtendsSumMirror , span)
497
+ else anonymousMirror(monoType, ExtendsSumMirror , None , span)
461
498
withNoErrors(mirrorRef.cast(mirrorType))
462
499
else if acceptableMsg.nonEmpty then
463
500
withErrors(i " type ` $mirroredType` is not a generic sum because $acceptableMsg" )
0 commit comments