Skip to content

Commit

Permalink
Merge pull request #11920 from dotty-staging/fix-11774-v2
Browse files Browse the repository at this point in the history
Only enable experimental features for snapshot and nightly (V2)
  • Loading branch information
odersky authored Mar 29, 2021
2 parents e7a6e31 + a9b808b commit cadf3bf
Show file tree
Hide file tree
Showing 15 changed files with 92 additions and 29 deletions.
3 changes: 3 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,9 @@ lazy val compilerVersion: String =
val file = communitybuildDir.resolve("scala3-bootstrapped.version")
new String(Files.readAllBytes(file), UTF_8)

lazy val compilerSupportExperimental: Boolean =
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(_)))
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
2 changes: 1 addition & 1 deletion 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
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
}

0 comments on commit cadf3bf

Please sign in to comment.