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 <empty> {
+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 = ???