-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathTrees.scala
1934 lines (1721 loc) · 87.4 KB
/
Trees.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 Types.*, Names.*, NameOps.*, Flags.*, util.Spans.*, Contexts.*, Constants.*
import typer.{ ConstFold, ProtoTypes }
import SymDenotations.*, Symbols.*, Denotations.*, StdNames.*, Comments.*
import collection.mutable.ListBuffer
import printing.Printer
import printing.Texts.Text
import util.{Stats, Attachment, Property, SourceFile, NoSource, SrcPos, SourcePosition}
import config.Config
import config.Printers.overload
import annotation.internal.sharable
import annotation.unchecked.uncheckedVariance
import annotation.constructorOnly
import compiletime.uninitialized
import Decorators.*
import staging.StagingLevel.*
object Trees {
type Untyped = Type | Null
/** The total number of created tree nodes, maintained if Stats.enabled */
@sharable var ntrees: Int = 0
/** Property key for trees with documentation strings attached */
val DocComment: Property.StickyKey[Comments.Comment] = Property.StickyKey()
/** Property key for backquoted identifiers and definitions */
val Backquoted: Property.StickyKey[Unit] = Property.StickyKey()
val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey()
/** Trees take a parameter indicating what the type of their `tpe` field
* is. Two choices: `Type` or `Untyped`.
* Untyped trees have type `Tree[Untyped]`.
*
* Tree typing uses a copy-on-write implementation:
*
* - You can never observe a `tpe` which is `null` (throws an exception)
* - So when creating a typed tree with `withType` we can re-use
* the existing tree transparently, assigning its `tpe` field.
* - It is impossible to embed untyped trees in typed ones.
* - Typed trees can be embedded in untyped ones provided they are rooted
* in a TypedSplice node.
* - Type checking an untyped tree should remove all embedded `TypedSplice`
* nodes.
*/
abstract class Tree[+T <: Untyped](implicit @constructorOnly src: SourceFile)
extends Positioned, SrcPos, Product, Attachment.Container, printing.Showable {
if (Stats.enabled) ntrees += 1
/** The type constructor at the root of the tree */
type ThisTree[T <: Untyped] <: Tree[T]
protected var myTpe: T @uncheckedVariance = uninitialized
/** Destructively set the type of the tree. This should be called only when it is known that
* it is safe under sharing to do so. One use-case is in the withType method below
* which implements copy-on-write. Another use-case is in method interpolateAndAdapt in Typer,
* where we overwrite with a simplified version of the type itself.
*/
private[dotc] def overwriteType(tpe: T @uncheckedVariance): Unit =
myTpe = tpe
/** The type of the tree. In case of an untyped tree,
* an UnAssignedTypeException is thrown. (Overridden by empty trees)
*/
final def tpe: T =
if myTpe == null then throw UnAssignedTypeException(this)
myTpe.uncheckedNN
/** Copy `tpe` attribute from tree `from` into this tree, independently
* whether it is null or not.
final def copyAttr[U <: Untyped](from: Tree[U]): ThisTree[T] = {
val t1 = this.withSpan(from.span)
val t2 =
if (from.myTpe != null) t1.withType(from.myTpe.asInstanceOf[Type])
else t1
t2.asInstanceOf[ThisTree[T]]
}
*/
/** Return a typed tree that's isomorphic to this tree, but has given
* type. (Overridden by empty trees)
*/
def withType(tpe: Type)(using Context): ThisTree[Type] = {
if (tpe.isInstanceOf[ErrorType])
assert(!Config.checkUnreportedErrors ||
ctx.reporter.errorsReported ||
ctx.settings.YshowPrintErrors.value
// under -Yshow-print-errors, errors might arise during printing, but they do not count as reported
)
else if (Config.checkTreesConsistent)
checkChildrenTyped(productIterator)
withTypeUnchecked(tpe)
}
/** Check that typed trees don't refer to untyped ones, except if
* - the parent tree is an import, or
* - the child tree is an identifier, or
* - errors were reported
*/
private def checkChildrenTyped(it: Iterator[Any])(using Context): Unit =
if (!this.isInstanceOf[Import[?]])
while (it.hasNext)
it.next() match {
case x: Ident[?] => // untyped idents are used in a number of places in typed trees
case x: Tree[?] =>
assert(x.hasType || ctx.reporter.errorsReported,
s"$this has untyped child $x")
case xs: List[?] => checkChildrenTyped(xs.iterator)
case _ =>
}
def withTypeUnchecked(tpe: Type): ThisTree[Type] = {
val tree =
(if (myTpe == null ||
(myTpe.asInstanceOf[AnyRef] eq tpe.asInstanceOf[AnyRef])) this
else cloneIn(source)).asInstanceOf[Tree[Type]]
tree overwriteType tpe
tree.asInstanceOf[ThisTree[Type]]
}
/** Does the tree have its type field set? Note: this operation is not
* referentially transparent, because it can observe the withType
* modifications. Should be used only in special circumstances (we
* need it for printing trees with optional type info).
*/
final def hasType: Boolean = myTpe != null
final def typeOpt: Type = myTpe match
case tp: Type => tp
case null => NoType
/** The denotation referred to by this tree.
* Defined for `DenotingTree`s and `ProxyTree`s, NoDenotation for other
* kinds of trees
*/
def denot(using Context): Denotation = NoDenotation
/** Shorthand for `denot.symbol`. */
final def symbol(using Context): Symbol = denot.symbol
/** Does this tree represent a type? */
def isType: Boolean = false
/** Does this tree represent a term? */
def isTerm: Boolean = false
/** Is this a legal part of a pattern which is not at the same time a term? */
def isPattern: Boolean = false
/** Does this tree define a new symbol that is not defined elsewhere? */
def isDef: Boolean = false
/** Is this tree either the empty tree or the empty ValDef or an empty type ident? */
def isEmpty: Boolean = false
/** Convert tree to a list. Gives a singleton list, except
* for thickets which return their element trees.
*/
def toList: List[Tree[T]] = this :: Nil
/** if this tree is the empty tree, the alternative, else this tree */
inline def orElse[U >: T <: Untyped](inline that: Tree[U]): Tree[U] =
if (this eq genericEmptyTree) that else this
/** The number of nodes in this tree */
def treeSize: Int = {
var s = 1
def addSize(elem: Any): Unit = elem match {
case t: Tree[?] => s += t.treeSize
case ts: List[?] => ts foreach addSize
case _ =>
}
productIterator foreach addSize
s
}
/** If this is a thicket, perform `op` on each of its trees
* otherwise, perform `op` ion tree itself.
*/
def foreachInThicket(op: Tree[T] => Unit): Unit = op(this)
override def toText(printer: Printer): Text = printer.toText(this)
def sameTree(that: Tree[?]): Boolean = {
def isSame(x: Any, y: Any): Boolean =
x.asInstanceOf[AnyRef].eq(y.asInstanceOf[AnyRef]) || {
x match {
case x: Tree[?] =>
y match {
case y: Tree[?] => x.sameTree(y)
case _ => false
}
case x: List[?] =>
y match {
case y: List[?] => x.corresponds(y)(isSame)
case _ => false
}
case _ =>
false
}
}
this.getClass == that.getClass && {
val it1 = this.productIterator
val it2 = that.productIterator
it1.corresponds(it2)(isSame)
}
}
override def hashCode(): Int = System.identityHashCode(this)
override def equals(that: Any): Boolean = this eq that.asInstanceOf[AnyRef]
}
class UnAssignedTypeException[T <: Untyped](tree: Tree[T]) extends RuntimeException {
override def getMessage: String = s"type of $tree is not assigned"
}
type LazyTree[+T <: Untyped] = Tree[T] | Lazy[Tree[T]]
type LazyTreeList[+T <: Untyped] = List[Tree[T]] | Lazy[List[Tree[T]]]
// ------ Categories of trees -----------------------------------
/** Instances of this class are trees for which isType is definitely true.
* Note that some trees have isType = true without being TypTrees (e.g. Ident, Annotated)
*/
trait TypTree[+T <: Untyped] extends Tree[T] {
type ThisTree[+T <: Untyped] <: TypTree[T]
override def isType: Boolean = true
}
/** Instances of this class are trees for which isTerm is definitely true.
* Note that some trees have isTerm = true without being TermTrees (e.g. Ident, Annotated)
*/
trait TermTree[+T <: Untyped] extends Tree[T] {
type ThisTree[+T <: Untyped] <: TermTree[T]
override def isTerm: Boolean = true
}
/** Instances of this class are trees which are not terms but are legal
* parts of patterns.
*/
trait PatternTree[+T <: Untyped] extends Tree[T] {
type ThisTree[+T <: Untyped] <: PatternTree[T]
override def isPattern: Boolean = true
}
/** Tree's denotation can be derived from its type */
abstract class DenotingTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[+T <: Untyped] <: DenotingTree[T]
override def denot(using Context): Denotation = typeOpt.stripped match
case tpe: NamedType => tpe.denot
case tpe: ThisType => tpe.cls.denot
case _ => NoDenotation
}
/** Tree's denot/isType/isTerm properties come from a subtree
* identified by `forwardTo`.
*/
abstract class ProxyTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[+T <: Untyped] <: ProxyTree[T]
def forwardTo: Tree[T]
override def denot(using Context): Denotation = forwardTo.denot
override def isTerm: Boolean = forwardTo.isTerm
override def isType: Boolean = forwardTo.isType
}
/** Tree has a name */
abstract class NameTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends DenotingTree[T] {
type ThisTree[+T <: Untyped] <: NameTree[T]
def name: Name
}
/** Tree refers by name to a denotation */
abstract class RefTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends NameTree[T] {
type ThisTree[+T <: Untyped] <: RefTree[T]
def qualifier: Tree[T]
override def isType: Boolean = name.isTypeName
override def isTerm: Boolean = name.isTermName
}
/** Tree defines a new symbol */
trait DefTree[+T <: Untyped] extends DenotingTree[T] {
type ThisTree[+T <: Untyped] <: DefTree[T]
private var myMods: untpd.Modifiers | Null = uninitialized
private[dotc] def rawMods: untpd.Modifiers =
if (myMods == null) untpd.EmptyModifiers else myMods.uncheckedNN
def withAnnotations(annots: List[untpd.Tree]): ThisTree[Untyped] = withMods(rawMods.withAnnotations(annots))
def withMods(mods: untpd.Modifiers): ThisTree[Untyped] = {
val tree = if (myMods == null || (myMods == mods)) this else cloneIn(source)
tree.setMods(mods)
tree.asInstanceOf[ThisTree[Untyped]]
}
def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags))
def withAddedFlags(flags: FlagSet): ThisTree[Untyped] = withMods(rawMods | flags)
def withAddedAnnotation(annot: Tree[Untyped]): ThisTree[Untyped] = withMods(rawMods.withAddedAnnotation(annot))
/** Destructively update modifiers. To be used with care. */
def setMods(mods: untpd.Modifiers): Unit = myMods = mods
override def isDef: Boolean = true
def namedType: NamedType = tpe.asInstanceOf[NamedType]
}
extension (mdef: untpd.DefTree) def mods: untpd.Modifiers = mdef.rawMods
sealed trait WithEndMarker[+T <: Untyped]:
self: PackageDef[T] | NamedDefTree[T] =>
import WithEndMarker.*
final def endSpan(using Context): Span =
if hasEndMarker then
val realName = srcName.stripModuleClassSuffix.lastPart
span.withStart(span.end - realName.length)
else
NoSpan
/** The name in source code that represents this construct,
* and is the name that the user must write to create a valid
* end marker.
* e.g. a constructor definition is terminated in the source
* code by `end this`, so it's `srcName` should return `this`.
*/
protected def srcName(using Context): Name
final def withEndMarker(): self.type =
self.withAttachment(HasEndMarker, ())
final def withEndMarker(copyFrom: WithEndMarker[?]): self.type =
if copyFrom.hasEndMarker then
this.withEndMarker()
else
this
final def dropEndMarker(): self.type =
self.removeAttachment(HasEndMarker)
this
protected def hasEndMarker: Boolean = self.hasAttachment(HasEndMarker)
object WithEndMarker:
/** Property key that signals the tree was terminated
* with an `end` marker in the source code
*/
private val HasEndMarker: Property.StickyKey[Unit] = Property.StickyKey()
end WithEndMarker
abstract class NamedDefTree[+T <: Untyped](implicit @constructorOnly src: SourceFile)
extends NameTree[T] with DefTree[T] with WithEndMarker[T] {
type ThisTree[+T <: Untyped] <: NamedDefTree[T]
protected def srcName(using Context): Name =
if name == nme.CONSTRUCTOR then nme.this_
else if symbol.isPackageObject then symbol.owner.name
else name
/** The position of the name defined by this definition.
* This is a point position if the definition is synthetic, or a range position
* if the definition comes from source.
* It might also be that the definition does not have a position (for instance when synthesized by
* a calling chain from `viewExists`), in that case the return position is NoSpan.
* Overridden in Bind
*/
def nameSpan(using Context): Span =
if (span.exists) {
val point = span.point
if (rawMods.is(Synthetic) || span.isSynthetic || name.toTermName == nme.ERROR) Span(point)
else {
val realName = srcName.stripModuleClassSuffix.lastPart
Span(point, point + realName.length, point)
}
}
else span
/** The source position of the name defined by this definition.
* This is a point position if the definition is synthetic, or a range position
* if the definition comes from source.
*/
def namePos(using Context): SourcePosition = source.atSpan(nameSpan)
}
/** Tree defines a new symbol and carries modifiers.
* The position of a MemberDef contains only the defined identifier or pattern.
* The envelope of a MemberDef contains the whole definition and has its point
* on the opening keyword (or the next token after that if keyword is missing).
*/
abstract class MemberDef[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends NamedDefTree[T] {
type ThisTree[+T <: Untyped] <: MemberDef[T]
def rawComment: Option[Comment] = getAttachment(DocComment)
def setComment(comment: Option[Comment]): this.type = {
comment.map(putAttachment(DocComment, _))
this
}
def name: Name
}
/** A ValDef or DefDef tree */
abstract class ValOrDefDef[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends MemberDef[T], WithLazyFields {
type ThisTree[+T <: Untyped] <: ValOrDefDef[T]
def name: TermName
def tpt: Tree[T]
def unforcedRhs: LazyTree[T]
def rhs(using Context): Tree[T]
}
trait ValOrTypeDef[+T <: Untyped] extends MemberDef[T]:
type ThisTree[+T <: Untyped] <: ValOrTypeDef[T]
type ParamClause[T <: Untyped] = List[ValDef[T]] | List[TypeDef[T]]
// ----------- Tree case classes ------------------------------------
/** name */
case class Ident[+T <: Untyped] private[ast] (name: Name)(implicit @constructorOnly src: SourceFile)
extends RefTree[T] {
type ThisTree[+T <: Untyped] = Ident[T]
def qualifier: Tree[T] = genericEmptyTree
def isBackquoted: Boolean = hasAttachment(Backquoted)
}
class SearchFailureIdent[+T <: Untyped] private[ast] (name: Name, expl: => String)(implicit @constructorOnly src: SourceFile)
extends Ident[T](name) {
def explanation = expl
override def toString: String = s"SearchFailureIdent($explanation)"
}
/** qualifier.name, or qualifier#name, if qualifier is a type */
case class Select[+T <: Untyped] private[ast] (qualifier: Tree[T], name: Name)(implicit @constructorOnly src: SourceFile)
extends RefTree[T] {
type ThisTree[+T <: Untyped] = Select[T]
override def denot(using Context): Denotation = typeOpt match
case ConstantType(_) if ConstFold.foldedUnops.contains(name) =>
// Recover the denotation of a constant-folded selection
qualifier.typeOpt.member(name).atSignature(Signature.NotAMethod, name)
case _ =>
super.denot
def nameSpan(using Context): Span =
if span.exists then
val point = span.point
if name.toTermName == nme.ERROR then
Span(point)
else if qualifier.span.exists && qualifier.span.start > span.point then // right associative
val realName = name.stripModuleClassSuffix.lastPart
Span(span.start, span.start + realName.length, point)
else if span.pointMayBeIncorrect then
val realName = name.stripModuleClassSuffix.lastPart
val probablyPoint = span.end - realName.length
Span(probablyPoint, span.end, probablyPoint)
else Span(point, span.end, point)
else span
}
class SelectWithSig[+T <: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature)(implicit @constructorOnly src: SourceFile)
extends Select[T](qualifier, name) {
override def toString: String = s"SelectWithSig($qualifier, $name, $sig)"
}
/** qual.this */
case class This[+T <: Untyped] private[ast] (qual: untpd.Ident)(implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] = This[T]
// Denotation of a This tree is always the underlying class; needs correction for modules.
override def denot(using Context): Denotation =
typeOpt match {
case tpe @ TermRef(pre, _) if tpe.symbol.is(Module) =>
tpe.symbol.moduleClass.denot.asSeenFrom(pre)
case _ =>
super.denot
}
}
/** C.super[mix], where qual = C.this */
case class Super[+T <: Untyped] private[ast] (qual: Tree[T], mix: untpd.Ident)(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] = Super[T]
def forwardTo: Tree[T] = qual
}
abstract class GenericApply[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends ProxyTree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] <: GenericApply[T]
val fun: Tree[T]
val args: List[Tree[T]]
def forwardTo: Tree[T] = fun
}
object GenericApply:
def unapply[T <: Untyped](tree: Tree[T]): Option[(Tree[T], List[Tree[T]])] = tree match
case tree: GenericApply[T] => Some((tree.fun, tree.args))
case _ => None
/** The kind of application */
enum ApplyKind:
case Regular // r.f(x)
case Using // r.f(using x)
case InfixTuple // r f (x1, ..., xN) where N != 1; needs to be treated specially for an error message in typedApply
/** fun(args) */
case class Apply[+T <: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends GenericApply[T] {
type ThisTree[+T <: Untyped] = Apply[T]
def setApplyKind(kind: ApplyKind) =
putAttachment(untpd.KindOfApply, kind)
this
/** The kind of this application. Works reliably only for untyped trees; typed trees
* are under no obligation to update it correctly.
*/
def applyKind: ApplyKind =
attachmentOrElse(untpd.KindOfApply, ApplyKind.Regular)
}
/** fun[args] */
case class TypeApply[+T <: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends GenericApply[T] {
type ThisTree[+T <: Untyped] = TypeApply[T]
}
/** const */
case class Literal[+T <: Untyped] private[ast] (const: Constant)(implicit @constructorOnly src: SourceFile)
extends Tree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] = Literal[T]
}
/** new tpt, but no constructor call */
case class New[+T <: Untyped] private[ast] (tpt: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] = New[T]
}
/** expr : tpt */
case class Typed[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] with TermTree[T] {
type ThisTree[+T <: Untyped] = Typed[T]
def forwardTo: Tree[T] = expr
}
/** name = arg, in a parameter list */
case class NamedArg[+T <: Untyped] private[ast] (name: Name, arg: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {
type ThisTree[+T <: Untyped] = NamedArg[T]
}
/** name = arg, outside a parameter list */
case class Assign[+T <: Untyped] private[ast] (lhs: Tree[T], rhs: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Assign[T]
}
/** { stats; expr } */
case class Block[+T <: Untyped] private[ast] (stats: List[Tree[T]], expr: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {
type ThisTree[+T <: Untyped] = Block[T]
override def isType: Boolean = expr.isType
override def isTerm: Boolean = !isType // this will classify empty trees as terms, which is necessary
}
/** if cond then thenp else elsep */
case class If[+T <: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = If[T]
def isInline = false
}
class InlineIf[+T <: Untyped] private[ast] (cond: Tree[T], thenp: Tree[T], elsep: Tree[T])(implicit @constructorOnly src: SourceFile)
extends If(cond, thenp, elsep) {
override def isInline = true
override def toString = s"InlineIf($cond, $thenp, $elsep)"
}
/** A closure with an environment and a reference to a method.
* @param env The captured parameters of the closure
* @param meth A ref tree that refers to the method of the closure.
* The first (env.length) parameters of that method are filled
* with env values.
* @param tpt Either EmptyTree or a TypeTree. If tpt is EmptyTree the type
* of the closure is a function type, otherwise it is the type
* given in `tpt`, which must be a SAM type.
*/
case class Closure[+T <: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Closure[T]
}
/** selector match { cases } */
case class Match[+T <: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Match[T]
def isInline = false
}
class InlineMatch[+T <: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
extends Match(selector, cases) {
override def isInline = true
override def toString = s"InlineMatch($selector, $cases)"
}
/** case pat if guard => body */
case class CaseDef[+T <: Untyped] private[ast] (pat: Tree[T], guard: Tree[T], body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {
type ThisTree[+T <: Untyped] = CaseDef[T]
}
/** label[tpt]: { expr } */
case class Labeled[+T <: Untyped] private[ast] (bind: Bind[T], expr: Tree[T])(implicit @constructorOnly src: SourceFile)
extends NameTree[T] {
type ThisTree[+T <: Untyped] = Labeled[T]
def name: Name = bind.name
}
/** return expr
* where `from` refers to the method or label from which the return takes place
* After program transformations this is not necessarily the enclosing method, because
* closures can intervene.
*/
case class Return[+T <: Untyped] private[ast] (expr: Tree[T], from: Tree[T] = genericEmptyTree)(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Return[T]
}
/** while (cond) { body } */
case class WhileDo[+T <: Untyped] private[ast] (cond: Tree[T], body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = WhileDo[T]
}
/** try block catch cases finally finalizer */
case class Try[+T <: Untyped] private[ast] (expr: Tree[T], cases: List[CaseDef[T]], finalizer: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Try[T]
}
/** Seq(elems)
* @param tpt The element type of the sequence.
*/
case class SeqLiteral[+T <: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {
type ThisTree[+T <: Untyped] = SeqLiteral[T]
}
/** Array(elems) */
class JavaSeqLiteral[+T <: Untyped] private[ast] (elems: List[Tree[T]], elemtpt: Tree[T])(implicit @constructorOnly src: SourceFile)
extends SeqLiteral(elems, elemtpt) {
override def toString: String = s"JavaSeqLiteral($elems, $elemtpt)"
}
/** A tree representing inlined code.
*
* @param call Info about the original call that was inlined
* Until PostTyper, this is the full call, afterwards only
* a reference to the toplevel class from which the call was inlined.
* @param bindings Bindings for proxies to be used in the inlined code
* @param expansion The inlined tree, minus bindings.
*
* The full inlined code is equivalent to
*
* { bindings; expansion }
*
* The reason to keep `bindings` separate is because they are typed in a
* different context: `bindings` represent the arguments to the inlined
* call, whereas `expansion` represents the body of the inlined function.
*/
case class Inlined[+T <: Untyped] private[ast] (call: tpd.Tree, bindings: List[MemberDef[T]], expansion: Tree[T])(implicit @constructorOnly src: SourceFile)
extends Tree[T] {
def inlinedFromOuterScope: Boolean = call.isEmpty
type ThisTree[+T <: Untyped] = Inlined[T]
override def isTerm = expansion.isTerm
override def isType = expansion.isType
}
/** A tree representing a quote `'{ body }` or `'[ body ]`.
* `Quote`s are created by the `Parser`. In typer they can be typed as a
* `Quote` with a known `tpt` or desugared and typed as a quote pattern.
*
* `Quotes` are checked and transformed in the `staging`, `splicing` and `pickleQuotes`
* phases. After `pickleQuotes` phase, the only quotes that exist are in `inline`
* methods. These are dropped when we remove the inline method implementations.
*
* Type quotes `'[body]` from the parser are typed into `QuotePattern`s when type checking.
* TASTy files will not contain type quotes. Type quotes are used again in the `staging`
* phase to represent the reification of `Type.of[T]]`.
*
* Type tags `tags` are always empty before the `staging` phase. Tags for stage inconsistent
* types are added in the `staging` phase to level 0 quotes. Tags for types that refer to
* definitions in an outer quote are added in the `splicing` phase
*
* @param body The tree that was quoted
* @param tags Term references to instances of `Type[T]` for `T`s that are used in the quote
*/
case class Quote[+T <: Untyped] private[ast] (body: Tree[T], tags: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Quote[T]
/** Is this a type quote `'[tpe]' */
def isTypeQuote = body.isType
/** Set the type of the body of the quote */
def withBodyType(tpe: Type)(using Context): Quote[Type] =
val exprType = // `Expr[T]` or `Type[T]`
if body.isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpe)
else defn.QuotedTypeClass.typeRef.appliedTo(tpe)
val quoteType = // `Quotes ?=> Expr[T]` or `Quotes ?=> Type[T]`
defn.FunctionType(1, isContextual = true)
.appliedTo(defn.QuotesClass.typeRef, exprType)
withType(quoteType)
}
/** A tree representing a splice `${ expr }`
*
* `Splice`s are created by the `Parser`. In typer they can be typed as a
* `Splice` with a known `tpt` or desugared and typed as a quote pattern holes.
*
* `Splice` are checked and transformed in the `staging` and `splicing` phases.
* After `splicing` phase, the only splices that exist are in `inline`
* methods. These are dropped when we remove the inline method implementations.
*
* @param expr The tree that was spliced
*/
case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = Splice[T]
}
/** A tree representing a quote pattern `'{ type binding1; ...; body }` or `'[ type binding1; ...; body ]`.
* `QuotePattern`s are created the type checker when typing an `untpd.Quote` in a pattern context.
*
* `QuotePattern`s are checked are encoded into `unapply`s in the `staging` phase.
*
* The `bindings` contain the list of quote pattern type variable definitions (`Bind`s) in the oreder in
* which they are defined in the source.
*
* @param bindings Type variable definitions (`Bind` tree)
* @param body Quoted pattern (without type variable definitions)
* @param quotes A reference to the given `Quotes` instance in scope
*/
case class QuotePattern[+T <: Untyped] private[ast] (bindings: List[Tree[T]], body: Tree[T], quotes: Tree[T])(implicit @constructorOnly src: SourceFile)
extends PatternTree[T] {
type ThisTree[+T <: Untyped] = QuotePattern[T]
}
/** A tree representing a pattern splice `${ pattern }`, `$ident` or `$ident(args*)` in a quote pattern.
*
* Parser will only create `${ pattern }` and `$ident`, hence they will not have args.
* While typing, the `$ident(args*)` the args are identified and desugared into a `SplicePattern`
* containing them.
*
* `SplicePattern` can only be contained within a `QuotePattern`.
*
* @param body The tree that was spliced
* @param typeargs The type arguments of the splice (the HOAS arguments)
* @param args The arguments of the splice (the HOAS arguments)
*/
case class SplicePattern[+T <: Untyped] private[ast] (body: Tree[T], typeargs: List[Tree[T]], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends TermTree[T] {
type ThisTree[+T <: Untyped] = SplicePattern[T]
}
/** A type tree that represents an existing or inferred type */
case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] with TypTree[T] {
type ThisTree[+T <: Untyped] <: TypeTree[T]
override def isEmpty: Boolean = !hasType
override def toString: String =
s"TypeTree${if (hasType) s"[$typeOpt]" else ""}"
def isInferred = false
}
/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
* It is only used when pickling quotes (will never be in a TASTy file).
*
* @param isTerm If this hole is a term, otherwise it is a type hole.
* @param idx The index of the hole in it's enclosing level 0 quote.
* @param args The arguments of the splice to compute its content
* @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle.
*/
case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[+T <: Untyped] <: Hole[T]
override def isType: Boolean = !isTerm
}
/** A type tree whose type is inferred. These trees appear in two contexts
* - as an argument of a TypeApply. In that case its type is always a TypeVar
* - as a (result-)type of an inferred ValDef or DefDef.
* Every TypeVar is created as the type of one InferredTypeTree.
*/
class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]:
type ThisTree[+T <: Untyped] <: InferredTypeTree[T]
override def isInferred = true
/** ref.type */
case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] with TypTree[T] {
type ThisTree[+T <: Untyped] = SingletonTypeTree[T]
}
/** tpt { refinements } */
case class RefinedTypeTree[+T <: Untyped] private[ast] (tpt: Tree[T], refinements: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] with TypTree[T] {
type ThisTree[+T <: Untyped] = RefinedTypeTree[T]
def forwardTo: Tree[T] = tpt
}
/** tpt[args] */
case class AppliedTypeTree[+T <: Untyped] private[ast] (tpt: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] with TypTree[T] {
type ThisTree[+T <: Untyped] = AppliedTypeTree[T]
def forwardTo: Tree[T] = tpt
}
/** [typeparams] -> tpt
*
* Note: the type of such a tree is not necessarily a `HKTypeLambda`, it can
* also be a `TypeBounds` where the upper bound is an `HKTypeLambda`, and the
* lower bound is either a reference to `Nothing` or an `HKTypeLambda`,
* this happens because these trees are typed by `HKTypeLambda#fromParams` which
* makes sure to move bounds outside of the type lambda itself to simplify their
* handling in the compiler.
*
* You may ask: why not normalize the trees too? That way,
*
* LambdaTypeTree(X, TypeBoundsTree(A, B))
*
* would become,
*
* TypeBoundsTree(LambdaTypeTree(X, A), LambdaTypeTree(X, B))
*
* which would maintain consistency between a tree and its type. The problem
* with this definition is that the same tree `X` appears twice, therefore
* we'd have to create two symbols for it which makes it harder to relate the
* source code written by the user with the trees used by the compiler (for
* example, to make "find all references" work in the IDE).
*/
case class LambdaTypeTree[+T <: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[+T <: Untyped] = LambdaTypeTree[T]
}
case class TermLambdaTypeTree[+T <: Untyped] private[ast] (params: List[ValDef[T]], body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[+T <: Untyped] = TermLambdaTypeTree[T]
}
/** [bound] selector match { cases } */
case class MatchTypeTree[+T <: Untyped] private[ast] (bound: Tree[T], selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[+T <: Untyped] = MatchTypeTree[T]
}
/** => T */
case class ByNameTypeTree[+T <: Untyped] private[ast] (result: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[+T <: Untyped] = ByNameTypeTree[T]
}
/** >: lo <: hi
* >: lo <: hi = alias for RHS of bounded opaque type
*/
case class TypeBoundsTree[+T <: Untyped] private[ast] (lo: Tree[T], hi: Tree[T], alias: Tree[T])(implicit @constructorOnly src: SourceFile)
extends TypTree[T] {
type ThisTree[+T <: Untyped] = TypeBoundsTree[T]
}
/** name @ body */
case class Bind[+T <: Untyped] private[ast] (name: Name, body: Tree[T])(implicit @constructorOnly src: SourceFile)
extends NamedDefTree[T] with PatternTree[T] {
type ThisTree[+T <: Untyped] = Bind[T]
override def isType: Boolean = name.isTypeName
override def isTerm: Boolean = name.isTermName
override def nameSpan(using Context): Span =
if span.exists then Span(span.start, span.start + name.toString.length) else span
}
/** tree_1 | ... | tree_n */
case class Alternative[+T <: Untyped] private[ast] (trees: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends PatternTree[T] {
type ThisTree[+T <: Untyped] = Alternative[T]
}
/** The typed translation of `extractor(patterns)` in a pattern. The translation has the following
* components:
*
* @param fun is `extractor.unapply` (or, for backwards compatibility, `extractor.unapplySeq`)
* possibly with type parameters
* @param implicits Any implicit parameters passed to the unapply after the selector
* @param patterns The argument patterns in the pattern match.
*
* It is typed with same type as first `fun` argument
* Given a match selector `sel` a pattern UnApply(fun, implicits, patterns) is roughly translated as follows
*
* val result = fun(sel)(implicits)
* if (result.isDefined) "match patterns against result"
*/
case class UnApply[+T <: Untyped] private[ast] (fun: Tree[T], implicits: List[Tree[T]], patterns: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] with PatternTree[T] {
type ThisTree[+T <: Untyped] = UnApply[T]
def forwardTo = fun
}
/** mods val name: tpt = rhs */
case class ValDef[+T <: Untyped] private[ast] (name: TermName, tpt: Tree[T], private var preRhs: LazyTree[T])(implicit @constructorOnly src: SourceFile)
extends ValOrDefDef[T], ValOrTypeDef[T] {
type ThisTree[+T <: Untyped] = ValDef[T]
assert(isEmpty || (tpt ne genericEmptyTree))
def unforcedRhs: LazyTree[T] = preRhs
def forceFields()(using Context): Unit = preRhs = force(preRhs)
def rhs(using Context): Tree[T] = { forceFields(); preRhs.asInstanceOf[Tree[T]] }
}
/** mods def name[tparams](vparams_1)...(vparams_n): tpt = rhs */
case class DefDef[+T <: Untyped] private[ast] (name: TermName,
paramss: List[ParamClause[T]], tpt: Tree[T], private var preRhs: LazyTree[T])(implicit @constructorOnly src: SourceFile)
extends ValOrDefDef[T] {
type ThisTree[+T <: Untyped] = DefDef[T]
assert(tpt ne genericEmptyTree)
def unforcedRhs: LazyTree[T] = preRhs
def forceFields()(using Context): Unit = preRhs = force(preRhs)
def rhs(using Context): Tree[T] = { forceFields(); preRhs.asInstanceOf[Tree[T]] }
def leadingTypeParams(using Context): List[TypeDef[T]] = paramss match
case (tparams @ (tparam: TypeDef[?]) :: _) :: _ => tparams.asInstanceOf[List[TypeDef[T]]]
case _ => Nil
def trailingParamss(using Context): List[ParamClause[T]] = paramss match
case ((tparam: TypeDef[?]) :: _) :: paramss1 => paramss1
case _ => paramss
def termParamss(using Context): List[List[ValDef[T]]] =
(if ctx.erasedTypes then paramss else untpd.termParamssIn(paramss))
.asInstanceOf[List[List[ValDef[T]]]]
}
/** mods class name template or
* mods trait name template or
* mods type name = rhs or
* mods type name >: lo <: hi, if rhs = TypeBoundsTree(lo, hi) or
* mods type name >: lo <: hi = rhs if rhs = TypeBoundsTree(lo, hi, alias) and opaque in mods
*/
case class TypeDef[+T <: Untyped] private[ast] (name: TypeName, rhs: Tree[T])(implicit @constructorOnly src: SourceFile)
extends MemberDef[T], ValOrTypeDef[T] {
type ThisTree[+T <: Untyped] = TypeDef[T]
/** Is this a definition of a class? */
def isClassDef: Boolean = rhs.isInstanceOf[Template[?]]
def isBackquoted: Boolean = hasAttachment(Backquoted)
}
/** extends parents { self => body }
* @param preParentsOrDerived A list of parents followed by a list of derived classes,
* if this is of class untpd.DerivingTemplate.
* Typed templates only have parents.
*/
case class Template[+T <: Untyped] private[ast] (constr: DefDef[T], private var preParentsOrDerived: LazyTreeList[T], self: ValDef[T], private var preBody: LazyTreeList[T])(implicit @constructorOnly src: SourceFile)
extends DefTree[T] with WithLazyFields {
type ThisTree[+T <: Untyped] = Template[T]
def forceFields()(using Context): Unit =
preParentsOrDerived = force(preParentsOrDerived)
preBody = force(preBody)
def unforcedBody: LazyTreeList[T] = preBody
def body(using Context): List[Tree[T]] = { forceFields(); preBody.asInstanceOf[List[Tree[T]]] }
def parentsOrDerived(using Context): List[Tree[T]] = { forceFields(); preParentsOrDerived.asInstanceOf[List[Tree[T]]] }
def parents(using Context): List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate
}
abstract class ImportOrExport[+T <: Untyped](implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] {
type ThisTree[+T <: Untyped] <: ImportOrExport[T]
val expr: Tree[T]
val selectors: List[untpd.ImportSelector]
}
/** import expr.selectors
* where a selector is either an untyped `Ident`, `name` or