Skip to content

Commit 5dd0ba5

Browse files
committed
Don't export members that will be synthesized in case classes
Fixes #13228
1 parent 2279afc commit 5dd0ba5

File tree

5 files changed

+32
-4
lines changed

5 files changed

+32
-4
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,9 @@ class Definitions {
12101210
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
12111211
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)
12121212

1213+
@tu lazy val caseClassSynthesized: Set[Symbol] = Set(Any_toString, Product_canEqual,
1214+
Product_productArity, Product_productPrefix, Product_productElement, Product_productElementName)
1215+
12131216
val LazyHolder: PerRun[Map[Symbol, Symbol]] = new PerRun({
12141217
def holderImpl(holderType: String) = requiredClass("scala.runtime." + holderType)
12151218
Map[Symbol, Symbol](

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
6363
private def initSymbols(using Context) =
6464
if (myValueSymbols.isEmpty) {
6565
myValueSymbols = List(defn.Any_hashCode, defn.Any_equals)
66-
myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual,
67-
defn.Product_productArity, defn.Product_productPrefix, defn.Product_productElement,
68-
defn.Product_productElementName)
66+
myCaseSymbols = myValueSymbols ++ defn.caseClassSynthesized
6967
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
7068
myEnumValueSymbols = List(defn.Product_productPrefix)
7169
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,10 +1154,19 @@ class Namer { typer: Typer =>
11541154
.flatMap(path.tpe.memberBasedOnFlags(_, excluded = Private|Given|ConstructorProxy).alternatives)
11551155
.foreach(addForwarder(name, _, span)) // ignore if any are not added
11561156

1157+
val fromCaseClass = path.tpe.widen.classSymbols.exists(_.is(Case))
1158+
1159+
/** Is symbol from a base trait of a case class so that it will be synthesized
1160+
* in the case class itself. Such members are treated like synthetic members,
1161+
* i.e. they don't get export forwarders.
1162+
*/
1163+
def isCaseClassSynthesized(mbr: Symbol) =
1164+
fromCaseClass && defn.caseClassSynthesized.contains(mbr)
1165+
11571166
def addWildcardForwarders(seen: List[TermName], span: Span): Unit =
11581167
val nonContextual = mutable.HashSet(seen: _*)
11591168
for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do
1160-
if !mbr.symbol.isSuperAccessor then
1169+
if !mbr.symbol.isSuperAccessor && !isCaseClassSynthesized(mbr.symbol) then
11611170
// Scala 2 superaccessors have neither Synthetic nor Artfact set, so we
11621171
// need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts)
11631172
val alias = mbr.name.toTermName

tests/run/i13228.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RegisteredUser(Id,User(Name))
2+
false
3+
false
4+
false
5+
false

tests/run/i13228.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
case class User(name: String)
2+
3+
case class RegisteredUser(id: String, data: User) {
4+
export data.*
5+
}
6+
7+
@main def Test() =
8+
println(RegisteredUser("Id", User("Name"))) // RegisteredUser(Name)
9+
println(RegisteredUser("Id", User("Name")).canEqual(User("Name"))) // True
10+
// The rest works as expected
11+
println(RegisteredUser("Id", User("Name")) == User("Name")) // False
12+
println(RegisteredUser("Id", User("Name")).hashCode == User("Name").hashCode) // False
13+
println(RegisteredUser("Id", User("Name")).productArity == User("Name").productArity) // False

0 commit comments

Comments
 (0)