Skip to content

Commit

Permalink
Add message parameter to @experimental annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Apr 2, 2024
1 parent 6bb6b43 commit e99f5e5
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 27 deletions.
30 changes: 19 additions & 11 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,7 @@ object Feature:

def checkExperimentalFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) =
if !isExperimentalEnabled then
report.error(
em"""Experimental $which may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.$note
""", srcPos)
report.error(experimentalUseSite(which) + note, srcPos)

private def ccException(sym: Symbol)(using Context): Boolean =
ccEnabled && defn.ccExperimental.contains(sym)
Expand All @@ -146,12 +141,25 @@ object Feature:
if sym.hasAnnotation(defn.ExperimentalAnnot) then sym
else if sym.owner.hasAnnotation(defn.ExperimentalAnnot) then sym.owner
else NoSymbol
if !ccException(experimentalSym) then
val note =
if !isExperimentalEnabled && !ccException(experimentalSym) then
val msg =
import ast.untpd.*
import Constants.Constant
experimentalSym.getAnnotation(defn.ExperimentalAnnot).map(_.tree).collect {
case Apply(_, List(Literal(Constant(msg: String)))) => s": $msg"
}.getOrElse("")
val markedExperimental =
if experimentalSym.exists
then i"$experimentalSym is marked @experimental"
else i"$sym inherits @experimental"
checkExperimentalFeature("definition", srcPos, s"\n\n$note")
then i"$experimentalSym is marked @experimental$msg"
else i"$sym inherits @experimental$msg"
report.error(markedExperimental + "\n\n" + experimentalUseSite("definition"), srcPos)

private def experimentalUseSite(which: String): String =
s"""Experimental $which may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
|""".stripMargin

/** Check that experimental compiler options are only set for snapshot or nightly compiler versions. */
def checkExperimentalSettings(using Context): Unit =
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/AccessProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package dotc
package transform

import core.*
import Annotations.Annotation
import Constants.Constant
import Contexts.*
import Symbols.*
import Flags.*
Expand Down Expand Up @@ -84,8 +86,7 @@ abstract class AccessProxies {
val sym = newSymbol(owner, name, Synthetic | Method, info, coord = accessed.span).entered
if accessed.is(Private) then sym.setFlag(Final)
else if sym.allOverriddenSymbols.exists(!_.is(Deferred)) then sym.setFlag(Override)
if accessed.hasAnnotation(defn.ExperimentalAnnot) then
sym.addAnnotation(defn.ExperimentalAnnot)
accessed.getAnnotation(defn.ExperimentalAnnot).foreach(sym.addAnnotation)
sym
}

Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -548,11 +548,10 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
def isTopLevelDefinitionInSource(sym: Symbol) =
!sym.is(Package) && !sym.name.isPackageObjectName &&
(sym.owner.is(Package) || (sym.owner.isPackageObject && !sym.isConstructor))
if !sym.hasAnnotation(defn.ExperimentalAnnot)
&& (ctx.settings.experimental.value && isTopLevelDefinitionInSource(sym))
|| (sym.is(Module) && sym.companionClass.hasAnnotation(defn.ExperimentalAnnot))
then
sym.addAnnotation(Annotation(defn.ExperimentalAnnot, sym.span))
if sym.is(Module) then
sym.companionClass.getAnnotation(defn.ExperimentalAnnot).foreach(sym.addAnnotation)
if !sym.hasAnnotation(defn.ExperimentalAnnot) && ctx.settings.experimental.value && isTopLevelDefinitionInSource(sym) then
sym.addAnnotation(Annotation(defn.ExperimentalAnnot, Literal(Constants.Constant("Added by -experimental")), sym.span))

private def scala2LibPatch(tree: TypeDef)(using Context) =
val sym = tree.symbol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ package scala.annotation
* @syntax markdown
*/
@deprecatedInheritance("Scheduled for being final in the future", "3.4.0")
class experimental extends StaticAnnotation
class experimental(message: String) extends StaticAnnotation:
def this() = this("")
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package scala.annotation

@deprecatedInheritance("Scheduled for being final in the future", "3.4.0")
class experimental extends StaticAnnotation
2 changes: 1 addition & 1 deletion library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ object language:
object captureChecking

/** Experimental support for automatic conversions of arguments, without requiring
* a langauge import `import scala.language.implicitConversions`.
* a language import `import scala.language.implicitConversions`.
*
* @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]]
*/
Expand Down
1 change: 1 addition & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ object MiMaFilters {
val ForwardsBreakingChanges: Map[String, Seq[ProblemFilter]] = Map(
// Additions that require a new minor version of the library
Build.previousDottyVersion -> Seq(
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.annotation.experimental.this"),
),

// Additions since last LTS
Expand Down
4 changes: 2 additions & 2 deletions tests/neg-macros/i18677-a.check
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
|The tree does not conform to the compiler's tree invariants.
|
|Macro was:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @scala.annotation.experimental @extendFoo class AFoo()
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @scala.annotation.experimental("Added by -experimental") @extendFoo class AFoo()
|
|The macro returned:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @scala.annotation.experimental @extendFoo class AFoo() extends Foo
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-a/Test_2.scala") @scala.annotation.experimental("Added by -experimental") @extendFoo class AFoo() extends Foo
|
|Error:
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
Expand Down
4 changes: 2 additions & 2 deletions tests/neg-macros/i18677-b.check
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
|The tree does not conform to the compiler's tree invariants.
|
|Macro was:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @scala.annotation.experimental @extendFoo class AFoo()
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @scala.annotation.experimental("Added by -experimental") @extendFoo class AFoo()
|
|The macro returned:
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @scala.annotation.experimental @extendFoo class AFoo() extends Foo
|@scala.annotation.internal.SourceFile("tests/neg-macros/i18677-b/Test_2.scala") @scala.annotation.experimental("Added by -experimental") @extendFoo class AFoo() extends Foo
|
|Error:
|assertion failed: Parents of class symbol differs from the parents in the tree for class AFoo
Expand Down
10 changes: 10 additions & 0 deletions tests/neg/experimental-message-experimental-flag.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

-- Error: tests/neg/experimental-message-experimental-flag/Test_2.scala:3:10 -------------------------------------------
3 |def g() = f() // error
| ^
| method f is marked @experimental: Added by -experimental
|
| Experimental definition may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
3 changes: 3 additions & 0 deletions tests/neg/experimental-message-experimental-flag/Lib_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//> using options -Yno-experimental -experimental

def f() = ???
3 changes: 3 additions & 0 deletions tests/neg/experimental-message-experimental-flag/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//> using options -Yno-experimental

def g() = f() // error
27 changes: 27 additions & 0 deletions tests/neg/experimental-message.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-- Error: tests/neg/experimental-message.scala:15:2 --------------------------------------------------------------------
15 | f1() // error
| ^^
| method f1 is marked @experimental
|
| Experimental definition may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
-- Error: tests/neg/experimental-message.scala:16:2 --------------------------------------------------------------------
16 | f2() // error
| ^^
| method f2 is marked @experimental
|
| Experimental definition may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
-- Error: tests/neg/experimental-message.scala:17:2 --------------------------------------------------------------------
17 | f3() // error
| ^^
| method f3 is marked @experimental: not yet stable
|
| Experimental definition may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
17 changes: 17 additions & 0 deletions tests/neg/experimental-message.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//> using options -Yno-experimental

import scala.annotation.experimental

@experimental
def f1() = ???

@experimental()
def f2() = ???

@experimental("not yet stable")
def f3() = ???

def g() =
f1() // error
f2() // error
f3() // error
5 changes: 2 additions & 3 deletions tests/neg/use-experimental-def.check
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
-- Error: tests/neg/use-experimental-def.scala:7:15 --------------------------------------------------------------------
7 |def bar: Int = foo // error
| ^^^
| method foo is marked @experimental
|
| Experimental definition may only be used under experimental mode:
| 1. in a definition marked as @experimental, or
| 2. compiling with the -experimental compiler flag, or
| 3. with a nightly or snapshot version of the compiler.
|
| method foo is marked @experimental
|

0 comments on commit e99f5e5

Please sign in to comment.