diff --git a/community-build/community-projects/scalap b/community-build/community-projects/scalap index 08e4a6172dfe..6dc9e16358a0 160000 --- a/community-build/community-projects/scalap +++ b/community-build/community-projects/scalap @@ -1 +1 @@ -Subproject commit 08e4a6172dfef0425abe0dac6808127ba58ce909 +Subproject commit 6dc9e16358a07150ca0e89df6ed38e79854a411c diff --git a/community-build/community-projects/stdLib213 b/community-build/community-projects/stdLib213 index 2361c1f0373f..d39b0169e2e1 160000 --- a/community-build/community-projects/stdLib213 +++ b/community-build/community-projects/stdLib213 @@ -1 +1 @@ -Subproject commit 2361c1f0373f8af5418abe393bb85663f8f95259 +Subproject commit d39b0169e2e1fa434d05669fe4abc720ac879e6e diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 673c21c3a42c..a44b44d6085f 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -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() diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 9c056bdb9ac4..82f635ffa508 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -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 @@ -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() @@ -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() diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 03b946c74d9f..20ae4da58d18 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 950cf8be9b56..2c1353250db2 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 35088cfadbf3..c009372f19d3 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -1,4 +1,5 @@ package dotty.tools.dotc.config +import annotation.internal.sharable object Config { diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 182dd4aed248..42522d38be51 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -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:feature * @@ -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 \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/config/Properties.scala b/compiler/src/dotty/tools/dotc/config/Properties.scala index 4e084e050cf6..3ec49d8eef68 100644 --- a/compiler/src/dotty/tools/dotc/config/Properties.scala +++ b/compiler/src/dotty/tools/dotc/config/Properties.scala @@ -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. */ diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index cf00cd131ef0..ad1e0ad7151a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -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.", "") diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 66817e59da10..cec916a4b826 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala index 65b86cb9fb25..a975dd206d22 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -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 = _ diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 4d040788f686..db05ed611a0e 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -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")), @@ -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")) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 2b64069c6314..5855af9f5b04 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -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 @@ -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` diff --git a/tests/neg-custom-args/no-experimental/experimental.scala b/tests/neg-custom-args/no-experimental/experimental.scala new file mode 100644 index 000000000000..26f9ba3d21c7 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimental.scala @@ -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 +} \ No newline at end of file