1
- package dotty .tools .dotc .transform
2
-
3
- import dotty .tools .dotc .ast .{Trees , tpd }
4
- import dotty .tools .dotc .core .Annotations .Annotation
5
- import dotty .tools .dotc .core .Contexts ._
6
- import dotty .tools .dotc .core .DenotTransformers .SymTransformer
7
- import dotty .tools .dotc .core .SymDenotations .SymDenotation
8
- import dotty .tools .dotc .core .NameOps ._
9
- import dotty .tools .dotc .core .Flags
10
- import dotty .tools .dotc .core .Names .Name
11
- import dotty .tools .dotc .core .StdNames .nme
12
- import dotty .tools .dotc .core .Symbols ._
13
- import dotty .tools .dotc .core .Types .MethodType
14
- import dotty .tools .dotc .transform .MegaPhase .MiniPhase
1
+ package dotty .tools .dotc
2
+ package transform
15
3
16
- object MoveStatics {
17
- val name : String = " moveStatic"
18
- }
4
+ import core ._
5
+ import Flags ._
6
+ import Contexts ._
7
+ import Symbols ._
8
+ import Decorators ._
9
+ import DenotTransformers .SymTransformer
10
+ import Types .MethodType
11
+ import Annotations .Annotation
12
+ import SymDenotations .SymDenotation
13
+ import Names .Name
14
+ import NameOps ._
19
15
20
- /** Move static methods from companion to the class itself */
16
+ import reporting ._
17
+ import ast ._
18
+
19
+ import SymUtils ._
20
+ import MegaPhase ._
21
+
22
+ /** A transformer that check that requirements of Static fields\methods are implemented:
23
+ *
24
+ * 1. Only objects can have members annotated with `@static`
25
+ * 2. The fields annotated with `@static` should precede any non-`@static` fields.
26
+ * This ensures that we do not introduce surprises for users in initialization order.
27
+ * 3. If a member `foo` of an `object C` is annotated with `@static`,
28
+ * the companion class `C` is not allowed to define term members with name `foo`.
29
+ * 4. If a member `foo` of an `object C` is annotated with `@static`, the companion class `C`
30
+ * is not allowed to inherit classes that define a term member with name `foo`.
31
+ * 5. Only `@static` methods and vals are supported in companions of traits.
32
+ * Java8 supports those, but not vars, and JavaScript does not have interfaces at all.
33
+ * 6. `@static` Lazy vals are currently unsupported.
34
+ *
35
+ * And move static methods from companion to the class itself.
36
+ */
21
37
class MoveStatics extends MiniPhase with SymTransformer {
38
+ import ast .tpd ._
22
39
23
- import tpd ._
24
40
override def phaseName : String = MoveStatics .name
25
41
26
42
def transformSym (sym : SymDenotation )(using Context ): SymDenotation =
@@ -32,50 +48,89 @@ class MoveStatics extends MiniPhase with SymTransformer {
32
48
}
33
49
else sym
34
50
51
+ override def transformTemplate (tree : tpd.Template )(using Context ): tpd.Tree = {
52
+ val defns = tree.body.collect{case t : ValOrDefDef => t}
53
+ var hadNonStaticField = false
54
+ for (defn <- defns)
55
+ if (defn.symbol.isScalaStatic) {
56
+ if (! ctx.owner.isStatic)
57
+ report.error(StaticFieldsOnlyAllowedInObjects (defn.symbol), defn.srcPos)
58
+ defn.symbol.resetFlag(JavaStatic )
59
+
60
+ if (defn.isInstanceOf [ValDef ] && hadNonStaticField)
61
+ report.error(StaticFieldsShouldPrecedeNonStatic (defn.symbol, defns), defn.srcPos)
62
+
63
+ val companion = ctx.owner.companionClass
64
+ def clashes = companion.typeRef.decl(defn.name)
65
+
66
+ if (! companion.exists)
67
+ report.error(MissingCompanionForStatic (defn.symbol), defn.srcPos)
68
+ else if (clashes.isOverloaded)
69
+ report.error(MemberWithSameNameAsStatic (), defn.srcPos)
70
+ else if (defn.symbol.is(Flags .Mutable ) && companion.is(Flags .Trait ))
71
+ report.error(TraitCompanionWithMutableStatic (), defn.srcPos)
72
+ else if (defn.symbol.is(Flags .Lazy ))
73
+ report.error(LazyStaticField (), defn.srcPos)
74
+ else if (defn.symbol.allOverriddenSymbols.nonEmpty)
75
+ report.error(StaticOverridingNonStaticMembers (), defn.srcPos)
76
+ }
77
+ else hadNonStaticField = hadNonStaticField || defn.isInstanceOf [ValDef ]
78
+
79
+ tree
80
+ }
81
+
82
+ override def transformSelect (tree : tpd.Select )(using Context ): tpd.Tree =
83
+ if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot )) {
84
+ def isSafeQual (t : Tree ): Boolean = // follow the desugared paths created by typer
85
+ t match {
86
+ case t : This => true
87
+ case t : Select => isSafeQual(t.qualifier)
88
+ case t : Block => t.stats.forall(tpd.isPureExpr) && isSafeQual(t.expr)
89
+ case _ => false
90
+ }
91
+ println(" tree.qualifier = " + tree.qualifier)
92
+ if (isSafeQual(tree.qualifier))
93
+ ref(tree.symbol)
94
+ else
95
+ Block (tree.qualifier :: Nil , ref(tree.symbol))
96
+ }
97
+ else tree
98
+
99
+
35
100
override def transformStats (trees : List [Tree ])(using Context ): List [Tree ] =
36
101
if (ctx.owner.is(Flags .Package )) {
37
102
val (classes, others) = trees.partition(x => x.isInstanceOf [TypeDef ] && x.symbol.isClass)
38
103
val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf [Map [Name , List [TypeDef ]]]
39
104
40
105
def rebuild (orig : TypeDef , newBody : List [Tree ]): Tree = {
41
- if (orig eq null ) return EmptyTree
42
-
43
- val staticFields = newBody.filter(x => x.isInstanceOf [ValDef ] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot )).asInstanceOf [List [ValDef ]]
44
- val newBodyWithStaticConstr =
45
- if (staticFields.nonEmpty) {
46
- /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */
47
- val staticCostructor = newSymbol(orig.symbol, nme.STATIC_CONSTRUCTOR , Flags .Synthetic | Flags .Method | Flags .Private , MethodType (Nil , defn.UnitType ))
48
- staticCostructor.addAnnotation(Annotation (defn.ScalaStaticAnnot ))
49
- staticCostructor.entered
50
-
51
- val staticAssigns = staticFields.map(x => Assign (ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor)))
52
- tpd.DefDef (staticCostructor, Block (staticAssigns, tpd.unitLiteral)) :: newBody
53
- }
54
- else newBody
55
-
56
106
val oldTemplate = orig.rhs.asInstanceOf [Template ]
57
- cpy.TypeDef (orig)(rhs = cpy.Template (oldTemplate)(body = newBodyWithStaticConstr ))
107
+ cpy.TypeDef (orig)(rhs = cpy.Template (oldTemplate)(body = newBody ))
58
108
}
59
109
60
110
def move (module : TypeDef , companion : TypeDef ): List [Tree ] = {
61
111
assert(companion != module)
62
112
if (! module.symbol.is(Flags .Module )) move(companion, module)
63
113
else {
64
- val allMembers =
65
- (if (companion != null ) {companion.rhs.asInstanceOf [Template ].body} else Nil ) ++
66
- module.rhs.asInstanceOf [Template ].body
67
- val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol})
68
- Trees .flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil )
114
+ val moduleTmpl = module.rhs.asInstanceOf [Template ]
115
+ val companionTmpl = companion.rhs.asInstanceOf [Template ]
116
+ val (staticDefs, remainingDefs) = moduleTmpl.body.partition {
117
+ case memberDef : MemberDef => memberDef.symbol.isScalaStatic
118
+ case _ => false
119
+ }
120
+
121
+ rebuild(companion, companionTmpl.body ++ staticDefs) :: rebuild(module, remainingDefs) :: Nil
69
122
}
70
123
}
71
124
val newPairs =
72
125
for ((name, classes) <- pairs)
73
126
yield
74
- if (classes.tail.isEmpty)
75
- if (classes.head.symbol.is(Flags .Module )) move(classes.head, null )
76
- else List (rebuild(classes.head, classes.head.rhs.asInstanceOf [Template ].body))
127
+ if (classes.tail.isEmpty) classes
77
128
else move(classes.head, classes.tail.head)
78
129
Trees .flatten(newPairs.toList.flatten ++ others)
79
130
}
80
131
else trees
81
132
}
133
+
134
+ object MoveStatics {
135
+ val name : String = " moveStatic"
136
+ }
0 commit comments