diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 42522d38be51..15c2baca1a2f 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -99,20 +99,33 @@ object Feature: 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) + 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) + + 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 + 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) /** 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") + 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 end Feature \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 73d3bdcefc2c..eaafcca3992e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -909,6 +909,7 @@ class Definitions { @tu lazy val ConstructorOnlyAnnot: ClassSymbol = requiredClass("scala.annotation.constructorOnly") @tu lazy val CompileTimeOnlyAnnot: ClassSymbol = requiredClass("scala.annotation.compileTimeOnly") @tu lazy val SwitchAnnot: ClassSymbol = requiredClass("scala.annotation.switch") + @tu lazy val ExperimentalAnnot: ClassSymbol = requiredClass("scala.annotation.experimental") @tu lazy val ThrowsAnnot: ClassSymbol = requiredClass("scala.throws") @tu lazy val TransientAnnot: ClassSymbol = requiredClass("scala.transient") @tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked") diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index eac39ca72120..42956026c2e7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3079,7 +3079,7 @@ object Parsers { if prefix == nme.experimental && selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros) then - Feature.checkExperimentalFeature("s", imp.srcPos) + Feature.checkExperimentalFeature("features", imp.srcPos) for case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors if allSourceVersionNames.contains(imported) diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala index a4db7ef44a86..14cb83fb6c6c 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala @@ -3,7 +3,7 @@ package plugins import core._ import Contexts._ -import config.{ PathResolver, Properties } +import config.{ PathResolver, Feature } import dotty.tools.io._ import Phases._ import config.Printers.plugins.{ println => debug } @@ -125,7 +125,7 @@ trait Plugins { val updatedPlan = Plugins.schedule(plan, pluginPhases) // add research plugins - if (Properties.experimental) + if (Feature.isExperimentalEnabled) plugins.collect { case p: ResearchPlugin => p }.foldRight(updatedPlan) { (plug, plan) => plug.init(options(plug), plan) } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5f4af166d943..ccdbec941166 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -255,6 +255,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase override def transform(tree: Tree)(using Context): Tree = try tree match { + // TODO move CaseDef case lower: keep most probable trees first for performance case CaseDef(pat, _, _) => val gadtCtx = pat.removeAttachment(typer.Typer.InferredGadtConstraints) match @@ -353,6 +354,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val sym = tree.symbol if (sym.isClass) VarianceChecker.check(tree) + annotateExperimental(sym) // Add SourceFile annotation to top-level classes if sym.owner.is(Package) && ctx.compilationUnit.source.exists @@ -443,5 +445,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase */ private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(using Context) = if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs + + private def annotateExperimental(sym: Symbol)(using Context): Unit = + if sym.is(Module) && sym.companionClass.hasAnnotation(defn.ExperimentalAnnot) then + sym.addAnnotation(defn.ExperimentalAnnot) + sym.companionModule.addAnnotation(defn.ExperimentalAnnot) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 3bbcdef68932..eae6241ed6e8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -259,6 +259,13 @@ object SymUtils: && self.owner.linkedClass.is(Case) && self.owner.linkedClass.isDeclaredInfix + /** 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.maybeOwner.isClass && self.owner.hasAnnotation(defn.ExperimentalAnnot)) + /** 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/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 7bad2b4a9fce..eaa15618ebda 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -21,6 +21,7 @@ import Annotations.Annotation import SymDenotations.SymDenotation import Inferencing.isFullyDefined import config.Printers.inlining +import config.Feature import ErrorReporting.errorTree import dotty.tools.dotc.util.{SimpleIdentityMap, SimpleIdentitySet, EqHashMap, SourceFile, SourcePosition, SrcPos} import dotty.tools.dotc.parsing.Parsers.Parser @@ -93,6 +94,7 @@ object Inliner { if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree) if (tree.symbol == defn.CompiletimeTesting_typeCheckErrors) return Intrinsics.typeCheckErrors(tree) + Feature.checkExperimentalDef(tree.symbol, tree) /** Set the position of all trees logically contained in the expansion of * inlined call `call` to the position of `call`. This transform is necessary diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 342e0bf3a3c3..e49c89734c16 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -24,6 +24,7 @@ import reporting._ import scala.util.matching.Regex._ import Constants.Constant import NullOpsDecorator._ +import dotty.tools.dotc.config.Feature object RefChecks { import tpd._ @@ -212,6 +213,7 @@ object RefChecks { * 1.9. If M is erased, O is erased. If O is erased, M is erased or inline. * 1.10. If O is inline (and deferred, otherwise O would be final), M must be inline * 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro. + * 1.12. If O is non-experimental, M must be non-experimental. * 2. Check that only abstract classes have deferred members * 3. Check that concrete classes do not have deferred definitions * that are not implemented in a subclass. @@ -477,6 +479,8 @@ object RefChecks { overrideError(i"needs to be declared with @targetName(${"\""}${other.targetName}${"\""}) so that external names match") else overrideError("cannot have a @targetName annotation since external names would be different") + else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.12) + overrideError("may not override non-experimental member") else checkOverrideDeprecated() } @@ -924,6 +928,7 @@ object RefChecks { // arbitrarily choose one as more important than the other. private def checkUndesiredProperties(sym: Symbol, pos: SrcPos)(using Context): Unit = checkDeprecated(sym, pos) + checkExperimental(sym, pos) val xMigrationValue = ctx.settings.Xmigration.value if xMigrationValue != NoScalaVersion then @@ -964,6 +969,29 @@ object RefChecks { val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("") 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 + Feature.checkExperimentalDef(sym, pos) + + private def checkExperimentalSignature(sym: Symbol, pos: SrcPos)(using Context): Unit = + val checker = new 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) + + 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 @migration is present (indicating that the symbol has changed semantics between versions), * emit a warning. */ @@ -1136,6 +1164,15 @@ object RefChecks { end checkImplicitNotFoundAnnotation + + /** Check that classes extending experimental classes or nested in experimental classes have the @experimental annotation. */ + private def checkExperimentalInheritance(cls: ClassSymbol)(using Context): Unit = + if !cls.hasAnnotation(defn.ExperimentalAnnot) then + cls.info.parents.find(_.typeSymbol.isExperimental) match + case Some(parent) => + report.error(em"extension of experimental ${parent.typeSymbol} must have @experimental annotation", cls.srcPos) + case _ => + end checkExperimentalInheritance } import RefChecks._ @@ -1192,6 +1229,8 @@ class RefChecks extends MiniPhase { thisPhase => override def transformValDef(tree: ValDef)(using Context): ValDef = { checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) + checkExperimentalAnnots(tree.symbol) + checkExperimentalSignature(tree.symbol, tree) val sym = tree.symbol if (sym.exists && sym.owner.isTerm) { tree.rhs match { @@ -1212,6 +1251,8 @@ class RefChecks extends MiniPhase { thisPhase => override def transformDefDef(tree: DefDef)(using Context): DefDef = { checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) + checkExperimentalAnnots(tree.symbol) + checkExperimentalSignature(tree.symbol, tree) checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot) tree } @@ -1224,6 +1265,8 @@ class RefChecks extends MiniPhase { thisPhase => checkCompanionNameClashes(cls) checkAllOverrides(cls) checkImplicitNotFoundAnnotation.template(cls.classDenot) + checkExperimentalInheritance(cls) + checkExperimentalAnnots(cls) tree } catch { @@ -1268,6 +1311,17 @@ class RefChecks extends MiniPhase { thisPhase => } tree } + + override def transformTypeTree(tree: TypeTree)(using Context): TypeTree = { + checkExperimental(tree.symbol, tree.srcPos) + tree + } + + override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = { + checkExperimental(tree.symbol, tree.srcPos) + checkExperimentalAnnots(tree.symbol) + tree + } } /* todo: rewrite and re-enable diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index db05ed611a0e..2d193a457afe 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -240,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", "-Yno-experimental") + Array("-Ycheck-reentrant", "-language:postfixOps", "-Xsemanticdb") ) val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped")) diff --git a/library/src/scala/annotation/experimental.scala b/library/src/scala/annotation/experimental.scala new file mode 100644 index 000000000000..4ccda84d623c --- /dev/null +++ b/library/src/scala/annotation/experimental.scala @@ -0,0 +1,14 @@ +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 + * ``` + * + * @syntax markdown + */ +// @experimental +class experimental extends StaticAnnotation diff --git a/library/src/scala/annotation/internal/ErasedParam.scala b/library/src/scala/annotation/internal/ErasedParam.scala index 8145712a52da..52f73c438fb3 100644 --- a/library/src/scala/annotation/internal/ErasedParam.scala +++ b/library/src/scala/annotation/internal/ErasedParam.scala @@ -1,6 +1,6 @@ -package scala.annotation.internal - -import scala.annotation.Annotation +package scala.annotation +package internal /** An annotation produced by Namer to indicate an erased parameter */ +@experimental final class ErasedParam() extends Annotation diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 5f286f88354e..360bbebecf7d 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -1,5 +1,6 @@ package scala.quoted +import scala.annotation.experimental import scala.reflect.TypeTest /** Current Quotes in scope @@ -2179,6 +2180,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this a given parameter clause `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */ def isGiven: Boolean /** Is this a erased parameter clause `(erased x1: X1, ..., xn: Xn)` */ + @experimental def isErased: Boolean end TermParamClauseMethods diff --git a/library/src/scala/util/FromDigits.scala b/library/src/scala/util/FromDigits.scala index 6064116d1443..1c5346ad6b97 100644 --- a/library/src/scala/util/FromDigits.scala +++ b/library/src/scala/util/FromDigits.scala @@ -3,6 +3,7 @@ import scala.math.{BigInt} import quoted._ import annotation.internal.sharable + /** A type class for types that admit numeric literals. */ trait FromDigits[T] { @@ -28,7 +29,7 @@ object FromDigits { trait WithRadix[T] extends FromDigits[T] { def fromDigits(digits: String): T = fromDigits(digits, 10) - /** Convert digits string with given radix to numberof type `T`. + /** Convert digits string with given radix to number of type `T`. * E.g. if radix is 16, digits `a..f` and `A..F` are also allowed. */ def fromDigits(digits: String, radix: Int): T diff --git a/tests/neg-custom-args/no-experimental/experimentalAnnot.scala b/tests/neg-custom-args/no-experimental/experimentalAnnot.scala new file mode 100644 index 000000000000..3bd87d67ed7b --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalAnnot.scala @@ -0,0 +1,7 @@ +import scala.annotation.experimental + +@experimental // error +class myExperimentalAnnot extends scala.annotation.Annotation + +@myExperimentalAnnot // error +def test: Unit = () diff --git a/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala b/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala new file mode 100644 index 000000000000..51d5b4957993 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalCaseClass.scala @@ -0,0 +1,26 @@ +import scala.annotation.experimental + +@experimental // error +case class Foo(a: Int) + +@experimental // error +case class Bar(a: Int) + +object Bar: + def f(): Unit = () + +def test: Unit = + Foo(2) // error + val x: Foo = ??? // error + + x match + case Foo(a) => // error + + + Bar(2) // error + val y: Bar = ??? // error + + y match + case Bar(a) => // error + + Bar.f() // error diff --git a/tests/neg-custom-args/no-experimental/experimentalEnum.scala b/tests/neg-custom-args/no-experimental/experimentalEnum.scala new file mode 100644 index 000000000000..3a4e3cc093f0 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalEnum.scala @@ -0,0 +1,12 @@ +import scala.annotation.experimental + +@experimental // error +enum E: + case A + case B + +def test: Unit = + E.A // error + E.B // error + val e: E = ??? // error + () diff --git a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala new file mode 100644 index 000000000000..d88406d634e7 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala @@ -0,0 +1,4 @@ +import scala.annotation.experimental + +class MyExperimentalAnnot // error + extends experimental // error diff --git a/tests/neg-custom-args/no-experimental/experimentalInline.scala b/tests/neg-custom-args/no-experimental/experimentalInline.scala new file mode 100644 index 000000000000..8827fd42e36a --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalInline.scala @@ -0,0 +1,8 @@ +import scala.annotation.experimental + +@experimental +inline def g() = () + +def test: Unit = + g() // errors + () diff --git a/tests/neg-custom-args/no-experimental/experimentalOverride.scala b/tests/neg-custom-args/no-experimental/experimentalOverride.scala new file mode 100644 index 000000000000..2a052d959f46 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalOverride.scala @@ -0,0 +1,39 @@ +import scala.annotation.experimental + +@experimental // error +class A: + def f() = 1 + +@experimental // error +class B extends A: + override def f() = 2 + +class C: + @experimental // error + def f() = 1 + +class D extends C: + override def f() = 2 + +trait A2: + @experimental // error + def f(): Int + +trait B2: + def f(): Int + +class C2 extends A2, B2: + def f(): Int = 1 + +def test: Unit = + val a: A = ??? // error + val b: B = ??? // error + val c: C = ??? + val d: D = ??? + val c2: C2 = ??? + a.f() // error + b.f() // error + c.f() // error + d.f() // ok because D.f is a stable API + c2.f() // ok because B2.f is a stable API + () diff --git a/tests/neg-custom-args/no-experimental/experimentalSam.scala b/tests/neg-custom-args/no-experimental/experimentalSam.scala new file mode 100644 index 000000000000..578fb8f93f94 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalSam.scala @@ -0,0 +1,11 @@ +import scala.annotation.experimental + +@experimental // error +trait ExpSAM { + def foo(x: Int): Int +} +def bar(f: ExpSAM): Unit = {} // error + +def test: Unit = + bar(x => x) // error + () diff --git a/tests/neg-custom-args/no-experimental/experimentalTerms.scala b/tests/neg-custom-args/no-experimental/experimentalTerms.scala new file mode 100644 index 000000000000..5fc3f2b3fad7 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalTerms.scala @@ -0,0 +1,19 @@ +import scala.annotation.experimental + +@experimental // error +val x = () + +@experimental // error +def f() = () + +@experimental // error +object X: + def fx() = 1 + +def test: Unit = + f() // error + x // error + X.fx() // error + import X.fx + fx() // error + () diff --git a/tests/neg-custom-args/no-experimental/experimentalType.scala b/tests/neg-custom-args/no-experimental/experimentalType.scala new file mode 100644 index 000000000000..e49b53192944 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalType.scala @@ -0,0 +1,31 @@ +import scala.annotation.experimental + +@experimental // error +class A + +@experimental // error +class B extends A + +@experimental +type X // error + +@experimental +type Y = Int // error + +@experimental +opaque type Z = Int // error + +type W = Z // error + +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 + () diff --git a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala new file mode 100644 index 000000000000..d5cd3ad55590 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala @@ -0,0 +1,18 @@ +import scala.annotation.experimental + +@experimental // error +class A + +object Extractor1: + def unapply(s: Any): Option[A] = ??? // error + +object Extractor2: + @experimental // error + def unapply(s: Any): Option[Int] = ??? + +def test: Unit = + (??? : Any) match + case _: A => // error // error + case Extractor1(_) => // error + case Extractor2(_) => // error + () diff --git a/tests/neg/experimentalExperimental.scala b/tests/neg/experimentalExperimental.scala new file mode 100644 index 000000000000..9011a3e49225 --- /dev/null +++ b/tests/neg/experimentalExperimental.scala @@ -0,0 +1 @@ +class MyExperimentalAnnot extends scala.annotation.experimental // error diff --git a/tests/neg/experimentalInheritance.scala b/tests/neg/experimentalInheritance.scala new file mode 100644 index 000000000000..8b6c0b11afa3 --- /dev/null +++ b/tests/neg/experimentalInheritance.scala @@ -0,0 +1,44 @@ +import scala.annotation.experimental + +@experimental +class A + +@experimental +trait T + +class B extends A // error + +@experimental +class B2 extends A + +class C extends T // error + +@experimental +class C2 extends T + +@experimental +class O: + class X + + @experimental + class Y + + object Z + +@experimental +object O: + class A + + @experimental + class B + + object C + +class OA extends O.A // error +class OB extends O.B // error + +@experimental +class OA2 extends O.A + +@experimental +class OB2 extends O.B diff --git a/tests/neg/experimentalOverloads.scala b/tests/neg/experimentalOverloads.scala new file mode 100644 index 000000000000..7adaf0b78840 --- /dev/null +++ b/tests/neg/experimentalOverloads.scala @@ -0,0 +1,11 @@ +import scala.annotation.experimental + +trait A: + def f: Int + def g: Int = 3 +trait B extends A: + @experimental + def f: Int = 4 // error + + @experimental + override def g: Int = 5 // error