Skip to content

Commit aa2f544

Browse files
committed
Allow experimental language imports in experimental scopes
An experimental language import is permitted of the enclosing scope is an experimental scopes, or if all statements in that scope are @experimental definitions.
1 parent b830af3 commit aa2f544

File tree

7 files changed

+50
-11
lines changed

7 files changed

+50
-11
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3114,10 +3114,6 @@ object Parsers {
31143114
languageImport(tree) match
31153115
case Some(prefix) =>
31163116
in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol)
3117-
if prefix == nme.experimental
3118-
&& selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros)
3119-
then
3120-
Feature.checkExperimentalFeature("features", imp.srcPos)
31213117
for
31223118
case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors
31233119
if allSourceVersionNames.contains(imported)

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Decorators._
1313
import Symbols._, SymUtils._, NameOps._
1414
import ContextFunctionResults.annotateContextResults
1515
import config.Printers.typr
16+
import config.Feature
1617
import reporting._
1718

1819
object PostTyper {
@@ -448,6 +449,31 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
448449
throw ex
449450
}
450451

452+
/** In addition to normal processing, check that experimental language
453+
* imports are done only in experimental scopes, or in scopes where
454+
* all statements are @experimental definitions.
455+
*/
456+
override def transformStats(trees: List[Tree], exprOwner: Symbol)(using Context): List[Tree] =
457+
458+
def onlyExperimentalDefs = trees.forall {
459+
case _: Import | EmptyTree => true
460+
case stat: MemberDef => stat.symbol.isExperimental
461+
case _ => false
462+
}
463+
464+
def checkExperimentalImports =
465+
for case imp @ Import(qual, selectors) <- trees do
466+
languageImport(qual) match
467+
case Some(nme.experimental)
468+
if !ctx.owner.isInExperimentalScope && !onlyExperimentalDefs
469+
&& selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros) =>
470+
Feature.checkExperimentalFeature("features", imp.srcPos)
471+
case _ =>
472+
473+
try super.transformStats(trees, exprOwner)
474+
finally checkExperimentalImports
475+
end transformStats
476+
451477
/** Transforms the rhs tree into a its default tree if it is in an `erased` val/def.
452478
* Performed to shrink the tree that is known to be erased later.
453479
*/

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class CompilationTests {
178178
compileFile("tests/neg-custom-args/matchable.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")),
179179
compileFile("tests/neg-custom-args/i7314.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")),
180180
compileFile("tests/neg-custom-args/feature-shadowing.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
181-
compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")),
181+
compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")),
182182
).checkExpectedErrors()
183183
}
184184

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import language.experimental.erasedDefinitions // error
2+
import annotation.experimental
3+
4+
@experimental
5+
erased class CanThrow[-E <: Exception]
6+
7+
def other = 1

tests/neg-custom-args/no-experimental/experimental.scala

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,4 @@ class Test2 {
2525

2626
class Test6 {
2727
import scala.language.experimental // ok
28-
}
29-
30-
class Test7 {
31-
import scala.language.experimental
32-
import experimental.genericNumberLiterals // error: no aliases can be used to refer to a language import
33-
val x: BigInt = 13232202002020202020202 // error
3428
}

tests/pos/experimental-erased-2.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import language.experimental.erasedDefinitions
2+
import annotation.experimental
3+
4+
@experimental object Test:
5+
6+
erased class CanThrow[-E <: Exception]
7+
8+
def other = 1
9+
10+

tests/pos/experimental-erased.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import language.experimental.erasedDefinitions
2+
import annotation.experimental
3+
4+
@experimental
5+
erased class CanThrow[-E <: Exception]
6+

0 commit comments

Comments
 (0)