diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 7c1c2494d323..018e68d46032 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1210,6 +1210,10 @@ 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: List[Symbol] = List( + Any_hashCode, Any_equals, 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]( diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 817be9cba633..a80b99ec7790 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -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 = defn.caseClassSynthesized myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals) myEnumValueSymbols = List(defn.Product_productPrefix) myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index b4bc79cd1f60..29a6da138e6c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1156,10 +1156,15 @@ class Namer { typer: Typer => def addWildcardForwarders(seen: List[TermName], span: Span): Unit = val nonContextual = mutable.HashSet(seen: _*) + val fromCaseClass = path.tpe.widen.classSymbols.exists(_.is(Case)) + def isCaseClassSynthesized(mbr: Symbol) = + fromCaseClass && defn.caseClassSynthesized.contains(mbr) 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) + // Symbols from base traits of case classes that will get synthesized implementations + // at PostTyper are also excluded. val alias = mbr.name.toTermName if mbr.symbol.is(Given) then if !seen.contains(alias) && mbr.matchesImportBound(givenBound) then diff --git a/tests/run/i13228.check b/tests/run/i13228.check new file mode 100644 index 000000000000..3cdefad74555 --- /dev/null +++ b/tests/run/i13228.check @@ -0,0 +1,5 @@ +RegisteredUser(Id,User(Name)) +false +false +false +false diff --git a/tests/run/i13228.scala b/tests/run/i13228.scala new file mode 100644 index 000000000000..e1b0dcc4fb46 --- /dev/null +++ b/tests/run/i13228.scala @@ -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