Skip to content

Commit 028c3ba

Browse files
committed
Fix #8571: A new source version and migration scheme
Unlike originally proposed in #8571, we now just use -source, the separate -migration setting is gone. We replace it by having source versions for migrations. So, right now, we have 3.0, 3.1, 3.0-migration and 3.1-migration. A X-migration source level means that migration warnings and changes in preparation for the X source level are enabled. So, the scenarios in #8571 would map as follows: - I want to cross compile between Scala 2 and Scala 3: not yet implemented, but it would be -source 2x3 - I want help with migration to Scala 3: -source 3.0-migration - I want help with migration to the cross-compilable subset: not yet implemented, but it would be -source 2x3-migration - I want to make sure my code is future-proof for 3.1: -source 3.1 - I want help making my code future-proof for 3.1: -source 3.1-migration - My compiler's version is3.1, but I want to stick to the more lenient 3.0 version for the moment: -source 3.0. - My compiler's version is 3.1, but I want to stick to the cross-compilable subset: not yet implemented, but it would be -source 2x3
1 parent 26032db commit 028c3ba

28 files changed

+313
-226
lines changed

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import java.nio.file.{Files, Paths}
55
import dotty.tools.FatalError
66
import config.CompilerCommand
77
import core.Comments.{ContextDoc, ContextDocstrings}
8-
import core.Contexts.{Context, ContextBase}
8+
import core.Contexts.{Context, ContextBase, inContext, ctx}
99
import core.{MacroClassLoader, Mode, TypeError}
10+
import core.StdNames.nme
1011
import dotty.tools.dotc.ast.Positioned
1112
import dotty.tools.io.File
1213
import reporting._
1314
import core.Decorators._
15+
import config.Feature
1416

1517
import scala.util.control.NonFatal
1618
import fromtasty.{TASTYCompiler, TastyFileUtil}
@@ -70,11 +72,14 @@ class Driver {
7072
MacroClassLoader.init(ictx)
7173
Positioned.updateDebugPos(ictx)
7274

73-
if (!ictx.settings.YdropComments.value(ictx) || ictx.mode.is(Mode.ReadComments))
74-
ictx.setProperty(ContextDoc, new ContextDocstrings)
75-
76-
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ictx)
77-
fromTastySetup(fileNames, ictx)
75+
inContext(ictx) {
76+
if !ctx.settings.YdropComments.value || ctx.mode.is(Mode.ReadComments) then
77+
ictx.setProperty(ContextDoc, new ContextDocstrings)
78+
if Feature.enabledBySetting(nme.Scala2Compat) then
79+
ctx.warning("-language:Scala2Compat will go away; use -source 3.0-migration instead")
80+
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)
81+
fromTastySetup(fileNames, ctx)
82+
}
7883
}
7984

8085
/** Setup extra classpath and figure out class names for tasty file inputs */

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Decorators.{given _}, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
1010
import typer.{FrontEnd, Namer}
1111
import util.{Property, SourceFile, SourcePosition}
12+
import config.Feature
1213
import collection.mutable.ListBuffer
1314
import reporting.messages._
1415
import reporting.trace
@@ -560,7 +561,7 @@ object desugar {
560561
ensureApplied(nu)
561562
}
562563

563-
val copiedAccessFlags = if (ctx.scala2CompatSetting) EmptyFlags else AccessFlags
564+
val copiedAccessFlags = if Feature.migrateTo3 then EmptyFlags else AccessFlags
564565

565566
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
566567
// def _1: T1 = this.p1
@@ -1659,7 +1660,7 @@ object desugar {
16591660
}
16601661
else {
16611662
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
1662-
if (!ctx.featureEnabled(nme.postfixOps)) {
1663+
if (!Feature.enabled(nme.postfixOps)) {
16631664
ctx.error(
16641665
s"""postfix operator `${op.name}` needs to be enabled
16651666
|by making the implicit value scala.language.postfixOps visible.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package dotty.tools
2+
package dotc
3+
package config
4+
5+
import core._
6+
import Contexts._, Symbols._, Names._
7+
import StdNames.nme
8+
import Decorators.{given _}
9+
import util.SourcePosition
10+
import SourceVersion._
11+
import reporting.Message
12+
13+
object Feature:
14+
15+
/** Is `feature` enabled by by a command-line setting? The enabling setting is
16+
*
17+
* -language:<prefix>feature
18+
*
19+
* where <prefix> is the fully qualified name of `owner`, followed by a ".",
20+
* but subtracting the prefix `scala.language.` at the front.
21+
*/
22+
def enabledBySetting(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
23+
def toPrefix(sym: Symbol): String =
24+
if !sym.exists || sym == defn.LanguageModule.moduleClass then ""
25+
else toPrefix(sym.owner) + sym.name + "."
26+
val prefix = if owner.exists then toPrefix(owner) else ""
27+
ctx.base.settings.language.value.contains(prefix + feature)
28+
29+
/** Is `feature` enabled by by an import? This is the case if the feature
30+
* is imported by a named import
31+
*
32+
* import owner.feature
33+
*
34+
* and there is no visible nested import that excludes the feature, as in
35+
*
36+
* import owner.{ feature => _ }
37+
*/
38+
def enabledByImport(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
39+
ctx.atPhase(ctx.typerPhase) {
40+
ctx.importInfo != null
41+
&& ctx.importInfo.featureImported(feature.toTermName,
42+
if owner.exists then owner else defn.LanguageModule.moduleClass)
43+
}
44+
45+
/** Is `feature` enabled by either a command line setting or an import?
46+
* @param feature The name of the feature
47+
* @param owner The prefix symbol (nested in `scala.language`) where the
48+
* feature is defined.
49+
*/
50+
def enabled(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
51+
enabledBySetting(feature, owner) || enabledByImport(feature, owner)
52+
53+
/** Is auto-tupling enabled? */
54+
def autoTuplingEnabled(using Context): Boolean =
55+
!enabled(nme.noAutoTupling)
56+
57+
def dynamicsEnabled(using Context): Boolean =
58+
enabled(nme.dynamics)
59+
60+
def sourceVersionSetting(using Context): SourceVersion =
61+
SourceVersion.valueOf(ctx.settings.source.value)
62+
63+
def sourceVersion(using Context): SourceVersion =
64+
if ctx.importInfo == null then sourceVersionSetting
65+
else ctx.importInfo.sourceVersion.getOrElse(sourceVersionSetting)
66+
67+
def migrateTo3(using Context): Boolean =
68+
sourceVersion == `3.0-migration` || enabledBySetting(nme.Scala2Compat)
69+
70+
/** If current source migrates to `version`, issue given warning message
71+
* and return `true`, otherwise return `false`.
72+
*/
73+
def warnOnMigration(msg: Message, pos: SourcePosition,
74+
version: SourceVersion = defaultSourceVersion)(using Context): Boolean =
75+
if sourceVersion.isMigrating && sourceVersion.stable == version then
76+
ctx.migrationWarning(msg, pos)
77+
true
78+
else
79+
false
80+
81+
end Feature

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ScalaSettings extends Settings.SettingGroup {
3434
val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.") withAbbreviation "--feature"
3535
val help: Setting[Boolean] = BooleanSetting("-help", "Print a synopsis of standard options.") withAbbreviation "--help"
3636
val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) withAbbreviation "--color"
37+
val source: Setting[String] = ChoiceSetting("-source", "source version", "source version", List("3.0", "3.1", "3.0-migration", "3.1-migration"), "3.0").withAbbreviation("--source")
3738
val target: Setting[String] = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
3839
List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"), "jvm-1.8") withAbbreviation "--target"
3940
val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).") withAbbreviation "--scalajs"
@@ -45,7 +46,7 @@ class ScalaSettings extends Settings.SettingGroup {
4546
val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", 80) withAbbreviation "--page-width"
4647
val strict: Setting[Boolean] = BooleanSetting("-strict", "Use strict type rules, which means some formerly legal code does not typecheck anymore.") withAbbreviation "--strict"
4748
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") withAbbreviation "--language"
48-
val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2Compat rewrites sources to migrate to new syntax.") withAbbreviation "--rewrite"
49+
val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with a `...-migration` source version, rewrites sources to migrate to new version.") withAbbreviation "--rewrite"
4950
val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.") withAbbreviation "--no-warnings"
5051
val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty in classpath. The arguments are used as class names.") withAbbreviation "--from-tasty"
5152

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools
2+
package dotc
3+
package config
4+
5+
import core.Contexts.{Context, ctx}
6+
import core.Names.TermName
7+
import core.StdNames.nme
8+
import core.Decorators.{given _}
9+
import util.Property
10+
11+
enum SourceVersion:
12+
case `3.0-migration`, `3.0`, `3.1-migration`, `3.1`
13+
14+
val isMigrating: Boolean = toString.endsWith("-migration")
15+
16+
def stable: SourceVersion =
17+
if isMigrating then SourceVersion.values(ordinal + 1) else this
18+
19+
def isAtLeast(v: SourceVersion) = stable.ordinal >= v.ordinal
20+
21+
object SourceVersion extends Property.Key[SourceVersion]:
22+
def defaultSourceVersion = `3.0`
23+
24+
val allSourceVersionNames = values.toList.map(_.toString.toTermName)
25+
end SourceVersion

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Decorators._
1717
import printing.Texts._
1818
import printing.Printer
1919
import io.AbstractFile
20+
import config.Feature.migrateTo3
2021
import config.Config
2122
import util.common._
2223
import typer.ProtoTypes.NoViewsAllowed
@@ -489,7 +490,7 @@ object Denotations {
489490
// things, starting with the return type of this method.
490491
if (preferSym(sym2, sym1)) info2
491492
else if (preferSym(sym1, sym2)) info1
492-
else if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2CompatMode)
493+
else if (pre.widen.classSymbol.is(Scala2x) || migrateTo3)
493494
info1 // follow Scala2 linearization -
494495
// compare with way merge is performed in SymDenotation#computeMembersNamed
495496
else throw new MergeError(ex.sym1, ex.sym2, ex.tp1, ex.tp2, pre)

compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Symbols._
77
import Types._
88
import Flags._
99
import dotty.tools.dotc.reporting.trace
10+
import config.Feature.migrateTo3
1011
import config.Printers._
1112

1213
trait PatternTypeConstrainer { self: TypeComparer =>
@@ -199,7 +200,9 @@ trait PatternTypeConstrainer { self: TypeComparer =>
199200
}
200201
}
201202

202-
val widePt = if (ctx.scala2CompatMode || refinementIsInvariant(patternTp)) scrutineeTp else widenVariantParams(scrutineeTp)
203+
val widePt =
204+
if migrateTo3 || refinementIsInvariant(patternTp) then scrutineeTp
205+
else widenVariantParams(scrutineeTp)
203206
val narrowTp = SkolemType(patternTp)
204207
trace(i"constraining simple pattern type $narrowTp <:< $widePt", gadts, res => s"$res\ngadt = ${ctx.gadt.debugBoundsDescription}") {
205208
isSubType(narrowTp, widePt)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ object StdNames {
502502
val java: N = "java"
503503
val key: N = "key"
504504
val lang: N = "lang"
505+
val language: N = "language"
505506
val length: N = "length"
506507
val lengthCompare: N = "lengthCompare"
507508
val macroThis : N = "_this"

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import StdNames.nme
88
import collection.mutable
99
import util.Stats
1010
import config.Config
11+
import config.Feature.migrateTo3
1112
import config.Printers.{constr, subtyping, gadts, noPrinter}
1213
import TypeErasure.{erasedLub, erasedGlb}
1314
import TypeApplications._
@@ -584,7 +585,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
584585
* am not sure how, since the code is buried so deep in subtyping logic.
585586
*/
586587
def boundsOK =
587-
ctx.scala2CompatMode ||
588+
migrateTo3 ||
588589
tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) =>
589590
isSubType(tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo))
590591
val saved = comparedTypeLambdas
@@ -1826,7 +1827,8 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
18261827
/** The greatest lower bound of a list types */
18271828
final def glb(tps: List[Type]): Type = tps.foldLeft(AnyType: Type)(glb)
18281829

1829-
def widenInUnions(implicit ctx: Context): Boolean = ctx.scala2CompatMode || ctx.erasedTypes
1830+
def widenInUnions(implicit ctx: Context): Boolean =
1831+
migrateTo3 || ctx.erasedTypes
18301832

18311833
/** The least upper bound of two types
18321834
* @param canConstrain If true, new constraints might be added to simplify the lub.

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import collection.mutable
1414
import ast.tpd._
1515
import reporting.{trace, Message}
1616
import config.Printers.{gadts, typr}
17+
import config.Feature
1718
import typer.Applications._
1819
import typer.ProtoTypes._
1920
import typer.ForceDegree
@@ -378,7 +379,8 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
378379
* type parameter corresponding to the wildcard.
379380
*/
380381
def skolemizeWildcardArgs(tps: List[Type], app: Type) = app match {
381-
case AppliedType(tycon: TypeRef, args) if tycon.typeSymbol.isClass && !scala2CompatMode =>
382+
case AppliedType(tycon: TypeRef, args)
383+
if tycon.typeSymbol.isClass && !Feature.migrateTo3 =>
382384
tps.zipWithConserve(tycon.typeSymbol.typeParams) {
383385
(tp, tparam) => tp match {
384386
case _: TypeBounds => app.select(tparam)
@@ -469,66 +471,6 @@ trait TypeOps { thisCtx: Context => // TODO: Make standalone object.
469471
/** Are we in a macro? */
470472
def inMacro: Boolean = owner.ownersIterator.exists(s => s.isInlineMethod && s.is(Macro))
471473

472-
/** Is `feature` enabled in class `owner`?
473-
* This is the case if one of the following two alternatives holds:
474-
*
475-
* 1. The feature is imported by a named import
476-
*
477-
* import owner.feature
478-
*
479-
* and there is no visible nested import that excludes the feature, as in
480-
*
481-
* import owner.{ feature => _ }
482-
*
483-
* The feature may be bunched with others, or renamed, but wildcard imports don't count.
484-
*
485-
* 2. The feature is enabled by a compiler option
486-
*
487-
* - language:<prefix>feature
488-
*
489-
* where <prefix> is the full name of the owner followed by a "." minus
490-
* the prefix "dotty.language.".
491-
*/
492-
def featureEnabled(feature: TermName, owner: Symbol = NoSymbol): Boolean = {
493-
def hasImport = {
494-
val owner1 = if (!owner.exists) defn.LanguageModule.moduleClass else owner
495-
thisCtx.importInfo != null &&
496-
thisCtx.importInfo.featureImported(feature, owner1)(using thisCtx.withPhase(thisCtx.typerPhase))
497-
}
498-
val hasOption = {
499-
def toPrefix(sym: Symbol): String =
500-
if (!sym.exists) ""
501-
else toPrefix(sym.owner) + sym.name + "."
502-
val featureName = toPrefix(owner) + feature
503-
thisCtx.base.settings.language.value contains featureName
504-
}
505-
hasOption || hasImport
506-
}
507-
508-
/** Is auto-tupling enabled? */
509-
def canAutoTuple: Boolean =
510-
!featureEnabled(nme.noAutoTupling)
511-
512-
def scala2CompatMode: Boolean =
513-
featureEnabled(nme.Scala2Compat)
514-
515-
def dynamicsEnabled: Boolean =
516-
featureEnabled(nme.dynamics)
517-
518-
def testScala2CompatMode(msg: Message, pos: SourcePosition, replace: => Unit = ()): Boolean = {
519-
if (scala2CompatMode) {
520-
migrationWarning(msg, pos)
521-
replace
522-
}
523-
scala2CompatMode
524-
}
525-
526-
/** Is option -language:Scala2Compat set?
527-
* This test is used when we are too early in the pipeline to consider imports.
528-
*/
529-
def scala2CompatSetting: Boolean =
530-
thisCtx.settings.language.value.contains(nme.Scala2Compat.toString)
531-
532474
/** Refine child based on parent
533475
*
534476
* In child class definition, we have:

0 commit comments

Comments
 (0)