Skip to content

Commit 689bb2e

Browse files
committed
fix #1642: disallow value classe wrapping value class
1 parent bc0ec86 commit 689bb2e

File tree

8 files changed

+21
-13
lines changed

8 files changed

+21
-13
lines changed

src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
427427
private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
428428
val cls = tref.symbol.asClass
429429
val underlying = underlyingOfValueClass(cls)
430-
if (underlying.exists) ErasedValueType(tref, valueErasure(underlying))
430+
if (underlying.exists && !isCyclic(cls)) ErasedValueType(tref, valueErasure(underlying))
431431
else NoType
432432
}
433433

src/dotty/tools/dotc/transform/ExtensionMethods.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
135135
// TODO: this is state and should be per-run
136136
// todo: check that when transformation finished map is empty
137137

138-
private def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Unit =
139-
if (seen contains clazz)
140-
ctx.error("value class may not unbox to itself", pos)
141-
else {
142-
val unboxed = underlyingOfValueClass(clazz).typeSymbol
143-
if (isDerivedValueClass(unboxed)) checkNonCyclic(pos, seen + clazz, unboxed.asClass)
144-
}
145-
146138
override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
147139
if (isDerivedValueClass(ctx.owner)) {
148140
/* This is currently redundant since value classes may not

src/dotty/tools/dotc/transform/ValueClasses.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,14 @@ object ValueClasses {
5353
def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type =
5454
valueClassUnbox(d).info.resultType
5555

56+
/** Whether a value class wraps itself */
57+
def isCyclic(cls: ClassSymbol)(implicit ctx: Context): Boolean = {
58+
def recur(seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Boolean =
59+
(seen contains clazz) || {
60+
val unboxed = underlyingOfValueClass(clazz).typeSymbol
61+
(isDerivedValueClass(unboxed)) && recur(seen + clazz, unboxed.asClass)
62+
}
63+
64+
recur(Set[Symbol](), cls)
65+
}
5666
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import config.{ScalaVersion, NoScalaVersion}
1818
import Decorators._
1919
import typer.ErrorReporting._
2020
import DenotTransformers._
21-
import ValueClasses.isDerivedValueClass
21+
import ValueClasses.{isDerivedValueClass, isCyclic}
2222

2323
object RefChecks {
2424
import tpd._
@@ -714,12 +714,15 @@ object RefChecks {
714714
ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
715715
if (!clazz.isStatic)
716716
ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos)
717+
if (isCyclic(clazz.asClass))
718+
ctx.error("value class cannot wrap itself", clazz.pos)
717719
else {
718-
val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method))
720+
val clParamAccessors = clazz.asClass.paramAccessors.filter(_.isTerm)
719721
clParamAccessors match {
720722
case List(param) =>
721723
if (param.is(Mutable))
722724
ctx.error("value class parameter must not be a var", param.pos)
725+
723726
case _ =>
724727
ctx.error("value class needs to have exactly one val parameter", clazz.pos)
725728
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
12651265
cls, isRequired, cdef.pos)
12661266
}
12671267

1268-
12691268
// check value class constraints
12701269
RefChecks.checkDerivedValueClass(cls, body1)
12711270

tests/neg/i1642.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class Test2(val valueVal: Test2) extends AnyVal

tests/neg/i1670.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
class A(a:Int, b:Int) extends AnyVal
1+
class A(a:Int, b:Int) extends AnyVal

tests/pos/i1642.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Test1(val x: Int) extends AnyVal
2+
class Test2(val y: Test1) extends AnyVal
3+

0 commit comments

Comments
 (0)