From 77fc0e21eeb486d94a61dccef734582645d32d17 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 15 Apr 2021 17:21:35 +0200 Subject: [PATCH 01/13] Add `@experimental` annotation The `@exerimental` annotation marks definitions as _experimental_ feature. These can be used in the same situattions where `languange.experimental` can be used. * A class is experimental if * It is annotated with `@experimental` * It is a nested class of an experimental class. Annotation `@experimental` is inferred. * It extends an experimental class. An error is emitted if it does not have @experimental. * A member definition is experimental if * It is annotated with `@experimental` * Its a member of an experimental class * `class experimental` is experimental --- .../src/dotty/tools/dotc/config/Feature.scala | 24 ++-- .../dotty/tools/dotc/core/Definitions.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../dotty/tools/dotc/plugins/Plugins.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 6 + .../dotty/tools/dotc/transform/SymUtils.scala | 10 ++ .../src/dotty/tools/dotc/typer/Inliner.scala | 2 + .../dotty/tools/dotc/typer/RefChecks.scala | 13 +++ .../dotty/tools/dotc/util/Experimental.scala | 45 ++++++++ .../dotty/tools/dotc/CompilationTests.scala | 2 +- .../src/scala/annotation/experimental.scala | 14 +++ library/src/scala/util/FromDigits.scala | 3 +- .../experimentalAnnotation.scala | 103 ++++++++++++++++++ .../experimentalExperimental.scala | 1 + .../no-experimental/experimentalnline.scala | 8 ++ tests/neg/experimentalExperimental.scala | 1 + tests/neg/experimentalInheritance.scala | 44 ++++++++ tests/neg/experimentalOverloads.scala | 11 ++ 18 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/util/Experimental.scala create mode 100644 library/src/scala/annotation/experimental.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalAnnotation.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalExperimental.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalnline.scala create mode 100644 tests/neg/experimentalExperimental.scala create mode 100644 tests/neg/experimentalInheritance.scala create mode 100644 tests/neg/experimentalOverloads.scala diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 42522d38be51..b165debe4277 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -99,20 +99,24 @@ 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 + report.error(i"$sym is marked @experimental 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..971d1d11d395 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -14,6 +14,8 @@ import Symbols._, SymUtils._, NameOps._ import ContextFunctionResults.annotateContextResults import config.Printers.typr import reporting._ +import util.Experimental + object PostTyper { val name: String = "posttyper" @@ -263,6 +265,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ctx super.transform(tree)(using gadtCtx) case tree: Ident if !tree.isType => + Experimental.checkExperimental(tree) if tree.symbol.is(Inline) && !Inliner.inInlineMethod then ctx.compilationUnit.needsInlining = true checkNoConstructorProxy(tree) @@ -271,6 +274,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case _ => tree } case tree @ Select(qual, name) => + Experimental.checkExperimental(tree) if tree.symbol.is(Inline) then ctx.compilationUnit.needsInlining = true if (name.isTypeName) { @@ -353,6 +357,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val sym = tree.symbol if (sym.isClass) VarianceChecker.check(tree) + Experimental.annotateExperimental(sym) // Add SourceFile annotation to top-level classes if sym.owner.is(Package) && ctx.compilationUnit.source.exists @@ -388,6 +393,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase Checking.checkRealizable(ref.tpe, ref.srcPos) super.transform(tree) case tree: TypeTree => + Experimental.checkExperimental(tree) tree.withType( tree.tpe match { case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot)) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 3bbcdef68932..b13a57db44a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -259,6 +259,16 @@ 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)) + + def isNestedInExperimental(using Context): Boolean = + self.ownersIterator.drop(1).exists(_.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..d6ef245d27fa 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -212,6 +212,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 +478,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() } @@ -1136,6 +1139,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._ @@ -1224,6 +1236,7 @@ class RefChecks extends MiniPhase { thisPhase => checkCompanionNameClashes(cls) checkAllOverrides(cls) checkImplicitNotFoundAnnotation.template(cls.classDenot) + checkExperimentalInheritance(cls) tree } catch { diff --git a/compiler/src/dotty/tools/dotc/util/Experimental.scala b/compiler/src/dotty/tools/dotc/util/Experimental.scala new file mode 100644 index 000000000000..c765b9ba1378 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/Experimental.scala @@ -0,0 +1,45 @@ +package dotty.tools.dotc +package util + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.config.Feature +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.transform.SymUtils._ + +object Experimental: + import tpd._ + + def checkExperimental(tree: Tree)(using Context): Unit = + if tree.symbol.isExperimental + && !tree.symbol.isConstructor // already reported on the class + && !ctx.owner.isExperimental // already reported on the @experimental of the owner + && !tree.symbol.is(ModuleClass) // already reported on the module + && (tree.span.exists || tree.symbol != defn.ExperimentalAnnot) // already reported on inferred annotations + then + Feature.checkExperimentalDef(tree.symbol, tree) + + def checkExperimentalTypes(tree: Tree)(using Context): Unit = + val checker = new TypeTraverser: + def traverse(tp: Type): Unit = + if tp.typeSymbol.isExperimental then + Feature.checkExperimentalDef(tp.typeSymbol, tree) + else + traverseChildren(tp) + if !tree.span.isSynthetic then // avoid double errors + checker.traverse(tree.tpe) + + def annotateExperimental(sym: Symbol)(using Context): Unit = + if sym.isClass && !sym.hasAnnotation(defn.ExperimentalAnnot) then + // Add @experimental annotation to all classes nested in an experimental definition + if !sym.owner.is(Package) && sym.isNestedInExperimental then + sym.addAnnotation(defn.ExperimentalAnnot) + + // Add @experimental annotation to enum class definitions + val compSym = sym.companionClass + if compSym.is(Enum) && compSym.hasAnnotation(defn.ExperimentalAnnot) then + sym.addAnnotation(defn.ExperimentalAnnot) + sym.companionModule.addAnnotation(defn.ExperimentalAnnot) \ No newline at end of file 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/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/experimentalAnnotation.scala b/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala new file mode 100644 index 000000000000..cbc22ef60730 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala @@ -0,0 +1,103 @@ +import scala.annotation.experimental + +@experimental // error +val x = () + +@experimental // error +def f() = () + +@experimental // error +class A: + def f() = 1 + +@experimental // error +class B extends A: + override def f() = 2 + +@experimental // error +type X + +@experimental // error +type Y = Int + +@experimental // error +opaque type Z = Int + +@experimental // error +object X: + def fx() = 1 + +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 + +object Extractor1: + def unapply(s: Any): Option[A] = ??? // error + +object Extractor2: + @experimental // error + def unapply(s: Any): Option[Int] = ??? + + +@experimental // error +trait ExpSAM { + def foo(x: Int): Int +} +def bar(f: ExpSAM): Unit = {} // error + +@experimental // error +enum E: + case A + case B + +def test( + p1: A, // error + p2: List[A], // error + p3: X, // error + p4: Y, // error + p5: Z, // error +): Unit = + f() // error + x // error + new A // error + new B // error + X.fx() // error + import X.fx // error + fx() // error + val i1 = identity[X] // error // error + val i2 = identity[A] // error // error + 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 + () + + (??? : Any) match + case _: A => // error // error + case Extractor1(_) => // error + case Extractor2(_) => // error + + bar(x => x) // error + + 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..9011a3e49225 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala @@ -0,0 +1 @@ +class MyExperimentalAnnot extends scala.annotation.experimental // error diff --git a/tests/neg-custom-args/no-experimental/experimentalnline.scala b/tests/neg-custom-args/no-experimental/experimentalnline.scala new file mode 100644 index 000000000000..146f93061d28 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalnline.scala @@ -0,0 +1,8 @@ +import scala.annotation.experimental + +@experimental // error +inline def g() = () + +def test: Unit = + g() // errors + () 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 From 4c67bb1be7d04550aafaea393be99537a976f126 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 28 Apr 2021 18:11:31 +0200 Subject: [PATCH 02/13] Do not add @experimental annotations to nested definitions --- .../src/dotty/tools/dotc/transform/SymUtils.scala | 3 --- .../src/dotty/tools/dotc/util/Experimental.scala | 13 ++++--------- .../no-experimental/experimentalAnnotation.scala | 4 ++-- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index b13a57db44a3..eae6241ed6e8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -266,9 +266,6 @@ object SymUtils: || self.hasAnnotation(defn.ExperimentalAnnot) || (self.maybeOwner.isClass && self.owner.hasAnnotation(defn.ExperimentalAnnot)) - def isNestedInExperimental(using Context): Boolean = - self.ownersIterator.drop(1).exists(_.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/util/Experimental.scala b/compiler/src/dotty/tools/dotc/util/Experimental.scala index c765b9ba1378..dc79d279e528 100644 --- a/compiler/src/dotty/tools/dotc/util/Experimental.scala +++ b/compiler/src/dotty/tools/dotc/util/Experimental.scala @@ -33,13 +33,8 @@ object Experimental: checker.traverse(tree.tpe) def annotateExperimental(sym: Symbol)(using Context): Unit = - if sym.isClass && !sym.hasAnnotation(defn.ExperimentalAnnot) then - // Add @experimental annotation to all classes nested in an experimental definition - if !sym.owner.is(Package) && sym.isNestedInExperimental then - sym.addAnnotation(defn.ExperimentalAnnot) - + if sym.is(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then // Add @experimental annotation to enum class definitions - val compSym = sym.companionClass - if compSym.is(Enum) && compSym.hasAnnotation(defn.ExperimentalAnnot) then - sym.addAnnotation(defn.ExperimentalAnnot) - sym.companionModule.addAnnotation(defn.ExperimentalAnnot) \ No newline at end of file + val compMod = sym.companionModule.moduleClass + compMod.addAnnotation(defn.ExperimentalAnnot) + compMod.companionModule.addAnnotation(defn.ExperimentalAnnot) diff --git a/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala b/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala index cbc22ef60730..06d05bb74670 100644 --- a/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala +++ b/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala @@ -59,9 +59,9 @@ trait ExpSAM { def bar(f: ExpSAM): Unit = {} // error @experimental // error -enum E: +enum E: // error case A - case B + case B // error def test( p1: A, // error From 90e17292c6271176b066e59b2fa910236243d74f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 09:21:36 +0200 Subject: [PATCH 03/13] Add comment --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 971d1d11d395..fa96dc86d1b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -257,6 +257,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 From a6e6c6b56e4c80868a98b59eb0ccb5d78c6b937a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 10:01:02 +0200 Subject: [PATCH 04/13] Split tests --- .../no-experimental/experimentalEnum.scala | 12 ++++++ .../experimentalOverride.scala | 39 +++++++++++++++++++ .../no-experimental/experimentalSam.scala | 11 ++++++ .../no-experimental/experimentalTerms.scala | 19 +++++++++ .../no-experimental/experimentalType.scala | 29 ++++++++++++++ .../no-experimental/experimentalUnapply.scala | 18 +++++++++ 6 files changed, 128 insertions(+) create mode 100644 tests/neg-custom-args/no-experimental/experimentalEnum.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalOverride.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalSam.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalTerms.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalType.scala create mode 100644 tests/neg-custom-args/no-experimental/experimentalUnapply.scala 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..88ea587e2e21 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalEnum.scala @@ -0,0 +1,12 @@ +import scala.annotation.experimental + +@experimental // error +enum E: // error + case A + case B // error + +def test: Unit = + E.A // error + E.B // error + val e: E = ??? // error + () 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..253c411f42ac --- /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 // error + 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..90c362432f61 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/experimentalType.scala @@ -0,0 +1,29 @@ +import scala.annotation.experimental + +@experimental // error +class A + +@experimental // error +class B extends A + +@experimental // error +type X + +@experimental // error +type Y = Int + +@experimental // 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 + () 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 + () From 540e22f17934f9f5ab94e82ed838c6f7807e5c75 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 10:25:31 +0200 Subject: [PATCH 05/13] Fixes after rebase --- .../tools/dotc/transform/PostTyper.scala | 3 - .../dotty/tools/dotc/typer/RefChecks.scala | 26 +++++ .../dotty/tools/dotc/util/Experimental.scala | 19 ---- .../experimentalAnnotation.scala | 103 ------------------ .../no-experimental/experimentalEnum.scala | 2 +- .../experimentalExperimental.scala | 3 +- .../experimentalOverride.scala | 8 +- .../no-experimental/experimentalSam.scala | 2 +- .../no-experimental/experimentalTerms.scala | 8 +- .../no-experimental/experimentalType.scala | 12 +- .../no-experimental/experimentalUnapply.scala | 6 +- .../no-experimental/experimentalnline.scala | 2 +- 12 files changed, 48 insertions(+), 146 deletions(-) delete mode 100644 tests/neg-custom-args/no-experimental/experimentalAnnotation.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index fa96dc86d1b4..7f4ee5cbeaae 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -266,7 +266,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ctx super.transform(tree)(using gadtCtx) case tree: Ident if !tree.isType => - Experimental.checkExperimental(tree) if tree.symbol.is(Inline) && !Inliner.inInlineMethod then ctx.compilationUnit.needsInlining = true checkNoConstructorProxy(tree) @@ -275,7 +274,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case _ => tree } case tree @ Select(qual, name) => - Experimental.checkExperimental(tree) if tree.symbol.is(Inline) then ctx.compilationUnit.needsInlining = true if (name.isTypeName) { @@ -394,7 +392,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase Checking.checkRealizable(ref.tpe, ref.srcPos) super.transform(tree) case tree: TypeTree => - Experimental.checkExperimental(tree) tree.withType( tree.tpe match { case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot)) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index d6ef245d27fa..0ef0ae238094 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._ @@ -927,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 @@ -967,6 +969,25 @@ 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 checkExperimentalTypes(tpe: Type, 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 !pos.span.isSynthetic then // avoid double errors + checker.traverse(tpe) + /** If @migration is present (indicating that the symbol has changed semantics between versions), * emit a warning. */ @@ -1281,6 +1302,11 @@ class RefChecks extends MiniPhase { thisPhase => } tree } + + override def transformTypeTree(tree: TypeTree)(using Context): TypeTree = { + checkUndesiredProperties(tree.symbol, tree.srcPos) + tree + } } /* todo: rewrite and re-enable diff --git a/compiler/src/dotty/tools/dotc/util/Experimental.scala b/compiler/src/dotty/tools/dotc/util/Experimental.scala index dc79d279e528..221ff5cbd235 100644 --- a/compiler/src/dotty/tools/dotc/util/Experimental.scala +++ b/compiler/src/dotty/tools/dotc/util/Experimental.scala @@ -13,25 +13,6 @@ import dotty.tools.dotc.transform.SymUtils._ object Experimental: import tpd._ - def checkExperimental(tree: Tree)(using Context): Unit = - if tree.symbol.isExperimental - && !tree.symbol.isConstructor // already reported on the class - && !ctx.owner.isExperimental // already reported on the @experimental of the owner - && !tree.symbol.is(ModuleClass) // already reported on the module - && (tree.span.exists || tree.symbol != defn.ExperimentalAnnot) // already reported on inferred annotations - then - Feature.checkExperimentalDef(tree.symbol, tree) - - def checkExperimentalTypes(tree: Tree)(using Context): Unit = - val checker = new TypeTraverser: - def traverse(tp: Type): Unit = - if tp.typeSymbol.isExperimental then - Feature.checkExperimentalDef(tp.typeSymbol, tree) - else - traverseChildren(tp) - if !tree.span.isSynthetic then // avoid double errors - checker.traverse(tree.tpe) - def annotateExperimental(sym: Symbol)(using Context): Unit = if sym.is(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then // Add @experimental annotation to enum class definitions diff --git a/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala b/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala deleted file mode 100644 index 06d05bb74670..000000000000 --- a/tests/neg-custom-args/no-experimental/experimentalAnnotation.scala +++ /dev/null @@ -1,103 +0,0 @@ -import scala.annotation.experimental - -@experimental // error -val x = () - -@experimental // error -def f() = () - -@experimental // error -class A: - def f() = 1 - -@experimental // error -class B extends A: - override def f() = 2 - -@experimental // error -type X - -@experimental // error -type Y = Int - -@experimental // error -opaque type Z = Int - -@experimental // error -object X: - def fx() = 1 - -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 - -object Extractor1: - def unapply(s: Any): Option[A] = ??? // error - -object Extractor2: - @experimental // error - def unapply(s: Any): Option[Int] = ??? - - -@experimental // error -trait ExpSAM { - def foo(x: Int): Int -} -def bar(f: ExpSAM): Unit = {} // error - -@experimental // error -enum E: // error - case A - case B // error - -def test( - p1: A, // error - p2: List[A], // error - p3: X, // error - p4: Y, // error - p5: Z, // error -): Unit = - f() // error - x // error - new A // error - new B // error - X.fx() // error - import X.fx // error - fx() // error - val i1 = identity[X] // error // error - val i2 = identity[A] // error // error - 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 - () - - (??? : Any) match - case _: A => // error // error - case Extractor1(_) => // error - case Extractor2(_) => // error - - bar(x => x) // error - - E.A // error - E.B // error - val e: E = ??? // error diff --git a/tests/neg-custom-args/no-experimental/experimentalEnum.scala b/tests/neg-custom-args/no-experimental/experimentalEnum.scala index 88ea587e2e21..5720b5d36048 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 // FIXME ERROR enum E: // error case A case B // error diff --git a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala index 9011a3e49225..8d841c3bfbb2 100644 --- a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala +++ b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala @@ -1 +1,2 @@ -class MyExperimentalAnnot extends scala.annotation.experimental // error +class MyExperimentalAnnot // error +extends scala.annotation.experimental // error diff --git a/tests/neg-custom-args/no-experimental/experimentalOverride.scala b/tests/neg-custom-args/no-experimental/experimentalOverride.scala index 2a052d959f46..d826508f4772 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 // FIXME ERROR class A: def f() = 1 -@experimental // error +@experimental // FIXME ERROR class B extends A: override def f() = 2 class C: - @experimental // error + @experimental // FIXME ERROR def f() = 1 class D extends C: override def f() = 2 trait A2: - @experimental // error + @experimental // FIXME ERROR def f(): Int trait B2: diff --git a/tests/neg-custom-args/no-experimental/experimentalSam.scala b/tests/neg-custom-args/no-experimental/experimentalSam.scala index 578fb8f93f94..cfb29c06832f 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 // FIXME ERROR trait ExpSAM { def foo(x: Int): Int } diff --git a/tests/neg-custom-args/no-experimental/experimentalTerms.scala b/tests/neg-custom-args/no-experimental/experimentalTerms.scala index 253c411f42ac..c0b764693ff3 100644 --- a/tests/neg-custom-args/no-experimental/experimentalTerms.scala +++ b/tests/neg-custom-args/no-experimental/experimentalTerms.scala @@ -1,12 +1,12 @@ import scala.annotation.experimental -@experimental // error +@experimental // FIXME ERROR val x = () -@experimental // error +@experimental // FIXME ERROR def f() = () -@experimental // error +@experimental // FIXME ERROR object X: def fx() = 1 @@ -14,6 +14,6 @@ def test: Unit = f() // error x // error X.fx() // error - import 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 index 90c362432f61..3b819da5aa09 100644 --- a/tests/neg-custom-args/no-experimental/experimentalType.scala +++ b/tests/neg-custom-args/no-experimental/experimentalType.scala @@ -1,23 +1,23 @@ import scala.annotation.experimental -@experimental // error +@experimental // FIXME ERROR class A -@experimental // error +@experimental // FIXME ERROR class B extends A -@experimental // error +@experimental // FIXME ERROR type X -@experimental // error +@experimental // FIXME ERROR type Y = Int -@experimental // error +@experimental // FIXME ERROR opaque type Z = Int def test( p1: A, // error - p2: List[A], // error + p2: List[A], // FIXME ERROR p3: X, // error p4: Y, // error p5: Z, // error diff --git a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala index d5cd3ad55590..0c1cd7fb7df0 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 // FIXME ERROR class A object Extractor1: - def unapply(s: Any): Option[A] = ??? // error + def unapply(s: Any): Option[A] = ??? // FIXME ERROR object Extractor2: - @experimental // error + @experimental // FIXME ERROR def unapply(s: Any): Option[Int] = ??? def test: Unit = diff --git a/tests/neg-custom-args/no-experimental/experimentalnline.scala b/tests/neg-custom-args/no-experimental/experimentalnline.scala index 146f93061d28..05d16e6ac645 100644 --- a/tests/neg-custom-args/no-experimental/experimentalnline.scala +++ b/tests/neg-custom-args/no-experimental/experimentalnline.scala @@ -1,6 +1,6 @@ import scala.annotation.experimental -@experimental // error +@experimental // FIXME ERROR inline def g() = () def test: Unit = From d8dd54b64072f70b1142a2e119591469bb2f1bec Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 11:30:51 +0200 Subject: [PATCH 06/13] Fix experimental annotation detection --- .../dotty/tools/dotc/typer/RefChecks.scala | 21 +++++++++++-------- .../no-experimental/experimentalAnnot.scala | 7 +++++++ .../no-experimental/experimentalEnum.scala | 7 ++++--- .../experimentalExperimental.scala | 4 +++- ...alnline.scala => experimentalInline.scala} | 2 +- .../experimentalOverride.scala | 8 +++---- .../no-experimental/experimentalSam.scala | 2 +- .../no-experimental/experimentalTerms.scala | 6 +++--- .../no-experimental/experimentalType.scala | 16 +++++++------- .../no-experimental/experimentalUnapply.scala | 4 ++-- 10 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 tests/neg-custom-args/no-experimental/experimentalAnnot.scala rename tests/neg-custom-args/no-experimental/{experimentalnline.scala => experimentalInline.scala} (76%) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 0ef0ae238094..125ab54af7c9 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -978,15 +978,9 @@ object RefChecks { then Feature.checkExperimentalDef(sym, pos) - private def checkExperimentalTypes(tpe: Type, 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 !pos.span.isSynthetic then // avoid double errors - checker.traverse(tpe) + private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit = + 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. @@ -1225,6 +1219,7 @@ class RefChecks extends MiniPhase { thisPhase => override def transformValDef(tree: ValDef)(using Context): ValDef = { checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) + checkExperimentalAnnots(tree.symbol) val sym = tree.symbol if (sym.exists && sym.owner.isTerm) { tree.rhs match { @@ -1245,6 +1240,7 @@ class RefChecks extends MiniPhase { thisPhase => override def transformDefDef(tree: DefDef)(using Context): DefDef = { checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) + checkExperimentalAnnots(tree.symbol) checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot) tree } @@ -1258,6 +1254,7 @@ class RefChecks extends MiniPhase { thisPhase => checkAllOverrides(cls) checkImplicitNotFoundAnnotation.template(cls.classDenot) checkExperimentalInheritance(cls) + checkExperimentalAnnots(cls) tree } catch { @@ -1307,6 +1304,12 @@ class RefChecks extends MiniPhase { thisPhase => checkUndesiredProperties(tree.symbol, tree.srcPos) tree } + + override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = { + checkUndesiredProperties(tree.symbol, tree.srcPos) + checkExperimentalAnnots(tree.symbol) + tree + } } /* todo: rewrite and re-enable 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/experimentalEnum.scala b/tests/neg-custom-args/no-experimental/experimentalEnum.scala index 5720b5d36048..769b76a4dbc7 100644 --- a/tests/neg-custom-args/no-experimental/experimentalEnum.scala +++ b/tests/neg-custom-args/no-experimental/experimentalEnum.scala @@ -1,9 +1,10 @@ +// nopos-error // nopos-error // nopos-error // FIXME redundant due to enum import scala.annotation.experimental -@experimental // FIXME ERROR -enum E: // error +@experimental // error +enum E: case A - case B // error + case B def test: Unit = E.A // error diff --git a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala index 8d841c3bfbb2..d88406d634e7 100644 --- a/tests/neg-custom-args/no-experimental/experimentalExperimental.scala +++ b/tests/neg-custom-args/no-experimental/experimentalExperimental.scala @@ -1,2 +1,4 @@ +import scala.annotation.experimental + class MyExperimentalAnnot // error -extends scala.annotation.experimental // error + extends experimental // error diff --git a/tests/neg-custom-args/no-experimental/experimentalnline.scala b/tests/neg-custom-args/no-experimental/experimentalInline.scala similarity index 76% rename from tests/neg-custom-args/no-experimental/experimentalnline.scala rename to tests/neg-custom-args/no-experimental/experimentalInline.scala index 05d16e6ac645..8827fd42e36a 100644 --- a/tests/neg-custom-args/no-experimental/experimentalnline.scala +++ b/tests/neg-custom-args/no-experimental/experimentalInline.scala @@ -1,6 +1,6 @@ import scala.annotation.experimental -@experimental // FIXME ERROR +@experimental inline def g() = () def test: Unit = diff --git a/tests/neg-custom-args/no-experimental/experimentalOverride.scala b/tests/neg-custom-args/no-experimental/experimentalOverride.scala index d826508f4772..2a052d959f46 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 // FIXME ERROR +@experimental // error class A: def f() = 1 -@experimental // FIXME ERROR +@experimental // error class B extends A: override def f() = 2 class C: - @experimental // FIXME ERROR + @experimental // error def f() = 1 class D extends C: override def f() = 2 trait A2: - @experimental // FIXME ERROR + @experimental // error def f(): Int trait B2: diff --git a/tests/neg-custom-args/no-experimental/experimentalSam.scala b/tests/neg-custom-args/no-experimental/experimentalSam.scala index cfb29c06832f..578fb8f93f94 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 // FIXME ERROR +@experimental // error trait ExpSAM { def foo(x: Int): Int } diff --git a/tests/neg-custom-args/no-experimental/experimentalTerms.scala b/tests/neg-custom-args/no-experimental/experimentalTerms.scala index c0b764693ff3..5fc3f2b3fad7 100644 --- a/tests/neg-custom-args/no-experimental/experimentalTerms.scala +++ b/tests/neg-custom-args/no-experimental/experimentalTerms.scala @@ -1,12 +1,12 @@ import scala.annotation.experimental -@experimental // FIXME ERROR +@experimental // error val x = () -@experimental // FIXME ERROR +@experimental // error def f() = () -@experimental // FIXME ERROR +@experimental // error object X: def fx() = 1 diff --git a/tests/neg-custom-args/no-experimental/experimentalType.scala b/tests/neg-custom-args/no-experimental/experimentalType.scala index 3b819da5aa09..a1827150d635 100644 --- a/tests/neg-custom-args/no-experimental/experimentalType.scala +++ b/tests/neg-custom-args/no-experimental/experimentalType.scala @@ -1,19 +1,19 @@ import scala.annotation.experimental -@experimental // FIXME ERROR +@experimental // error class A -@experimental // FIXME ERROR +@experimental // error class B extends A -@experimental // FIXME ERROR -type X +@experimental +type X // error -@experimental // FIXME ERROR -type Y = Int +@experimental +type Y = Int // error -@experimental // FIXME ERROR -opaque type Z = Int +@experimental +opaque type Z = Int // error def test( p1: A, // error diff --git a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala index 0c1cd7fb7df0..45b077f38deb 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 // FIXME ERROR +@experimental // error class A object Extractor1: def unapply(s: Any): Option[A] = ??? // FIXME ERROR object Extractor2: - @experimental // FIXME ERROR + @experimental // error def unapply(s: Any): Option[Int] = ??? def test: Unit = From bac0e9891d6a8bafe7f9912a75cc6b728f222829 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 11:34:41 +0200 Subject: [PATCH 07/13] Refacor code --- .../tools/dotc/transform/PostTyper.scala | 10 +++++++-- .../dotty/tools/dotc/util/Experimental.scala | 21 ------------------- 2 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/util/Experimental.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 7f4ee5cbeaae..1a95211fb50f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -14,7 +14,6 @@ import Symbols._, SymUtils._, NameOps._ import ContextFunctionResults.annotateContextResults import config.Printers.typr import reporting._ -import util.Experimental object PostTyper { @@ -356,7 +355,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val sym = tree.symbol if (sym.isClass) VarianceChecker.check(tree) - Experimental.annotateExperimental(sym) + annotateExperimental(sym) // Add SourceFile annotation to top-level classes if sym.owner.is(Package) && ctx.compilationUnit.source.exists @@ -447,5 +446,12 @@ 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(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then + // Add @experimental annotation to enum class definitions + val compMod = sym.companionModule.moduleClass + compMod.addAnnotation(defn.ExperimentalAnnot) + compMod.companionModule.addAnnotation(defn.ExperimentalAnnot) } } diff --git a/compiler/src/dotty/tools/dotc/util/Experimental.scala b/compiler/src/dotty/tools/dotc/util/Experimental.scala deleted file mode 100644 index 221ff5cbd235..000000000000 --- a/compiler/src/dotty/tools/dotc/util/Experimental.scala +++ /dev/null @@ -1,21 +0,0 @@ -package dotty.tools.dotc -package util - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.config.Feature -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.transform.SymUtils._ - -object Experimental: - import tpd._ - - def annotateExperimental(sym: Symbol)(using Context): Unit = - if sym.is(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then - // Add @experimental annotation to enum class definitions - val compMod = sym.companionModule.moduleClass - compMod.addAnnotation(defn.ExperimentalAnnot) - compMod.companionModule.addAnnotation(defn.ExperimentalAnnot) From 112bea7a12958d9e8ea500f3cd8dada9e12f11e5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 3 May 2021 14:26:42 +0200 Subject: [PATCH 08/13] Do not check deprecation on TypeDef --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 125ab54af7c9..0e5501fde667 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1301,12 +1301,12 @@ class RefChecks extends MiniPhase { thisPhase => } override def transformTypeTree(tree: TypeTree)(using Context): TypeTree = { - checkUndesiredProperties(tree.symbol, tree.srcPos) + checkExperimental(tree.symbol, tree.srcPos) tree } override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = { - checkUndesiredProperties(tree.symbol, tree.srcPos) + checkExperimental(tree.symbol, tree.srcPos) checkExperimentalAnnots(tree.symbol) tree } From c8eb6cd3151f97ff5ecdfcada9b3f68bc43aef39 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 5 May 2021 10:37:32 +0200 Subject: [PATCH 09/13] Fix experimental in signature check --- .../src/dotty/tools/dotc/transform/PostTyper.scala | 1 - compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 12 ++++++++++++ .../no-experimental/experimentalType.scala | 4 +++- .../no-experimental/experimentalUnapply.scala | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 1a95211fb50f..d9c22a71db1c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -15,7 +15,6 @@ import ContextFunctionResults.annotateContextResults import config.Printers.typr import reporting._ - object PostTyper { val name: String = "posttyper" } diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 0e5501fde667..4597e5b0e96d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -978,6 +978,16 @@ object RefChecks { then Feature.checkExperimentalDef(sym, pos) + private def checkExperimentalTypes(tpe: Type, 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 !pos.span.isSynthetic then // avoid double errors + checker.traverse(tpe) + private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit = for annot <- sym.annotations if annot.symbol.isExperimental do Feature.checkExperimentalDef(annot.symbol, annot.tree) @@ -1220,6 +1230,7 @@ class RefChecks extends MiniPhase { thisPhase => checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) + checkExperimentalTypes(tree.symbol.info, tree) val sym = tree.symbol if (sym.exists && sym.owner.isTerm) { tree.rhs match { @@ -1241,6 +1252,7 @@ class RefChecks extends MiniPhase { thisPhase => checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) + checkExperimentalTypes(tree.symbol.info, tree) checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot) tree } diff --git a/tests/neg-custom-args/no-experimental/experimentalType.scala b/tests/neg-custom-args/no-experimental/experimentalType.scala index a1827150d635..e49b53192944 100644 --- a/tests/neg-custom-args/no-experimental/experimentalType.scala +++ b/tests/neg-custom-args/no-experimental/experimentalType.scala @@ -15,9 +15,11 @@ type Y = Int // error @experimental opaque type Z = Int // error +type W = Z // error + def test( p1: A, // error - p2: List[A], // FIXME ERROR + p2: List[A], // error p3: X, // error p4: Y, // error p5: Z, // error diff --git a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala index 45b077f38deb..d5cd3ad55590 100644 --- a/tests/neg-custom-args/no-experimental/experimentalUnapply.scala +++ b/tests/neg-custom-args/no-experimental/experimentalUnapply.scala @@ -4,7 +4,7 @@ import scala.annotation.experimental class A object Extractor1: - def unapply(s: Any): Option[A] = ??? // FIXME ERROR + def unapply(s: Any): Option[A] = ??? // error object Extractor2: @experimental // error From c9500065f755e0c31f7eb229d97a219f7671d5e4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 5 May 2021 10:45:55 +0200 Subject: [PATCH 10/13] Avoid reporting extra errors on enums --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- tests/neg-custom-args/no-experimental/experimentalEnum.scala | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 4597e5b0e96d..b239afd1e667 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -989,7 +989,7 @@ object RefChecks { checker.traverse(tpe) private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit = - for annot <- sym.annotations if annot.symbol.isExperimental do + 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), diff --git a/tests/neg-custom-args/no-experimental/experimentalEnum.scala b/tests/neg-custom-args/no-experimental/experimentalEnum.scala index 769b76a4dbc7..3a4e3cc093f0 100644 --- a/tests/neg-custom-args/no-experimental/experimentalEnum.scala +++ b/tests/neg-custom-args/no-experimental/experimentalEnum.scala @@ -1,4 +1,3 @@ -// nopos-error // nopos-error // nopos-error // FIXME redundant due to enum import scala.annotation.experimental @experimental // error From 59a2f3218dd5cc44b0bac36d2af4ba564ea50d60 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 09:30:17 +0200 Subject: [PATCH 11/13] Improve error messages for @experimental on case classes --- .../src/dotty/tools/dotc/config/Feature.scala | 11 +++++++- .../tools/dotc/transform/PostTyper.scala | 4 +++ .../dotty/tools/dotc/typer/RefChecks.scala | 10 +++---- .../experimentalCaseClass.scala | 26 +++++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 tests/neg-custom-args/no-experimental/experimentalCaseClass.scala diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index b165debe4277..15c2baca1a2f 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -105,7 +105,16 @@ object Feature: def checkExperimentalDef(sym: Symbol, srcPos: SrcPos)(using Context) = if !isExperimentalEnabled then - report.error(i"$sym is marked @experimental and therefore may only be used with a nightly or snapshot version of the compiler", srcPos) + 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 = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index d9c22a71db1c..ac28b6797c1f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -447,6 +447,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase 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) + if sym.is(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then // Add @experimental annotation to enum class definitions val compMod = sym.companionModule.moduleClass diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index b239afd1e667..e49c89734c16 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -978,15 +978,15 @@ object RefChecks { then Feature.checkExperimentalDef(sym, pos) - private def checkExperimentalTypes(tpe: Type, pos: SrcPos)(using Context): Unit = + 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 !pos.span.isSynthetic then // avoid double errors - checker.traverse(tpe) + 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 @@ -1230,7 +1230,7 @@ class RefChecks extends MiniPhase { thisPhase => checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) - checkExperimentalTypes(tree.symbol.info, tree) + checkExperimentalSignature(tree.symbol, tree) val sym = tree.symbol if (sym.exists && sym.owner.isTerm) { tree.rhs match { @@ -1252,7 +1252,7 @@ class RefChecks extends MiniPhase { thisPhase => checkNoPrivateOverrides(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) - checkExperimentalTypes(tree.symbol.info, tree) + checkExperimentalSignature(tree.symbol, tree) checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot) tree } 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 From b5a2752d6bf7c9af3688dfd4f2993f01c8f11bdc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 09:52:23 +0200 Subject: [PATCH 12/13] Remove redundant code --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ac28b6797c1f..ccdbec941166 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -451,10 +451,5 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase sym.addAnnotation(defn.ExperimentalAnnot) sym.companionModule.addAnnotation(defn.ExperimentalAnnot) - if sym.is(Enum) && sym.hasAnnotation(defn.ExperimentalAnnot) then - // Add @experimental annotation to enum class definitions - val compMod = sym.companionModule.moduleClass - compMod.addAnnotation(defn.ExperimentalAnnot) - compMod.companionModule.addAnnotation(defn.ExperimentalAnnot) } } From 8c0fe71778bf84f99ae99235e352ad5ce05d230e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 10 May 2021 10:41:28 +0200 Subject: [PATCH 13/13] Mark ErasedParam and TermParamClause.isErased as @experimental --- library/src/scala/annotation/internal/ErasedParam.scala | 6 +++--- library/src/scala/quoted/Quotes.scala | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) 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