-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathDesugar.scala
2442 lines (2245 loc) · 101 KB
/
Desugar.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package dotty.tools
package dotc
package ast
import core.*
import util.Spans.*, Types.*, Contexts.*, Constants.*, Names.*, NameOps.*, Flags.*
import Symbols.*, StdNames.*, Trees.*, ContextOps.*
import Decorators.*
import Annotations.Annotation
import NameKinds.{UniqueName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName}
import typer.{Namer, Checking}
import util.{Property, SourceFile, SourcePosition, SrcPos, Chars}
import config.{Feature, Config}
import config.Feature.{sourceVersion, migrateTo3, enabled, betterForsEnabled}
import config.SourceVersion.*
import collection.mutable
import reporting.*
import annotation.constructorOnly
import printing.Formatting.hl
import config.Printers
import parsing.Parsers
import scala.annotation.internal.sharable
import scala.annotation.threadUnsafe
object desugar {
import untpd.*
import DesugarEnums.*
/** An attachment for companion modules of classes that have a `derives` clause.
* The position value indicates the start position of the template of the
* deriving class.
*/
val DerivingCompanion: Property.Key[SourcePosition] = Property.Key()
/** An attachment for match expressions generated from a PatDef or GenFrom.
* Value of key == one of IrrefutablePatDef, IrrefutableGenFrom
*/
val CheckIrrefutable: Property.Key[MatchCheck] = Property.StickyKey()
/** A multi-line infix operation with the infix operator starting a new line.
* Used for explaining potential errors.
*/
val MultiLineInfix: Property.Key[Unit] = Property.StickyKey()
/** An attachment key to indicate that a ValDef originated from parameter untupling.
*/
val UntupledParam: Property.Key[Unit] = Property.StickyKey()
/** An attachment key to indicate that a ValDef originated from a pattern.
*/
val PatternVar: Property.Key[Unit] = Property.StickyKey()
/** An attachment key for Trees originating in for-comprehension, such as tupling of assignments.
*/
val ForArtifact: Property.Key[Unit] = Property.StickyKey()
/** An attachment key to indicate that a ValDef is an evidence parameter
* for a context bound.
*/
val ContextBoundParam: Property.Key[Unit] = Property.StickyKey()
/** Marks a poly fcuntion apply method, so that we can handle adding evidence parameters to them in a special way
*/
val PolyFunctionApply: Property.Key[Unit] = Property.StickyKey()
/** What static check should be applied to a Match? */
enum MatchCheck {
case None, Exhaustive, IrrefutablePatDef, IrrefutableGenFrom
}
/** Is `name` the name of a method that can be invalidated as a compiler-generated
* case class method if it clashes with a user-defined method?
*/
def isRetractableCaseClassMethodName(name: Name)(using Context): Boolean = name match {
case nme.apply | nme.unapply | nme.unapplySeq | nme.copy => true
case DefaultGetterName(nme.copy, _) => true
case _ => false
}
/** Is `name` the name of a method that is added unconditionally to case classes? */
def isDesugaredCaseClassMethodName(name: Name)(using Context): Boolean =
isRetractableCaseClassMethodName(name) || name.isSelectorName
// ----- DerivedTypeTrees -----------------------------------
class SetterParamTree(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
def derivedTree(sym: Symbol)(using Context): tpd.TypeTree = tpd.TypeTree(sym.info.resultType)
}
class TypeRefTree(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
def derivedTree(sym: Symbol)(using Context): tpd.TypeTree = tpd.TypeTree(sym.typeRef)
}
class TermRefTree(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
def derivedTree(sym: Symbol)(using Context): tpd.Tree = tpd.ref(sym)
}
/** A type tree that computes its type from an existing parameter. */
class DerivedFromParamTree()(implicit @constructorOnly src: SourceFile) extends DerivedTypeTree {
/** Complete the appropriate constructors so that OriginalSymbol attachments are
* pushed to DerivedTypeTrees.
*/
override def ensureCompletions(using Context): Unit = {
def completeConstructor(sym: Symbol) =
sym.infoOrCompleter match {
case completer: Namer#ClassCompleter if !sym.isCompleting =>
// An example, derived from tests/run/t6385.scala
//
// class Test():
// def t1: Foo = Foo(1)
// final case class Foo(value: Int)
//
// Here's the sequence of events:
// * The symbol for Foo.apply is forced to complete
// * The symbol for the `value` parameter of the apply method is forced to complete
// * Completing that value parameter requires typing its type, which is a DerivedTypeTrees,
// which only types if it has an OriginalSymbol.
// * So if the case class hasn't been completed, we need (at least) its constructor to be completed
//
// Test tests/neg/i9294.scala is an example of why isCompleting is necessary.
// Annotations are added while completing the constructor,
// so the back reference to foo reaches here which re-initiates the constructor completion.
// So we just skip, as completion is already being triggered.
completer.completeConstructor(sym)
case _ =>
}
if (!ctx.owner.is(Package))
if (ctx.owner.isClass) {
completeConstructor(ctx.owner)
if (ctx.owner.is(ModuleClass))
completeConstructor(ctx.owner.linkedClass)
}
else ensureCompletions(using ctx.outer)
}
/** Return info of original symbol, where all references to siblings of the
* original symbol (i.e. sibling and original symbol have the same owner)
* are rewired to same-named parameters or accessors in the scope enclosing
* the current scope. The current scope is the scope owned by the defined symbol
* itself, that's why we have to look one scope further out. If the resulting
* type is an alias type, dealias it. This is necessary because the
* accessor of a type parameter is a private type alias that cannot be accessed
* from subclasses.
*/
def derivedTree(sym: Symbol)(using Context): tpd.TypeTree = {
val relocate = new TypeMap {
val originalOwner = sym.owner
def apply(tp: Type) = tp match {
case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
val defctx = mapCtx.outersIterator.dropWhile(_.scope eq mapCtx.scope).next()
var local = defctx.denotNamed(tp.name).suchThat(_.isParamOrAccessor).symbol
if (local.exists) (defctx.owner.thisType select local).dealiasKeepAnnots
else {
def msg =
em"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}"
ErrorType(msg).assertingErrorsReported(msg)
}
case _ =>
mapOver(tp)
}
}
tpd.TypeTree(relocate(sym.info))
}
}
/** A type definition copied from `tdef` with a rhs typetree derived from it */
def derivedTypeParam(tdef: TypeDef)(using Context): TypeDef =
cpy.TypeDef(tdef)(
rhs = DerivedFromParamTree().withSpan(tdef.rhs.span).watching(tdef)
)
/** A derived type definition watching `sym` */
def derivedTypeParamWithVariance(sym: TypeSymbol)(using Context): TypeDef =
val variance = VarianceFlags & sym.flags
TypeDef(sym.name, DerivedFromParamTree().watching(sym)).withFlags(TypeParam | Synthetic | variance)
/** A value definition copied from `vdef` with a tpt typetree derived from it */
def derivedTermParam(vdef: ValDef)(using Context): ValDef =
derivedTermParam(vdef, vdef.unforcedRhs)
def derivedTermParam(vdef: ValDef, rhs: LazyTree)(using Context): ValDef =
cpy.ValDef(vdef)(
tpt = DerivedFromParamTree().withSpan(vdef.tpt.span).watching(vdef),
rhs = rhs
)
// ----- Desugar methods -------------------------------------------------
/** Setter generation is needed for:
* - non-private class members
* - all trait members
* - all package object members
*/
def isSetterNeeded(valDef: ValDef)(using Context): Boolean = {
val mods = valDef.mods
mods.is(Mutable)
&& ctx.owner.isClass
&& (!mods.is(Private) || ctx.owner.is(Trait) || ctx.owner.isPackageObject)
}
/** var x: Int = expr
* ==>
* def x: Int = expr
* def x_=($1: <TypeTree()>): Unit = ()
*
* Generate setter where needed
*/
def valDef(vdef0: ValDef)(using Context): Tree =
val vdef @ ValDef(_, tpt, rhs) = vdef0
val valName = normalizeName(vdef, tpt).asTermName
var mods1 = vdef.mods
val vdef1 = cpy.ValDef(vdef)(name = valName).withMods(mods1)
if isSetterNeeded(vdef) then
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
// The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase)
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else syntheticUnitLiteral
val setter = cpy.DefDef(vdef)(
name = valName.setterName,
paramss = (setterParam :: Nil) :: Nil,
tpt = TypeTree(defn.UnitType),
rhs = setterRhs
).withMods((vdef.mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
.dropEndMarker() // the end marker should only appear on the getter definition
Thicket(vdef1, setter)
else vdef1
end valDef
def mapParamss(paramss: List[ParamClause])
(mapTypeParam: TypeDef => TypeDef)
(mapTermParam: ValDef => ValDef)(using Context): List[ParamClause] =
paramss.mapConserve {
case TypeDefs(tparams) => tparams.mapConserve(mapTypeParam)
case ValDefs(vparams) => vparams.mapConserve(mapTermParam)
case _ => unreachable()
}
/** 1. Expand context bounds to evidence params. E.g.,
*
* def f[T >: L <: H : B](params)
* ==>
* def f[T >: L <: H](params)(implicit evidence$0: B[T])
*
* 2. Expand default arguments to default getters. E.g,
*
* def f[T: B](x: Int = 1)(y: String = x + "m") = ...
* ==>
* def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ...
* def f$default$1[T] = 1
* def f$default$2[T](x: Int) = x + "m"
*/
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree =
addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor).asInstanceOf[DefDef])
/** Drop context bounds in given TypeDef, replacing them with evidence ValDefs that
* get added to a buffer.
* @param tdef The given TypeDef
* @param evidenceBuf The buffer to which evidence gets added. This buffer
* is shared between desugarings of different type parameters
* of the same method.
* @param evidenceFlags The flags to use for evidence definitions
* @param freshName A function to generate fresh names for evidence definitions
* @param allParamss If `tdef` is a type paramter, all parameters of the owning method,
* otherwise the empty list.
*/
private def desugarContextBounds(
tdef: TypeDef,
evidenceBuf: mutable.ListBuffer[ValDef],
evidenceFlags: FlagSet,
freshName: untpd.Tree => TermName,
allParamss: List[ParamClause])(using Context): TypeDef =
val evidenceNames = mutable.ListBuffer[TermName]()
def desugarRHS(rhs: Tree): Tree = rhs match
case ContextBounds(tbounds, ctxbounds) =>
val isMember = evidenceFlags.isAllOf(DeferredGivenFlags)
for bound <- ctxbounds do
val (evidenceName, spanPoint) = bound match
case ContextBoundTypeTree(_, _, ownName) if !ownName.isEmpty =>
val realName = ownName.stripModuleClassSuffix.lastPart
(ownName, bound.span.end - realName.length) // if there is an explicitly given name, use it.
case _ =>
if Config.nameSingleContextBounds
&& !isMember
&& ctxbounds.tail.isEmpty
&& Feature.enabled(Feature.modularity)
then (tdef.name.toTermName, bound.span.point)
else (freshName(bound), bound.span.point)
evidenceNames += evidenceName
val evidenceParam =
ValDef(evidenceName, bound, EmptyTree).withFlags(evidenceFlags).withSpan(bound.span.withPoint(spanPoint))
evidenceParam.pushAttachment(ContextBoundParam, ())
evidenceBuf += evidenceParam
tbounds
case LambdaTypeTree(tparams, body) =>
cpy.LambdaTypeTree(rhs)(tparams, desugarRHS(body))
case _ =>
rhs
val tdef1 = cpy.TypeDef(tdef)(rhs = desugarRHS(tdef.rhs))
// Under x.modularity, if there was a context bound, and `tdef`s name as a term name is
// neither a name of an existing parameter nor a name of generated evidence for
// the same method, add a WitnessAnnotation with all generated evidence names to `tdef`.
// This means a context bound proxy will be created later.
if Feature.enabled(Feature.modularity)
&& evidenceNames.nonEmpty
&& !evidenceBuf.exists(_.name == tdef.name.toTermName)
&& !allParamss.nestedExists(_.name == tdef.name.toTermName)
then
tdef1.withAddedAnnotation:
WitnessNamesAnnot(evidenceNames.toList).withSpan(tdef.span)
else
tdef1
end desugarContextBounds
def elimContextBounds(meth: Tree, isPrimaryConstructor: Boolean = false)(using Context): Tree =
val evidenceParamBuf = mutable.ListBuffer[ValDef]()
var seenContextBounds: Int = 0
def freshName(unused: Tree) =
seenContextBounds += 1 // Start at 1 like FreshNameCreator.
ContextBoundParamName(EmptyTermName, seenContextBounds)
// Just like with `makeSyntheticParameter` on nameless parameters of
// using clauses, we only need names that are unique among the
// parameters of the method since shadowing does not affect
// implicit resolution in Scala 3.
def paramssNoContextBounds(paramss: List[ParamClause]): List[ParamClause] =
val iflag = paramss.lastOption.flatMap(_.headOption) match
case Some(param) if param.mods.isOneOf(GivenOrImplicit) =>
param.mods.flags & GivenOrImplicit
case _ =>
if Feature.sourceVersion.isAtLeast(`3.6`) then Given
else Implicit
val flags = if isPrimaryConstructor then iflag | LocalParamAccessor else iflag | Param
mapParamss(paramss) {
tparam => desugarContextBounds(tparam, evidenceParamBuf, flags, freshName, paramss)
}(identity)
meth match
case meth @ DefDef(_, paramss, tpt, rhs) =>
val newParamss = paramssNoContextBounds(paramss)
rhs match
case MacroTree(call) =>
cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased)
case _ =>
addEvidenceParams(
cpy.DefDef(meth)(
name = normalizeName(meth, tpt).asTermName,
paramss = newParamss
),
evidenceParamBuf.toList
)
case meth @ PolyFunction(tparams, fun) =>
val PolyFunction(tparams: List[untpd.TypeDef] @unchecked, fun) = meth: @unchecked
val Function(vparams: List[untpd.ValDef] @unchecked, rhs) = fun: @unchecked
val newParamss = paramssNoContextBounds(tparams :: vparams :: Nil)
val params = evidenceParamBuf.toList
if params.isEmpty then
meth
else
val boundNames = getBoundNames(params, newParamss)
val recur = fitEvidenceParams(params, nme.apply, boundNames)
val (paramsFst, paramsSnd) = recur(newParamss)
functionsOf((paramsFst ++ paramsSnd).filter(_.nonEmpty), rhs)
end elimContextBounds
def addDefaultGetters(meth: DefDef)(using Context): Tree =
/** The longest prefix of parameter lists in paramss whose total number of
* ValDefs does not exceed `n`
*/
def takeUpTo(paramss: List[ParamClause], n: Int): List[ParamClause] = paramss match
case ValDefs(vparams) :: paramss1 =>
val len = vparams.length
if len <= n then vparams :: takeUpTo(paramss1, n - len) else Nil
case TypeDefs(tparams) :: paramss1 =>
tparams :: takeUpTo(paramss1, n)
case _ =>
Nil
def dropContextBounds(tparam: TypeDef): TypeDef =
def dropInRhs(rhs: Tree): Tree = rhs match
case ContextBounds(tbounds, _) =>
tbounds
case rhs @ LambdaTypeTree(tparams, body) =>
cpy.LambdaTypeTree(rhs)(tparams, dropInRhs(body))
case _ =>
rhs
cpy.TypeDef(tparam)(rhs = dropInRhs(tparam.rhs))
def paramssNoRHS = mapParamss(meth.paramss)(identity) {
vparam =>
if vparam.rhs.isEmpty then vparam
else cpy.ValDef(vparam)(rhs = EmptyTree).withMods(vparam.mods | HasDefault)
}
def getterParamss(n: Int): List[ParamClause] =
mapParamss(takeUpTo(paramssNoRHS, n)) {
tparam => dropContextBounds(toMethParam(tparam, KeepAnnotations.All))
} {
vparam => toMethParam(vparam, KeepAnnotations.All, keepDefault = false)
}
def defaultGetters(paramss: List[ParamClause], n: Int): List[DefDef] = paramss match
case ValDefs(vparam :: vparams) :: paramss1 =>
def defaultGetter: DefDef =
DefDef(
name = DefaultGetterName(meth.name, n),
paramss = getterParamss(n),
tpt = TypeTree(),
rhs = vparam.rhs
)
.withMods(Modifiers(
meth.mods.flags & (AccessFlags | Synthetic) | (vparam.mods.flags & Inline),
meth.mods.privateWithin))
val rest = defaultGetters(vparams :: paramss1, n + 1)
if vparam.rhs.isEmpty then rest else defaultGetter :: rest
case _ :: paramss1 => // skip empty parameter lists and type parameters
defaultGetters(paramss1, n)
case Nil =>
Nil
val defGetters = defaultGetters(meth.paramss, 0)
if defGetters.isEmpty then meth
else Thicket(cpy.DefDef(meth)(paramss = paramssNoRHS) :: defGetters)
end addDefaultGetters
/** Add an explicit ascription to the `expectedTpt` to every tail splice.
*
* - `'{ x }` -> `'{ x }`
* - `'{ $x }` -> `'{ $x: T }`
* - `'{ if (...) $x else $y }` -> `'{ if (...) ($x: T) else ($y: T) }`
*
* Note that the splice `$t: T` will be typed as `${t: Expr[T]}`
*/
def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = {
def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match {
// Add the expected type as an ascription
case _: untpd.SplicePattern =>
untpd.Typed(tree, expectedTpt).withSpan(tree.span)
case Typed(expr: untpd.SplicePattern, tpt) =>
cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span))
// Propagate down the expected type to the leafs of the expression
case Block(stats, expr) =>
cpy.Block(tree)(stats, adaptToExpectedTpt(expr))
case If(cond, thenp, elsep) =>
cpy.If(tree)(cond, adaptToExpectedTpt(thenp), adaptToExpectedTpt(elsep))
case untpd.Parens(expr) =>
cpy.Parens(tree)(adaptToExpectedTpt(expr))
case Match(selector, cases) =>
val newCases = cases.map(cdef => cpy.CaseDef(cdef)(body = adaptToExpectedTpt(cdef.body)))
cpy.Match(tree)(selector, newCases)
case untpd.ParsedTry(expr, handler, finalizer) =>
cpy.ParsedTry(tree)(adaptToExpectedTpt(expr), adaptToExpectedTpt(handler), finalizer)
// Tree does not need to be ascribed
case _ =>
tree
}
adaptToExpectedTpt(tree)
}
/** Split out the quoted pattern type variable definition from the pattern.
*
* Type variable definitions are all the `type t` defined at the start of a quoted pattern.
* Where name `t` is a pattern type variable name (i.e. lower case letters).
*
* ```
* type t1; ...; type tn; <pattern>
* ```
* is split into
* ```
* (List(<type t1>; ...; <type tn>), <pattern>)
* ```
*/
def quotedPatternTypeVariables(tree: untpd.Tree)(using Context): (List[untpd.TypeDef], untpd.Tree) =
tree match
case untpd.Block(stats, expr) =>
val (untpdTypeVariables, otherStats) = stats.span {
case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern
case _ => false
}
val untpdCaseTypeVariables = untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]].map {
tdef => tdef.withMods(tdef.mods | Case)
}
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
(untpdCaseTypeVariables, pattern)
case _ =>
(Nil, tree)
private def referencesName(vdef: ValDef, names: Set[TermName])(using Context): Boolean =
vdef.tpt.existsSubTree:
case Ident(name: TermName) => names.contains(name)
case _ => false
/** Fit evidence `params` into the `mparamss` parameter lists, making sure
* that all parameters referencing `params` are after them.
* - for methods the final parameter lists are := result._1 ++ result._2
* - for poly functions, each element of the pair contains at most one term
* parameter list
*
* @param params the evidence parameters list that should fit into `mparamss`
* @param methName the name of the method that `mparamss` belongs to
* @param boundNames the names of the evidence parameters
* @param mparamss the original parameter lists of the method
* @return a pair of parameter lists containing all parameter lists in a
* reference-correct order; make sure that `params` is always at the
* intersection of the pair elements; this is relevant, for poly functions
* where `mparamss` is guaranteed to have exectly one term parameter list,
* then each pair element will have at most one term parameter list
*/
private def fitEvidenceParams(
params: List[ValDef],
methName: Name,
boundNames: Set[TermName]
)(mparamss: List[ParamClause])(using Context): (List[ParamClause], List[ParamClause]) = mparamss match
case ValDefs(mparams) :: _ if mparams.exists(referencesName(_, boundNames)) =>
(params :: Nil) -> mparamss
case ValDefs(mparams @ (mparam :: _)) :: Nil if mparam.mods.isOneOf(GivenOrImplicit) =>
val normParams =
if params.head.mods.flags.is(Given) != mparam.mods.flags.is(Given) then
params.map: param =>
val normFlags = param.mods.flags &~ GivenOrImplicit | (mparam.mods.flags & (GivenOrImplicit))
param.withMods(param.mods.withFlags(normFlags))
.showing(i"adapted param $result ${result.mods.flags} for ${methName}", Printers.desugar)
else params
((normParams ++ mparams) :: Nil) -> Nil
case mparams :: mparamss1 =>
val (fst, snd) = fitEvidenceParams(params, methName, boundNames)(mparamss1)
(mparams :: fst) -> snd
case Nil =>
Nil -> (params :: Nil)
/** Create a chain of possibly contextual functions from the parameter lists */
private def functionsOf(paramss: List[ParamClause], rhs: Tree)(using Context): Tree = paramss match
case Nil => rhs
case ValDefs(head @ (fst :: _)) :: rest if fst.mods.isOneOf(GivenOrImplicit) =>
val paramTpts = head.map(_.tpt)
val paramNames = head.map(_.name)
val paramsErased = head.map(_.mods.flags.is(Erased))
makeContextualFunction(paramTpts, paramNames, functionsOf(rest, rhs), paramsErased).withSpan(rhs.span)
case ValDefs(head) :: rest =>
Function(head, functionsOf(rest, rhs))
case TypeDefs(head) :: rest =>
PolyFunction(head, functionsOf(rest, rhs))
case _ =>
assert(false, i"unexpected paramss $paramss")
EmptyTree
private def getBoundNames(params: List[ValDef], paramss: List[ParamClause])(using Context): Set[TermName] =
var boundNames = params.map(_.name).toSet // all evidence parameter + context bound proxy names
for mparams <- paramss; mparam <- mparams do
mparam match
case tparam: TypeDef if tparam.mods.annotations.exists(WitnessNamesAnnot.unapply(_).isDefined) =>
boundNames += tparam.name.toTermName
case _ =>
boundNames
/** Add all evidence parameters in `params` as implicit parameters to `meth`.
* The position of the added parameters is determined as follows:
*
* - If there is an existing parameter list that refers to one of the added
* parameters or their future context bound proxies in one of its parameter
* types, add the new parameters in front of the first such parameter list.
* - Otherwise, if the last parameter list consists of implicit or using parameters,
* join the new parameters in front of this parameter list, creating one
* parameter list (this is equivalent to Scala 2's scheme).
* - Otherwise, add the new parameter list at the end as a separate parameter clause.
*/
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef =
if params.isEmpty then return meth
val boundNames = getBoundNames(params, meth.paramss)
val fitParams = fitEvidenceParams(params, meth.name, boundNames)
if meth.removeAttachment(PolyFunctionApply).isDefined then
// for PolyFunctions we are limited to a single term param list, so we
// reuse the fitEvidenceParams logic to compute the new parameter lists
// and then we add the other parameter lists as function types to the
// return type
val (paramsFst, paramsSnd) = fitParams(meth.paramss)
if ctx.mode.is(Mode.Type) then
cpy.DefDef(meth)(paramss = paramsFst, tpt = functionsOf(paramsSnd, meth.tpt))
else
cpy.DefDef(meth)(paramss = paramsFst, rhs = functionsOf(paramsSnd, meth.rhs))
else
val (paramsFst, paramsSnd) = fitParams(meth.paramss)
cpy.DefDef(meth)(paramss = paramsFst ++ paramsSnd)
end addEvidenceParams
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
for
case ValDefs(vparams @ (vparam :: _)) <- meth.paramss
if vparam.mods.isOneOf(GivenOrImplicit)
param <- vparams.takeWhile(_.hasAttachment(ContextBoundParam))
yield
param
@sharable private val synthetic = Modifiers(Synthetic)
/** Which annotations to keep in derived parameters */
private enum KeepAnnotations:
case None, All, WitnessOnly
/** Filter annotations in `mods` according to `keep` */
private def filterAnnots(mods: Modifiers, keep: KeepAnnotations)(using Context) = keep match
case KeepAnnotations.None => mods.withAnnotations(Nil)
case KeepAnnotations.All => mods
case KeepAnnotations.WitnessOnly =>
mods.withAnnotations:
mods.annotations.filter:
case WitnessNamesAnnot(_) => true
case _ => false
/** Map type parameter accessor to corresponding method (i.e. constructor) parameter */
private def toMethParam(tparam: TypeDef, keep: KeepAnnotations)(using Context): TypeDef =
val mods = filterAnnots(tparam.rawMods, keep)
tparam.withMods(mods & EmptyFlags | Param)
/** Map term parameter accessor to corresponding method (i.e. constructor) parameter */
private def toMethParam(vparam: ValDef, keep: KeepAnnotations, keepDefault: Boolean)(using Context): ValDef = {
val mods = filterAnnots(vparam.rawMods, keep)
val hasDefault = if keepDefault then HasDefault else EmptyFlags
// Need to ensure that tree is duplicated since term parameters can be watched
// and cloning a term parameter will copy its watchers to the clone, which means
// we'd get cross-talk between the original parameter and the clone.
ValDef(vparam.name, vparam.tpt, vparam.rhs)
.withSpan(vparam.span)
.withAttachmentsFrom(vparam)
.withMods(mods & (GivenOrImplicit | Erased | hasDefault | Tracked) | Param)
}
/** Desugar type def (not param): Under x.moduliity this can expand
* context bounds, which are expanded to evidence ValDefs. These will
* ultimately map to deferred givens.
*/
def typeDef(tdef: TypeDef)(using Context): Tree =
val evidenceBuf = new mutable.ListBuffer[ValDef]
val result = desugarContextBounds(
tdef, evidenceBuf,
(tdef.mods.flags.toTermFlags & AccessFlags) | Lazy | DeferredGivenFlags,
inventGivenName, Nil)
if evidenceBuf.isEmpty then result else Thicket(result :: evidenceBuf.toList)
/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(using Context): Tree = {
val impl @ Template(constr0, _, self, _) = cdef.rhs: @unchecked
val className = normalizeName(cdef, impl).asTypeName
val parents = impl.parents
val mods = cdef.mods
val companionMods = mods
.withFlags((mods.flags & (AccessFlags | Final)).toCommonFlags)
.withMods(Nil)
.withAnnotations(Nil)
var defaultGetters: List[Tree] = Nil
def decompose(ddef: Tree): DefDef = ddef match {
case meth: DefDef => meth
case Thicket((meth: DefDef) :: defaults) =>
defaultGetters = defaults
meth
}
val constr1 = decompose(defDef(impl.constr, isPrimaryConstructor = true))
// The original type and value parameters in the constructor already have the flags
// needed to be type members (i.e. param, and possibly also private and local unless
// prefixed by type or val). `tparams` and `vparamss` are the type parameters that
// go in `constr`, the constructor after desugaring.
/** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
def isAnyVal(tree: Tree): Boolean = tree match {
case Ident(tpnme.AnyVal) => true
case Select(qual, tpnme.AnyVal) => isScala(qual)
case _ => false
}
def isScala(tree: Tree): Boolean = tree match {
case Ident(nme.scala) => true
case Select(Ident(nme.ROOTPKG), nme.scala) => true
case _ => false
}
def namePos = cdef.sourcePos.withSpan(cdef.nameSpan)
val isObject = mods.is(Module)
val isCaseClass = mods.is(Case) && !isObject
val isCaseObject = mods.is(Case) && isObject
val isEnum = mods.isEnumClass && !mods.is(Module)
def isEnumCase = mods.isEnumCase
def isNonEnumCase = !isEnumCase && (isCaseClass || isCaseObject)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
val caseClassInScala2Library = isCaseClass && ctx.settings.YcompileScala2Library.value
val originalTparams = constr1.leadingTypeParams
val originalVparamss = asTermOnly(constr1.trailingParamss)
lazy val derivedEnumParams = enumClass.typeParams.map(derivedTypeParamWithVariance)
val enumTParams =
if isEnumCase then
val tparamReferenced = typeParamIsReferenced(
enumClass.typeParams, originalTparams, originalVparamss, parents)
if originalTparams.isEmpty && (parents.isEmpty || tparamReferenced) then
derivedEnumParams.map(tdef => tdef.withFlags(tdef.mods.flags | PrivateLocal))
else Nil
else Nil
val impliedTparams = enumTParams ++ originalTparams
if mods.is(Trait) then
for vparams <- originalVparamss; vparam <- vparams do
if isByNameType(vparam.tpt) then
report.error(em"implementation restriction: traits cannot have by name parameters", vparam.srcPos)
// Annotations on class _type_ parameters are set on the derived parameters
// but not on the constructor parameters. The reverse is true for
// annotations on class _value_ parameters.
val constrTparams = impliedTparams.map(toMethParam(_, KeepAnnotations.WitnessOnly))
val constrVparamss =
if (originalVparamss.isEmpty) { // ensure parameter list is non-empty
if (isCaseClass)
report.error(CaseClassMissingParamList(cdef), namePos)
ListOfNil
}
else if (isCaseClass && originalVparamss.head.exists(_.mods.isOneOf(GivenOrImplicit))) {
report.error(CaseClassMissingNonImplicitParamList(cdef), namePos)
ListOfNil
}
else originalVparamss.nestedMap(toMethParam(_, KeepAnnotations.All, keepDefault = true))
val derivedTparams =
constrTparams.zipWithConserve(impliedTparams)((tparam, impliedParam) =>
derivedTypeParam(tparam).withAnnotations(impliedParam.mods.annotations))
val derivedVparamss =
constrVparamss.nestedMap: vparam =>
val derived =
if ctx.compilationUnit.isJava then derivedTermParam(vparam, Parsers.unimplementedExpr)
else derivedTermParam(vparam)
derived.withAnnotations(Nil)
val constr = cpy.DefDef(constr1)(paramss = joinParams(constrTparams, constrVparamss))
if enumTParams.nonEmpty then
defaultGetters = defaultGetters.map:
case ddef: DefDef =>
val tParams = enumTParams.map(tparam => toMethParam(tparam, KeepAnnotations.All))
cpy.DefDef(ddef)(paramss = joinParams(tParams, ddef.trailingParamss))
val (normalizedBody, enumCases, enumCompanionRef) = {
// Add constructor type parameters and evidence implicit parameters
// to auxiliary constructors; set defaultGetters as a side effect.
def expandConstructor(tree: Tree) = tree match {
case ddef: DefDef if ddef.name.isConstructorName =>
decompose(
defDef(
addEvidenceParams(
cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)),
evidenceParams(constr1).map(toMethParam(_, KeepAnnotations.None, keepDefault = false)))))
case stat =>
stat
}
// The Identifiers defined by a case
def caseIds(tree: Tree): List[Ident] = tree match {
case tree: MemberDef => Ident(tree.name.toTermName) :: Nil
case PatDef(_, ids: List[Ident] @ unchecked, _, _) => ids
}
val stats0 = impl.body.map(expandConstructor)
val stats =
if (ctx.owner eq defn.ScalaPackageClass) && defn.hasProblematicGetClass(className) then
stats0.filterConserve {
case ddef: DefDef =>
ddef.name ne nme.getClass_
case _ =>
true
}
else
stats0
if (isEnum) {
val (enumCases, enumStats) = stats.partition(DesugarEnums.isEnumCase)
if (enumCases.isEmpty)
report.error(EnumerationsShouldNotBeEmpty(cdef), namePos)
else
enumCases.last.pushAttachment(DesugarEnums.DefinesEnumLookupMethods, ())
val enumCompanionRef = TermRefTree()
val enumImport =
Import(enumCompanionRef, enumCases.flatMap(caseIds).map(
enumCase =>
ImportSelector(enumCase.withSpan(enumCase.span.startPos))
)
)
(enumImport :: enumStats, enumCases, enumCompanionRef)
}
else (stats, Nil, EmptyTree)
}
def anyRef = ref(defn.AnyRefAlias.typeRef)
val arity = constrVparamss.head.length
val classTycon: Tree = TypeRefTree() // watching is set at end of method
def appliedTypeTree(tycon: Tree, args: List[Tree]) =
(if (args.isEmpty) tycon else AppliedTypeTree(tycon, args))
.withSpan(cdef.span.startPos)
def isHK(tparam: Tree): Boolean = tparam match {
case TypeDef(_, LambdaTypeTree(tparams, body)) => true
case TypeDef(_, rhs: DerivedTypeTree) => isHK(rhs.watched)
case _ => false
}
/** Is this a repeated argument x* (using a spread operator)? */
def isRepeated(tree: Tree): Boolean = stripByNameType(tree) match
case PostfixOp(_, Ident(tpnme.raw.STAR)) => true
case _ => false
def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams, widenHK: Boolean = false) = {
val targs = for (tparam <- tparams) yield {
val targ = refOfDef(tparam)
def fullyApplied(tparam: Tree): Tree = tparam match {
case TypeDef(_, LambdaTypeTree(tparams, body)) =>
AppliedTypeTree(targ, tparams.map(_ => WildcardTypeBoundsTree()))
case TypeDef(_, rhs: DerivedTypeTree) =>
fullyApplied(rhs.watched)
case _ =>
targ
}
if (widenHK) fullyApplied(tparam) else targ
}
appliedTypeTree(tycon, targs)
}
// a reference to the class type bound by `cdef`, with type parameters coming from the constructor
val classTypeRef = appliedRef(classTycon)
// a reference to `enumClass`, with type parameters coming from the case constructor
lazy val enumClassTypeRef =
if (enumClass.typeParams.isEmpty)
enumClassRef
else if (originalTparams.isEmpty)
appliedRef(enumClassRef)
else {
report.error(TypedCaseDoesNotExplicitlyExtendTypedEnum(enumClass, cdef)
, cdef.srcPos.startPos)
appliedTypeTree(enumClassRef, constrTparams map (_ => anyRef))
}
// new C[Ts](paramss)
lazy val creatorExpr =
val vparamss = constrVparamss match
case (vparam :: _) :: _ if vparam.mods.is(Implicit) => // add a leading () to match class parameters
Nil :: constrVparamss
case _ =>
if constrVparamss.nonEmpty && constrVparamss.forall {
case vparam :: _ => vparam.mods.is(Given)
case _ => false
}
then constrVparamss :+ Nil // add a trailing () to match class parameters
else constrVparamss
val nu = vparamss.foldLeft(makeNew(classTypeRef)) { (nu, vparams) =>
val app = Apply(nu, vparams.map(refOfDef))
vparams match {
case vparam :: _ if vparam.mods.isOneOf(GivenOrImplicit) || vparam.name.is(ContextBoundParamName) =>
app.setApplyKind(ApplyKind.Using)
case _ => app
}
}
ensureApplied(nu)
val copiedAccessFlags = if Feature.migrateTo3 then EmptyFlags else AccessFlags
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
// def _1: T1 = this.p1
// ...
// def _N: TN = this.pN (unless already given as valdef or parameterless defdef)
// def copy(p1: T1 = p1..., pN: TN = pN)(moreParams) =
// new C[...](p1, ..., pN)(moreParams)
val (caseClassMeths, enumScaffolding) = {
def syntheticProperty(name: TermName, tpt: Tree, rhs: Tree) =
DefDef(name, Nil, tpt, rhs).withMods(synthetic)
def productElemMeths =
if caseClassInScala2Library then Nil
else
val caseParams = derivedVparamss.head.toArray
val selectorNamesInBody = normalizedBody.collect {
case vdef: ValDef if vdef.name.isSelectorName =>
vdef.name
case ddef: DefDef if ddef.name.isSelectorName && ddef.paramss.isEmpty =>
ddef.name
}
for i <- List.range(0, arity)
selName = nme.selectorName(i)
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
yield syntheticProperty(selName, caseParams(i).tpt,
Select(This(EmptyTypeIdent), caseParams(i).name))
def enumCaseMeths =
if isEnumCase then
val (ordinal, scaffolding) = nextOrdinal(className, CaseKind.Class, definesEnumLookupMethods(cdef))
(ordinalMethLit(ordinal) :: Nil, scaffolding)
else (Nil, Nil)
def copyMeths = {
val hasRepeatedParam = constrVparamss.nestedExists {
case ValDef(_, tpt, _) => isRepeated(tpt)
}
if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
else {
val copyFirstParams = derivedVparamss.head.map(vparam =>
cpy.ValDef(vparam)(rhs = refOfDef(vparam)))
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
cpy.ValDef(vparam)(rhs = EmptyTree))
var flags = Synthetic | constr1.mods.flags & copiedAccessFlags
if ctx.settings.YcompileScala2Library.value then flags &~= Private
DefDef(
nme.copy,
joinParams(derivedTparams, copyFirstParams :: copyRestParamss),
TypeTree(),
creatorExpr
).withMods(Modifiers(flags, constr1.mods.privateWithin)) :: Nil
}
}
if isCaseClass then
val (enumMeths, enumScaffolding) = enumCaseMeths
(copyMeths ::: enumMeths ::: productElemMeths, enumScaffolding)
else (Nil, Nil)
}
var parents1 = parents
if (isEnumCase && parents.isEmpty)
parents1 = enumClassTypeRef :: Nil
if (isNonEnumCase)
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
if (isEnum)
parents1 = parents1 :+ ref(defn.EnumClass)
// derived type classes of non-module classes go to their companions
val (clsDerived, companionDerived) =
if (mods.is(Module)) (impl.derived, Nil) else (Nil, impl.derived)
// The thicket which is the desugared version of the companion object
// synthetic object C extends parentTpt derives class-derived { defs }
def companionDefs(parentTpt: Tree, defs: List[Tree]) = {
val mdefs = moduleDef(
ModuleDef(
className.toTermName, Template(emptyConstructor, parentTpt :: Nil, companionDerived, EmptyValDef, defs))
.withMods(companionMods | Synthetic))
.withSpan(cdef.span).toList
if (companionDerived.nonEmpty)
for (case modClsDef @ TypeDef(_, _) <- mdefs)
modClsDef.putAttachment(DerivingCompanion, impl.srcPos.startPos)
mdefs
}
val companionMembers = defaultGetters ::: enumCases
// The companion object definitions, if a companion is needed, Nil otherwise.
// companion definitions include:
// 1. If class is a case class case class C[Ts](p1: T1, ..., pN: TN)(moreParams):
// def apply[Ts](p1: T1, ..., pN: TN)(moreParams) = new C[Ts](p1, ..., pN)(moreParams) (unless C is abstract)
// def unapply[Ts]($1: C[Ts]) = $1 // if not repeated
// def unapplySeq[Ts]($1: C[Ts]) = $1 // if repeated
// 2. The default getters of the constructor
// The parent of the companion object of a non-parameterized case class
// (T11, ..., T1N) => ... => (TM1, ..., TMN) => C
// For all other classes, the parent is AnyRef.
val companions =
if (isCaseClass) {
val applyMeths =
if (mods.is(Abstract)) Nil
else {
val appMods =
var flags = Synthetic | constr1.mods.flags & copiedAccessFlags
if ctx.settings.YcompileScala2Library.value then flags &~= Private
Modifiers(flags).withPrivateWithin(constr1.mods.privateWithin)
val appParamss =
derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) =>
ap.withMods(ap.mods | (cp.mods.flags & HasDefault)))
DefDef(nme.apply, joinParams(derivedTparams, appParamss), TypeTree(), creatorExpr)
.withMods(appMods) :: Nil
}
val unapplyMeth = {
def scala2LibCompatUnapplyRhs(unapplyParamName: Name) =
assert(arity <= Definitions.MaxTupleArity, "Unexpected case class with tuple larger than 22: "+ cdef.show)
derivedVparamss.head match
case vparam :: Nil =>
Apply(scalaDot(nme.Option), Select(Ident(unapplyParamName), vparam.name))
case vparams =>
val tupleApply = Select(Ident(nme.scala), s"Tuple$arity".toTermName)
val members = vparams.map(vparam => Select(Ident(unapplyParamName), vparam.name))
Apply(scalaDot(nme.Option), Apply(tupleApply, members))