Skip to content

Add missing parents to companions of case classes in stdlib-bootstrapped #18153

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
46 changes: 36 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -549,14 +549,19 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>

private def scala2LibPatch(tree: TypeDef)(using Context) =
val sym = tree.symbol
if compilingScala2StdLib
&& sym.is(ModuleClass) && !sym.derivesFrom(defn.SerializableClass)
&& sym.companionClass.derivesFrom(defn.SerializableClass)
then
// Add Serializable to companion objects of serializable classes
if compilingScala2StdLib && sym.is(ModuleClass) then
// Add Serializable to companion objects of serializable classes,
// and add AbstractFunction1 to companion objects of case classes with 1 parameter.
tree.rhs match
case impl: Template =>
val parents1 = impl.parents :+ TypeTree(defn.SerializableType)
var parents1 = impl.parents
val companionClass = sym.companionClass
if !sym.derivesFrom(defn.SerializableClass) && companionClass.derivesFrom(defn.SerializableClass) then
parents1 = parents1 :+ TypeTree(defn.SerializableType)
argTypeOfCaseClassThatNeedsAbstractFunction1(sym) match
case Some(args) if parents1.head.symbol.owner == defn.ObjectClass =>
parents1 = New(defn.AbstractFunctionClass(1).typeRef).select(nme.CONSTRUCTOR).appliedToTypes(args).ensureApplied :: parents1.tail
case _ =>
val impl1 = cpy.Template(impl)(parents = parents1)
cpy.TypeDef(tree)(rhs = impl1)
else tree
Expand All @@ -567,10 +572,31 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>

def transformInfo(tp: Type, sym: Symbol)(using Context): Type = tp match
case info: ClassInfo =>
if !sym.derivesFrom(defn.SerializableClass)
&& sym.companionClass.derivesFrom(defn.SerializableClass)
then
info.derivedClassInfo(declaredParents = info.parents :+ defn.SerializableType)
var parents1 = info.parents
val companionClass = sym.companionClass
if !sym.derivesFrom(defn.SerializableClass) && companionClass.derivesFrom(defn.SerializableClass) then
parents1 = parents1 :+ defn.SerializableType
argTypeOfCaseClassThatNeedsAbstractFunction1(sym) match
case Some(args) if parents1.head.typeSymbol == defn.ObjectClass =>
parents1 = defn.AbstractFunctionClass(1).typeRef.appliedTo(args) :: parents1.tail
case _ =>
if parents1 ne info.parents then info.derivedClassInfo(declaredParents = parents1)
else tp
case _ => tp

private def argTypeOfCaseClassThatNeedsAbstractFunction1(sym: Symbol)(using Context): Option[List[Type]] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why only those with 1 argument? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(TASTy) MiMa did not find cases with 2 or more arguments in the Scala 2 library.

val companionClass = sym.companionClass
if companionClass.is(CaseClass)
&& !companionClass.primaryConstructor.is(Private)
&& !companionClass.primaryConstructor.info.isVarArgsMethod
then
sym.info.decl(nme.apply).info match
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it is overloaded?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the cases in the library are overloaded. This would need to be disambiguated if a new apply method is added to those objects.

case info: MethodType =>
info.paramInfos match
case arg :: Nil =>
Some(arg :: info.resultType :: Nil)
case args => None
case _ => None
else
None
}
10 changes: 2 additions & 8 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,6 @@ object MiMaFilters {
// Companion module class
ProblemFilters.exclude[FinalClassProblem]("scala.*$"),

// Missing types {scala.runtime.AbstractFunction1}
ProblemFilters.exclude[MissingTypesProblem]("scala.ScalaReflectionException$"),
ProblemFilters.exclude[MissingTypesProblem]("scala.UninitializedFieldError$"),
ProblemFilters.exclude[MissingTypesProblem]("scala.collection.StringView$"),

// Scala 2 intrinsic macros
ProblemFilters.exclude[FinalMethodProblem]("scala.StringContext.s"),

Expand Down Expand Up @@ -131,15 +126,12 @@ object MiMaFilters {
"scala.collection.mutable.LinkedHashMap.newBuilder", "scala.collection.mutable.LinkedHashSet.newBuilder",
"scala.collection.mutable.LongMap#ToBuildFrom.newBuilder",
"scala.collection.mutable.PriorityQueue#ResizableArrayAccess.this",
"scala.collection.StringView.andThen", "scala.collection.StringView.compose",
"scala.concurrent.BatchingExecutor#AbstractBatch.this",
"scala.concurrent.Channel#LinkedList.this",
"scala.Enumeration#ValueOrdering.this",
"scala.io.Source#RelaxedPosition.this",
"scala.PartialFunction#OrElse.andThen", "scala.PartialFunction#OrElse.orElse",
"scala.runtime.Rich*.num", "scala.runtime.Rich*.ord",
"scala.ScalaReflectionException.andThen", "scala.ScalaReflectionException.compose",
"scala.UninitializedFieldError.andThen", "scala.UninitializedFieldError.compose",
"scala.util.Properties.<clinit>",
"scala.util.Sorting.scala$util$Sorting$$mergeSort$default$5",
).map(ProblemFilters.exclude[DirectMissingMethodProblem])
Expand Down Expand Up @@ -216,6 +208,8 @@ object MiMaFilters {
"scala.collection.mutable.RedBlackTree#Node.apply", "scala.collection.mutable.RedBlackTree#Node.leaf", "scala.collection.mutable.RedBlackTree#Node.unapply", "scala.collection.mutable.RedBlackTree#Tree.empty",
"scala.collection.mutable.UnrolledBuffer.unrolledlength", "scala.collection.mutable.UnrolledBuffer#Unrolled.<init>$default$4",
"scala.collection.Searching#Found.apply", "scala.collection.Searching#Found.unapply",
"scala.collection.Searching#Found.andThen", "scala.collection.Searching#Found.compose",
"scala.collection.Searching#InsertionPoint.andThen", "scala.collection.Searching#InsertionPoint.compose",
"scala.collection.Searching#InsertionPoint.apply", "scala.collection.Searching#InsertionPoint.unapply",
"scala.collection.SortedMapFactoryDefaults.empty", "scala.collection.SortedMapFactoryDefaults.fromSpecific",
"scala.collection.SortedMapOps.ordMsg", "scala.collection.SortedSetOps.ordMsg",
Expand Down
9 changes: 0 additions & 9 deletions project/TastyMiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,6 @@ object TastyMiMaFilters {
// Probably OK: Case class with varargs
ProblemMatcher.make(ProblemKind.IncompatibleTypeChange, "scala.StringContext.parts"), // before: scala.<repeated>[Predef.String]; after: scala.collection.immutable.Seq[Predef.String] @scala.annotation.internal.Repeated

// Problem: Missing type {scala.runtime.AbstractFunction1}
ProblemMatcher.make(ProblemKind.MissingParent, "scala.collection.Searching.Found$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.collection.Searching.InsertionPoint$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.collection.StringView$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.jdk.FunctionWrappers.AsJava*$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.jdk.FunctionWrappers.FromJava*$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.ScalaReflectionException$"),
ProblemMatcher.make(ProblemKind.MissingParent, "scala.UninitializedFieldError$"),

// Probably OK: ConstantType for `null` versus `scala.Null`
// Calls to the default getter seem to link correctly.
// Tested in stdlib-bootstrapped/test/scala/collection/UnrolledBufferTest.scala
Expand Down