diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala
index 15c2baca1a2f..df2cab2c3375 100644
--- a/compiler/src/dotty/tools/dotc/config/Feature.scala
+++ b/compiler/src/dotty/tools/dotc/config/Feature.scala
@@ -97,8 +97,6 @@ object Feature:
else
false
- private val assumeExperimentalIn = Set("dotty.tools.vulpix.ParallelTesting")
-
def checkExperimentalFeature(which: String, srcPos: SrcPos)(using Context) =
if !isExperimentalEnabled then
report.error(i"Experimental $which may only be used with a nightly or snapshot version of the compiler", srcPos)
@@ -106,15 +104,13 @@ object Feature:
def checkExperimentalDef(sym: Symbol, srcPos: SrcPos)(using Context) =
if !isExperimentalEnabled then
val symMsg =
- if sym eq defn.ExperimentalAnnot then
- i"use of @experimental is experimental"
- else if sym.hasAnnotation(defn.ExperimentalAnnot) then
+ if sym.hasAnnotation(defn.ExperimentalAnnot) then
i"$sym is marked @experimental"
else if sym.owner.hasAnnotation(defn.ExperimentalAnnot) then
i"${sym.owner} is marked @experimental"
else
i"$sym inherits @experimental"
- report.error(s"$symMsg and therefore may only be used with a nightly or snapshot version of the compiler", srcPos)
+ report.error(s"$symMsg and therefore may only be used in an experimental scope.", srcPos)
/** Check that experimental compiler options are only set for snapshot or nightly compiler versions. */
def checkExperimentalSettings(using Context): Unit =
@@ -123,9 +119,6 @@ object Feature:
do checkExperimentalFeature(s"feature $setting", NoSourcePosition)
def isExperimentalEnabled(using Context): Boolean =
- def hasSpecialPermission =
- Thread.currentThread.getStackTrace.exists(elem =>
- assumeExperimentalIn.exists(elem.getClassName().startsWith(_)))
- (Properties.experimental || hasSpecialPermission) && !ctx.settings.YnoExperimental.value
+ Properties.experimental && !ctx.settings.YnoExperimental.value
end Feature
\ No newline at end of file
diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
index 691425bfb713..353ebee25c29 100644
--- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
+++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala
@@ -267,11 +267,23 @@ object SymUtils:
/** Is symbol declared or inherits @experimental? */
def isExperimental(using Context): Boolean =
- // TODO should be add `@experimental` to `class experimental` in PostTyper?
- self.eq(defn.ExperimentalAnnot)
- || self.hasAnnotation(defn.ExperimentalAnnot)
+ self.hasAnnotation(defn.ExperimentalAnnot)
|| (self.maybeOwner.isClass && self.owner.hasAnnotation(defn.ExperimentalAnnot))
+ def isInExperimentalScope(using Context): Boolean =
+ def isDefaultArgumentOfExperimentalMethod =
+ self.name.is(DefaultGetterName)
+ && self.owner.isClass
+ && {
+ val overloads = self.owner.asClass.membersNamed(self.name.firstPart)
+ overloads.filterWithFlags(HasDefaultParams, EmptyFlags) match
+ case denot: SymDenotation => denot.symbol.isExperimental
+ case _ => false
+ }
+ self.hasAnnotation(defn.ExperimentalAnnot)
+ || isDefaultArgumentOfExperimentalMethod
+ || (!self.is(Package) && self.owner.isInExperimentalScope)
+
/** The declared self type of this class, as seen from `site`, stripping
* all refinements for opaque types.
*/
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
index 6e9d4caa7f6a..53a2e4a5a4f5 100644
--- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -948,27 +948,23 @@ object RefChecks {
report.deprecationWarning(s"${sym.showLocated} is deprecated${since}${msg}", pos)
private def checkExperimental(sym: Symbol, pos: SrcPos)(using Context): Unit =
- if sym.isExperimental
- && !sym.isConstructor // already reported on the class
- && !ctx.owner.isExperimental // already reported on the @experimental of the owner
- && !sym.is(ModuleClass) // already reported on the module
- && (sym.span.exists || sym != defn.ExperimentalAnnot) // already reported on inferred annotations
- then
+ if sym.isExperimental && !ctx.owner.isInExperimentalScope then
Feature.checkExperimentalDef(sym, pos)
private def checkExperimentalSignature(sym: Symbol, pos: SrcPos)(using Context): Unit =
- val checker = new TypeTraverser:
+ class Checker extends TypeTraverser:
def traverse(tp: Type): Unit =
if tp.typeSymbol.isExperimental then
Feature.checkExperimentalDef(tp.typeSymbol, pos)
else
traverseChildren(tp)
- if !sym.owner.isExperimental && !pos.span.isSynthetic then // avoid double errors
- checker.traverse(sym.info)
+ if !sym.isInExperimentalScope then
+ new Checker().traverse(sym.info)
private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit =
- for annot <- sym.annotations if annot.symbol.isExperimental && annot.tree.span.exists do
- Feature.checkExperimentalDef(annot.symbol, annot.tree)
+ if !sym.isInExperimentalScope then
+ for annot <- sym.annotations if annot.symbol.isExperimental do
+ Feature.checkExperimentalDef(annot.symbol, annot.tree)
/** If @migration is present (indicating that the symbol has changed semantics between versions),
* emit a warning.
@@ -1338,7 +1334,6 @@ class RefChecks extends MiniPhase { thisPhase =>
}
override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = {
- checkExperimental(tree.symbol, tree.srcPos)
checkExperimentalAnnots(tree.symbol)
tree
}
diff --git a/docs/docs/reference/other-new-features/experimental-defs.md b/docs/docs/reference/other-new-features/experimental-defs.md
new file mode 100644
index 000000000000..d3e42395162a
--- /dev/null
+++ b/docs/docs/reference/other-new-features/experimental-defs.md
@@ -0,0 +1,305 @@
+---
+layout: doc-page
+title: "Experimental definitions"
+---
+
+## Experimental definitions
+
+The `@experimental` annotation allows the definition of an API that is not guaranteed backward binary or source compatibility.
+This annotation can be placed on term or type definitions.
+
+### References to experimental definitions
+
+Experimental definitions can only be referenced in an experimental scope. Experimental scopes are defined as follows.
+
+(1) The RHS of an experimental `def`, `val`, `var`, `given` or `type` is an experimental scope.
+
+
+Examples
+
+```scala
+import scala.annotation.experimental
+
+@experimental
+def x = ()
+
+def d1 = x // error: value x is marked @experimental and therefore ...
+@experimental def d2 = x
+
+val v1 = x // error: value x is marked @experimental and therefore ...
+@experimental val v2 = x
+
+var vr1 = x // error: value x is marked @experimental and therefore ...
+@experimental var vr2 = x
+
+lazy val lv1 = x // error: value x is marked @experimental and therefore ...
+@experimental lazy val lv2 = x
+```
+
+```scala
+import scala.annotation.experimental
+
+@experimental
+val x = ()
+
+@experimental
+def f() = ()
+
+@experimental
+object X:
+ def fx() = 1
+
+def test1: Unit =
+ f() // error: def f is marked @experimental and therefore ...
+ x // error: value x is marked @experimental and therefore ...
+ X.fx() // error: object X is marked @experimental and therefore ...
+ import X.fx
+ fx() // error: object X is marked @experimental and therefore ...
+
+@experimental
+def test2: Unit =
+ // references to f, x and X are ok because `test2` is experimental
+ f()
+ x
+ X.fx()
+ import X.fx
+ fx()
+```
+
+```scala
+import scala.annotation.experimental
+
+@experimental type E
+
+type A = E // error type E is marked @experimental and therefore ...
+@experimental type B = E
+```
+
+```scala
+import scala.annotation.experimental
+
+@experimental class A
+@experimental type X
+@experimental type Y = Int
+@experimental opaque type Z = Int
+
+def test: Unit =
+ new A // error: class A is marked @experimental and therefore ...
+ val i0: A = ??? // error: class A is marked @experimental and therefore ...
+ val i1: X = ??? // error: type X is marked @experimental and therefore ...
+ val i2: Y = ??? // error: type Y is marked @experimental and therefore ...
+ val i2: Z = ??? // error: type Y is marked @experimental and therefore ...
+ ()
+```
+
+```scala
+@experimental
+trait ExpSAM {
+ def foo(x: Int): Int
+}
+def bar(f: ExpSAM): Unit = {} // error: error form rule 2
+
+def test: Unit =
+ bar(x => x) // error: reference to experimental SAM
+ ()
+```
+
+
+
+(2.) The signatures of an experimental `def`, `val`, `var`, `given` and `type`, or constructors of `class` and `trait` are experimental scopes.
+
+
+Examples
+
+```scala
+import scala.annotation.experimental
+
+@experimental def x = 2
+@experimental class A
+@experimental type X
+@experimental type Y = Int
+@experimental opaque type Z = Int
+
+def test1(
+ p1: A, // error: class A is marked @experimental and therefore ...
+ p2: List[A], // error: class A is marked @experimental and therefore ...
+ p3: X, // error: type X is marked @experimental and therefore ...
+ p4: Y, // error: type Y is marked @experimental and therefore ...
+ p5: Z, // error: type Z is marked @experimental and therefore ...
+ p6: Any = x // error: def x is marked @experimental and therefore ...
+): A = ??? // error: class A is marked @experimental and therefore ...
+
+@experimental def test2(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+ p6: Any = x
+): A = ???
+
+class Test1(
+ p1: A, // error
+ p2: List[A], // error
+ p3: X, // error
+ p4: Y, // error
+ p5: Z, // error
+ p6: Any = x // error
+) {}
+
+@experimental class Test2(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+ p6: Any = x
+) {}
+
+trait Test1(
+ p1: A, // error
+ p2: List[A], // error
+ p3: X, // error
+ p4: Y, // error
+ p5: Z, // error
+ p6: Any = x // error
+) {}
+
+@experimental trait Test2(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+ p6: Any = x
+) {}
+```
+
+
+
+(3.) The `extends` clause of an experimental `class`, `trait` or `object` is an experimental scope.
+
+
+Examples
+
+```scala
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+@experimental class A1(x: Any)
+class A2(x: Any)
+
+
+@experimental class B1 extends A1(1)
+class B2 extends A1(1) // error: class A1 is marked @experimental and therefore marked @experimental and therefore ...
+
+@experimental class C1 extends A2(x)
+class C2 extends A2(x) // error def x is marked @experimental and therefore
+```
+
+
+
+(4.) The body of an experimental `class`, `trait` or `object` is an experimental scope.
+
+
+Examples
+
+```scala
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+@experimental class A {
+ def f = x // ok because A is experimental
+}
+
+@experimental class B {
+ def f = x // ok because A is experimental
+}
+
+@experimental object C {
+ def f = x // ok because A is experimental
+}
+
+@experimental class D {
+ def f = {
+ object B {
+ x // ok because A is experimental
+ }
+ }
+}
+```
+
+
+
+(5.) Annotations of an experimental definition are in experimental scopes.
+
+
+Examples
+
+```scala
+import scala.annotation.experimental
+
+@experimental class myExperimentalAnnot extends scala.annotation.Annotation
+
+@myExperimentalAnnot // error
+def test: Unit = ()
+
+@experimental
+@myExperimentalAnnot
+def test: Unit = ()
+```
+
+
+
+(6.) Any code compiled using a _Nightly_ 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.
+
+In any other situation, a reference to an experimental definition will cause a compilation error.
+
+### Experimental inheritance
+
+All subclasses of an experimental `class` or `trait` must be marked as `@experimental` even if they are in an experimental scope.
+Anonymous classes and SAMs of experimental classes are considered experimental.
+
+We require explicit annotations to make sure we do not have completion or cycles issues with nested classes. This restriction could be relaxed in the future.
+
+### Experimental overriding
+
+For an overriding member `M` and overridden member `O`, if `O` is non-experimental then `M` must be non-experimental.
+
+This makes sure that we cannot have accidental binary incompatibilities such as the following change.
+```diff
+class A:
+ def f: Any = 1
+class B extends A:
+- @experimental def f: Int = 2
+```
+### Test frameworks
+
+Tests can be defined as experimental. Tests frameworks can execute tests using reflection even if they are in an experimental class, object or method.
+
+
+Examples
+
+Test that touch experimental APIs can be written as follows
+
+```scala
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+class MyTests {
+ /*@Test*/ def test1 = x // error
+ @experimental /*@Test*/ def test2 = x
+}
+
+@experimental
+class MyExperimentalTests {
+ /*@Test*/ def test1 = x
+ /*@Test*/ def test2 = x
+}
+```
+
+
diff --git a/docs/sidebar.yml b/docs/sidebar.yml
index 471382fa292f..4983c1e4b33a 100644
--- a/docs/sidebar.yml
+++ b/docs/sidebar.yml
@@ -67,6 +67,7 @@ sidebar:
- page: docs/reference/other-new-features/explicit-nulls.md
- page: docs/reference/other-new-features/safe-initialization.md
- page: docs/reference/other-new-features/type-test.md
+ - page: docs/reference/other-new-features/experimental-defs.md
- title: Other Changed Features
subsection:
- page: docs/reference/changed-features/numeric-literals.md
diff --git a/library/src-non-bootstrapped/scala/annotation/internal/experimentalTest.scala b/library/src-non-bootstrapped/scala/annotation/internal/experimentalTest.scala
new file mode 100644
index 000000000000..1240f9039670
--- /dev/null
+++ b/library/src-non-bootstrapped/scala/annotation/internal/experimentalTest.scala
@@ -0,0 +1,10 @@
+// KEEP IN NON-BOOTSTRAPPED SOURCES
+package scala.annotation
+package internal
+
+/** This is a dummy definition that tests that the stdlib can be compiled with an experimental definition.
+ * This might be redundant, we keep it definition in case there are no other @experimental definitions in the library.
+ * As this definition is in `src-non-bootstrapped`, it will not be published.
+ * It may accidentally be visible while compiling the non-bootstrapped library.
+ */
+@experimental def testExperimental = 4
diff --git a/library/src/scala/annotation/experimental.scala b/library/src/scala/annotation/experimental.scala
index 4ccda84d623c..3d7a023176e3 100644
--- a/library/src/scala/annotation/experimental.scala
+++ b/library/src/scala/annotation/experimental.scala
@@ -2,13 +2,7 @@ package scala.annotation
/** An annotation that can be used to mark a definition as experimental.
*
- * This class is experimental as well as if it was defined as
- * ```scala
- * @experimental
- * class experimental extends StaticAnnotation
- * ```
- *
+ * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/experimental-defs]]
* @syntax markdown
*/
-// @experimental
class experimental extends StaticAnnotation
diff --git a/tests/neg-custom-args/no-experimental/experimentalAnnot.scala b/tests/neg-custom-args/no-experimental/experimentalAnnot.scala
index 3bd87d67ed7b..e6dfbf28f8bb 100644
--- a/tests/neg-custom-args/no-experimental/experimentalAnnot.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalAnnot.scala
@@ -1,7 +1,22 @@
import scala.annotation.experimental
-@experimental // error
-class myExperimentalAnnot extends scala.annotation.Annotation
+@experimental class myExperimentalAnnot extends scala.annotation.Annotation
@myExperimentalAnnot // error
-def test: Unit = ()
+def test1: Unit = ()
+
+@experimental
+@myExperimentalAnnot
+def test2: Unit = ()
+
+@experimental
+class Foo {
+ @myExperimentalAnnot
+ def test3: Unit = ()
+
+ def test4: Unit = {
+ @myExperimentalAnnot
+ val f: Unit = ()
+ f
+ }
+}
diff --git a/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala b/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala
index 51d5b4957993..b112c8a1213a 100644
--- a/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala
@@ -1,9 +1,9 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
case class Foo(a: Int)
-@experimental // error
+@experimental
case class Bar(a: Int)
object Bar:
diff --git a/tests/neg-custom-args/no-experimental/experimentalDefaultParams.scala b/tests/neg-custom-args/no-experimental/experimentalDefaultParams.scala
new file mode 100644
index 000000000000..4dedb3afa11d
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalDefaultParams.scala
@@ -0,0 +1,27 @@
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+def test1(
+ p6: Any = x // error: def x is marked @experimental and therefore ...
+): Any = ???
+
+@experimental def test2(
+ p6: Any = x
+): Any = ???
+
+class Test1(
+ p6: Any = x // error
+) {}
+
+@experimental class Test2(
+ p6: Any = x
+) {}
+
+trait Test3(
+ p6: Any = x // error
+) {}
+
+@experimental trait Test4(
+ p6: Any = x
+) {}
diff --git a/tests/neg-custom-args/no-experimental/experimentalEnum.scala b/tests/neg-custom-args/no-experimental/experimentalEnum.scala
index 3a4e3cc093f0..1cbe78ca5427 100644
--- a/tests/neg-custom-args/no-experimental/experimentalEnum.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalEnum.scala
@@ -1,6 +1,6 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
enum E:
case A
case B
diff --git a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala
deleted file mode 100644
index d88406d634e7..000000000000
--- a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala
+++ /dev/null
@@ -1,4 +0,0 @@
-import scala.annotation.experimental
-
-class MyExperimentalAnnot // error
- extends experimental // error
diff --git a/tests/neg-custom-args/no-experimental/experimentalInheritance.scala b/tests/neg-custom-args/no-experimental/experimentalInheritance.scala
new file mode 100644
index 000000000000..f6eab1224310
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalInheritance.scala
@@ -0,0 +1,14 @@
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+@experimental class A1(x: Any)
+class A2(x: Any)
+
+
+@experimental class B1 extends A1(1)
+class B2 // error: extension of experimental class A1 must have @experimental annotation
+extends A1(1) // error: class A1 is marked @experimental ...
+
+@experimental class C1 extends A2(x)
+class C2 extends A2(x) // error def x is marked @experimental and therefore
diff --git a/tests/neg-custom-args/no-experimental/experimentalMembers.scala b/tests/neg-custom-args/no-experimental/experimentalMembers.scala
new file mode 100644
index 000000000000..e30f27b069a8
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalMembers.scala
@@ -0,0 +1,39 @@
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+@experimental class A {
+ def f = x // ok because A is experimental
+}
+
+@experimental class B {
+ def f = x // ok because A is experimental
+}
+
+@experimental object C {
+ def f = x // ok because A is experimental
+}
+
+@experimental class D {
+ def f = {
+ object B {
+ x // ok because A is experimental
+ }
+ }
+}
+
+@experimental class E {
+ def f = {
+ def g = {
+ x // ok because A is experimental
+ }
+ }
+}
+
+class F {
+ def f = {
+ def g = {
+ x // error
+ }
+ }
+}
diff --git a/tests/neg-custom-args/no-experimental/experimentalOverride.scala b/tests/neg-custom-args/no-experimental/experimentalOverride.scala
index 2a052d959f46..653bd3b23da4 100644
--- a/tests/neg-custom-args/no-experimental/experimentalOverride.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalOverride.scala
@@ -1,22 +1,22 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
class A:
def f() = 1
-@experimental // error
+@experimental
class B extends A:
override def f() = 2
class C:
- @experimental // error
+ @experimental
def f() = 1
class D extends C:
override def f() = 2
trait A2:
- @experimental // error
+ @experimental
def f(): Int
trait B2:
diff --git a/tests/neg-custom-args/no-experimental/experimentalRHS.scala b/tests/neg-custom-args/no-experimental/experimentalRHS.scala
new file mode 100644
index 000000000000..27143c120b96
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalRHS.scala
@@ -0,0 +1,16 @@
+import scala.annotation.experimental
+
+@experimental
+def x = ()
+
+def d1 = x // error: value x is marked @experimental and therefore ...
+@experimental def d2 = x
+
+val v1 = x // error: value x is marked @experimental and therefore ...
+@experimental val v2 = x
+
+var vr1 = x // error: value x is marked @experimental and therefore ...
+@experimental var vr2 = x
+
+lazy val lv1 = x // error: value x is marked @experimental and therefore ...
+@experimental lazy val lv2 = x
diff --git a/tests/neg-custom-args/no-experimental/experimentalSam.scala b/tests/neg-custom-args/no-experimental/experimentalSam.scala
index 578fb8f93f94..cdc9e61858d9 100644
--- a/tests/neg-custom-args/no-experimental/experimentalSam.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalSam.scala
@@ -1,6 +1,6 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
trait ExpSAM {
def foo(x: Int): Int
}
diff --git a/tests/neg-custom-args/no-experimental/experimentalSignature.scala b/tests/neg-custom-args/no-experimental/experimentalSignature.scala
new file mode 100644
index 000000000000..9b1d3c5e999f
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalSignature.scala
@@ -0,0 +1,54 @@
+import scala.annotation.experimental
+
+@experimental class A
+@experimental type X
+@experimental type Y = Int
+@experimental opaque type Z = Int
+
+def test1(
+ p1: A, // error: class A is marked @experimental and therefore ...
+ p2: List[A], // error: class A is marked @experimental and therefore ...
+ p3: X, // error: type X is marked @experimental and therefore ...
+ p4: Y, // error: type Y is marked @experimental and therefore ...
+ p5: Z, // error: type Z is marked @experimental and therefore ...
+): A = ??? // error: class A is marked @experimental and therefore ...
+
+@experimental def test2(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+): A = ???
+
+class Test1(
+ p1: A, // error
+ p2: List[A], // error
+ p3: X, // error
+ p4: Y, // error
+ p5: Z, // error
+) {}
+
+@experimental class Test2(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+) {}
+
+trait Test3(
+ p1: A, // error
+ p2: List[A], // error
+ p3: X, // error
+ p4: Y, // error
+ p5: Z, // error
+) {}
+
+@experimental trait Test4(
+ p1: A,
+ p2: List[A],
+ p3: X,
+ p4: Y,
+ p5: Z,
+) {}
diff --git a/tests/neg-custom-args/no-experimental/experimentalTerms.scala b/tests/neg-custom-args/no-experimental/experimentalTerms.scala
index 5fc3f2b3fad7..09b69d2da381 100644
--- a/tests/neg-custom-args/no-experimental/experimentalTerms.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalTerms.scala
@@ -1,19 +1,27 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
val x = ()
-@experimental // error
+@experimental
def f() = ()
-@experimental // error
+@experimental
object X:
def fx() = 1
-def test: Unit =
- f() // error
- x // error
- X.fx() // error
+def test1: Unit =
+ f() // error: def f is marked @experimental and therefore ...
+ x // error: value x is marked @experimental and therefore ...
+ X.fx() // error: object X is marked @experimental and therefore ...
import X.fx
- fx() // error
- ()
+ fx() // error: object X is marked @experimental and therefore ...
+
+@experimental
+def test2: Unit =
+ // references to f, x and X are ok because `test2` is experimental
+ f()
+ x
+ X.fx()
+ import X.fx
+ fx()
diff --git a/tests/neg-custom-args/no-experimental/experimentalTests.scala b/tests/neg-custom-args/no-experimental/experimentalTests.scala
new file mode 100644
index 000000000000..f3fbcf8c587c
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalTests.scala
@@ -0,0 +1,15 @@
+import scala.annotation.experimental
+
+@experimental def x = 2
+
+class MyTests {
+ /*@Test*/ def test1 = x // error
+ @experimental
+ /*@Test*/ def test2 = x
+}
+
+@experimental
+class MyExperimentalTests {
+ /*@Test*/ def test1 = x
+ /*@Test*/ def test2 = x
+}
diff --git a/tests/neg-custom-args/no-experimental/experimentalType.scala b/tests/neg-custom-args/no-experimental/experimentalType.scala
index e49b53192944..f4013788796a 100644
--- a/tests/neg-custom-args/no-experimental/experimentalType.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalType.scala
@@ -1,31 +1,22 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
class A
-@experimental // error
+@experimental
class B extends A
@experimental
-type X // error
+type X
@experimental
-type Y = Int // error
+type Y = Int
@experimental
-opaque type Z = Int // error
-
-type W = Z // error
+opaque type Z = Int
-def test(
- p1: A, // error
- p2: List[A], // error
- p3: X, // error
- p4: Y, // error
- p5: Z, // error
-): Unit =
- new A // error
- new B // error
- val i1 = identity[X] // error // error
- val i2 = identity[A] // error // error
- ()
+type AA = A // error
+type BB = Z // error
+type XX = Z // error
+type YY = Z // error
+type ZZ = Z // error
diff --git a/tests/neg-custom-args/no-experimental/experimentalTypeRHS.scala b/tests/neg-custom-args/no-experimental/experimentalTypeRHS.scala
new file mode 100644
index 000000000000..3aaeb960bae9
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalTypeRHS.scala
@@ -0,0 +1,6 @@
+import scala.annotation.experimental
+
+@experimental type E
+
+type A = E // error
+@experimental type B = E
diff --git a/tests/neg-custom-args/no-experimental/experimentalTypes2.scala b/tests/neg-custom-args/no-experimental/experimentalTypes2.scala
new file mode 100644
index 000000000000..706fd39fd15c
--- /dev/null
+++ b/tests/neg-custom-args/no-experimental/experimentalTypes2.scala
@@ -0,0 +1,14 @@
+import scala.annotation.experimental
+
+@experimental class A
+@experimental type X
+@experimental type Y = Int
+@experimental opaque type Z = Int
+
+def test2: Unit =
+ new A // error: class A is marked @experimental and therefore ...
+ val i0: A = ??? // error: class A is marked @experimental and therefore ...
+ val i1: X = ??? // error: type X is marked @experimental and therefore ...
+ val i2: Y = ??? // error: type Y is marked @experimental and therefore ...
+ val i3: Z = ??? // error: type Z is marked @experimental and therefore ...
+ ()
diff --git a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala
index d5cd3ad55590..0ba338a15a96 100644
--- a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala
+++ b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala
@@ -1,13 +1,13 @@
import scala.annotation.experimental
-@experimental // error
+@experimental
class A
object Extractor1:
def unapply(s: Any): Option[A] = ??? // error
object Extractor2:
- @experimental // error
+ @experimental
def unapply(s: Any): Option[Int] = ???
def test: Unit =
diff --git a/tests/neg-custom-args/no-experimental/i13091.scala b/tests/neg-custom-args/no-experimental/i13091.scala
index b2e0c1844540..2b08788ebbc1 100644
--- a/tests/neg-custom-args/no-experimental/i13091.scala
+++ b/tests/neg-custom-args/no-experimental/i13091.scala
@@ -1,5 +1,5 @@
import annotation.experimental
-@experimental class Foo // error: use of @experimental is experimental ...
+@experimental class Foo
def test: Unit = new Foo // error: class Foo is marked @experimental ...
diff --git a/tests/neg/experimentalExperimental.scala b/tests/neg/experimentalExperimental.scala
deleted file mode 100644
index 9011a3e49225..000000000000
--- a/tests/neg/experimentalExperimental.scala
+++ /dev/null
@@ -1 +0,0 @@
-class MyExperimentalAnnot extends scala.annotation.experimental // error
diff --git a/tests/neg/experimentalInheritance2.scala b/tests/neg/experimentalInheritance2.scala
new file mode 100644
index 000000000000..84668ac5850f
--- /dev/null
+++ b/tests/neg/experimentalInheritance2.scala
@@ -0,0 +1,6 @@
+import scala.annotation.experimental
+
+@experimental class A
+
+class B // // error: extension of experimental class A1 must have @experimental annotation
+ extends A
diff --git a/tests/pos/experimentalExperimental.scala b/tests/pos/experimentalExperimental.scala
new file mode 100644
index 000000000000..4b57e5b94346
--- /dev/null
+++ b/tests/pos/experimentalExperimental.scala
@@ -0,0 +1 @@
+class MyExperimentalAnnot extends scala.annotation.experimental
diff --git a/tests/run/experimentalRun.scala b/tests/run/experimentalRun.scala
new file mode 100644
index 000000000000..2d93c15c606f
--- /dev/null
+++ b/tests/run/experimentalRun.scala
@@ -0,0 +1,6 @@
+import scala.annotation.experimental
+
+@experimental
+def f = 3
+
+@experimental @main def Test = f