diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index b627e149e5fb..ccf475a917de 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -30,6 +30,8 @@ class CompilationUnit protected (val source: SourceFile, val info: CompilationUn /** Is this the compilation unit of a Java file */ def isJava: Boolean = source.file.ext.isJava + def isScala2 = sourceVersion.map(_.isScala2).getOrElse(false) + /** Is this the compilation unit of a Java file, or TASTy derived from a Java file */ def typedAsJava = val ext = source.file.ext diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 471a9953c4f0..499ff742e195 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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.sourceVersion.isScala2 val originalTparams = constr1.leadingTypeParams val originalVparamss = asTermOnly(constr1.trailingParamss) @@ -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.sourceVersion.isScala2 then flags &~= Private DefDef( nme.copy, joinParams(derivedTparams, copyFirstParams :: copyRestParamss), @@ -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.sourceVersion.isScala2 then flags &~= Private Modifiers(flags).withPrivateWithin(constr1.mods.privateWithin) val appParamss = derivedVparamss.nestedZipWithConserve(constrVparamss)((ap, cp) => @@ -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.sourceVersion.isScala2 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( diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 45e17794ec96..bcadbe2fb2ba 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -15,6 +15,7 @@ import Constants.Constant import scala.collection.mutable import scala.annotation.tailrec +import dotty.tools.dotc.config.Feature trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => @@ -466,7 +467,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.sourceVersion.isScala2 => if (tree.isClassDef) EmptyFlags else NoInitsInterface case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface case tree: DefDef => @@ -479,7 +480,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.sourceVersion.isScala2 then EmptyFlags else NoInits diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 0517d5b46b9e..5b6eb90e685d 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -434,7 +434,6 @@ private sealed trait YSettings: val YshowTreeIds: Setting[Boolean] = BooleanSetting(ForkSetting, "Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.") val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting(ForkSetting, "Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty.") val YlegacyLazyVals: Setting[Boolean] = BooleanSetting(ForkSetting, "Ylegacy-lazy-vals", "Use legacy (pre 3.3.0) implementation of lazy vals.") - val YcompileScala2Library: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycompile-scala2-library", "Used when compiling the Scala 2 standard library.") val YprofileEnabled: Setting[Boolean] = BooleanSetting(ForkSetting, "Yprofile-enabled", "Enable profiling.") val YprofileDestination: Setting[String] = StringSetting(ForkSetting, "Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "", depends = List(YprofileEnabled -> true)) val YprofileExternalTool: Setting[List[String]] = PhasesSetting(ForkSetting, "Yprofile-external-tool", "Enable profiling for a phase using an external tool hook. Generally only useful for a single phase.", "typer", depends = List(YprofileEnabled -> true)) diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 30a88fb79f2a..63850aff4c86 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -17,6 +17,7 @@ enum SourceVersion: case `3.7-migration`, `3.7` case `3.8-migration`, `3.8` // !!! 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 @@ -39,6 +40,8 @@ enum SourceVersion: def enablesNamedTuples = isAtLeast(`3.7`) def enablesBetterFors(using Context) = isAtLeast(`3.7`) && isPreviewEnabled + def isScala2 = this == `2.13` + object SourceVersion extends Property.Key[SourceVersion]: def defaultSourceVersion = `3.7` diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 14f2491214e2..3b280c99a852 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1537,9 +1537,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) @@ -1880,7 +1878,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.sourceVersion.isScala2 then for base <- bases; tp <- paramTypes do cls.enter(newSymbol(cls, base.specializedName(List(tp)), Method, ExprType(tp))) cls @@ -1923,7 +1921,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.sourceVersion.isScala2 // 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 @@ -1945,7 +1943,7 @@ class Definitions { case _ => false }) - && !ctx.settings.YcompileScala2Library.value // We do not add the specilized FunctionN methods/classes when compiling the stdlib + && !Feature.sourceVersion.isScala2 // We do not add the specilized FunctionN methods/classes when compiling the stdlib @tu lazy val Function0SpecializedApplyNames: List[TermName] = for r <- Function0SpecializedReturnTypes diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index fcf1b384fda1..c2e279adaf41 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -289,9 +289,10 @@ class Pickler extends Phase { // assert that Java sources didn't reach Pickler without `-Xjava-tasty`. assert(ctx.settings.XjavaTasty.value, "unexpected Java source file without -Xjava-tasty") 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.sourceVersion.isScala2, explicitNulls = ctx.settings.YexplicitNulls.value, captureChecked = Feature.ccEnabled, withPureFuns = Feature.pureFunsEnabled, diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 3ca67c5798fe..915c3ecc18d8 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -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.sourceVersion(using ctx).isScala2 val superAcc: SuperAccessors = new SuperAccessors(thisPhase) val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeApplyMethods.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeApplyMethods.scala index fd314b94e50c..220ed8e1d929 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeApplyMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeApplyMethods.scala @@ -7,6 +7,7 @@ import SymDenotations.*, Scopes.*, StdNames.*, NameOps.*, Names.* import MegaPhase.MiniPhase import scala.collection.mutable +import dotty.tools.dotc.config.Feature /** This phase synthesizes specialized methods for FunctionN, this is done @@ -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.sourceVersion.isScala2 private def specApplySymbol(sym: Symbol, args: List[Type], ret: Type)(using Context): Symbol = { val name = nme.apply.specializedFunction(ret, args) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 4bc9575996d1..c1a23f1a1d74 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -16,6 +16,7 @@ import util.Spans.Span import config.Printers.derive import NullOpsDecorator.* import scala.runtime.Statics +import dotty.tools.dotc.config.Feature object SyntheticMembers { @@ -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.sourceVersion.isScala2 && clazz.isValueClass && (sym == defn.Any_equals || sym == defn.Any_hashCode) then NoSymbol else if existing != sym && !existing.is(Deferred) then existing @@ -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.sourceVersion.isScala2 then productElementBodyForScala2Compat(accessors.length, vrefss.head.head) else productElementBody(accessors.length, vrefss.head.head) case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head) } @@ -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.sourceVersion.isScala2 then impl1 else addMirrorSupport(impl1) } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 5d6be936f11b..7ab9198dc58a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -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.sourceVersion.isScala2 then flags |= Scala2x val name = checkNoConflict(tree.name, tree.span).asTypeName val cls = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 79aaf367851a..ce679007a3c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3206,7 +3206,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.sourceVersion.isScala2 then constr1.symbol.resetFlag(Private) val self1 = typed(self)(using ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 8899f734aece..0f5e904e29bb 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -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]] diff --git a/project/Build.scala b/project/Build.scala index 3ea865fb00db..e17967ded6cb 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1239,7 +1239,7 @@ object Build { settings(scala2LibraryBootstrappedSettings). settings( moduleName := "scala2-library-cc", - scalacOptions += "-Ycheck:all", + //scalacOptions += "-Ycheck:all", ) lazy val scala2LibraryBootstrappedSettings = Seq( @@ -1248,7 +1248,7 @@ object Build { Seq("-sourcepath", ((Compile/sourceManaged).value / "scala-library-src").toString) }, Compile / doc / scalacOptions += "-Ydocument-synthetic-types", - scalacOptions += "-Ycompile-scala2-library", + scalacOptions ++= Seq("-source", "2.13"), scalacOptions += "-Yscala2-unpickler:never", scalacOptions += "-Werror:false", Compile / compile / logLevel.withRank(KeyRanks.Invisible) := Level.Error, @@ -1698,7 +1698,7 @@ object Build { (Test / scalaJSModuleInitializers) ++= build.TestSuiteLinkerOptions.moduleInitializers, // Perform Ycheck after the Scala.js-specific transformation phases - scalacOptions += "-Ycheck:prepjsinterop,explicitJSClasses,addLocalJSFakeNews", + //scalacOptions += "-Ycheck:prepjsinterop,explicitJSClasses,addLocalJSFakeNews", Test / jsEnvInput := { val resourceDir = fetchScalaJSSource.value / "test-suite/js/src/test/resources" diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index a7f857e8a719..59052036e2ab 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -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 diff --git a/project/scripts/scala2-library-tasty-mima.sh b/project/scripts/scala2-library-tasty-mima.sh index 7118ee28c2f3..ddb3a6e09e69 100755 --- a/project/scripts/scala2-library-tasty-mima.sh +++ b/project/scripts/scala2-library-tasty-mima.sh @@ -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 diff --git a/tests/init-global/pos/scala2-library.scala b/tests/init-global/pos/scala2-library.scala index 8fa9245aebe0..1985ce5335b3 100644 --- a/tests/init-global/pos/scala2-library.scala +++ b/tests/init-global/pos/scala2-library.scala @@ -1,2 +1,5 @@ -//> using options -Ycompile-scala2-library +package scala + +import scala.language.`2.13` + case class UninitializedFieldError(msg: String) extends RuntimeException(msg) diff --git a/tests/printing/posttyper/i22533.check b/tests/printing/posttyper/i22533.check index 15741bbd3114..ca7c670ae98e 100644 --- a/tests/printing/posttyper/i22533.check +++ b/tests/printing/posttyper/i22533.check @@ -1,5 +1,6 @@ [[syntax trees at end of posttyper]] // tests/printing/posttyper/i22533.scala -package { +package scala { + import scala.language.2.13 @SourceFile("tests/printing/posttyper/i22533.scala") trait A() extends Any { override def equals(x: Any): Boolean = ??? override def hashCode(): Int = ??? diff --git a/tests/printing/posttyper/i22533.flags b/tests/printing/posttyper/i22533.flags deleted file mode 100644 index 21379f85d52a..000000000000 --- a/tests/printing/posttyper/i22533.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompile-scala2-library diff --git a/tests/printing/posttyper/i22533.scala b/tests/printing/posttyper/i22533.scala index 07e9e1c4c011..f38eda14aaf2 100644 --- a/tests/printing/posttyper/i22533.scala +++ b/tests/printing/posttyper/i22533.scala @@ -1,4 +1,6 @@ -//> using options -Ycompile-scala2-library +package scala + +import scala.language.`2.13` trait A extends Any: override def equals(x: Any): Boolean = ???