Skip to content

Commit f1b3859

Browse files
committed
Add well-formedness checking for created symbols
Enforces various restrictions of definitions.
1 parent 54f5899 commit f1b3859

File tree

6 files changed

+138
-2
lines changed

6 files changed

+138
-2
lines changed

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,49 @@ object Checking {
230230
}
231231
checkTree((), refinement)
232232
}
233+
234+
/** Check that symbol's definition is well-formed. */
235+
def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = {
236+
//println(i"check wf $sym with flags ${sym.flags}")
237+
def fail(msg: String) = ctx.error(msg, sym.pos)
238+
def varNote =
239+
if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)"
240+
else ""
241+
242+
def checkWithDeferred(flag: FlagSet) =
243+
if (sym.is(flag))
244+
fail(i"abstract member may not have `$flag' modifier")
245+
def checkNoConflict(flag1: FlagSet, flag2: FlagSet) =
246+
if (sym.is(allOf(flag1, flag2)))
247+
fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym")
248+
249+
if (sym.is(ImplicitCommon)) {
250+
if (sym.owner.is(Package))
251+
fail(i"`implicit' modifier cannot be used for top-level definitions")
252+
if (sym.isType)
253+
fail(i"`implicit' modifier cannot be used for types or traits")
254+
}
255+
if (!sym.isClass && sym.is(Abstract))
256+
fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members")
257+
if (sym.is(AbsOverride) && !sym.owner.is(Trait))
258+
fail(i"`abstract override' modifier only allowed for members of traits")
259+
if (sym.is(Trait) && sym.is(Final))
260+
fail(i"$sym may not be `final'")
261+
if (sym.hasAnnotation(defn.NativeAnnot))
262+
if (sym.is(Deferred)) sym.resetFlag(Deferred)
263+
else fail(i"`@native' members may not have implementation")
264+
if (sym.is(Deferred, butNot = Param) && !sym.isSelfSym) {
265+
if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass)
266+
fail(i"only classes can have declared but undefined members$varNote")
267+
checkWithDeferred(Private)
268+
checkWithDeferred(Final)
269+
}
270+
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
271+
fail(i"$sym cannot extend AnyVal")
272+
checkNoConflict(Final, Sealed)
273+
checkNoConflict(Private, Protected)
274+
checkNoConflict(Abstract, Override)
275+
}
233276
}
234277

235278
trait Checking {

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,18 @@ class Namer { typer: Typer =>
234234
sym
235235
}
236236

237+
def checkFlags(flags: FlagSet) =
238+
if (flags.isEmpty) flags
239+
else {
240+
val (ok, adapted, kind) = tree match {
241+
case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
242+
case _ => (flags.isTermFlags, flags.toTermFlags, "value")
243+
}
244+
if (!ok)
245+
ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos)
246+
adapted
247+
}
248+
237249
/** Add moduleClass/sourceModule to completer if it is for a module val or class */
238250
def adjustIfModule(completer: LazyType, tree: MemberDef) =
239251
if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode)
@@ -261,14 +273,16 @@ class Namer { typer: Typer =>
261273
tree match {
262274
case tree: TypeDef if tree.isClassDef =>
263275
val name = checkNoConflict(tree.name.encode).asTypeName
276+
val flags = checkFlags(tree.mods.flags &~ Implicit)
264277
val cls = record(ctx.newClassSymbol(
265-
ctx.owner, name, tree.mods.flags | inSuperCall,
278+
ctx.owner, name, flags | inSuperCall,
266279
cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree),
267280
privateWithinClass(tree.mods), tree.pos, ctx.source.file))
268281
cls.completer.asInstanceOf[ClassCompleter].init()
269282
cls
270283
case tree: MemberDef =>
271284
val name = checkNoConflict(tree.name.encode)
285+
val flags = checkFlags(tree.mods.flags)
272286
val isDeferred = lacksDefinition(tree)
273287
val deferred = if (isDeferred) Deferred else EmptyFlags
274288
val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags
@@ -291,7 +305,7 @@ class Namer { typer: Typer =>
291305
val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx
292306

293307
record(ctx.newSymbol(
294-
ctx.owner, name, tree.mods.flags | deferred | method | higherKinded | inSuperCall1,
308+
ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1,
295309
adjustIfModule(new Completer(tree)(cctx), tree),
296310
privateWithinClass(tree.mods), tree.pos))
297311
case tree: Import =>
@@ -517,6 +531,7 @@ class Namer { typer: Typer =>
517531
def completeInCreationContext(denot: SymDenotation): Unit = {
518532
denot.info = typeSig(denot.symbol)
519533
addAnnotations(denot)
534+
Checking.checkWellFormed(denot.symbol)
520535
}
521536
}
522537

@@ -585,6 +600,7 @@ class Namer { typer: Typer =>
585600
index(rest)(inClassContext(selfInfo))
586601
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
587602
addAnnotations(denot)
603+
Checking.checkWellFormed(cls)
588604
if (isDerivedValueClass(cls)) cls.setFlag(Final)
589605
cls.setApplicableFlags(
590606
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))

test/dotc/tests.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ class tests extends CompilerTest {
157157
@Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3)
158158
@Test def neg_implicitLowerBound = compileFile(negDir, "implicit-lower-bound", xerrors = 1)
159159
@Test def neg_partialApplications = compileFile(negDir, "partialApplications", xerrors = 8)
160+
@Test def neg_validate = compileFile(negDir, "validate", xerrors = 18)
161+
@Test def neg_validateParsing = compileFile(negDir, "validate-parsing", xerrors = 7)
162+
@Test def neg_validateRefchecks = compileFile(negDir, "validate-refchecks", xerrors = 2)
160163

161164
@Test def run_all = runFiles(runDir)
162165

tests/neg/validate-parsing.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object A {
2+
sealed def y: Int = 1 // error: modifier(s) `sealed' not allowed for method
3+
sealed var x = 1 // error: modifier(s) `sealed' not allowed for variable
4+
lazy trait T // error: modifier(s) `lazy' not allowed for trait
5+
}
6+
7+
class C () {
8+
implicit this() = this() // error: ';' expected but 'implicit' found.
9+
override this() = this() // error: ';' expected but 'override' found.
10+
}
11+
class D override() // error: ';' expected but 'override' found.
12+
13+
case class ByName(x: => Int) // error: `val' parameters may not be call-by-name

tests/neg/validate-refchecks.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
trait A {
3+
class C {}
4+
}
5+
6+
trait B extends A {
7+
class C {} // error: cannot override
8+
}
9+
10+
trait C extends A {
11+
type C = Int // error: cannot override
12+
}
13+

tests/neg/validate.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
trait X {
2+
type Y
3+
abstract val v: Y // error: abstract term
4+
abstract def y: Y // error: abstract term
5+
}
6+
7+
implicit object Z { // error: implict at toplevel
8+
implicit case class C() // error: implicit classes may not be case classes
9+
implicit type T = Int // error: implicit modifier cannot be used for types or traits
10+
implicit trait U // error: implicit modifier cannot be used for types or traits
11+
val x: X = new X {
12+
type Y = Int
13+
val v: Int = 1
14+
}
15+
var y: Int // error: only classes can have declared but undefined members
16+
val z: Int = {
17+
val u: Int // error: only classes can have declared but undefined members
18+
1
19+
}
20+
}
21+
22+
trait T {
23+
type X
24+
def foo: Unit = {
25+
var x: Int // error: only classes can have declared but undefined members
26+
()
27+
}
28+
private def bar: Int // error: abstract member may not have private modifier
29+
final def baz: Int // error: abstract member may not have final modifier
30+
}
31+
32+
final sealed class A { // error: illegal combination of modifiers: final and sealed
33+
private protected def f: Int = 1 // error: illegal combination of modifiers: private and protected
34+
}
35+
36+
37+
class E extends T {
38+
abstract override def foo: Unit // error: abstract override only allowed for members of traits
39+
}
40+
41+
trait U extends T {
42+
abstract override type X // error: `abstract override' incompatible with type definition
43+
@native def f(): Unit = 1 // error: `@native' members may not have implementation
44+
}
45+
46+
trait TT extends AnyVal // error: trait TT annot extend AnyVal
47+
48+
final trait UU // error: trait UU may not be `final'

0 commit comments

Comments
 (0)