Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add message parameter to @experimental annotation #19935

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the note parameter is never used anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used here

report.error(experimentalUseSite(which) + note, srcPos)
                                          ^^^^

and comes from

// Checking.scala
Feature.checkExperimentalFeature("features", imp.srcPos,
              s"\n\nNote: the scope enclosing the import is not considered experimental because it contains the\nnon-experimental $stable")

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
|
Loading