Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't export members that will be synthesized in case classes #13234

Merged
merged 8 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
dwijnand marked this conversation as resolved.
Show resolved Hide resolved
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
dwijnand marked this conversation as resolved.
Show resolved Hide resolved
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
myEnumValueSymbols = List(defn.Product_productPrefix)
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString
Expand Down
11 changes: 10 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1154,10 +1154,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