From abb9aa802789e6b43ff07c45281eedbb09512585 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 7 Nov 2016 23:57:06 +0100 Subject: fix #1642: disallow value classe wrapping value class --- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala | 8 -------- compiler/src/dotty/tools/dotc/transform/ValueClasses.scala | 10 ++++++++++ compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 7 +++++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 1 - 5 files changed, 16 insertions(+), 12 deletions(-) (limited to 'compiler/src') diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index abbacee49..28dbed8f6 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -427,7 +427,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = { val cls = tref.symbol.asClass val underlying = underlyingOfValueClass(cls) - if (underlying.exists) ErasedValueType(tref, valueErasure(underlying)) + if (underlying.exists && !isCyclic(cls)) ErasedValueType(tref, valueErasure(underlying)) else NoType } diff --git a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 5ae4e8a54..925ec08b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -135,14 +135,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful // TODO: this is state and should be per-run // todo: check that when transformation finished map is empty - private def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Unit = - if (seen contains clazz) - ctx.error("value class may not unbox to itself", pos) - else { - val unboxed = underlyingOfValueClass(clazz).typeSymbol - if (isDerivedValueClass(unboxed)) checkNonCyclic(pos, seen + clazz, unboxed.asClass) - } - override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { if (isDerivedValueClass(ctx.owner)) { /* This is currently redundant since value classes may not diff --git a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala index 93005c57a..b16d05644 100644 --- a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -53,4 +53,14 @@ object ValueClasses { def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type = valueClassUnbox(d).info.resultType + /** Whether a value class wraps itself */ + def isCyclic(cls: ClassSymbol)(implicit ctx: Context): Boolean = { + def recur(seen: Set[Symbol], clazz: ClassSymbol)(implicit ctx: Context): Boolean = + (seen contains clazz) || { + val unboxed = underlyingOfValueClass(clazz).typeSymbol + (isDerivedValueClass(unboxed)) && recur(seen + clazz, unboxed.asClass) + } + + recur(Set[Symbol](), cls) + } } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index feeb80547..f9c9ddee4 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -18,7 +18,7 @@ import config.{ScalaVersion, NoScalaVersion} import Decorators._ import typer.ErrorReporting._ import DenotTransformers._ -import ValueClasses.isDerivedValueClass +import ValueClasses.{isDerivedValueClass, isCyclic} object RefChecks { import tpd._ @@ -707,12 +707,15 @@ object RefChecks { ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos) if (!clazz.isStatic) ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos) + if (isCyclic(clazz.asClass)) + ctx.error("value class cannot wrap itself", clazz.pos) else { - val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method)) + val clParamAccessors = clazz.asClass.paramAccessors.filter(_.isTerm) clParamAccessors match { case List(param) => if (param.is(Mutable)) ctx.error("value class parameter must not be a var", param.pos) + case _ => ctx.error("value class needs to have exactly one val parameter", clazz.pos) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 34300a5e3..9d29c3a4e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1270,7 +1270,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cls, isRequired, cdef.pos) } - // check value class constraints RefChecks.checkDerivedValueClass(cls, body1) -- cgit v1.2.3