aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-10-21 17:49:27 +0200
committerMartin Odersky <odersky@gmail.com>2015-10-22 12:21:26 +0200
commitf1b3859911ee04a90a0b169c5eefa2c64ce5d265 (patch)
tree0d8d90bc89d62a83c7bd1395ecac1e43f922bd09
parent54f5899b0888983495f5ff70f561d9634350f3f2 (diff)
downloaddotty-f1b3859911ee04a90a0b169c5eefa2c64ce5d265.tar.gz
dotty-f1b3859911ee04a90a0b169c5eefa2c64ce5d265.tar.bz2
dotty-f1b3859911ee04a90a0b169c5eefa2c64ce5d265.zip
Add well-formedness checking for created symbols
Enforces various restrictions of definitions.
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala43
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala20
-rw-r--r--test/dotc/tests.scala3
-rw-r--r--tests/neg/validate-parsing.scala13
-rw-r--r--tests/neg/validate-refchecks.scala13
-rw-r--r--tests/neg/validate.scala48
6 files changed, 138 insertions, 2 deletions
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 8f4a8b72b..1c0274c84 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -230,6 +230,49 @@ object Checking {
}
checkTree((), refinement)
}
+
+ /** Check that symbol's definition is well-formed. */
+ def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = {
+ //println(i"check wf $sym with flags ${sym.flags}")
+ def fail(msg: String) = ctx.error(msg, sym.pos)
+ def varNote =
+ if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)"
+ else ""
+
+ def checkWithDeferred(flag: FlagSet) =
+ if (sym.is(flag))
+ fail(i"abstract member may not have `$flag' modifier")
+ def checkNoConflict(flag1: FlagSet, flag2: FlagSet) =
+ if (sym.is(allOf(flag1, flag2)))
+ fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym")
+
+ if (sym.is(ImplicitCommon)) {
+ if (sym.owner.is(Package))
+ fail(i"`implicit' modifier cannot be used for top-level definitions")
+ if (sym.isType)
+ fail(i"`implicit' modifier cannot be used for types or traits")
+ }
+ if (!sym.isClass && sym.is(Abstract))
+ fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members")
+ if (sym.is(AbsOverride) && !sym.owner.is(Trait))
+ fail(i"`abstract override' modifier only allowed for members of traits")
+ if (sym.is(Trait) && sym.is(Final))
+ fail(i"$sym may not be `final'")
+ if (sym.hasAnnotation(defn.NativeAnnot))
+ if (sym.is(Deferred)) sym.resetFlag(Deferred)
+ else fail(i"`@native' members may not have implementation")
+ if (sym.is(Deferred, butNot = Param) && !sym.isSelfSym) {
+ if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass)
+ fail(i"only classes can have declared but undefined members$varNote")
+ checkWithDeferred(Private)
+ checkWithDeferred(Final)
+ }
+ if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
+ fail(i"$sym cannot extend AnyVal")
+ checkNoConflict(Final, Sealed)
+ checkNoConflict(Private, Protected)
+ checkNoConflict(Abstract, Override)
+ }
}
trait Checking {
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index d97bbff91..52989c1a5 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -234,6 +234,18 @@ class Namer { typer: Typer =>
sym
}
+ def checkFlags(flags: FlagSet) =
+ if (flags.isEmpty) flags
+ else {
+ val (ok, adapted, kind) = tree match {
+ case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
+ case _ => (flags.isTermFlags, flags.toTermFlags, "value")
+ }
+ if (!ok)
+ ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos)
+ adapted
+ }
+
/** Add moduleClass/sourceModule to completer if it is for a module val or class */
def adjustIfModule(completer: LazyType, tree: MemberDef) =
if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode)
@@ -261,14 +273,16 @@ class Namer { typer: Typer =>
tree match {
case tree: TypeDef if tree.isClassDef =>
val name = checkNoConflict(tree.name.encode).asTypeName
+ val flags = checkFlags(tree.mods.flags &~ Implicit)
val cls = record(ctx.newClassSymbol(
- ctx.owner, name, tree.mods.flags | inSuperCall,
+ ctx.owner, name, flags | inSuperCall,
cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree),
privateWithinClass(tree.mods), tree.pos, ctx.source.file))
cls.completer.asInstanceOf[ClassCompleter].init()
cls
case tree: MemberDef =>
val name = checkNoConflict(tree.name.encode)
+ val flags = checkFlags(tree.mods.flags)
val isDeferred = lacksDefinition(tree)
val deferred = if (isDeferred) Deferred else EmptyFlags
val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags
@@ -291,7 +305,7 @@ class Namer { typer: Typer =>
val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx
record(ctx.newSymbol(
- ctx.owner, name, tree.mods.flags | deferred | method | higherKinded | inSuperCall1,
+ ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1,
adjustIfModule(new Completer(tree)(cctx), tree),
privateWithinClass(tree.mods), tree.pos))
case tree: Import =>
@@ -517,6 +531,7 @@ class Namer { typer: Typer =>
def completeInCreationContext(denot: SymDenotation): Unit = {
denot.info = typeSig(denot.symbol)
addAnnotations(denot)
+ Checking.checkWellFormed(denot.symbol)
}
}
@@ -585,6 +600,7 @@ class Namer { typer: Typer =>
index(rest)(inClassContext(selfInfo))
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
addAnnotations(denot)
+ Checking.checkWellFormed(cls)
if (isDerivedValueClass(cls)) cls.setFlag(Final)
cls.setApplicableFlags(
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 7674b1d6f..337285c04 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -157,6 +157,9 @@ class tests extends CompilerTest {
@Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3)
@Test def neg_implicitLowerBound = compileFile(negDir, "implicit-lower-bound", xerrors = 1)
@Test def neg_partialApplications = compileFile(negDir, "partialApplications", xerrors = 8)
+ @Test def neg_validate = compileFile(negDir, "validate", xerrors = 18)
+ @Test def neg_validateParsing = compileFile(negDir, "validate-parsing", xerrors = 7)
+ @Test def neg_validateRefchecks = compileFile(negDir, "validate-refchecks", xerrors = 2)
@Test def run_all = runFiles(runDir)
diff --git a/tests/neg/validate-parsing.scala b/tests/neg/validate-parsing.scala
new file mode 100644
index 000000000..d0eee526a
--- /dev/null
+++ b/tests/neg/validate-parsing.scala
@@ -0,0 +1,13 @@
+object A {
+ sealed def y: Int = 1 // error: modifier(s) `sealed' not allowed for method
+ sealed var x = 1 // error: modifier(s) `sealed' not allowed for variable
+ lazy trait T // error: modifier(s) `lazy' not allowed for trait
+}
+
+class C () {
+ implicit this() = this() // error: ';' expected but 'implicit' found.
+ override this() = this() // error: ';' expected but 'override' found.
+}
+class D override() // error: ';' expected but 'override' found.
+
+case class ByName(x: => Int) // error: `val' parameters may not be call-by-name
diff --git a/tests/neg/validate-refchecks.scala b/tests/neg/validate-refchecks.scala
new file mode 100644
index 000000000..10e61407d
--- /dev/null
+++ b/tests/neg/validate-refchecks.scala
@@ -0,0 +1,13 @@
+
+trait A {
+ class C {}
+}
+
+trait B extends A {
+ class C {} // error: cannot override
+}
+
+trait C extends A {
+ type C = Int // error: cannot override
+}
+
diff --git a/tests/neg/validate.scala b/tests/neg/validate.scala
new file mode 100644
index 000000000..38da83fd7
--- /dev/null
+++ b/tests/neg/validate.scala
@@ -0,0 +1,48 @@
+trait X {
+ type Y
+ abstract val v: Y // error: abstract term
+ abstract def y: Y // error: abstract term
+}
+
+implicit object Z { // error: implict at toplevel
+ implicit case class C() // error: implicit classes may not be case classes
+ implicit type T = Int // error: implicit modifier cannot be used for types or traits
+ implicit trait U // error: implicit modifier cannot be used for types or traits
+ val x: X = new X {
+ type Y = Int
+ val v: Int = 1
+ }
+ var y: Int // error: only classes can have declared but undefined members
+ val z: Int = {
+ val u: Int // error: only classes can have declared but undefined members
+ 1
+ }
+}
+
+trait T {
+ type X
+ def foo: Unit = {
+ var x: Int // error: only classes can have declared but undefined members
+ ()
+ }
+ private def bar: Int // error: abstract member may not have private modifier
+ final def baz: Int // error: abstract member may not have final modifier
+}
+
+final sealed class A { // error: illegal combination of modifiers: final and sealed
+ private protected def f: Int = 1 // error: illegal combination of modifiers: private and protected
+}
+
+
+class E extends T {
+ abstract override def foo: Unit // error: abstract override only allowed for members of traits
+}
+
+trait U extends T {
+ abstract override type X // error: `abstract override' incompatible with type definition
+ @native def f(): Unit = 1 // error: `@native' members may not have implementation
+}
+
+trait TT extends AnyVal // error: trait TT annot extend AnyVal
+
+final trait UU // error: trait UU may not be `final'