Skip to content

Disable experimental on nightly/snapshot build without -experimental #18577

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

Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 11 additions & 7 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,11 @@ object Feature:

def checkExperimentalFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) =
if !isExperimentalEnabled then
report.error(em"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos)
report.error(
em"""$which may only be used under experimental mode:
| 1. In a definition marked as @experimental
| 2. Compiling with the -experimental compiler flag$note
""", srcPos)

private def ccException(sym: Symbol)(using Context): Boolean =
ccEnabled && defn.ccExperimental.contains(sym)
Expand All @@ -148,18 +152,18 @@ object Feature:
if !ccException(experimentalSym) then
val symMsg =
if experimentalSym.exists
then i"$experimentalSym is marked @experimental"
else i"$sym inherits @experimental"
report.error(em"$symMsg and therefore may only be used in an experimental scope.", srcPos)
then i"$experimentalSym is marked @experimental, it"
else i"$sym inherits @experimental, it"
checkExperimentalFeature(symMsg, srcPos)

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

def isExperimentalEnabled(using Context): Boolean =
Properties.experimental && !ctx.settings.YnoExperimental.value
ctx.settings.experimental.value

/** Handle language import `import language.<prefix>.<imported>` if it is one
* of the global imports `pureFunctions` or `captureChecking`. In this case
Expand Down
8 changes: 0 additions & 8 deletions compiler/src/dotty/tools/dotc/config/Properties.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,6 @@ trait PropertiesTrait {
*/
val versionString: String = "version " + simpleVersionString

/** Whether the current version of compiler is experimental
*
* 1. Snapshot, nightly releases and non-bootstrapped compiler are experimental.
* 2. Features supported by experimental versions of the compiler:
* - research plugins
*/
val experimental: Boolean = versionString.contains("SNAPSHOT") || versionString.contains("NIGHTLY") || versionString.contains("nonbootstrapped")

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

/** This is the encoding to use reading in source files, overridden with -encoding
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ trait CommonScalaSettings:
val explainTypes: Setting[Boolean] = BooleanSetting("-explain-types", "Explain type errors in more detail (deprecated, use -explain instead).", aliases = List("--explain-types", "-explaintypes"))
val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked"))
val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.", aliases = List("--language"))
val experimental: Setting[Boolean] = BooleanSetting("-experimental", "Annotate all top-level definitions with @experimental. This enables the use of experimental features anywhere in the project.")

/* Coverage settings */
val coverageOutputDir = PathSetting("-coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out"))
Expand Down Expand Up @@ -367,7 +368,6 @@ private sealed trait YSettings:
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 YlegacyLazyVals: Setting[Boolean] = BooleanSetting("-Ylegacy-lazy-vals", "Use legacy (pre 3.3.0) implementation of lazy vals.")
val Yscala2Stdlib: Setting[Boolean] = BooleanSetting("-Yscala2-stdlib", "Used when compiling the Scala 2 standard library.")
val YoutputOnlyTasty: Setting[Boolean] = BooleanSetting("-Youtput-only-tasty", "Used to only generate the TASTy file without the classfiles")
Expand Down
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,15 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
)
}
case tree: ValDef =>
annotateExperimental(tree.symbol)
registerIfHasMacroAnnotations(tree)
checkErasedDef(tree)
val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
if tree1.removeAttachment(desugar.UntupledParam).isDefined then
checkStableSelection(tree.rhs)
processValOrDefDef(super.transform(tree1))
case tree: DefDef =>
annotateExperimental(tree.symbol)
registerIfHasMacroAnnotations(tree)
checkErasedDef(tree)
annotateContextResults(tree)
Expand Down Expand Up @@ -537,9 +539,14 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
report.error("`erased` definition cannot be implemented with en expression of type Null", tree.srcPos)

private def annotateExperimental(sym: Symbol)(using Context): Unit =
if sym.is(Module) && sym.companionClass.hasAnnotation(defn.ExperimentalAnnot) then
def isTopLevelDefinitionInSource(sym: Symbol) =
!sym.is(Package) && !sym.name.isPackageObjectName &&
(sym.owner.is(Package) || (sym.owner.name.isPackageObjectName && !sym.isConstructor))
if !sym.hasAnnotation(defn.ExperimentalAnnot)
&& (ctx.settings.experimental.value && isTopLevelDefinitionInSource(sym))
|| (sym.is(Module) && sym.companionClass.hasAnnotation(defn.ExperimentalAnnot))
then
sym.addAnnotation(Annotation(defn.ExperimentalAnnot, sym.span))
sym.companionModule.addAnnotation(Annotation(defn.ExperimentalAnnot, sym.span))

private def scala2LibPatch(tree: TypeDef)(using Context) =
val sym = tree.symbol
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -805,15 +805,15 @@ object Checking {
case Some(nme.experimental)
if !ctx.owner.isInExperimentalScope && !selectors.forall(isAllowedImport) =>
def check(stable: => String) =
Feature.checkExperimentalFeature("features", imp.srcPos,
Feature.checkExperimentalFeature("Experimental features", imp.srcPos,
s"\n\nNote: the scope enclosing the import is not considered experimental because it contains the\nnon-experimental $stable")
if ctx.owner.is(Package) then
// allow top-level experimental imports if all definitions are @experimental
nonExperimentalStat(trees) match
case EmptyTree =>
case tree: MemberDef => check(i"${tree.symbol}")
case tree => check(i"expression ${tree}")
else Feature.checkExperimentalFeature("features", imp.srcPos)
else Feature.checkExperimentalFeature("Experimental features", imp.srcPos)
case _ =>
end checkExperimentalImports
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ trait QuotesAndSplices {
getQuotedPatternTypeVariable(tree.name.asTypeName) match
case Some(typeSym) =>
checkExperimentalFeature(
"support for multiple references to the same type (without backticks) in quoted type patterns (SIP-53)",
"Experimental support for multiple references to the same type (without backticks) in quoted type patterns (SIP-53)",
tree.srcPos,
"\n\nSIP-53: https://docs.scala-lang.org/sips/quote-pattern-type-variable-syntax.html")
warnOnInferredBounds(typeSym)
Expand Down Expand Up @@ -225,7 +225,7 @@ trait QuotesAndSplices {

if quoted.isType && untpdTypeVariables.nonEmpty then
checkExperimentalFeature(
"explicit type variable declarations quoted type patterns (SIP-53)",
"Experimental explicit type variable declarations quoted type patterns (SIP-53)",
untpdTypeVariables.head.srcPos,
"\n\nSIP-53: https://docs.scala-lang.org/sips/quote-pattern-type-variable-syntax.html")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class BootstrappedOnlyCompilationTests {
val targets = dirs.map { dir =>
val compileDir = createOutputDirsForDir(dir, sourceDir, outDir)
Files.copy(dir.toPath.resolve(pluginFile), compileDir.toPath.resolve(pluginFile), StandardCopyOption.REPLACE_EXISTING)
val flags = TestFlags(withCompilerClasspath, noCheckOptions).and("-Xplugin:" + compileDir.getAbsolutePath)
val flags = TestFlags(withCompilerClasspath, noCheckOptions).and("-Xplugin:" + compileDir.getAbsolutePath).and("-experimental")
SeparateCompilationSource("testPlugins", dir, flags, compileDir)
}

Expand Down
9 changes: 6 additions & 3 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class CompilationTests {
compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
compileFilesInDir("tests/pos-scala2", defaultOptions.and("-source", "3.0-migration")),
compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")),
compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-experimental")),
compileFilesInDir("tests/pos-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions", "-experimental", "-Ysafe-init")),
compileFile("tests/pos-special/utf8encoded.scala", defaultOptions.and("-encoding", "UTF8")),
compileFile("tests/pos-special/utf16encoded.scala", defaultOptions.and("-encoding", "UTF16")),
// Run tests for legacy lazy vals
Expand Down Expand Up @@ -125,7 +126,8 @@ class CompilationTests {
aggregateTests(
compileFilesInDir("tests/neg", defaultOptions),
compileFilesInDir("tests/neg-deep-subtype", allowDeepSubtypes),
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")),
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-experimental")),
compileFilesInDir("tests/neg-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions", "-experimental")),
compileFile("tests/neg-custom-args/sourcepath/outer/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath")),
compileDir("tests/neg-custom-args/sourcepath2/hi", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath2", "-Xfatal-warnings")),
compileList("duplicate source", List(
Expand All @@ -148,7 +150,8 @@ class CompilationTests {
aggregateTests(
compileFilesInDir("tests/run", defaultOptions.and("-Ysafe-init")),
compileFilesInDir("tests/run-deep-subtype", allowDeepSubtypes),
compileFilesInDir("tests/run-custom-args/captures", allowDeepSubtypes.and("-language:experimental.captureChecking")),
compileFilesInDir("tests/run-custom-args/captures", allowDeepSubtypes.and("-language:experimental.captureChecking", "-experimental")),
compileFilesInDir("tests/run-custom-args/erased", allowDeepSubtypes.and("-language:experimental.erasedDefinitions", "-experimental")),
// Run tests for legacy lazy vals.
compileFilesInDir("tests/run", defaultOptions.and("-Ysafe-init", "-Ylegacy-lazy-vals", "-Ycheck-constraint-deps"), FileFilter.include(TestSources.runLazyValsAllowlist)),
).checkRuns()
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/changed-features/compiler-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ For experimentation and research, Scala 3 introduces _research plugin_. Research
are more powerful than Scala 2 analyzer plugins as they let plugin authors customize
the whole compiler pipeline. One can easily replace the standard typer by a custom one or
create a parser for a domain-specific language. However, research plugins are only
enabled for nightly or snaphot releases of Scala 3.
enabled with the `-experimental` compiler flag.

Common plugins that add new phases to the compiler pipeline are called
_standard plugins_ in Scala 3. In terms of features, they are similar to
Expand Down
4 changes: 4 additions & 0 deletions docs/_docs/reference/experimental/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ They are enabled by importing the feature or using the `-language` compiler flag
In general, experimental language features can be imported in an experimental scope (see [experimental definitions](../other-new-features/experimental-defs.md)).
They can be imported at the top-level if all top-level definitions are `@experimental`.

### `-experimental` compiler flag

This flag will make all top-level definitions have the `@experimental` annotation. This effectively enables the use of any experimental language feature in the project.

## Experimental language features supported by special compiler options

Some experimental language features that are still in research and development can be enabled with special compiler options. These include
Expand Down
7 changes: 5 additions & 2 deletions docs/_docs/reference/other-new-features/experimental-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,7 @@ Experimental definitions can only be referenced in an experimental scope. Experi

</details>

6. Any code compiled using a [_Nightly_](https://search.maven.org/artifact/org.scala-lang/scala3-compiler_3) or _Snapshot_ version of the compiler is considered to be in an experimental scope.
Can use the `-Yno-experimental` compiler flag to disable it and run as a proper release.
6. Any code compiled using the `-experimental` compiler flag considered to be in an experimental scope.

In any other situation, a reference to an experimental definition will cause a compilation error.

Expand Down Expand Up @@ -309,3 +308,7 @@ class MyExperimentalTests {
```

</details>

## `-experimental` compiler flag

This flag will make all top-level definitions have the `@experimental` annotation. This effectively enables the use of any experimental definition or language feature in the project.
3 changes: 0 additions & 3 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2026,9 +2026,6 @@ object Build {
settings(
versionScheme := Some("semver-spec"),
libraryDependencies += "org.scala-lang" % "scala-library" % stdlibVersion,
// Make sure we do not refer to experimental features outside an experimental scope.
// In other words, disable NIGHTLY/SNAPSHOT experimental scope.
scalacOptions += "-Yno-experimental",
).
settings(dottyLibrarySettings)
if (mode == Bootstrapped) {
Expand Down
2 changes: 2 additions & 0 deletions tests/coverage/run/erased/test.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//> using options -experimental

import scala.language.experimental.erasedDefinitions

erased def parameterless: String = "y"
Expand Down
66 changes: 33 additions & 33 deletions tests/coverage/run/erased/test.scoverage.check
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ test$package$
Object
<empty>.test$package$
foo
181
203
6
214
236
8
println
Apply
false
Expand All @@ -42,9 +42,9 @@ test$package$
Object
<empty>.test$package$
foo
189
202
6
222
235
8
s
Apply
false
Expand All @@ -59,9 +59,9 @@ test$package$
Object
<empty>.test$package$
foo
132
139
5
165
172
7
foo
DefDef
false
Expand All @@ -76,9 +76,9 @@ test$package$
Object
<empty>.test$package$
identity
245
269
10
278
302
12
println
Apply
false
Expand All @@ -93,9 +93,9 @@ test$package$
Object
<empty>.test$package$
identity
253
268
10
286
301
12
s
Apply
false
Expand All @@ -110,9 +110,9 @@ test$package$
Object
<empty>.test$package$
identity
209
221
9
242
254
11
identity
DefDef
false
Expand All @@ -127,9 +127,9 @@ test$package$
Object
<empty>.test$package$
Test
300
323
15
333
356
17
foo
Apply
false
Expand All @@ -144,9 +144,9 @@ test$package$
Object
<empty>.test$package$
Test
326
342
16
359
375
18
foo
Apply
false
Expand All @@ -161,9 +161,9 @@ test$package$
Object
<empty>.test$package$
Test
345
374
17
378
407
19
foo
Apply
false
Expand All @@ -178,9 +178,9 @@ test$package$
Object
<empty>.test$package$
Test
357
373
17
390
406
19
identity
Apply
false
Expand All @@ -195,9 +195,9 @@ test$package$
Object
<empty>.test$package$
Test
275
289
14
308
322
16
Test
DefDef
false
Expand Down
2 changes: 2 additions & 0 deletions tests/init-global/pos/global-region1.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//> using options -experimental

import scala.annotation.init.region

trait B { def foo(): Int }
Expand Down
2 changes: 2 additions & 0 deletions tests/init/pos/interleaving-overload.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//> using options -experimental

import scala.language.experimental.clauseInterleaving

class A{
Expand Down
2 changes: 2 additions & 0 deletions tests/init/pos/interleaving-params.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//> using options -experimental

import scala.collection.mutable.AbstractSet
import scala.collection.mutable.BitSet
import scala.language.experimental.clauseInterleaving
Expand Down
2 changes: 2 additions & 0 deletions tests/neg-macros/BigFloat/BigFloat_1.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//> using options -experimental

package test
import language.experimental.genericNumberLiterals
import scala.util.FromDigits
Expand Down
Loading