Skip to content

Commit 52b720a

Browse files
committed
Merge pull request #710 from dotty-staging/fix/#705-inner-value-class
Value class related checks
2 parents 3512840 + f57a6d9 commit 52b720a

File tree

6 files changed

+58
-8
lines changed

6 files changed

+58
-8
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import ErrorReporting._
1616
import tpd.ListOfTreeDecorator
1717
import config.Printers._
1818
import Annotations._
19+
import transform.ValueClasses._
1920
import language.implicitConversions
2021

2122
trait NamerContextOps { this: Context =>
@@ -580,6 +581,7 @@ class Namer { typer: Typer =>
580581
index(rest)(inClassContext(selfInfo))
581582
denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
582583
addAnnotations(denot)
584+
if (isDerivedValueClass(cls)) cls.setFlag(Final)
583585
cls.setApplicableFlags(
584586
(NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
585587
}

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

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,17 @@ object RefChecks {
7171
}
7272
}
7373

74-
/** Check that self type of this class conforms to self types of parents */
75-
private def checkSelfType(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match {
74+
/** Check that final and sealed restrictions on class parents
75+
* and that self type of this class conforms to self types of parents.
76+
*/
77+
private def checkParents(clazz: Symbol)(implicit ctx: Context): Unit = clazz.info match {
7678
case cinfo: ClassInfo =>
7779
for (parent <- cinfo.classParents) {
80+
val pclazz = parent.classSymbol
81+
if (pclazz.is(Final))
82+
ctx.error(d"cannot extend final $pclazz", clazz.pos)
83+
if (pclazz.is(Sealed) && pclazz.associatedFile != clazz.associatedFile)
84+
ctx.error(d"cannot extend sealed $pclazz in different compilation unit", clazz.pos)
7885
val pself = parent.givenSelfType.asSeenFrom(clazz.thisType, parent.classSymbol)
7986
if (pself.exists && !(cinfo.selfType <:< pself))
8087
ctx.error(d"illegal inheritance: self type ${cinfo.selfType} of $clazz does not conform to self type $pself of parent ${parent.classSymbol}", clazz.pos)
@@ -648,13 +655,27 @@ object RefChecks {
648655
}
649656

650657
/** Verify classes extending AnyVal meet the requirements */
651-
private def checkAnyValSubclass(clazz: Symbol)(implicit ctx: Context) =
658+
private def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = {
659+
def checkValueClassMember(stat: Tree) = stat match {
660+
case _: ValDef if !stat.symbol.is(ParamAccessor) =>
661+
ctx.error(s"value class may not define non-parameter field", stat.pos)
662+
case _: DefDef if stat.symbol.isConstructor =>
663+
ctx.error(s"value class may not define secondary constructor", stat.pos)
664+
case _: MemberDef | _: Import | EmptyTree =>
665+
// ok
666+
case _ =>
667+
ctx.error(s"value class may not contain initialization statements", stat.pos)
668+
}
652669
if (isDerivedValueClass(clazz)) {
653670
if (clazz.is(Trait))
654671
ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos)
655-
else if (clazz.is(Abstract))
672+
if (clazz.is(Abstract))
656673
ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
674+
if (!clazz.isStatic)
675+
ctx.error("value class cannot be an inner class", clazz.pos)
676+
stats.foreach(checkValueClassMember)
657677
}
678+
}
658679

659680
type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]
660681

@@ -701,7 +722,7 @@ import RefChecks._
701722
* - only one overloaded alternative defines default arguments
702723
* - applyDynamic methods are not overloaded
703724
* - all overrides conform to rules laid down by `checkAllOverrides`.
704-
* - any value classes conform to rules laid down by `checkAnyValSubClass`.
725+
* - any value classes conform to rules laid down by `checkDerivedValueClass`.
705726
* - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals).
706727
* - no forward reference in a local block jumps over a non-lazy val definition.
707728
* - a class and its companion object do not both define a class or module with the same name.
@@ -768,10 +789,10 @@ class RefChecks extends MiniPhase { thisTransformer =>
768789
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
769790
val cls = ctx.owner
770791
checkOverloadedRestrictions(cls)
771-
checkSelfType(cls)
792+
checkParents(cls)
772793
checkCompanionNameClashes(cls)
773794
checkAllOverrides(cls)
774-
checkAnyValSubclass(cls)
795+
checkDerivedValueClass(cls, tree.body)
775796
tree
776797
}
777798

test/dotc/tests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class tests extends CompilerTest {
133133
@Test def neg_i0248_inherit_refined = compileFile(negDir, "i0248-inherit-refined", xerrors = 4)
134134
@Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3)
135135
@Test def neg_i583 = compileFile(negDir, "i0583-skolemize", xerrors = 2)
136+
@Test def neg_finalSealed = compileFile(negDir, "final-sealed", xerrors = 2)
137+
@Test def neg_i705 = compileFile(negDir, "i705-inner-value-class", xerrors = 7)
136138
@Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4)
137139
@Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2)
138140
@Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8)

tests/neg/final-sealed.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
final class A
2+
class B extends A
3+
class C extends Option[Int]
4+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Foo {
2+
class B(val a: Int) extends AnyVal // error
3+
}
4+
5+
class VCwithBadMembers(val a: Int) extends AnyVal {
6+
def this() = this(1) // error
7+
var x = 0 // error
8+
val y = 2 // error
9+
println("hi") // error
10+
}
11+
12+
object Test {
13+
class B(val a: Int) extends AnyVal // ok
14+
def f = {
15+
class C(val a: Int) extends AnyVal // error
16+
new C(1)
17+
}
18+
class B1(val b: Int) extends B(b)
19+
// class D extends B( { class E(val a: Int) extends AnyVal; new E(1) } ) // error
20+
}
21+
22+

tests/pos/extmethods.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
trait That1[A]
22
class T[A, This <: That1[A]](val x: Int) extends AnyVal {
33
self: This =>
4-
var next: This = _
54
final def loop(x: This, cnt: Int): Int = loop(x, cnt + 1)
65
def const[B](): Boolean = return true
76
}

0 commit comments

Comments
 (0)