Skip to content

Commit def9302

Browse files
committed
Enable experimental mode when experimental feature is imported
The `@experimental` flag is added to top-level definitions in the package where the language feature is imported.
1 parent 5fbf3e3 commit def9302

File tree

6 files changed

+33
-24
lines changed

6 files changed

+33
-24
lines changed

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ object Feature:
136136
report.error(
137137
em"""Experimental $which may only be used under experimental mode:
138138
| 1. in a definition marked as @experimental, or
139-
| 2. compiling with the -experimental compiler flag, or
140-
| 3. with a nightly or snapshot version of the compiler.$note
139+
| 2. an experimental feature is imported in at the package level, or
140+
| 3. compiling with the -experimental compiler flag, or
141+
| 4. with a nightly or snapshot version of the compiler.$note
141142
""", srcPos)
142143

143144
private def ccException(sym: Symbol)(using Context): Boolean =

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -806,23 +806,22 @@ object Checking {
806806
* scope with only @experimental definitions.
807807
*/
808808
def checkExperimentalImports(trees: List[Tree])(using Context): Unit =
809-
810-
def nonExperimentalStat(trees: List[Tree]): Tree = trees match
811-
case (_: Import | EmptyTree) :: rest =>
812-
nonExperimentalStat(rest)
809+
def nonExperimentalStats(trees: List[Tree]): List[Tree] = trees match
810+
case (_: ImportOrExport | EmptyTree) :: rest =>
811+
nonExperimentalStats(rest)
813812
case (tree @ TypeDef(_, impl: Template)) :: rest if tree.symbol.isPackageObject =>
814-
nonExperimentalStat(impl.body).orElse(nonExperimentalStat(rest))
813+
nonExperimentalStats(impl.body) ::: nonExperimentalStats(rest)
815814
case (tree: PackageDef) :: rest =>
816-
nonExperimentalStat(tree.stats).orElse(nonExperimentalStat(rest))
815+
nonExperimentalStats(tree.stats) ::: nonExperimentalStats(rest)
817816
case (tree: MemberDef) :: rest =>
818817
if tree.symbol.isExperimental || tree.symbol.is(Synthetic) then
819-
nonExperimentalStat(rest)
818+
nonExperimentalStats(rest)
820819
else
821-
tree
820+
tree :: nonExperimentalStats(rest)
822821
case tree :: rest =>
823-
tree
822+
tree :: nonExperimentalStats(rest)
824823
case Nil =>
825-
EmptyTree
824+
Nil
826825

827826
for case imp @ Import(qual, selectors) <- trees do
828827
def isAllowedImport(sel: untpd.ImportSelector) =
@@ -838,11 +837,16 @@ object Checking {
838837
Feature.checkExperimentalFeature("features", imp.srcPos,
839838
s"\n\nNote: the scope enclosing the import is not considered experimental because it contains the\nnon-experimental $stable")
840839
if ctx.owner.is(Package) then
841-
// allow top-level experimental imports if all definitions are @experimental
842-
nonExperimentalStat(trees) match
843-
case EmptyTree =>
844-
case tree: MemberDef => check(i"${tree.symbol}")
845-
case tree => check(i"expression ${tree}")
840+
// mark all top-level definitions as @experimental
841+
for tree <- nonExperimentalStats(trees) do
842+
tree match
843+
case tree: MemberDef =>
844+
// TODO move this out of checking (into posttyper?)
845+
val sym = tree.symbol
846+
if !sym.isExperimental then
847+
sym.addAnnotation(Annotations.Annotation(defn.ExperimentalAnnot, sym.span))
848+
case tree =>
849+
check(i"expression ${tree}")
846850
else Feature.checkExperimentalFeature("features", imp.srcPos)
847851
case _ =>
848852
end checkExperimentalImports

docs/_docs/reference/other-new-features/experimental-defs.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ Experimental definitions can only be referenced in an experimental scope. Experi
265265

266266
</details>
267267

268-
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.
268+
6. An experimental language feature is imported in at the package level
269+
270+
7. 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.
269271
Can use the `-Yno-experimental` compiler flag to disable it and run as a proper release.
270272

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

tests/neg/expeimental-flag-with-lang-feature-2.scala renamed to tests/pos/expeimental-flag-with-lang-feature-2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//> using options -Yno-experimental
22

3-
import scala.language.experimental.namedTypeArguments // error
3+
import scala.language.experimental.namedTypeArguments
44

55
def namedTypeArgumentsFun[T, U]: Int =
66
namedTypeArgumentsFun[T = Int, U = Int]

tests/neg/experimental-package-imports.scala renamed to tests/pos/experimental-package-imports.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import annotation.experimental
44

55
package foo {
6-
import language.experimental.namedTypeArguments // error
7-
import language.experimental.genericNumberLiterals // error
8-
import language.experimental.erasedDefinitions // ok: only check at erased definition
6+
import language.experimental.namedTypeArguments
7+
import language.experimental.genericNumberLiterals
8+
import language.experimental.erasedDefinitions
99

1010
package bar {
11-
def foo = 1
11+
def foo = 1 // marked as @experimental because of the language imports
1212
}
1313
}
1414

tests/pos/interleaving-chainedParams.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
//> using options -Yno-experimental
2+
13
import scala.language.experimental.clauseInterleaving
24

35
object chainedParams{
46

57
trait Chain{
68
type Tail <: Chain
79
}
8-
10+
911
def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ???
1012

1113
val self = new Chain{ type Tail = this.type }

0 commit comments

Comments
 (0)