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 5, 2024
1 parent 6bb6b43 commit dbdfcff
Show file tree
Hide file tree
Showing 16 changed files with 125 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 @@ -10,6 +10,7 @@ import util.{SrcPos, NoSourcePosition}
import SourceVersion.*
import reporting.Message
import NameKinds.QualifiedName
import Annotations.ExperimentalAnnotation

object Feature:

Expand Down Expand Up @@ -131,12 +132,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 +142,24 @@ 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 =
experimentalSym.getAnnotation(defn.ExperimentalAnnot).map {
case ExperimentalAnnotation(msg) if msg.nonEmpty => s": $msg"
case _ => ""
}.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
26 changes: 26 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,30 @@ object Annotations {
}
}
}

object ExperimentalAnnotation {

/** Create an instance of `@experimental(<msg>)` */
def apply(msg: String, span: Span)(using Context): Annotation =
Annotation(defn.ExperimentalAnnot, Literal(Constant(msg)), span)

/** Matches and extracts the message from an instance of `@experimental(msg)`
* Returns `Some("")` for `@experimental` with no message.
*/
def unapply(a: Annotation)(using Context): Option[String] =
if a.symbol ne defn.ExperimentalAnnot then
None
else a.argumentConstant(0) match
case Some(Constant(msg: String)) => Some(msg)
case _ => Some("")

/** Makes a copy of the `@experimental(msg)` annotation on `sym`
* None is returned if the symbol does not have an `@experimental` annotation.
*/
def copy(sym: Symbol)(using Context): Option[Annotation] =
sym.getAnnotation(defn.ExperimentalAnnot).map {
case annot @ ExperimentalAnnotation(msg) => ExperimentalAnnotation(msg, annot.tree.span)
}
}

}
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/AccessProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Decorators.*
import Types.*
import util.Spans.Span
import config.Printers.transforms
import Annotations.ExperimentalAnnotation

/** A utility class for generating access proxies. Currently used for
* inline accessors and protected accessors.
Expand Down Expand Up @@ -84,8 +85,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)
ExperimentalAnnotation.copy(accessed).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
ExperimentalAnnotation.copy(sym.companionClass).foreach(sym.addAnnotation)
if !sym.hasAnnotation(defn.ExperimentalAnnot) && ctx.settings.experimental.value && isTopLevelDefinitionInSource(sym) then
sym.addAnnotation(ExperimentalAnnotation("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 dbdfcff

Please sign in to comment.