Skip to content

Only enable experimental features for snapshot and nightly (V2) #11920

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 17 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 16 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
4 changes: 4 additions & 0 deletions community-build/src/scala/dotty/communitybuild/projects.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ lazy val compilerVersion: String =
val file = communitybuildDir.resolve("scala3-bootstrapped.version")
new String(Files.readAllBytes(file), UTF_8)

lazy val compilerSupportExperimental: Boolean =
false &&
compilerVersion.contains("SNAPSHOT") || compilerVersion.contains("NIGHTLY")

lazy val sbtPluginFilePath: String =
// Workaround for https://github.com/sbt/sbt/issues/4395
new File(sys.props("user.home") + "/.sbt/1.0/plugins").mkdirs()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class CommunityBuildTestB extends CommunityBuildTest:
@Test def disciplineSpecs2 = projects.disciplineSpecs2.run()
@Test def munit = projects.munit.run()
@Test def perspective = projects.perspective.run()
@Test def scodec = projects.scodec.run()
@Test def scodecBits = projects.scodecBits.run()
@Test def scodec = if compilerSupportExperimental then projects.scodec.run()
@Test def scodecBits = if compilerSupportExperimental then projects.scodecBits.run()
@Test def simulacrumScalafixAnnotations = projects.simulacrumScalafixAnnotations.run()
end CommunityBuildTestB

Expand All @@ -134,7 +134,7 @@ class CommunityBuildTestC extends CommunityBuildTest:
@Test def fansi = projects.fansi.run()
@Test def fastparse = projects.fastparse.run()
@Test def geny = projects.geny.run()
@Test def intent = projects.intent.run()
@Test def intent = if compilerSupportExperimental then projects.intent.run()
@Test def minitest = projects.minitest.run()
@Test def onnxScala = projects.onnxScala.run()
@Test def oslib = projects.oslib.run()
Expand All @@ -151,7 +151,7 @@ class CommunityBuildTestC extends CommunityBuildTest:
@Test def scalatestplusScalacheck = projects.scalatestplusScalacheck.run()
@Test def scalaXml = projects.scalaXml.run()
@Test def scalaz = projects.scalaz.run()
@Test def scas = projects.scas.run()
@Test def scas = if compilerSupportExperimental then projects.scas.run()
@Test def sconfig = projects.sconfig.run()
@Test def shapeless = projects.shapeless.run()
@Test def sourcecode = projects.sourcecode.run()
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Driver {
val ictx = rootCtx.fresh
val summary = command.distill(args, ictx.settings)(ictx.settingsState)(using ictx)
ictx.setSettings(summary.sstate)
Feature.checkExperimentalSettings(using ictx)
MacroClassLoader.init(ictx)
Positioned.init(using ictx)

Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => None
case _ => None

def isLanguageImport(path: Tree): Boolean = languageImport(path).isDefined

/** The underlying pattern ignoring any bindings */
def unbind(x: Tree): Tree = unsplice(x) match {
case Bind(_, y) => unbind(y)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package dotty.tools.dotc.config
import annotation.internal.sharable

object Config {

Expand Down
28 changes: 23 additions & 5 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ import core._
import Contexts._, Symbols._, Names._, NameOps._, Phases._
import StdNames.nme
import Decorators.{_, given}
import util.SrcPos
import util.{SrcPos, NoSourcePosition}
import SourceVersion._
import reporting.Message
import NameKinds.QualifiedName

object Feature:

private def experimental(str: String): TermName =
def experimental(str: PreName): TermName =
QualifiedName(nme.experimental, str.toTermName)

private def deprecated(str: String): TermName =
private def deprecated(str: PreName): TermName =
QualifiedName(nme.deprecated, str.toTermName)

private val namedTypeArguments = experimental("namedTypeArguments")
private val genericNumberLiterals = experimental("genericNumberLiterals")
private val scala2macros = experimental("macros")
val scala2macros = experimental("macros")

val dependent = experimental("dependent")
val erasedDefinitions = experimental("erasedDefinitions")
val symbolLiterals = deprecated("symbolLiterals")
val fewerBraces = experimental("fewerBraces")

/** Is `feature` enabled by by a command-line setting? The enabling setting is
/** Is `feature` enabled by by a command-line setting? The enabling setting is
*
* -language:<prefix>feature
*
Expand Down Expand Up @@ -97,4 +97,22 @@ object Feature:
else
false

private val assumeExperimentalIn = Set("dotty.tools.vulpix.ParallelTesting")

def checkExperimentalFeature(which: String, srcPos: SrcPos = NoSourcePosition)(using Context) =
def hasSpecialPermission =
new Exception().getStackTrace.exists(elem =>
assumeExperimentalIn.exists(elem.getClassName().startsWith(_)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that we can run the tests from a release compiler

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we want to run tests on experimental features on a release compiler? These cannot be used and there will be a nightly that will cover those tests on that day anyway.

Copy link
Contributor Author

@odersky odersky Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are there so that we can make sure that the compiler code base can in fact handle the experimental features. There's only one codebase. Those tests can be run from either release or experimental versions. The fact that compilers flag experimental imports as errors unless their version string is a nightly or snapshot is orthogonal to this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the previous workaround of experimental subdirectores has been reverted.

if !(Properties.experimental || hasSpecialPermission)
|| ctx.settings.YnoExperimental.value
then
//println(i"${new Exception().getStackTrace.map(_.getClassName).toList}%\n%")
report.error(i"Experimental feature$which may only be used with nightly or snapshot version of compiler", srcPos)

/** Check that experimental compiler options are only set for snapshot or nightly compiler versions. */
def checkExperimentalSettings(using Context): Unit =
for setting <- ctx.settings.language.value
if setting.startsWith("experimental.") && setting != "experimental.macros"
do checkExperimentalFeature(s" $setting")

end Feature
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/config/Properties.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import java.nio.charset.StandardCharsets
/** Loads `library.properties` from the jar. */
object Properties extends PropertiesTrait {
protected def propCategory: String = "compiler"
protected def pickJarBasedOn: Class[Option[?]] = classOf[Option[?]]
protected def pickJarBasedOn: Class[PropertiesTrait] = classOf[PropertiesTrait]

/** Scala manifest attributes.
*/
Expand Down Expand Up @@ -88,7 +88,9 @@ trait PropertiesTrait {
* 2. Features supported by experimental versions of the compiler:
* - research plugins
*/
val experimental: Boolean = versionString.contains("SNAPSHOT") || versionString.contains("NIGHTLY")
val experimental: Boolean =
false &&
versionString.contains("SNAPSHOT") || versionString.contains("NIGHTLY")

val copyrightString: String = scalaPropOrElse("copyright.string", "(c) 2002-2017 LAMP/EPFL")

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings {
val YretainTrees: Setting[Boolean] = BooleanSetting("-Yretain-trees", "Retain trees for top-level classes, accessible from ClassSymbol#tree")
val YshowTreeIds: Setting[Boolean] = BooleanSetting("-Yshow-tree-ids", "Uniquely tag all tree nodes in debugging output.")
val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting("-Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty")
val YnoExperimental: Setting[Boolean] = BooleanSetting("-Yno-experimental", "Disable experimental language features")

val YprofileEnabled: Setting[Boolean] = BooleanSetting("-Yprofile-enabled", "Enable profiling.")
val YprofileDestination: Setting[String] = StringSetting("-Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "")
Expand Down
30 changes: 18 additions & 12 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3077,18 +3077,24 @@ object Parsers {
/** Create an import node and handle source version imports */
def mkImport(outermost: Boolean = false): ImportConstr = (tree, selectors) =>
val imp = Import(tree, selectors)
if isLanguageImport(tree) then
in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol)
for
case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors
if allSourceVersionNames.contains(imported)
do
if !outermost then
syntaxError(i"source version import is only allowed at the toplevel", id.span)
else if ctx.compilationUnit.sourceVersion.isDefined then
syntaxError(i"duplicate source version import", id.span)
else
ctx.compilationUnit.sourceVersion = Some(SourceVersion.valueOf(imported.toString))
languageImport(tree) match
case Some(prefix) =>
in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol)
if prefix == nme.experimental
&& selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros)
then
Feature.checkExperimentalFeature("s", imp.srcPos)
for
case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors
if allSourceVersionNames.contains(imported)
do
if !outermost then
syntaxError(i"source version import is only allowed at the toplevel", id.span)
else if ctx.compilationUnit.sourceVersion.isDefined then
syntaxError(i"duplicate source version import", id.span)
else
ctx.compilationUnit.sourceVersion = Some(SourceVersion.valueOf(imported.toString))
case None =>
imp

/** ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ class ImportInfo(symf: Context ?=> Symbol,
assert(myUnimported != null)
myUnimported

private val isLanguageImport: Boolean = untpd.isLanguageImport(qualifier)
private val isLanguageImport: Boolean = untpd.languageImport(qualifier).isDefined

private var myUnimported: Symbol = _

Expand Down
3 changes: 2 additions & 1 deletion compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class CompilationTests {
compileFilesInDir("tests/neg-custom-args/allow-double-bindings", allowDoubleBindings),
compileFilesInDir("tests/neg-custom-args/allow-deep-subtypes", allowDeepSubtypes),
compileFilesInDir("tests/neg-custom-args/explicit-nulls", defaultOptions.and("-Yexplicit-nulls")),
compileFilesInDir("tests/neg-custom-args/no-experimental", defaultOptions.and("-Yno-experimental")),
compileDir("tests/neg-custom-args/impl-conv", defaultOptions.and("-Xfatal-warnings", "-feature")),
compileFile("tests/neg-custom-args/implicit-conversions.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
compileFile("tests/neg-custom-args/implicit-conversions-old.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
Expand Down Expand Up @@ -239,7 +240,7 @@ class CompilationTests {
Properties.compilerInterface, Properties.scalaLibrary, Properties.scalaAsm,
Properties.dottyInterfaces, Properties.jlineTerminal, Properties.jlineReader,
).mkString(File.pathSeparator),
Array("-Ycheck-reentrant", "-language:postfixOps", "-Xsemanticdb")
Array("-Ycheck-reentrant", "-language:postfixOps", "-Xsemanticdb", "-Yno-experimental")
)

val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped"))
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import dotc.report
import dotc.interfaces.Diagnostic.ERROR
import dotc.reporting.{Reporter, TestReporter}
import dotc.reporting.Diagnostic
import dotc.config.Config
import dotc.util.DiffUtil
import io.AbstractFile
import dotty.tools.vulpix.TestConfiguration.defaultOptions
Expand All @@ -36,7 +37,6 @@ import dotty.tools.vulpix.TestConfiguration.defaultOptions
* test suite itself runs with a high level of concurrency.
*/
trait ParallelTesting extends RunnerOrchestration { self =>

import ParallelTesting._

/** If the running environment supports an interactive terminal, each `Test`
Expand Down
34 changes: 34 additions & 0 deletions tests/neg-custom-args/no-experimental/experimental.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class Test0 {
import language.experimental.namedTypeArguments // error
object Foo {
inline def f[S, T](x: S): T = ???
def g(x: Int) = f[T = Any](x)
}
}

class Test1 {
import scala.language.experimental.erasedDefinitions // error
import scala.compiletime.erasedValue
type UnivEq[A]
object UnivEq:
erased def force[A]: UnivEq[A] = erasedValue
extension [A](erased proof: UnivEq[A])
inline def univEq(a: A, b: A): Boolean =
a == b
}

class Test2 {
import _root_.scala.language.experimental.{genericNumberLiterals, namedTypeArguments => _} // error
val x: BigInt = 13232202002020202020202
val y: BigInt = -0xaabb12345ACF12345AC
}

class Test6 {
import scala.language.experimental // ok
}

class Test7 {
import scala.language.experimental
import experimental.genericNumberLiterals // error: no aliases can be used to refer to a language import
val x: BigInt = 13232202002020202020202 // error
}