Skip to content

Commit b36c9be

Browse files
authored
Merge pull request #2926 from dotty-staging/phantoms-in-value-classes
Implement value classes with phantom parameters
2 parents a0d703b + e2db292 commit b36c9be

File tree

9 files changed

+54
-15
lines changed

9 files changed

+54
-15
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ object desugar {
503503
companionDefs(anyRef, companionMeths)
504504
else if (isValueClass) {
505505
constr0.vparamss match {
506-
case List(_ :: Nil) => companionDefs(anyRef, Nil)
506+
case (_ :: Nil) :: _ => companionDefs(anyRef, Nil)
507507
case _ => Nil // error will be emitted in typer
508508
}
509509
}

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,9 +1636,9 @@ object messages {
16361636
|"""
16371637
}
16381638

1639-
case class ValueClassNeedsExactlyOneValParam(valueClass: Symbol)(implicit ctx: Context)
1639+
case class ValueClassNeedsOneValParam(valueClass: Symbol)(implicit ctx: Context)
16401640
extends Message(ValueClassNeedsExactlyOneValParamID) {
1641-
val msg = hl"""value class needs to have exactly one ${"val"} parameter"""
1641+
val msg = hl"""value class needs one ${"val"} parameter"""
16421642
val kind = "Syntax"
16431643
val explanation = ""
16441644
}

compiler/src/dotty/tools/dotc/transform/Constructors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th
129129
// Produce aligned accessors and constructor parameters. We have to adjust
130130
// for any outer parameters, which are last in the sequence of original
131131
// parameter accessors but come first in the constructor parameter list.
132-
val accessors = cls.paramAccessors.filterNot(_.isSetter)
132+
val accessors = cls.paramAccessors.filterNot(x => x.isSetter || x.info.resultType.classSymbol == defn.ErasedPhantomClass)
133133
val vparamsWithOuterLast = vparams match {
134134
case vparam :: rest if vparam.name == nme.OUTER => rest ::: vparam :: Nil
135135
case _ => vparams

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ object Erasure {
432432
}
433433
}
434434

435-
if (tree.symbol eq defn.Phantom_assume) PhantomErasure.erasedAssume
435+
if ((origSym eq defn.Phantom_assume) || (origSym.is(Flags.ParamAccessor) && wasPhantom(pt)))
436+
PhantomErasure.erasedAssume
436437
else recur(typed(tree.qualifier, AnySelectionProto))
437438
}
438439

compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
159159
val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0
160160
def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp))
161161
val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C)
162-
val comparisons = accessors map (accessor =>
163-
This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)))
162+
val comparisons = accessors collect { case accessor if !accessor.info.isPhantom =>
163+
This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)) }
164164
val rhs = // this.x == this$0.x && this.y == x$0.y
165165
if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
166166
val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
@@ -186,7 +186,8 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {
186186
* ```
187187
*/
188188
def valueHashCodeBody(implicit ctx: Context): Tree = {
189-
assert(accessors.length == 1)
189+
assert(accessors.nonEmpty)
190+
assert(accessors.tail.forall(_.info.isPhantom))
190191
ref(accessors.head).select(nme.hashCode_).ensureApplied
191192
}
192193

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,13 +490,17 @@ object Checking {
490490
param.isTerm && !param.is(Flags.Accessor)
491491
}
492492
clParamAccessors match {
493-
case List(param) =>
493+
case param :: params =>
494494
if (param.is(Mutable))
495495
ctx.error(ValueClassParameterMayNotBeAVar(clazz, param), param.pos)
496496
if (param.info.isPhantom)
497-
ctx.error("value class parameter must not be phantom", param.pos)
498-
case _ =>
499-
ctx.error(ValueClassNeedsExactlyOneValParam(clazz), clazz.pos)
497+
ctx.error("value class first parameter must not be phantom", param.pos)
498+
else {
499+
for (p <- params if !p.info.isPhantom)
500+
ctx.error("value class can only have one non phantom parameter", p.pos)
501+
}
502+
case Nil =>
503+
ctx.error(ValueClassNeedsOneValParam(clazz), clazz.pos)
500504
}
501505
}
502506
stats.foreach(checkValueClassMember)

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -787,14 +787,14 @@ class ErrorMessagesTests extends ErrorMessagesTest {
787787
assertEquals("variable i", param.show)
788788
}
789789

790-
@Test def valueClassNeedsExactlyOneVal =
790+
@Test def valueClassNeedsOneVal =
791791
checkMessagesAfter("refchecks") {
792-
"""class MyValue(var i: Int, j: Int) extends AnyVal"""
792+
"""class MyValue() extends AnyVal"""
793793
}
794794
.expect { (ictx, messages) =>
795795
implicit val ctx: Context = ictx
796796
assertMessageCount(1, messages)
797-
val ValueClassNeedsExactlyOneValParam(valueClass) :: Nil = messages
797+
val ValueClassNeedsOneValParam(valueClass) :: Nil = messages
798798
assertEquals("class MyValue", valueClass.show)
799799
}
800800

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import MyPhantom._
2+
3+
4+
class Cursed1(val p: Boo) extends AnyVal // error
5+
6+
class Cursed2(val n: Int)(val a: Int) extends AnyVal // error
7+
8+
object MyPhantom extends Phantom {
9+
type Boo <: super[MyPhantom].Any
10+
def boo: Boo = assume
11+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import MyPhantom._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val cursed = new Cursed(7)(boo)
6+
val cursed2 = new Cursed(7)(boo)
7+
cursed.p
8+
cursed2.p
9+
}
10+
}
11+
12+
13+
class Cursed(val n: Int)(val p: Boo) extends AnyVal
14+
15+
class Cursed2[B <: Boo](val n: Int)(val p: B) extends AnyVal
16+
17+
class Cursed3[B <: Boo](val n: Int)(val p1: Boo, val p2: B) extends AnyVal
18+
19+
object MyPhantom extends Phantom {
20+
type Boo <: super[MyPhantom].Any
21+
def boo: Boo = assume
22+
}

0 commit comments

Comments
 (0)