Skip to content

Commit 34fe91c

Browse files
committed
Fix #8022: Refactor MoveStatics
1 parent 3536773 commit 34fe91c

File tree

7 files changed

+137
-149
lines changed

7 files changed

+137
-149
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ class Compiler {
5959
new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars
6060
new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes
6161
new CookComments, // Cook the comments: expand variables, doc, etc.
62-
new CheckStatic, // Check restrictions that apply to @static members
6362
new BetaReduce, // Reduce closure applications
6463
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
6564
new init.Checker) :: // Check initialization of objects
@@ -94,6 +93,7 @@ class Compiler {
9493
new ParamForwarding, // Add forwarders for aliases of superclass parameters
9594
new TupleOptimizations, // Optimize generic operations on tuples
9695
new LetOverApply, // Lift blocks from receivers of applications
96+
new MoveStatics, // Move static methods from companion to the class itself
9797
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
9898
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
9999
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
@@ -120,7 +120,6 @@ class Compiler {
120120
new Flatten, // Lift all inner classes to package scope
121121
new RenameLifted, // Renames lifted classes to local numbering scheme
122122
new TransformWildcards, // Replace wildcards with default values
123-
new MoveStatics, // Move static methods from companion to the class itself
124123
new ExpandPrivate, // Widen private definitions accessed from nested classes
125124
new RestoreScopes, // Repair scopes rendered invalid by moving definitions in prior phases of the group
126125
new SelectStatic, // get rid of selects that would be compiled into GetStatic

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,7 @@ import transform.SymUtils._
19431943
}
19441944

19451945
class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) {
1946-
def msg = em"${hl("@static")} $member in ${member.owner} must be defined inside an ${hl("object")}."
1946+
def msg = em"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}."
19471947
def explain =
19481948
em"${hl("@static")} members are only allowed inside objects."
19491949
}

compiler/src/dotty/tools/dotc/transform/CheckStatic.scala

Lines changed: 0 additions & 83 deletions
This file was deleted.

compiler/src/dotty/tools/dotc/transform/Constructors.scala

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,25 @@ package transform
33

44
import core._
55
import MegaPhase._
6-
import dotty.tools.dotc.ast.tpd._
7-
import dotty.tools.dotc.core.Contexts._
8-
import dotty.tools.dotc.core.StdNames._
9-
import ast._
10-
import Trees._
6+
import Contexts._
117
import Flags._
8+
import StdNames._
129
import NameOps._
1310
import SymUtils._
1411
import Symbols._
1512
import Decorators._
1613
import DenotTransformers._
1714
import Constants.Constant
15+
import Annotations.Annotation
16+
import Types.MethodType
17+
18+
import ast._
19+
import Trees._
20+
import tpd._
21+
1822
import collection.mutable
1923

24+
2025
object Constructors {
2126
val name: String = "constructors"
2227
}
@@ -31,7 +36,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
3136
import tpd._
3237

3338
override def phaseName: String = Constructors.name
34-
override def runsAfter: Set[String] = Set(HoistSuperArgs.name)
39+
override def runsAfter: Set[String] = Set(HoistSuperArgs.name, MoveStatics.name)
3540
override def runsAfterGroupsOf: Set[String] = Set(Memoize.name)
3641
// Memoized needs to be finished because we depend on the ownerchain after Memoize
3742
// when checking whether an ident is an access in a constructor or outside it.
@@ -311,6 +316,19 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
311316
}
312317
else cpy.DefDef(constr)(rhs = Block(finalConstrStats, unitLiteral))
313318

319+
320+
val staticFields = clsStats.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).toList.asInstanceOf[List[ValDef]]
321+
if (staticFields.nonEmpty) {
322+
/* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */
323+
val staticCostructor = newSymbol(cls, nme.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method | Flags.Private, MethodType(Nil, defn.UnitType))
324+
staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot))
325+
staticCostructor.entered
326+
327+
val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor)))
328+
val staticConstr = tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral))
329+
clsStats.prepend(staticConstr)
330+
}
331+
314332
cpy.Template(tree)(constr = expandedConstr, body = clsStats.toList)
315333
}
316334
}

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ object Erasure {
695695

696696
val owner = mapOwner(origSym)
697697
val sym = if (owner eq origSym.maybeOwner) origSym else owner.info.decl(tree.name).symbol
698-
assert(sym.exists, origSym.showLocated)
698+
assert(sym.exists, tree.show)
699699

700700
if owner == defn.ObjectClass then checkValue(qual1)
701701

Lines changed: 98 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,42 @@
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
153

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._
1915

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+
*/
2137
class MoveStatics extends MiniPhase with SymTransformer {
38+
import ast.tpd._
2239

23-
import tpd._
2440
override def phaseName: String = MoveStatics.name
2541

2642
def transformSym(sym: SymDenotation)(using Context): SymDenotation =
@@ -32,50 +48,89 @@ class MoveStatics extends MiniPhase with SymTransformer {
3248
}
3349
else sym
3450

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+
35100
override def transformStats(trees: List[Tree])(using Context): List[Tree] =
36101
if (ctx.owner.is(Flags.Package)) {
37102
val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass)
38103
val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]]
39104

40105
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-
56106
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))
58108
}
59109

60110
def move(module: TypeDef, companion: TypeDef): List[Tree] = {
61111
assert(companion != module)
62112
if (!module.symbol.is(Flags.Module)) move(companion, module)
63113
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
69122
}
70123
}
71124
val newPairs =
72125
for ((name, classes) <- pairs)
73126
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
77128
else move(classes.head, classes.tail.head)
78129
Trees.flatten(newPairs.toList.flatten ++ others)
79130
}
80131
else trees
81132
}
133+
134+
object MoveStatics {
135+
val name: String = "moveStatic"
136+
}

0 commit comments

Comments
 (0)