Skip to content

Fix #8571: A new source version and migration scheme #8700

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 18 commits into from
Apr 13, 2020
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
2 changes: 1 addition & 1 deletion community-build/community-projects/ScalaPB
Submodule ScalaPB updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/algebra
Submodule algebra updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/betterfiles
Submodule betterfiles updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/fastparse
Submodule fastparse updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/scala-xml
Submodule scala-xml updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/scalacheck
Submodule scalacheck updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/scalap
Submodule scalap updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/scopt
Submodule scopt updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/squants
Submodule squants updated 1 files
+1 −1 build.sbt
2 changes: 1 addition & 1 deletion community-build/community-projects/upickle
Submodule upickle updated 1 files
+1 −1 build.sc
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import typer.PrepareInlineable.InlineAccessors
import typer.Nullables
import transform.SymUtils._
import core.Decorators.{given _}
import config.SourceVersion

class CompilationUnit protected (val source: SourceFile) {

Expand All @@ -24,6 +25,9 @@ class CompilationUnit protected (val source: SourceFile) {

def isJava: Boolean = source.file.name.endsWith(".java")

/** The source version for this unit, as determined by a language import */
var sourceVersion: Option[SourceVersion] = None

/** Pickled TASTY binaries, indexed by class. */
var pickled: Map[ClassSymbol, Array[Byte]] = Map()

Expand Down
17 changes: 11 additions & 6 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import java.nio.file.{Files, Paths}
import dotty.tools.FatalError
import config.CompilerCommand
import core.Comments.{ContextDoc, ContextDocstrings}
import core.Contexts.{Context, ContextBase}
import core.Contexts.{Context, ContextBase, inContext, ctx}
import core.{MacroClassLoader, Mode, TypeError}
import core.StdNames.nme
import dotty.tools.dotc.ast.Positioned
import dotty.tools.io.File
import reporting._
import core.Decorators._
import config.Feature

import scala.util.control.NonFatal
import fromtasty.{TASTYCompiler, TastyFileUtil}
Expand Down Expand Up @@ -70,11 +72,14 @@ class Driver {
MacroClassLoader.init(ictx)
Positioned.updateDebugPos(ictx)

if (!ictx.settings.YdropComments.value(ictx) || ictx.mode.is(Mode.ReadComments))
ictx.setProperty(ContextDoc, new ContextDocstrings)

val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ictx)
fromTastySetup(fileNames, ictx)
inContext(ictx) {
if !ctx.settings.YdropComments.value || ctx.mode.is(Mode.ReadComments) then
ictx.setProperty(ContextDoc, new ContextDocstrings)
if Feature.enabledBySetting(nme.Scala2Compat) && false then // TODO: enable
ctx.warning("-language:Scala2Compat will go away; use -source 3.0-migration instead")
val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)
fromTastySetup(fileNames, ctx)
}
}

/** Setup extra classpath and figure out class names for tasty file inputs */
Expand Down
12 changes: 7 additions & 5 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import Decorators.{given _}, transform.SymUtils._
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
import typer.{FrontEnd, Namer}
import util.{Property, SourceFile, SourcePosition}
import config.Feature.{sourceVersion, migrateTo3, enabled}
import config.SourceVersion._
import collection.mutable.ListBuffer
import reporting.messages._
import reporting.trace
Expand Down Expand Up @@ -210,7 +212,7 @@ object desugar {
val epbuf = ListBuffer[ValDef]()
def desugarContextBounds(rhs: Tree): Tree = rhs match {
case ContextBounds(tbounds, cxbounds) =>
val iflag = if ctx.settings.strict.value then Given else Implicit
val iflag = if sourceVersion.isAtLeast(`3.1`) then Given else Implicit
epbuf ++= makeImplicitParameters(cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor)
tbounds
case LambdaTypeTree(tparams, body) =>
Expand Down Expand Up @@ -406,8 +408,8 @@ object desugar {
case _ => false
}
def isScala(tree: Tree): Boolean = tree match {
case Ident(nme.scala_) => true
case Select(Ident(nme.ROOTPKG), nme.scala_) => true
case Ident(nme.scala) => true
case Select(Ident(nme.ROOTPKG), nme.scala) => true
case _ => false
}

Expand Down Expand Up @@ -560,7 +562,7 @@ object desugar {
ensureApplied(nu)
}

val copiedAccessFlags = if (ctx.scala2CompatSetting) EmptyFlags else AccessFlags
val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags

// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
// def _1: T1 = this.p1
Expand Down Expand Up @@ -1659,7 +1661,7 @@ object desugar {
}
else {
assert(ctx.mode.isExpr || ctx.reporter.errorsReported || ctx.mode.is(Mode.Interactive), ctx.mode)
if (!ctx.featureEnabled(nme.postfixOps)) {
if (!enabled(nme.postfixOps)) {
ctx.error(
s"""postfix operator `${op.name}` needs to be enabled
|by making the implicit value scala.language.postfixOps visible.
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
enum GenCheckMode {
case Ignore // neither filter nor check since filtering was done before
case Check // check that pattern is irrefutable
case FilterNow //filter out non-matching elements since we are not in -strict
case FilterNow // filter out non-matching elements since we are not yet in 3.1
case FilterAlways // filter out non-matching elements since pattern is prefixed by `case`
}

Expand Down Expand Up @@ -447,7 +447,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
TypedSplice(tpd.ref(tp))

def rootDot(name: Name)(implicit src: SourceFile): Select = Select(Ident(nme.ROOTPKG), name)
def scalaDot(name: Name)(implicit src: SourceFile): Select = Select(rootDot(nme.scala_), name)
def scalaDot(name: Name)(implicit src: SourceFile): Select = Select(rootDot(nme.scala), name)
def scalaAnnotationDot(name: Name)(using SourceFile): Select = Select(scalaDot(nme.annotation), name)
def scalaUnit(implicit src: SourceFile): Select = scalaDot(tpnme.Unit)
def scalaAny(implicit src: SourceFile): Select = scalaDot(tpnme.Any)
Expand Down
83 changes: 83 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package dotty.tools
package dotc
package config

import core._
import Contexts._, Symbols._, Names._
import StdNames.nme
import Decorators.{given _}
import util.SourcePosition
import SourceVersion._
import reporting.Message

object Feature:

/** Is `feature` enabled by by a command-line setting? The enabling setting is
*
* -language:<prefix>feature
*
* where <prefix> is the fully qualified name of `owner`, followed by a ".",
* but subtracting the prefix `scala.language.` at the front.
*/
def enabledBySetting(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
def toPrefix(sym: Symbol): String =
if !sym.exists || sym == defn.LanguageModule.moduleClass then ""
else toPrefix(sym.owner) + sym.name + "."
val prefix = if owner.exists then toPrefix(owner) else ""
ctx.base.settings.language.value.contains(prefix + feature)

/** Is `feature` enabled by by an import? This is the case if the feature
* is imported by a named import
*
* import owner.feature
*
* and there is no visible nested import that excludes the feature, as in
*
* import owner.{ feature => _ }
*/
def enabledByImport(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
ctx.atPhase(ctx.typerPhase) {
ctx.importInfo != null
&& ctx.importInfo.featureImported(feature.toTermName,
if owner.exists then owner else defn.LanguageModule.moduleClass)
}

/** Is `feature` enabled by either a command line setting or an import?
* @param feature The name of the feature
* @param owner The prefix symbol (nested in `scala.language`) where the
* feature is defined.
*/
def enabled(feature: TermName, owner: Symbol = NoSymbol)(using Context): Boolean =
enabledBySetting(feature, owner) || enabledByImport(feature, owner)

/** Is auto-tupling enabled? */
def autoTuplingEnabled(using Context): Boolean =
!enabled(nme.noAutoTupling)

def dynamicsEnabled(using Context): Boolean =
enabled(nme.dynamics)

def sourceVersionSetting(using Context): SourceVersion =
SourceVersion.valueOf(ctx.settings.source.value)

def sourceVersion(using Context): SourceVersion =
if ctx.compilationUnit == null then sourceVersionSetting
else ctx.compilationUnit.sourceVersion.getOrElse(sourceVersionSetting)

def migrateTo3(using Context): Boolean =
sourceVersion == `3.0-migration` || enabledBySetting(nme.Scala2Compat)

/** If current source migrates to `version`, issue given warning message
* and return `true`, otherwise return `false`.
*/
def warnOnMigration(msg: Message, pos: SourcePosition,
version: SourceVersion = defaultSourceVersion)(using Context): Boolean =
if sourceVersion.isMigrating && sourceVersion.stable == version
|| version == `3.0` && migrateTo3
then
ctx.migrationWarning(msg, pos)
true
else
false

end Feature
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ScalaSettings extends Settings.SettingGroup {
val feature: Setting[Boolean] = BooleanSetting("-feature", "Emit warning and location for usages of features that should be imported explicitly.") withAbbreviation "--feature"
val help: Setting[Boolean] = BooleanSetting("-help", "Print a synopsis of standard options.") withAbbreviation "--help"
val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) withAbbreviation "--color"
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")
val target: Setting[String] = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
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"
val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).") withAbbreviation "--scalajs"
Expand All @@ -43,9 +44,8 @@ class ScalaSettings extends Settings.SettingGroup {
val verbose: Setting[Boolean] = BooleanSetting("-verbose", "Output messages about what the compiler is doing.") withAbbreviation "--verbose"
val version: Setting[Boolean] = BooleanSetting("-version", "Print product version and exit.") withAbbreviation "--version"
val pageWidth: Setting[Int] = IntSetting("-pagewidth", "Set page width", 80) withAbbreviation "--page-width"
val strict: Setting[Boolean] = BooleanSetting("-strict", "Use strict type rules, which means some formerly legal code does not typecheck anymore.") withAbbreviation "--strict"
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.") withAbbreviation "--language"
val rewrite: Setting[Option[Rewrites]] = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2Compat rewrites sources to migrate to new syntax.") withAbbreviation "--rewrite"
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"
val silentWarnings: Setting[Boolean] = BooleanSetting("-nowarn", "Silence all warnings.") withAbbreviation "--no-warnings"
val fromTasty: Setting[Boolean] = BooleanSetting("-from-tasty", "Compile classes from tasty in classpath. The arguments are used as class names.") withAbbreviation "--from-tasty"

Expand Down
11 changes: 5 additions & 6 deletions compiler/src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,13 @@ object Settings {
case (ListTag, _) =>
if (argRest.isEmpty) missingArg
else update((argRest split ",").toList, args)
case (StringTag, _) if choices.nonEmpty =>
if (argRest.isEmpty) missingArg
else if (!choices.contains(argRest))
case (StringTag, _) if choices.nonEmpty && argRest.nonEmpty =>
if (!choices.contains(argRest))
fail(s"$arg is not a valid choice for $name", args)
else update(argRest, args)
case (StringTag, arg2 :: args2) =>
if (arg2 startsWith "-") missingArg
else update(arg2, args2)
case (OutputTag, arg :: args) =>
val path = Directory(arg)
val isJar = path.extension == "jar"
Expand All @@ -149,9 +151,6 @@ object Settings {
val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path)
update(output, args)
}
case (StringTag, arg2 :: args2) =>
if (arg2 startsWith "-") missingArg
else update(arg2, args2)
case (IntTag, arg2 :: args2) =>
try {
val x = arg2.toInt
Expand Down
25 changes: 25 additions & 0 deletions compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dotty.tools
package dotc
package config

import core.Contexts.{Context, ctx}
import core.Names.TermName
import core.StdNames.nme
import core.Decorators.{given _}
import util.Property

enum SourceVersion:
case `3.0-migration`, `3.0`, `3.1-migration`, `3.1`

val isMigrating: Boolean = toString.endsWith("-migration")

def stable: SourceVersion =
if isMigrating then SourceVersion.values(ordinal + 1) else this

def isAtLeast(v: SourceVersion) = stable.ordinal >= v.ordinal

object SourceVersion extends Property.Key[SourceVersion]:
def defaultSourceVersion = `3.0`

val allSourceVersionNames = values.toList.map(_.toString.toTermName)
end SourceVersion
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import Contexts._, Types._, Symbols._, Names._, Flags._
import Denotations.SingleDenotation
import Decorators._
import collection.mutable
import config.SourceVersion.`3.1`
import config.Feature.sourceVersion

/** Realizability status */
object CheckRealizable {
Expand Down Expand Up @@ -197,8 +199,8 @@ class CheckRealizable(implicit ctx: Context) {
realizability(fld.info).mapError(r => new HasProblemField(fld, r))
}
}
if (ctx.settings.strict.value)
// check fields only under strict mode for now.
if sourceVersion.isAtLeast(`3.1`) then
// check fields only from version 3.1.
// Reason: An embedded field could well be nullable, which means it
// should not be part of a path and need not be checked; but we cannot recognize
// this situation until we have a typesystem that tracks nullability.
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Definitions {
val cls = denot.asClass.classSymbol
val decls = newScope
val arity = name.functionArity
val paramNamePrefix = tpnme.scala_ ++ str.NAME_JOIN ++ name ++ str.EXPAND_SEPARATOR
val paramNamePrefix = tpnme.scala ++ str.NAME_JOIN ++ name ++ str.EXPAND_SEPARATOR
val argParamRefs = List.tabulate(arity) { i =>
enterTypeParam(cls, paramNamePrefix ++ "T" ++ (i + 1).toString, Contravariant, decls).typeRef
}
Expand Down Expand Up @@ -199,7 +199,7 @@ class Definitions {
@tu lazy val OpsPackageVal: TermSymbol = ctx.newCompletePackageSymbol(RootClass, nme.OPS_PACKAGE).entered
@tu lazy val OpsPackageClass: ClassSymbol = OpsPackageVal.moduleClass.asClass

@tu lazy val ScalaPackageVal: TermSymbol = ctx.requiredPackage(nme.scala_)
@tu lazy val ScalaPackageVal: TermSymbol = ctx.requiredPackage(nme.scala)
@tu lazy val ScalaMathPackageVal: TermSymbol = ctx.requiredPackage("scala.math")
@tu lazy val ScalaPackageClass: ClassSymbol = {
val cls = ScalaPackageVal.moduleClass.asClass
Expand Down Expand Up @@ -1370,7 +1370,7 @@ class Definitions {

// /** The `Class[?]` of a primitive value type name */
// def valueTypeNameToJavaType(name: TypeName)(implicit ctx: Context): Option[Class[?]] =
// valueTypeNamesToJavaType.get(if (name.firstPart eq nme.scala_) name.lastPart.toTypeName else name)
// valueTypeNamesToJavaType.get(if (name.firstPart eq nme.scala) name.lastPart.toTypeName else name)

type PrimitiveClassEnc = Int

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Decorators._
import printing.Texts._
import printing.Printer
import io.AbstractFile
import config.Feature.migrateTo3
import config.Config
import util.common._
import typer.ProtoTypes.NoViewsAllowed
Expand Down Expand Up @@ -489,7 +490,7 @@ object Denotations {
// things, starting with the return type of this method.
if (preferSym(sym2, sym1)) info2
else if (preferSym(sym1, sym2)) info1
else if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2CompatMode)
else if (pre.widen.classSymbol.is(Scala2x) || migrateTo3)
info1 // follow Scala2 linearization -
// compare with way merge is performed in SymDenotation#computeMembersNamed
else throw new MergeError(ex.sym1, ex.sym2, ex.tp1, ex.tp2, pre)
Expand Down Expand Up @@ -1355,7 +1356,7 @@ object Denotations {
def isPackageFromCoreLibMissing: Boolean =
owner.symbol == defn.RootClass &&
(
selector == nme.scala_ || // if the scala package is missing, the stdlib must be missing
selector == nme.scala || // if the scala package is missing, the stdlib must be missing
selector == nme.scalaShadowing // if the scalaShadowing package is missing, the dotty library must be missing
)
if (owner.exists) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Symbols._
import Types._
import Flags._
import dotty.tools.dotc.reporting.trace
import config.Feature.migrateTo3
import config.Printers._

trait PatternTypeConstrainer { self: TypeComparer =>
Expand Down Expand Up @@ -199,7 +200,9 @@ trait PatternTypeConstrainer { self: TypeComparer =>
}
}

val widePt = if (ctx.scala2CompatMode || refinementIsInvariant(patternTp)) scrutineeTp else widenVariantParams(scrutineeTp)
val widePt =
if migrateTo3 || refinementIsInvariant(patternTp) then scrutineeTp
else widenVariantParams(scrutineeTp)
val narrowTp = SkolemType(patternTp)
trace(i"constraining simple pattern type $narrowTp <:< $widePt", gadts, res => s"$res\ngadt = ${ctx.gadt.debugBoundsDescription}") {
isSubType(narrowTp, widePt)
Expand Down
Loading