Skip to content

chore: add scala.language.2.13 #23219

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 2 commits into from
May 21, 2025
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
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ object desugar {
def isNonEnumCase = !isEnumCase && (isCaseClass || isCaseObject)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
val caseClassInScala2Library = isCaseClass && ctx.settings.YcompileScala2Library.value
val caseClassInScala2Library = isCaseClass && Feature.shouldBehaveAsScala2

val originalTparams = constr1.leadingTypeParams
val originalVparamss = asTermOnly(constr1.trailingParamss)
Expand Down Expand Up @@ -922,7 +922,7 @@ object desugar {
val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
cpy.ValDef(vparam)(rhs = EmptyTree))
var flags = Synthetic | constr1.mods.flags & copiedAccessFlags
if ctx.settings.YcompileScala2Library.value then flags &~= Private
if Feature.shouldBehaveAsScala2 then flags &~= Private
DefDef(
nme.copy,
joinParams(derivedTparams, copyFirstParams :: copyRestParamss),
Expand Down Expand Up @@ -983,7 +983,7 @@ object desugar {
else {
val appMods =
var flags = Synthetic | constr1.mods.flags & copiedAccessFlags
if ctx.settings.YcompileScala2Library.value then flags &~= Private
if Feature.shouldBehaveAsScala2 then flags &~= Private
Modifiers(flags).withPrivateWithin(constr1.mods.privateWithin)
val appParamss =
derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) =>
Expand Down Expand Up @@ -1066,7 +1066,7 @@ object desugar {
paramss // drop leading () that got inserted by class
// TODO: drop this once we do not silently insert empty class parameters anymore
case paramss => paramss
val finalFlag = if ctx.settings.YcompileScala2Library.value then EmptyFlags else Final
val finalFlag = if Feature.shouldBehaveAsScala2 then EmptyFlags else Final
// implicit wrapper is typechecked in same scope as constructor, so
// we can reuse the constructor parameters; no derived params are needed.
DefDef(
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Annotations.Annotation
import NameKinds.ContextBoundParamName
import typer.ConstFold
import reporting.trace
import config.Feature

import Decorators.*
import Constants.Constant
Expand Down Expand Up @@ -479,7 +480,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
*/
private def defKind(tree: Tree)(using Context): FlagSet = unsplice(tree) match {
case EmptyTree | _: Import => NoInitsInterface
case tree: TypeDef if ctx.settings.YcompileScala2Library.value =>
case tree: TypeDef if Feature.shouldBehaveAsScala2 =>
if (tree.isClassDef) EmptyFlags else NoInitsInterface
case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
case tree: DefDef =>
Expand All @@ -492,7 +493,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
NoInitsInterface
else if tree.mods.is(Given) && tree.paramss.isEmpty then
EmptyFlags // might become a lazy val: TODO: check whether we need to suppress NoInits once we have new lazy val impl
else if ctx.settings.YcompileScala2Library.value then
else if Feature.shouldBehaveAsScala2 then
EmptyFlags
else
NoInits
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ object Feature:
case Some(v) => v
case none => sourceVersionSetting

/* Should we behave as scala 2?*/
def shouldBehaveAsScala2(using Context): Boolean =
ctx.settings.YcompileScala2Library.value || sourceVersion.isScala2

def migrateTo3(using Context): Boolean =
sourceVersion == `3.0-migration`

Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ enum SourceVersion:
case `3.6-migration`, `3.6`
case `3.7-migration`, `3.7`
case `3.8-migration`, `3.8`
// Add 3.x-migration and 3.x here
// !!! Keep in sync with scala.runtime.stdlibPatches.language !!!
case `2.13`
case `future-migration`, `future`

case `never` // needed for MigrationVersion.errorFrom if we never want to issue an error
Expand All @@ -34,6 +36,8 @@ enum SourceVersion:

def isAtMost(v: SourceVersion) = stable.ordinal <= v.ordinal

def isScala2 = this == `2.13`

def enablesFewerBraces = isAtLeast(`3.3`)
def enablesClauseInterleaving = isAtLeast(`3.6`)
def enablesNewGivens = isAtLeast(`3.6`)
Expand All @@ -46,7 +50,7 @@ object SourceVersion extends Property.Key[SourceVersion]:
val defaultSourceVersion = `3.7`

/* Illegal source versions that may not appear in the settings `-source:<...>` */
val illegalInSettings = List(`3.1-migration`, `never`)
val illegalInSettings = List(`2.13`, `3.1-migration`, `never`)

/* Illegal source versions that may not appear as an import `import scala.language.<...>` */
val illegalInImports = List(`3.1-migration`, `never`)
Expand Down
10 changes: 4 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1538,9 +1538,7 @@ class Definitions {
denot.sourceModule.info = denot.typeRef // we run into a cyclic reference when patching if this line is omitted
patch2(denot, patchCls)

if ctx.settings.YcompileScala2Library.value then
()
else if denot.name == tpnme.Predef.moduleClassName && denot.symbol == ScalaPredefModuleClass then
if denot.name == tpnme.Predef.moduleClassName && denot.symbol == ScalaPredefModuleClass then
patchWith(ScalaPredefModuleClassPatch)
else if denot.name == tpnme.language.moduleClassName && denot.symbol == LanguageModuleClass then
patchWith(LanguageModuleClassPatch)
Expand Down Expand Up @@ -1883,7 +1881,7 @@ class Definitions {
|| tp.derivesFrom(defn.PolyFunctionClass) // TODO check for refinement?

private def withSpecMethods(cls: ClassSymbol, bases: List[Name], paramTypes: Set[TypeRef]) =
if !ctx.settings.YcompileScala2Library.value then
if !Feature.shouldBehaveAsScala2 then
for base <- bases; tp <- paramTypes do
cls.enter(newSymbol(cls, base.specializedName(List(tp)), Method, ExprType(tp)))
cls
Expand Down Expand Up @@ -1926,7 +1924,7 @@ class Definitions {
case List(x, y) => Tuple2SpecializedParamClasses().contains(x.classSymbol) && Tuple2SpecializedParamClasses().contains(y.classSymbol)
case _ => false
&& base.owner.denot.info.member(base.name.specializedName(args)).exists // when dotc compiles the stdlib there are no specialised classes
&& !ctx.settings.YcompileScala2Library.value // We do not add the specilized TupleN methods/classes when compiling the stdlib
&& !Feature.shouldBehaveAsScala2 // We do not add the specilized TupleN methods/classes when compiling the stdlib

def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(using Context): Boolean =
paramTypes.length <= 2
Expand All @@ -1948,7 +1946,7 @@ class Definitions {
case _ =>
false
})
&& !ctx.settings.YcompileScala2Library.value // We do not add the specilized FunctionN methods/classes when compiling the stdlib
&& !Feature.shouldBehaveAsScala2 // We do not add the specilized FunctionN methods/classes when compiling the stdlib

@tu lazy val Function0SpecializedApplyNames: List[TermName] =
for r <- Function0SpecializedReturnTypes
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class Pickler extends Phase {
val isOutline = isJavaAttr // TODO: later we may want outline for Scala sources too
val attributes = Attributes(
sourceFile = sourceRelativePath,
scala2StandardLibrary = ctx.settings.YcompileScala2Library.value,
scala2StandardLibrary = Feature.shouldBehaveAsScala2,
explicitNulls = ctx.settings.YexplicitNulls.value,
captureChecked = Feature.ccEnabled,
withPureFuns = Feature.pureFunsEnabled,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
private var compilingScala2StdLib = false
override def initContext(ctx: FreshContext): Unit =
initContextCalled = true
compilingScala2StdLib = ctx.settings.YcompileScala2Library.value(using ctx)
compilingScala2StdLib = Feature.shouldBehaveAsScala2(using ctx)

val superAcc: SuperAccessors = new SuperAccessors(thisPhase)
val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ast.Trees.*, ast.tpd, core.*
import Contexts.*, Types.*, Decorators.*, Symbols.*, DenotTransformers.*
import SymDenotations.*, Scopes.*, StdNames.*, NameOps.*, Names.*
import MegaPhase.MiniPhase
import config.Feature

import scala.collection.mutable

Expand All @@ -25,7 +26,7 @@ class SpecializeApplyMethods extends MiniPhase with InfoTransformer {
override def description: String = SpecializeApplyMethods.description

override def isEnabled(using Context): Boolean =
!ctx.settings.scalajs.value && !ctx.settings.YcompileScala2Library.value
!ctx.settings.scalajs.value && !Feature.shouldBehaveAsScala2

private def specApplySymbol(sym: Symbol, args: List[Type], ret: Type)(using Context): Symbol = {
val name = nme.apply.specializedFunction(ret, args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import NameOps.*
import Annotations.Annotation
import typer.ProtoTypes.constrained
import ast.untpd
import config.Feature

import util.Property
import util.Spans.Span
Expand Down Expand Up @@ -79,7 +80,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {

private def existingDef(sym: Symbol, clazz: ClassSymbol)(using Context): Symbol =
val existing = sym.matchingMember(clazz.thisType)
if ctx.settings.YcompileScala2Library.value && clazz.isValueClass && (sym == defn.Any_equals || sym == defn.Any_hashCode) then
if Feature.shouldBehaveAsScala2 && clazz.isValueClass && (sym == defn.Any_equals || sym == defn.Any_hashCode) then
NoSymbol
else if existing != sym && !existing.is(Deferred) then
existing
Expand Down Expand Up @@ -168,7 +169,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
case nme.productPrefix if isEnumValue => nameRef
case nme.productPrefix => ownNameLit
case nme.productElement =>
if ctx.settings.YcompileScala2Library.value then productElementBodyForScala2Compat(accessors.length, vrefss.head.head)
if Feature.shouldBehaveAsScala2 then productElementBodyForScala2Compat(accessors.length, vrefss.head.head)
else productElementBody(accessors.length, vrefss.head.head)
case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head)
}
Expand Down Expand Up @@ -720,7 +721,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
val syntheticMembers = serializableObjectMethod(clazz) ::: serializableEnumValueMethod(clazz) ::: caseAndValueMethods(clazz)
checkInlining(syntheticMembers)
val impl1 = cpy.Template(impl)(body = syntheticMembers ::: impl.body)
if ctx.settings.YcompileScala2Library.value then impl1
if Feature.shouldBehaveAsScala2 then impl1
else addMirrorSupport(impl1)
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ class Namer { typer: Typer =>
tree match {
case tree: TypeDef if tree.isClassDef =>
var flags = checkFlags(tree.mods.flags)
if ctx.settings.YcompileScala2Library.value then
if Feature.shouldBehaveAsScala2 then
flags |= Scala2x
val name = checkNoConflict(tree.name, tree.span).asTypeName
val cls =
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3261,7 +3261,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer

checkEnumParent(cls, firstParent)

if defn.ScalaValueClasses()(cls) && ctx.settings.YcompileScala2Library.value then
if defn.ScalaValueClasses()(cls) && Feature.shouldBehaveAsScala2 then
constr1.symbol.resetFlag(Private)

val self1 = typed(self)(using ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
Expand Down
6 changes: 6 additions & 0 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ object language:
@compileTimeOnly("`future-migration` can only be used at compile time in import statements")
object `future-migration`

/** Set source version to 2.13. Effectively, this doesn't change the source language,
* but rather adapts the generated code as if it was compiled with Scala 2.13
*/
@compileTimeOnly("`2.13` can only be used at compile time in import statements")
private[scala] object `2.13`

/** Set source version to 3.0-migration.
*
* @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]]
Expand Down
7 changes: 2 additions & 5 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1236,11 +1236,8 @@ object Build {
withCommonSettings(Bootstrapped).
dependsOn(dottyCompiler(Bootstrapped) % "provided; compile->runtime; test->test").
settings(scala2LibraryBootstrappedSettings).
settings(
moduleName := "scala2-library-cc",
scalacOptions += "-Ycheck:all",
)

settings(moduleName := "scala2-library-cc")

lazy val scala2LibraryBootstrappedSettings = Seq(
javaOptions := (`scala3-compiler-bootstrapped` / javaOptions).value,
Compile / scalacOptions ++= {
Expand Down
3 changes: 3 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ object MiMaFilters {
// Scala.js-only class
ProblemFilters.exclude[FinalClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunctionXXL.this"),

ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.2.13"),
ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$2$u002E13$")
),

// Additions since last LTS
Expand Down
2 changes: 1 addition & 1 deletion project/scripts/scala2-library-tasty-mima.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ setTastyVersion $MINOR_TASTY_VERSION_SUPPORTED_BY_TASTY_MIMA 0
# We clean before to make sure all sources are recompiled using the new TASTY version.
# We clean after to make sure no other test will use the TASTy generated with this version.
# We set -Ycheck:all to check that -Ycompile-scala2-library does not gererate inconsistent trees.
"$SBT" 'clean; scala2-library-bootstrapped/clean; reload; set `scala2-library-bootstrapped`/scalacOptions += "-Ycheck:all"; scala2-library-bootstrapped/tastyMiMaReportIssues; clean; scala2-library-bootstrapped/clean'
"$SBT" 'clean; scala2-library-bootstrapped/clean; reload; scala2-library-bootstrapped/tastyMiMaReportIssues; clean; scala2-library-bootstrapped/clean'

setTastyVersion $MINOR_TASTY_VERSION $EXPERIMENTAL_TASTY_VERSION
Loading