diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/tpd.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Annotations.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/CheckStatic.scala | 94 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/LazyVals.scala | 7 | ||||
-rw-r--r-- | src/scala/annotation/static.scala | 13 |
8 files changed, 124 insertions, 3 deletions
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 5776cc8e2..ef8e4997f 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -601,7 +601,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context isPrivate //|| (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass) def isFinal: Boolean = sym is Flags.Final - def isStaticMember: Boolean = (sym ne NoSymbol) && ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass)) + def isStaticMember: Boolean = (sym ne NoSymbol) && + ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot)) // guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index aee9b5b15..d9f1a3dca 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -44,6 +44,7 @@ class Compiler { List(new FirstTransform, new CheckReentrant), List(new RefChecks, + new CheckStatic, new ElimRepeated, new NormalizeFlags, new ExtensionMethods, diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 7cd469d7a..bf6084d7a 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -320,7 +320,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case _ => false } - try test || tp.symbol.is(JavaStatic) + try test || tp.symbol.is(JavaStatic) || tp.symbol.hasAnnotation(defn.ScalaStaticAnnot) catch { // See remark in SymDenotations#accessWithin case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK)) } @@ -337,6 +337,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (prefixIsElidable(tp)) Ident(tp) else if (tp.symbol.is(Module) && ctx.owner.isContainedIn(tp.symbol.moduleClass)) followOuterLinks(This(tp.symbol.moduleClass.asClass)) + else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot) + Ident(tp) else tp.prefix match { case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp) case pre => SelectFromTypeTree(TypeTree(pre), tp) diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala index b9c0eca43..2b27b5e01 100644 --- a/src/dotty/tools/dotc/core/Annotations.scala +++ b/src/dotty/tools/dotc/core/Annotations.scala @@ -47,6 +47,9 @@ object Annotations { def apply(tree: Tree) = ConcreteAnnotation(tree) + def apply(cls: ClassSymbol)(implicit ctx: Context): Annotation = + apply(cls, Nil) + def apply(cls: ClassSymbol, arg: Tree)(implicit ctx: Context): Annotation = apply(cls, arg :: Nil) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index f16f25b23..6f8a8f837 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -462,6 +462,8 @@ class Definitions { def ScalaLongSignatureAnnot(implicit ctx: Context) = ScalaLongSignatureAnnotType.symbol.asClass lazy val ScalaStrictFPAnnotType = ctx.requiredClassRef("scala.annotation.strictfp") def ScalaStrictFPAnnot(implicit ctx: Context) = ScalaStrictFPAnnotType.symbol.asClass + lazy val ScalaStaticAnnotType = ctx.requiredClassRef("scala.annotation.static") + def ScalaStaticAnnot(implicit ctx: Context) = ScalaStaticAnnotType.symbol.asClass lazy val SerialVersionUIDAnnotType = ctx.requiredClassRef("scala.SerialVersionUID") def SerialVersionUIDAnnot(implicit ctx: Context) = SerialVersionUIDAnnotType.symbol.asClass lazy val TASTYSignatureAnnotType = ctx.requiredClassRef("scala.annotation.internal.TASTYSignature") diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala new file mode 100644 index 000000000..445e9f839 --- /dev/null +++ b/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -0,0 +1,94 @@ +package dotty.tools.dotc +package transform + +import core._ +import Names._ +import StdNames.nme +import Types._ +import dotty.tools.dotc.transform.TreeTransforms.{AnnotationTransformer, TransformerInfo, MiniPhaseTransform, TreeTransformer} +import ast.Trees._ +import Flags._ +import Contexts.Context +import Symbols._ +import Constants._ +import Denotations._, SymDenotations._ +import Decorators.StringInterpolators +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Annotations.ConcreteAnnotation +import scala.collection.mutable +import DenotTransformers._ +import Names.Name +import NameOps._ +import Decorators._ +import TypeUtils._ + +/** A transformer that check that requirements of Static fields\methods are implemented: + * 1. Only objects can have members annotated with `@static` + * 2. The fields annotated with `@static` should preceed any non-`@static` fields. + * This ensures that we do not introduce surprises for users in initialization order. + * 3. If a member `foo` of an `object C` is annotated with `@static`, + * the companion class `C` is not allowed to define term members with name `foo`. + * 4. If a member `foo` of an `object C` is annotated with `@static`, the companion class `C` + * is not allowed to inherit classes that define a term member with name `foo`. + * 5. Only `@static` methods and vals are supported in companions of traits. + * Java8 supports those, but not vars, and JavaScript does not have interfaces at all. + */ +class CheckStatic extends MiniPhaseTransform { thisTransformer => + import ast.tpd._ + + override def phaseName = "checkStatic" + + + def check(tree: tpd.DefTree)(implicit ctx: Context) = { + + } + + override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + val defns = tree.body.collect{case t: ValOrDefDef => t} + var hadNonStaticField = false + for(defn <- defns) { + if (defn.symbol.hasAnnotation(ctx.definitions.ScalaStaticAnnot)) { + if(!ctx.owner.is(Module)) { + ctx.error("@static fields are only allowed inside objects", defn.pos) + } + + if (defn.isInstanceOf[ValDef] && hadNonStaticField) { + ctx.error("@static fields should preceed non-static ones", defn.pos) + } + + val companion = ctx.owner.companionClass + if (!companion.exists) { + ctx.error("object that conatin @static members should have companion class", defn.pos) + } + + val clashes = companion.asClass.membersNamed(defn.name) + if (clashes.exists) { + ctx.error("companion classes cannot define members with same name as @static member", defn.pos) + } + + if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { + ctx.error("Companions of traits cannot define mutable @static fields") + } + } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] + + } + tree + } + + override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): 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) + } + } + if (isSafeQual(tree.qualifier)) + ref(tree.symbol) + else tree + } else tree + } +} diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 0d0ba191e..25b9afa68 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -1,6 +1,7 @@ package dotty.tools.dotc package transform +import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Phases.NeedsCompanions import dotty.tools.dotc.typer.Mode @@ -91,6 +92,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee appendOffsetDefs.get(cls) match { case None => template case Some(data) => + data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot))) cpy.Template(template)(body = addInFront(data.defs, template.body)) } @@ -357,6 +359,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee .symbol.asTerm } else { // need to create a new flag offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) @@ -366,6 +369,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee case None => offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + "0").toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) @@ -375,9 +379,10 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this) + val containerTree = ValDef(containerSymbol, defaultValue(tpe)) - val offset = ref(companion).ensureApplied.select(offsetSymbol) + val offset = ref(offsetSymbol) val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get) val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag) val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification) diff --git a/src/scala/annotation/static.scala b/src/scala/annotation/static.scala new file mode 100644 index 000000000..b00072b18 --- /dev/null +++ b/src/scala/annotation/static.scala @@ -0,0 +1,13 @@ +package scala.annotation + +import scala.annotation.meta._ + +/** https://github.com/scala/scala.github.com/pull/491 */ + +@field +@getter +@beanGetter +@beanSetter +@param +@setter +final class static extends StaticAnnotation |