Skip to content

Fix #8022: Refactor MoveStatics #11126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,7 @@ import transform.SymUtils._
}

class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) {
def msg = em"${hl("@static")} $member in ${member.owner} must be defined inside an ${hl("object")}."
def msg = em"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}."
def explain =
em"${hl("@static")} members are only allowed inside objects."
}
Expand Down
19 changes: 1 addition & 18 deletions compiler/src/dotty/tools/dotc/transform/CheckStatic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class CheckStatic extends MiniPhase {
var hadNonStaticField = false
for (defn <- defns)
if (defn.symbol.isScalaStatic) {
if (!ctx.owner.is(Module))
if (!ctx.owner.isStaticOwner)
report.error(StaticFieldsOnlyAllowedInObjects(defn.symbol), defn.srcPos)
defn.symbol.resetFlag(JavaStatic)

Expand All @@ -59,23 +59,6 @@ class CheckStatic extends MiniPhase {

tree
}

override def transformSelect(tree: tpd.Select)(using Context): tpd.Tree =
if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot)) {
val symbolWhitelist = tree.symbol.ownersIterator.flatMap(x => if (x.is(Flags.Module)) List(x, x.companionModule) else List(x)).toSet
def isSafeQual(t: Tree): Boolean = // follow the desugared paths created by typer
t match {
case t: This => true
case t: Select => isSafeQual(t.qualifier) && symbolWhitelist.contains(t.symbol)
case t: Ident => symbolWhitelist.contains(t.symbol)
case t: Block => t.stats.forall(tpd.isPureExpr) && isSafeQual(t.expr)
case _ => false
}
if (isSafeQual(tree.qualifier))
ref(tree.symbol)
else tree
}
else tree
}

object CheckStatic {
Expand Down
64 changes: 37 additions & 27 deletions compiler/src/dotty/tools/dotc/transform/MoveStatics.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
package dotty.tools.dotc.transform
package dotty.tools.dotc
package transform

import dotty.tools.dotc.ast.{Trees, tpd}
import dotty.tools.dotc.core.Annotations.Annotation
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.DenotTransformers.SymTransformer
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import dotty.tools.dotc.core.NameOps._
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.StdNames.nme
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types.MethodType
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
import core._
import Flags._
import Contexts._
import Symbols._
import Decorators._
import DenotTransformers.SymTransformer
import Types.MethodType
import Annotations.Annotation
import SymDenotations.SymDenotation
import Names.Name
import StdNames.nme
import NameOps._

object MoveStatics {
val name: String = "moveStatic"
}
import reporting._
import ast._

import SymUtils._
import MegaPhase._

/** Move static methods from companion to the class itself */
class MoveStatics extends MiniPhase with SymTransformer {
import ast.tpd._

import tpd._
override def phaseName: String = MoveStatics.name

def transformSym(sym: SymDenotation)(using Context): SymDenotation =
Expand All @@ -38,8 +41,6 @@ class MoveStatics extends MiniPhase with SymTransformer {
val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]]

def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = {
if (orig eq null) return EmptyTree

val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]]
val newBodyWithStaticConstr =
if (staticFields.nonEmpty) {
Expand All @@ -61,21 +62,30 @@ class MoveStatics extends MiniPhase with SymTransformer {
assert(companion != module)
if (!module.symbol.is(Flags.Module)) move(companion, module)
else {
val allMembers =
(if (companion != null) {companion.rhs.asInstanceOf[Template].body} else Nil) ++
module.rhs.asInstanceOf[Template].body
val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol})
Trees.flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil)
val moduleTmpl = module.rhs.asInstanceOf[Template]
val companionTmpl = companion.rhs.asInstanceOf[Template]
val (staticDefs, remainingDefs) = moduleTmpl.body.partition {
case memberDef: MemberDef => memberDef.symbol.isScalaStatic
case _ => false
}

rebuild(companion, companionTmpl.body ++ staticDefs) :: rebuild(module, remainingDefs) :: Nil
}
}
val newPairs =
for ((name, classes) <- pairs)
yield
if (classes.tail.isEmpty)
if (classes.head.symbol.is(Flags.Module)) move(classes.head, null)
else List(rebuild(classes.head, classes.head.rhs.asInstanceOf[Template].body))
if (classes.tail.isEmpty) {
val classDef = classes.head
val tmpl = classDef.rhs.asInstanceOf[Template]
rebuild(classDef, tmpl.body) :: Nil
}
else move(classes.head, classes.tail.head)
Trees.flatten(newPairs.toList.flatten ++ others)
}
else trees
}

object MoveStatics {
val name: String = "moveStatic"
}
14 changes: 14 additions & 0 deletions tests/neg/i11100.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.annotation.static

class C {
val a: Int = 3
class D
object D {
@static def foo: Int = a * a // error
}
}

@main
def Test =
val c = new C
println(c.D.foo)
25 changes: 12 additions & 13 deletions tests/run/statics.scala
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import scala.annotation.static

class Foo{
class Foo {
class Bar {
def qwa =
Bar.field
Bar.field
// 0: invokestatic #31 // Method Foo$Bar$.field:()I
// 3: ireturn
}
object Bar {
@static
val field = 1
}
}

object Foo{
@static
def method = 1
object Foo {
@static
def method = 1

@static
val field = 2
@static
val field = 2

@static
var mutable = 3
@static
var mutable = 3

@static
def accessor = field
@static
def accessor = field
}

object Test {
Expand All @@ -34,7 +33,7 @@ object Test {
}
}

class WithLazies{
class WithLazies {
lazy val s = 1
// 98: getstatic #30 // Field WithLazies$.OFFSET$0:J
}