Skip to content

Commit 19b51b9

Browse files
authored
Merge pull request #8074 from dotty-staging/fix-#8069
Fix #8069: Disallow parameter dependent case classes
2 parents b96a026 + 27f4f2c commit 19b51b9

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

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

+19-6
Original file line numberDiff line numberDiff line change
@@ -683,15 +683,28 @@ object desugar {
683683
val mods = constr1.mods
684684
mods.is(Private) || (!mods.is(Protected) && mods.hasPrivateWithin)
685685
}
686+
687+
/** Does one of the parameter's types (in the first param clause)
688+
* mention a preceding parameter?
689+
*/
690+
def isParamDependent = constrVparamss match
691+
case vparams :: _ =>
692+
val paramNames = vparams.map(_.name).toSet
693+
vparams.exists(_.tpt.existsSubTree {
694+
case Ident(name: TermName) => paramNames.contains(name)
695+
case _ => false
696+
})
697+
case _ => false
686698

687699
val companionParent =
688-
if (constrTparams.nonEmpty ||
689-
constrVparamss.length > 1 ||
690-
mods.is(Abstract) ||
691-
restrictedAccess ||
692-
isEnumCase) anyRef
700+
if constrTparams.nonEmpty
701+
|| constrVparamss.length > 1
702+
|| mods.is(Abstract)
703+
|| restrictedAccess
704+
|| isParamDependent
705+
|| isEnumCase
706+
then anyRef
693707
else
694-
// todo: also use anyRef if constructor has a dependent method type (or rule that out)!
695708
constrVparamss.foldRight(classTypeRef)((vparams, restpe) => Function(vparams map (_.tpt), restpe))
696709
def widenedCreatorExpr =
697710
widenDefs.foldLeft(creatorExpr)((rhs, meth) => Apply(Ident(meth.name), rhs :: Nil))

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

+8
Original file line numberDiff line numberDiff line change
@@ -747,4 +747,12 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
747747
class UntypedDeepFolder[X](f: (X, Tree) => X) extends UntypedTreeAccumulator[X] {
748748
def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree)
749749
}
750+
751+
/** Is there a subtree of this tree that satisfies predicate `p`? */
752+
def (tree: Tree).existsSubTree(p: Tree => Boolean)(implicit ctx: Context): Boolean = {
753+
val acc = new UntypedTreeAccumulator[Boolean] {
754+
def apply(x: Boolean, t: Tree)(implicit ctx: Context) = x || p(t) || foldOver(x, t)
755+
}
756+
acc(false, tree)
757+
}
750758
}

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -1130,8 +1130,13 @@ class Namer { typer: Typer =>
11301130

11311131
index(constr)
11321132
index(rest)(localCtx)
1133-
symbolOfTree(constr).ensureCompleted()
1134-
1133+
symbolOfTree(constr).info.stripPoly match // Completes constr symbol as a side effect
1134+
case mt: MethodType if cls.is(Case) && mt.isParamDependent =>
1135+
// See issue #8073 for background
1136+
ctx.error(
1137+
i"""Implementation restriction: case classes cannot have dependencies between parameters""",
1138+
cls.sourcePos)
1139+
case _ =>
11351140
denot.info = savedInfo
11361141
}
11371142

tests/neg/i8069.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait A
2+
type B
3+
4+
enum Test
5+
case Test(a: A, b: a.B) // error: Implementation restriction: case classes cannot have dependencies between parameters
6+
7+
case class Test2(a: A, b: a.B) // error: Implementation restriction: case classes cannot have dependencies between parameters
8+

tests/pos/i8069.scala

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class A {
2+
class B
3+
}
4+
5+
class C(val a: A, val b: a.B)
6+
7+
@main def Test =
8+
val a = A()
9+
val b = a.B()
10+
val c = C(a, b)
11+
val d = c.b
12+
val d1: c.a.B = d

0 commit comments

Comments
 (0)