Skip to content

Commit

Permalink
Don't export members that will be synthesized in case classes
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Aug 2, 2021
1 parent 49d7399 commit e7b7621
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 5 deletions.
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,9 @@ class Definitions {
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)

@tu lazy val caseClassSynthesized: Set[Symbol] = Set(Any_toString, Product_canEqual,
Product_productArity, Product_productPrefix, Product_productElement, Product_productElementName)

val LazyHolder: PerRun[Map[Symbol, Symbol]] = new PerRun({
def holderImpl(holderType: String) = requiredClass("scala.runtime." + holderType)
Map[Symbol, Symbol](
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
private def initSymbols(using Context) =
if (myValueSymbols.isEmpty) {
myValueSymbols = List(defn.Any_hashCode, defn.Any_equals)
myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual,
defn.Product_productArity, defn.Product_productPrefix, defn.Product_productElement,
defn.Product_productElementName)
myCaseSymbols = myValueSymbols ++ defn.caseClassSynthesized
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
myEnumValueSymbols = List(defn.Product_productPrefix)
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString
Expand Down
13 changes: 11 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ class Namer { typer: Typer =>
* is still missing its parents. Parents are set to Nil when completion starts and are
* set to the actual parents later. If a superclass completes a subclass in one
* of its parents, the parents of the superclass or some intervening class might
* not yet be set. This situation can be detected by asking for the baseType of Any -
* not yet be set. This situation can be detected by asking for the baseType of Any -
* if that type does not exist, one of the base classes of this class misses its parents.
* If this situation arises, the computation of the superclass might be imprecise.
* For instance, in i12722.scala, the superclass of `IPersonalCoinOps` is computed
Expand Down Expand Up @@ -1151,10 +1151,19 @@ class Namer { typer: Typer =>
.flatMap(path.tpe.memberBasedOnFlags(_, excluded = Private|Given|ConstructorProxy).alternatives)
.foreach(addForwarder(name, _, span)) // ignore if any are not added

val fromCaseClass = path.tpe.widen.classSymbols.exists(_.is(Case))

/** Is symbol from a base trait of a case class so that it will be synthesized
* in the case class itself. Such members are treated like synthetic members,
* i.e. they don't get export forwarders.
*/
def isCaseClassSynthesized(mbr: Symbol) =
fromCaseClass && defn.caseClassSynthesized.contains(mbr)

def addWildcardForwarders(seen: List[TermName], span: Span): Unit =
val nonContextual = mutable.HashSet(seen: _*)
for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do
if !mbr.symbol.isSuperAccessor then
if !mbr.symbol.isSuperAccessor && !isCaseClassSynthesized(mbr.symbol) then
// Scala 2 superaccessors have neither Synthetic nor Artfact set, so we
// need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts)
val alias = mbr.name.toTermName
Expand Down
5 changes: 5 additions & 0 deletions tests/run/i13228.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RegisteredUser(Id,User(Name))
false
false
false
false
13 changes: 13 additions & 0 deletions tests/run/i13228.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
case class User(name: String)

case class RegisteredUser(id: String, data: User) {
export data.*
}

@main def Test() =
println(RegisteredUser("Id", User("Name"))) // RegisteredUser(Name)
println(RegisteredUser("Id", User("Name")).canEqual(User("Name"))) // True
// The rest works as expected
println(RegisteredUser("Id", User("Name")) == User("Name")) // False
println(RegisteredUser("Id", User("Name")).hashCode == User("Name").hashCode) // False
println(RegisteredUser("Id", User("Name")).productArity == User("Name").productArity) // False

0 comments on commit e7b7621

Please sign in to comment.