Skip to content

Commit

Permalink
Remove by-name restriction for case copy
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed May 8, 2024
1 parent ab41073 commit 914c657
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
import s.XsourceFeatures.contains
def caseApplyCopyAccess = isScala3 && contains(o.caseApplyCopyAccess)
def caseCompanionFunction = isScala3 && contains(o.caseCompanionFunction)
def caseCopyByName = isScala3 && contains(o.caseCopyByName)
def inferOverride = isScala3 && contains(o.inferOverride)
def any2StringAdd = isScala3 && contains(o.any2StringAdd)
def unicodeEscapesRaw = isScala3 && contains(o.unicodeEscapesRaw)
Expand Down
18 changes: 10 additions & 8 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "")
val reporter = StringSetting ("-Xreporter", "classname", "Specify a custom subclass of FilteringReporter for compiler messages.", "scala.tools.nsc.reporters.ConsoleReporter")
private val XsourceHelp =
sm"""|-Xsource:3 is for migrating a codebase, -Xsource-features can be added for
|cross-building to adopt certain Scala 3 behavior.
sm"""|-Xsource:3 is for migrating a codebase. -Xsource-features can be added for
|cross-building to adopt certain Scala 3 behaviors.
|
|See also "Scala 2 with -Xsource:3" on docs.scala-lang.org.
|
Expand Down Expand Up @@ -169,9 +169,10 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
// buffet of features available under -Xsource:3
object sourceFeatures extends MultiChoiceEnumeration {
// Changes affecting binary encoding
val caseApplyCopyAccess = Choice("case-apply-copy-access", "Constructor modifiers are used for apply / copy methods of case classes. [bin]")
val caseApplyCopyAccess = Choice("case-apply-copy-access", "Constructor modifiers are used for apply / copy methods of case classes. [bin]")
val caseCompanionFunction = Choice("case-companion-function", "Synthetic case companion objects no longer extend FunctionN. [bin]")
val inferOverride = Choice("infer-override", "Inferred type of member uses type of overridden member. [bin]")
val caseCopyByName = Choice("case-copy-by-name", "Synthesize case copy method with by-name parameters. [bin]")
val inferOverride = Choice("infer-override", "Inferred type of member uses type of overridden member. [bin]")

// Other semantic changes
val any2StringAdd = Choice("any2stringadd", "Implicit `any2stringadd` is never inferred.")
Expand Down Expand Up @@ -203,10 +204,11 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett
helpText = Some(
sm"""Enable Scala 3 features under -Xsource:3.
|
|Instead of `-Xsource-features:_`, it is recommended to enable specific features, for
|example `-Xsource-features:v2.13.14,-case-companion-function` (-x to exclude x).
|This way, new semantic changes in future Scala versions are not silently adopted;
|new features can be enabled after auditing the corresponding migration warnings.
|Instead of `-Xsource-features:_`, it is recommended to enable individual features.
|Features can also be removed from a feature group by prefixing with `-`;
|for example, `-Xsource-features:v2.13.14,-case-companion-function`.
|Listing features explicitly ensures new semantic changes in future Scala versions are
|not silently adopted; new features can be enabled after auditing migration warnings.
|
|`-Xsource:3-cross` is a shorthand for `-Xsource:3 -Xsource-features:_`.
|
Expand Down
20 changes: 9 additions & 11 deletions src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ trait Unapplies extends ast.TreeDSL {

import global._
import definitions._
import CODE.{ CASE => _, _ }
import treeInfo.{ isRepeatedParamType, isByNameParamType }
import CODE.{CASE => _, _}
import treeInfo.{isByNameParamType, isRepeatedParamType}

private def unapplyParamName = nme.x_0
private def caseMods = Modifiers(SYNTHETIC | CASE)
Expand Down Expand Up @@ -265,26 +265,24 @@ trait Unapplies extends ast.TreeDSL {
* ClassDef of the case class.
*/
def caseClassCopyMeth(cdef: ClassDef): Option[DefDef] = {
def isDisallowed(vd: ValDef) = isRepeatedParamType(vd.tpt) || isByNameParamType(vd.tpt)
val classParamss = constrParamss(cdef)

if (cdef.symbol.hasAbstractFlag || mexists(classParamss)(isDisallowed)) None
else {
val classParamss = constrParamss(cdef)
def warn() = runReporting.warning(cdef.namePos, "case `copy` method is allowed to have by-name parameters under Scala 3 (or with -Xsource-features:case-copy-by-name)", Scala3Migration, cdef.symbol)
def isDisallowed(vd: ValDef) = isRepeatedParamType(vd.tpt) || isByNameParamType(vd.tpt).tap(if (_) warn())
def copyOK = currentRun.sourceFeatures.caseCopyByName || !mexists(classParamss)(isDisallowed)
def synthesizeCopy = {
def makeCopyParam(vd: ValDef, putDefault: Boolean) = {
val rhs = if (putDefault) toIdent(vd) else EmptyTree
val flags = PARAM | (vd.mods.flags & IMPLICIT) | (if (putDefault) DEFAULTPARAM else 0)
// empty tpt: see comment above
val tpt = atPos(vd.pos.focus)(TypeTree() setOriginal vd.tpt)
treeCopy.ValDef(vd, Modifiers(flags), vd.name, tpt, rhs)
}

val tparams = constrTparamsInvariant(cdef)
val paramss = classParamss match {
case Nil => Nil
case ps :: pss =>
ps.map(makeCopyParam(_, putDefault = true)) :: mmap(pss)(makeCopyParam(_, putDefault = false))
}

val classTpe = classType(cdef, tparams)
val argss = mmap(paramss)(toIdent)
val body: Tree = New(classTpe, argss)
Expand All @@ -301,10 +299,10 @@ trait Unapplies extends ast.TreeDSL {
}
}
else synth
val copyDefDef = atPos(cdef.pos.focus)(
atPos(cdef.pos.focus)(
DefDef(copyMods, nme.copy, tparams, paramss, TypeTree(), body)
)
Some(copyDefDef)
}
if (!cdef.symbol.hasAbstractFlag && copyOK) Some(synthesizeCopy) else None
}
}
6 changes: 6 additions & 0 deletions test/files/neg/t7879.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
t7879.scala:2: error: case `copy` method is allowed to have by-name parameters under Scala 3 (or with -Xsource-features:case-copy-by-name)
Scala 3 migration messages are issued as errors under -Xsource:3. Use -Wconf or @nowarn to demote them to warnings or suppress.
Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=scala3-migration, site=C
case class C(i: Int)(j: => Int)(k: => Int) { def sum = i + j + k }
^
1 error
2 changes: 2 additions & 0 deletions test/files/neg/t7879.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//> using options -Xsource:3
case class C(i: Int)(j: => Int)(k: => Int) { def sum = i + j + k }
25 changes: 25 additions & 0 deletions test/files/run/t7879.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

scala> case class C(i: Int)(j: => Int) { def sum = i + j }
class C

scala> def z = 27.tap(println)
def z: Int

scala> val c = C(42)(z)
val c: C = C(42)

scala> c.sum
27
val res0: Int = 69

scala> def y = 28.tap(println)
def y: Int

scala> c.copy()(y)
val res1: C = C(42)

scala> c.copy()(y).sum
28
val res2: Int = 70

scala> :quit
6 changes: 6 additions & 0 deletions test/files/run/t7879.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

import scala.tools.partest.SessionTest

object Test extends SessionTest {
override def extraSettings = "-Yimports:java.lang,scala,scala.Predef,scala.util.chaining -Xsource:3 -Xsource-features:case-copy-by-name"
}

0 comments on commit 914c657

Please sign in to comment.