From b05c8a70533a3203dee3d21434e31f3c70ece83e Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 30 Dec 2024 13:49:29 +0100 Subject: [PATCH 1/6] Add `@scala.annotation.preview` annotation and `-preview` flag. --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- .../src/dotty/tools/dotc/config/Feature.scala | 41 ++++++++++++++++++- .../tools/dotc/config/ScalaSettings.scala | 3 +- .../dotty/tools/dotc/core/Annotations.scala | 13 +++++- .../dotty/tools/dotc/core/Definitions.scala | 6 +++ .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../src/dotty/tools/dotc/core/SymUtils.scala | 29 ++++++++----- .../src/dotty/tools/dotc/typer/Checking.scala | 2 + .../tools/dotc/typer/CrossVersionChecks.scala | 12 +++++- .../dotty/tools/dotc/typer/RefChecks.scala | 5 ++- .../other-new-features/preview-defs.md | 38 +++++++++++++++++ library/src/scala/annotation/preview.scala | 9 ++++ .../runtime/stdLibPatches/language.scala | 15 +++++++ tests/neg/preview-message.check | 27 ++++++++++++ tests/neg/preview-message.scala | 17 ++++++++ tests/neg/preview-non-viral/defs_1.scala | 6 +++ tests/neg/preview-non-viral/usage_2.scala | 5 +++ tests/neg/previewOverloads.scala | 13 ++++++ tests/neg/previewOverride.scala | 41 +++++++++++++++++++ tests/pos/preview-flag.scala | 18 ++++++++ 20 files changed, 286 insertions(+), 17 deletions(-) create mode 100644 docs/_docs/reference/other-new-features/preview-defs.md create mode 100644 library/src/scala/annotation/preview.scala create mode 100644 tests/neg/preview-message.check create mode 100644 tests/neg/preview-message.scala create mode 100644 tests/neg/preview-non-viral/defs_1.scala create mode 100644 tests/neg/preview-non-viral/usage_2.scala create mode 100644 tests/neg/previewOverloads.scala create mode 100644 tests/neg/previewOverride.scala create mode 100644 tests/pos/preview-flag.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index e0fe17755257..b68ec47d176a 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -297,7 +297,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case TypeDefs(_) => true case _ => false - private val languageSubCategories = Set(nme.experimental, nme.deprecated) + private val languageSubCategories = Set(nme.experimental, nme.preview, nme.deprecated) /** If `path` looks like a language import, `Some(name)` where name * is `experimental` if that sub-module is imported, and the empty diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 8b9a64924ace..b0785b63406c 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -11,12 +11,16 @@ import SourceVersion.* import reporting.Message import NameKinds.QualifiedName import Annotations.ExperimentalAnnotation +import Annotations.PreviewAnnotation import Settings.Setting.ChoiceWithHelp object Feature: def experimental(str: PreName): TermName = QualifiedName(nme.experimental, str.toTermName) + + def preview(str: PreName): TermName = + QualifiedName(nme.preview, str.toTermName) private def deprecated(str: PreName): TermName = QualifiedName(nme.deprecated, str.toTermName) @@ -44,6 +48,10 @@ object Feature: defn.languageExperimentalFeatures .map(sym => experimental(sym.name)) .filterNot(_ == captureChecking) // TODO is this correct? + + def previewAutoEnableFeatures(using Context): List[TermName] = + defn.languagePreviewFeatures + .map(sym => preview(sym.name)) val values = List( (nme.help, "Display all available features"), @@ -224,7 +232,7 @@ object Feature: def isExperimentalEnabledByImport(using Context): Boolean = experimentalAutoEnableFeatures.exists(enabledByImport) - + /** Handle language import `import language..` if it is one * of the global imports `pureFunctions` or `captureChecking`. In this case * make the compilation unit's and current run's fields accordingly. @@ -242,4 +250,35 @@ object Feature: true else false + + def isPreviewEnabled(using Context): Boolean = + ctx.settings.preview.value || + previewAutoEnableFeatures.exists(enabled) + + def checkPreviewFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) = + if !isPreviewEnabled then + report.error(previewUseSite(which) + note, srcPos) + + def checkPreviewDef(sym: Symbol, srcPos: SrcPos)(using Context) = if !isPreviewEnabled then + val previewSym = + if sym.hasAnnotation(defn.PreviewAnnot) then sym + else if sym.owner.hasAnnotation(defn.PreviewAnnot) then sym.owner + else NoSymbol + val msg = + previewSym.getAnnotation(defn.PreviewAnnot).collectFirst { + case PreviewAnnotation(msg) if msg.nonEmpty => s": $msg" + }.getOrElse("") + val markedPreview = + if previewSym.exists + then i"$previewSym is marked @preview$msg" + else i"$sym inherits @preview$msg" + report.error(markedPreview + "\n\n" + previewUseSite("definition"), srcPos) + + private def previewUseSite(which: String): String = + s"""Preview $which may only be used under preview mode: + | 1. in a definition marked as @preview, or + | 2. a preview feature is imported at the package level, or + | 3. compiling with the -preview compiler flag. + |""".stripMargin end Feature + diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 599812a9a390..c82139439e83 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -116,7 +116,8 @@ trait CommonScalaSettings: val unchecked: Setting[Boolean] = BooleanSetting(RootSetting, "unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked")) val language: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting(RootSetting, "language", "feature", "Enable one or more language features.", choices = ScalaSettingsProperties.supportedLanguageFeatures, legacyChoices = ScalaSettingsProperties.legacyLanguageFeatures, default = Nil, aliases = List("--language")) val experimental: Setting[Boolean] = BooleanSetting(RootSetting, "experimental", "Annotate all top-level definitions with @experimental. This enables the use of experimental features anywhere in the project.") - + val preview: Setting[Boolean] = BooleanSetting(RootSetting, "preview", "Enable the use of preview features anywhere in the project.") + /* Coverage settings */ val coverageOutputDir = PathSetting(RootSetting, "coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out")) val coverageExcludeClasslikes: Setting[List[String]] = MultiStringSetting(RootSetting, "coverage-exclude-classlikes", "packages, classes and modules", "List of regexes for packages, classes and modules to exclude from coverage.", aliases = List("--coverage-exclude-classlikes")) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 1a5cf2b03e06..1615679a036e 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -303,5 +303,16 @@ object Annotations { case annot @ ExperimentalAnnotation(msg) => ExperimentalAnnotation(msg, annot.tree.span) } } - + + object PreviewAnnotation { + /** Matches and extracts the message from an instance of `@preview(msg)` + * Returns `Some("")` for `@preview` with no message. + */ + def unapply(a: Annotation)(using Context): Option[String] = + if a.symbol ne defn.PreviewAnnot then + None + else a.argumentConstant(0) match + case Some(Constant(msg: String)) => Some(msg) + case _ => Some("") + } } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f15595fbcdb6..fcaf41afebaa 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -808,6 +808,7 @@ class Definitions { @tu lazy val LanguageModule: Symbol = requiredModule("scala.language") @tu lazy val LanguageModuleClass: Symbol = LanguageModule.moduleClass.asClass @tu lazy val LanguageExperimentalModule: Symbol = requiredModule("scala.language.experimental") + @tu lazy val LanguagePreviewModule: Symbol = requiredModule("scala.language.preview") @tu lazy val LanguageDeprecatedModule: Symbol = requiredModule("scala.language.deprecated") @tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl") @tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable") @@ -1053,6 +1054,7 @@ class Definitions { @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 PreviewAnnot: ClassSymbol = requiredClass("scala.annotation.preview") @tu lazy val ThrowsAnnot: ClassSymbol = requiredClass("scala.throws") @tu lazy val TransientAnnot: ClassSymbol = requiredClass("scala.transient") @tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked") @@ -2079,6 +2081,10 @@ class Definitions { @tu lazy val languageExperimentalFeatures: List[TermSymbol] = LanguageExperimentalModule.moduleClass.info.decls.toList.filter(_.isAllOf(Lazy | Module)).map(_.asTerm) + /** Preview language features defined in `scala.runtime.stdLibPatches.language.preview` */ + @tu lazy val languagePreviewFeatures: List[TermSymbol] = + LanguagePreviewModule.moduleClass.info.decls.toList.filter(_.isAllOf(Lazy | Module)).map(_.asTerm) + // ----- primitive value class machinery ------------------------------------------ class PerRun[T](generate: Context ?=> T) { diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 56d71c7fb57e..90497784d761 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -579,6 +579,7 @@ object StdNames { val parts: N = "parts" val postfixOps: N = "postfixOps" val prefix : N = "prefix" + val preview: N = "preview" val processEscapes: N = "processEscapes" val productArity: N = "productArity" val productElement: N = "productElement" diff --git a/compiler/src/dotty/tools/dotc/core/SymUtils.scala b/compiler/src/dotty/tools/dotc/core/SymUtils.scala index 1a762737d52f..75f161699257 100644 --- a/compiler/src/dotty/tools/dotc/core/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/SymUtils.scala @@ -366,24 +366,31 @@ class SymUtils: && self.owner.linkedClass.isDeclaredInfix /** Is symbol declared or inherits @experimental? */ - def isExperimental(using Context): Boolean = - self.hasAnnotation(defn.ExperimentalAnnot) - || (self.maybeOwner.isClass && self.owner.hasAnnotation(defn.ExperimentalAnnot)) - - def isInExperimentalScope(using Context): Boolean = - def isDefaultArgumentOfExperimentalMethod = + def isExperimental(using Context): Boolean = isFeatureAnnotated(defn.ExperimentalAnnot) + def isInExperimentalScope(using Context): Boolean = isInFeatureScope(defn.ExperimentalAnnot, _.isExperimental, _.isInExperimentalScope) + + /** Is symbol declared or inherits @preview? */ + def isPreview(using Context): Boolean = isFeatureAnnotated(defn.PreviewAnnot) + def isInPreviewScope(using Context): Boolean = isInFeatureScope(defn.PreviewAnnot, _.isPreview, _.isInPreviewScope) + + private inline def isFeatureAnnotated(checkAnnotaton: ClassSymbol)(using Context): Boolean = + self.hasAnnotation(checkAnnotaton) + || (self.maybeOwner.isClass && self.owner.hasAnnotation(checkAnnotaton)) + + private inline def isInFeatureScope(checkAnnotation: ClassSymbol, checkSymbol: Symbol => Boolean, checkOwner: Symbol => Boolean)(using Context): Boolean = + def isDefaultArgumentOfCheckedMethod = self.name.is(DefaultGetterName) && self.owner.isClass && { val overloads = self.owner.asClass.membersNamed(self.name.firstPart) overloads.filterWithFlags(HasDefaultParams, EmptyFlags) match - case denot: SymDenotation => denot.symbol.isExperimental + case denot: SymDenotation => checkSymbol(denot.symbol) case _ => false } - self.hasAnnotation(defn.ExperimentalAnnot) - || isDefaultArgumentOfExperimentalMethod - || (!self.is(Package) && self.owner.isInExperimentalScope) - + self.hasAnnotation(checkAnnotation) + || isDefaultArgumentOfCheckedMethod + || (!self.is(Package) && checkOwner(self.owner)) + /** 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/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e8d3a75b4dec..42e636f3779f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1079,6 +1079,7 @@ trait Checking { case Some(prefix) => val required = if prefix == nme.experimental then defn.LanguageExperimentalModule + else if prefix == nme.preview then defn.LanguagePreviewModule else if prefix == nme.deprecated then defn.LanguageDeprecatedModule else defn.LanguageModule if path.symbol != required then @@ -1087,6 +1088,7 @@ trait Checking { val foundClasses = path.tpe.classSymbols if foundClasses.contains(defn.LanguageModule.moduleClass) || foundClasses.contains(defn.LanguageExperimentalModule.moduleClass) + || foundClasses.contains(defn.LanguagePreviewModule.moduleClass) then report.error(em"no aliases can be used to refer to a language import", path.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 8f8a68aa5735..f0d4d617bb74 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -141,10 +141,12 @@ class CrossVersionChecks extends MiniPhase: if tree.span.isSourceDerived then checkDeprecatedRef(sym, tree.srcPos) checkExperimentalRef(sym, tree.srcPos) + checkPreviewFeatureRef(sym, tree.srcPos) case TermRef(_, sym: Symbol) => if tree.span.isSourceDerived then checkDeprecatedRef(sym, tree.srcPos) checkExperimentalRef(sym, tree.srcPos) + checkPreviewFeatureRef(sym, tree.srcPos) case AnnotatedType(_, annot) => checkUnrollAnnot(annot.symbol, tree.srcPos) case _ => @@ -174,11 +176,12 @@ object CrossVersionChecks: val description: String = "check issues related to deprecated and experimental" /** Check that a reference to an experimental definition with symbol `sym` meets cross-version constraints - * for `@deprecated` and `@experimental`. + * for `@deprecated`, `@experimental` and `@preview`. */ def checkRef(sym: Symbol, pos: SrcPos)(using Context): Unit = checkDeprecatedRef(sym, pos) checkExperimentalRef(sym, pos) + checkPreviewFeatureRef(sym, pos) /** Check that a reference to an experimental definition with symbol `sym` is only * used in an experimental scope @@ -187,6 +190,13 @@ object CrossVersionChecks: if sym.isExperimental && !ctx.owner.isInExperimentalScope then Feature.checkExperimentalDef(sym, pos) + /** Check that a reference to a preview definition with symbol `sym` is only + * used in a preview mode. + */ + private[CrossVersionChecks] def checkPreviewFeatureRef(sym: Symbol, pos: SrcPos)(using Context): Unit = + if sym.isPreview && !ctx.owner.isInPreviewScope then + Feature.checkPreviewDef(sym, pos) + /** If @deprecated is present, and the point of reference is not enclosed * in either a deprecated member or a scala bridge method, issue a warning. * diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index b444ba595a12..0ed7e0f3a969 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -306,6 +306,7 @@ object RefChecks { * that passes its value on to O. * 1.13. If O is non-experimental, M must be non-experimental. * 1.14. If O has @publicInBinary, M must have @publicInBinary. + * 1.15. If O is non-preview, M must be non-preview * 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. @@ -643,8 +644,10 @@ object RefChecks { MigrationVersion.OverrideValParameter) else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.13) overrideError("may not override non-experimental member") - else if !member.hasAnnotation(defn.PublicInBinaryAnnot) && other.hasAnnotation(defn.PublicInBinaryAnnot) then // (1.14) + else if !member.hasAnnotation(defn.PublicInBinaryAnnot) && other.hasAnnotation(defn.PublicInBinaryAnnot) then // (1.14) overrideError("also needs to be declared with @publicInBinary") + else if !other.isPreview && member.hasAnnotation(defn.PreviewAnnot) then // (1.15) + overrideError("may not override non-preview member") else if other.hasAnnotation(defn.DeprecatedOverridingAnnot) then overrideDeprecation("", member, other, "removed or renamed") end checkOverride diff --git a/docs/_docs/reference/other-new-features/preview-defs.md b/docs/_docs/reference/other-new-features/preview-defs.md new file mode 100644 index 000000000000..05b994d74538 --- /dev/null +++ b/docs/_docs/reference/other-new-features/preview-defs.md @@ -0,0 +1,38 @@ +--- +layout: doc-page +title: "Preview Definitions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/preview-defs.html +--- + +The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/preview.html) annotation allows the definition of an API that is not guaranteed backward binary, but might become stable in next minor version of the compiler. + +New Scala language features or standard library APIs initially introduced as experimental can become a preview features when they have become fully implemented and acceppted by the [SIP](https://docs.scala-lang.org/sips/) before they're accepted as standard features. +Such definitions can be used by early adopters that can accept possibility of binary compatibility breakage, for example these can be used for project internal tools and applications, but are discouraged to be used by libraries. + +The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/preview.html) definitions follows similar rules as the [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) - to enable access to preview feature or API in given compilation unit Scala compiler requires either: + +- explicit `-preview` flag passed to the compiler, +- top level import for explicit `scala.language.preview.`, +- annotating defintion that referes to preview feature with `@preview` + +The biggest difference of preview features when compared with experimental features is their non-viral behaviour. +Any defintion that was compiles in the preview scope (using `-preview` flag or `scala.language.preview` top-level import) is not annotated as `@preview` defintion itself. It behaviour allows to use preview features transitively in other compilation units without enabled preview mode. + +```scala +//> using options -preview +import scala.annotation.preview + +@preview def previewFeature: Unit = () + +// Can be used in non-preview scope +def usePreviewFeature = previewFeature +``` + +```scala +def usePreviewFeatureTransitively = usePreviewFeature +def usePreviewFeatureDirectly = previewFeature // error - refering to preview definition outside preview scope +def useWrappedPreviewFeature = wrappedPreviewFeature // error - refering to preview definition outside preview scope + +@scala.annotation.preview +def wrappedPreviewFeature = previewFeature +``` diff --git a/library/src/scala/annotation/preview.scala b/library/src/scala/annotation/preview.scala new file mode 100644 index 000000000000..cc32d9389f82 --- /dev/null +++ b/library/src/scala/annotation/preview.scala @@ -0,0 +1,9 @@ +package scala.annotation + +/** An annotation that can be used to mark a definition as preview. + * + * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/preview-defs]] + * @syntax markdown + */ +final class preview(message: String) extends StaticAnnotation: + def this() = this("") diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 402e5af1735f..8e589d317991 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -5,6 +5,21 @@ import scala.annotation.compileTimeOnly /** Scala 3 additions and replacements to the `scala.language` object. */ object language: + + /** The preview object contains previously experimental features that are fully implemented + * but are awaiting to be stablized as a standard features. + * + * Preview features '''may undergo binary compatibility changes''' in future releases, + * but their API is unlikely to change. These can be used by early adopters that do don't care + * about the binary breakage, i.e. applications, but not libraries. + * + * Programmers are encouraged to try out preview features and + * [[https://github.com/scala/scala3/issues report any bugs or API inconsistencies]] + * they encounter so they can be improved in future releases. + * + * @group preview + */ + object preview /** The experimental object contains features that have been recently added but have not * been thoroughly tested in production yet. diff --git a/tests/neg/preview-message.check b/tests/neg/preview-message.check new file mode 100644 index 000000000000..86638de33308 --- /dev/null +++ b/tests/neg/preview-message.check @@ -0,0 +1,27 @@ +-- Error: tests/neg/preview-message.scala:15:2 ------------------------------------------------------------------------- +15 | f1() // error + | ^^ + | method f1 is marked @preview + | + | Preview definition may only be used under preview mode: + | 1. in a definition marked as @preview, or + | 2. a preview feature is imported at the package level, or + | 3. compiling with the -preview compiler flag. +-- Error: tests/neg/preview-message.scala:16:2 ------------------------------------------------------------------------- +16 | f2() // error + | ^^ + | method f2 is marked @preview + | + | Preview definition may only be used under preview mode: + | 1. in a definition marked as @preview, or + | 2. a preview feature is imported at the package level, or + | 3. compiling with the -preview compiler flag. +-- Error: tests/neg/preview-message.scala:17:2 ------------------------------------------------------------------------- +17 | f3() // error + | ^^ + | method f3 is marked @preview: not yet stable + | + | Preview definition may only be used under preview mode: + | 1. in a definition marked as @preview, or + | 2. a preview feature is imported at the package level, or + | 3. compiling with the -preview compiler flag. diff --git a/tests/neg/preview-message.scala b/tests/neg/preview-message.scala new file mode 100644 index 000000000000..db85869d9345 --- /dev/null +++ b/tests/neg/preview-message.scala @@ -0,0 +1,17 @@ + + +import scala.annotation.preview + +@preview +def f1() = ??? + +@preview() +def f2() = ??? + +@preview("not yet stable") +def f3() = ??? + +def g() = + f1() // error + f2() // error + f3() // error diff --git a/tests/neg/preview-non-viral/defs_1.scala b/tests/neg/preview-non-viral/defs_1.scala new file mode 100644 index 000000000000..2fc7d51ab260 --- /dev/null +++ b/tests/neg/preview-non-viral/defs_1.scala @@ -0,0 +1,6 @@ +//> using options -preview +import scala.annotation.preview + +@preview def previewFeature = 42 + +def usePreviewFeature = previewFeature diff --git a/tests/neg/preview-non-viral/usage_2.scala b/tests/neg/preview-non-viral/usage_2.scala new file mode 100644 index 000000000000..5b533cf1b0d6 --- /dev/null +++ b/tests/neg/preview-non-viral/usage_2.scala @@ -0,0 +1,5 @@ +def usePreviewFeatureTransitively = usePreviewFeature +def usePreviewFeatureDirectly = previewFeature // error + +@scala.annotation.preview +def wrappedPreviewFeature = previewFeature \ No newline at end of file diff --git a/tests/neg/previewOverloads.scala b/tests/neg/previewOverloads.scala new file mode 100644 index 000000000000..e55eee554c13 --- /dev/null +++ b/tests/neg/previewOverloads.scala @@ -0,0 +1,13 @@ + + +import scala.annotation.preview + +trait A: + def f: Int + def g: Int = 3 +trait B extends A: + @preview + def f: Int = 4 // error + + @preview + override def g: Int = 5 // error diff --git a/tests/neg/previewOverride.scala b/tests/neg/previewOverride.scala new file mode 100644 index 000000000000..0a10c6ffe440 --- /dev/null +++ b/tests/neg/previewOverride.scala @@ -0,0 +1,41 @@ + + +import scala.annotation.preview + +@preview +class A: + def f() = 1 + +@preview +class B extends A: + override def f() = 2 + +class C: + @preview + def f() = 1 + +class D extends C: + override def f() = 2 + +trait A2: + @preview + 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/pos/preview-flag.scala b/tests/pos/preview-flag.scala new file mode 100644 index 000000000000..7498b5dacf40 --- /dev/null +++ b/tests/pos/preview-flag.scala @@ -0,0 +1,18 @@ +//> using options -preview + +import scala.annotation.preview + +class Foo: + def foo: Int = previewDef + +class Bar: + def bar: Int = previewDef +object Bar: + def bar: Int = previewDef + +object Baz: + def bar: Int = previewDef + +def toplevelMethod: Int = previewDef + +@preview def previewDef: Int = 1 From 30a4f6005b54daaddafd31e052364bba83d6dc47 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 7 Jan 2025 15:21:47 +0100 Subject: [PATCH 2/6] Adjust MiMa filters --- project/MiMaFilters.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 00e7153bcb83..05aa95ffef9a 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -13,6 +13,9 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.quotedPatternsWithPolymorphicFunctions"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$quotedPatternsWithPolymorphicFunctions$"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.runtime.Patterns.higherOrderHoleWithTypes"), + ProblemFilters.exclude[MissingClassProblem]("scala.annotation.preview"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.preview"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$preview$"), ), // Additions since last LTS From 0ab56bd9adc8cf53868b8f4c1d948cbcc4828ea2 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 23 Jan 2025 15:07:13 +0100 Subject: [PATCH 3/6] Make preview `private[scala]`, enable all preview features globally using flag only --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- .../src/dotty/tools/dotc/config/Feature.scala | 29 +++++------------- .../tools/dotc/config/ScalaSettings.scala | 2 +- .../dotty/tools/dotc/core/Definitions.scala | 7 +---- .../src/dotty/tools/dotc/core/StdNames.scala | 1 - .../src/dotty/tools/dotc/core/SymUtils.scala | 10 +++---- .../src/dotty/tools/dotc/typer/Checking.scala | 2 -- .../dotty/tools/dotc/typer/RefChecks.scala | 8 ++--- .../other-new-features/preview-defs.md | 30 ++++++++----------- docs/sidebar.yml | 1 + .../annotation/{ => internal}/preview.scala | 4 ++- .../runtime/stdLibPatches/language.scala | 15 ---------- project/MiMaFilters.scala | 4 +-- tests/neg/preview-message.check | 15 ++-------- tests/neg/preview-message.scala | 4 +-- tests/neg/preview-non-viral/defs_1.scala | 3 +- tests/neg/preview-non-viral/usage_2.scala | 7 ++--- tests/neg/previewOverloads.scala | 4 +-- tests/neg/previewOverride.scala | 4 +-- tests/pos/preview-flag.scala | 6 ++-- 20 files changed, 54 insertions(+), 104 deletions(-) rename library/src/scala/annotation/{ => internal}/preview.scala (70%) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index b68ec47d176a..e0fe17755257 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -297,7 +297,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case TypeDefs(_) => true case _ => false - private val languageSubCategories = Set(nme.experimental, nme.preview, nme.deprecated) + private val languageSubCategories = Set(nme.experimental, nme.deprecated) /** If `path` looks like a language import, `Some(name)` where name * is `experimental` if that sub-module is imported, and the empty diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index b0785b63406c..c6dbd31dc9bb 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -18,9 +18,6 @@ object Feature: def experimental(str: PreName): TermName = QualifiedName(nme.experimental, str.toTermName) - - def preview(str: PreName): TermName = - QualifiedName(nme.preview, str.toTermName) private def deprecated(str: PreName): TermName = QualifiedName(nme.deprecated, str.toTermName) @@ -48,10 +45,6 @@ object Feature: defn.languageExperimentalFeatures .map(sym => experimental(sym.name)) .filterNot(_ == captureChecking) // TODO is this correct? - - def previewAutoEnableFeatures(using Context): List[TermName] = - defn.languagePreviewFeatures - .map(sym => preview(sym.name)) val values = List( (nme.help, "Display all available features"), @@ -232,7 +225,7 @@ object Feature: def isExperimentalEnabledByImport(using Context): Boolean = experimentalAutoEnableFeatures.exists(enabledByImport) - + /** Handle language import `import language..` if it is one * of the global imports `pureFunctions` or `captureChecking`. In this case * make the compilation unit's and current run's fields accordingly. @@ -250,15 +243,14 @@ object Feature: true else false - - def isPreviewEnabled(using Context): Boolean = - ctx.settings.preview.value || - previewAutoEnableFeatures.exists(enabled) - + + def isPreviewEnabled(using Context): Boolean = + ctx.settings.preview.value + def checkPreviewFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) = if !isPreviewEnabled then report.error(previewUseSite(which) + note, srcPos) - + def checkPreviewDef(sym: Symbol, srcPos: SrcPos)(using Context) = if !isPreviewEnabled then val previewSym = if sym.hasAnnotation(defn.PreviewAnnot) then sym @@ -273,12 +265,7 @@ object Feature: then i"$previewSym is marked @preview$msg" else i"$sym inherits @preview$msg" report.error(markedPreview + "\n\n" + previewUseSite("definition"), srcPos) - + private def previewUseSite(which: String): String = - s"""Preview $which may only be used under preview mode: - | 1. in a definition marked as @preview, or - | 2. a preview feature is imported at the package level, or - | 3. compiling with the -preview compiler flag. - |""".stripMargin + s"Preview $which may only be used when compiling with the `-preview` compiler flag" end Feature - diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index c82139439e83..6f7dd940c573 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -117,7 +117,7 @@ trait CommonScalaSettings: val language: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting(RootSetting, "language", "feature", "Enable one or more language features.", choices = ScalaSettingsProperties.supportedLanguageFeatures, legacyChoices = ScalaSettingsProperties.legacyLanguageFeatures, default = Nil, aliases = List("--language")) val experimental: Setting[Boolean] = BooleanSetting(RootSetting, "experimental", "Annotate all top-level definitions with @experimental. This enables the use of experimental features anywhere in the project.") val preview: Setting[Boolean] = BooleanSetting(RootSetting, "preview", "Enable the use of preview features anywhere in the project.") - + /* Coverage settings */ val coverageOutputDir = PathSetting(RootSetting, "coverage-out", "Destination for coverage classfiles and instrumentation data.", "", aliases = List("--coverage-out")) val coverageExcludeClasslikes: Setting[List[String]] = MultiStringSetting(RootSetting, "coverage-exclude-classlikes", "packages, classes and modules", "List of regexes for packages, classes and modules to exclude from coverage.", aliases = List("--coverage-exclude-classlikes")) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index fcaf41afebaa..8ad23c736d74 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -808,7 +808,6 @@ class Definitions { @tu lazy val LanguageModule: Symbol = requiredModule("scala.language") @tu lazy val LanguageModuleClass: Symbol = LanguageModule.moduleClass.asClass @tu lazy val LanguageExperimentalModule: Symbol = requiredModule("scala.language.experimental") - @tu lazy val LanguagePreviewModule: Symbol = requiredModule("scala.language.preview") @tu lazy val LanguageDeprecatedModule: Symbol = requiredModule("scala.language.deprecated") @tu lazy val NonLocalReturnControlClass: ClassSymbol = requiredClass("scala.runtime.NonLocalReturnControl") @tu lazy val SelectableClass: ClassSymbol = requiredClass("scala.Selectable") @@ -1054,7 +1053,7 @@ class Definitions { @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 PreviewAnnot: ClassSymbol = requiredClass("scala.annotation.preview") + @tu lazy val PreviewAnnot: ClassSymbol = requiredClass("scala.annotation.internal.preview") @tu lazy val ThrowsAnnot: ClassSymbol = requiredClass("scala.throws") @tu lazy val TransientAnnot: ClassSymbol = requiredClass("scala.transient") @tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked") @@ -2081,10 +2080,6 @@ class Definitions { @tu lazy val languageExperimentalFeatures: List[TermSymbol] = LanguageExperimentalModule.moduleClass.info.decls.toList.filter(_.isAllOf(Lazy | Module)).map(_.asTerm) - /** Preview language features defined in `scala.runtime.stdLibPatches.language.preview` */ - @tu lazy val languagePreviewFeatures: List[TermSymbol] = - LanguagePreviewModule.moduleClass.info.decls.toList.filter(_.isAllOf(Lazy | Module)).map(_.asTerm) - // ----- primitive value class machinery ------------------------------------------ class PerRun[T](generate: Context ?=> T) { diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 90497784d761..56d71c7fb57e 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -579,7 +579,6 @@ object StdNames { val parts: N = "parts" val postfixOps: N = "postfixOps" val prefix : N = "prefix" - val preview: N = "preview" val processEscapes: N = "processEscapes" val productArity: N = "productArity" val productElement: N = "productElement" diff --git a/compiler/src/dotty/tools/dotc/core/SymUtils.scala b/compiler/src/dotty/tools/dotc/core/SymUtils.scala index 75f161699257..54ba0e3bdd06 100644 --- a/compiler/src/dotty/tools/dotc/core/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/SymUtils.scala @@ -370,13 +370,13 @@ class SymUtils: def isInExperimentalScope(using Context): Boolean = isInFeatureScope(defn.ExperimentalAnnot, _.isExperimental, _.isInExperimentalScope) /** Is symbol declared or inherits @preview? */ - def isPreview(using Context): Boolean = isFeatureAnnotated(defn.PreviewAnnot) + def isPreview(using Context): Boolean = isFeatureAnnotated(defn.PreviewAnnot) def isInPreviewScope(using Context): Boolean = isInFeatureScope(defn.PreviewAnnot, _.isPreview, _.isInPreviewScope) - - private inline def isFeatureAnnotated(checkAnnotaton: ClassSymbol)(using Context): Boolean = + + private inline def isFeatureAnnotated(checkAnnotaton: ClassSymbol)(using Context): Boolean = self.hasAnnotation(checkAnnotaton) || (self.maybeOwner.isClass && self.owner.hasAnnotation(checkAnnotaton)) - + private inline def isInFeatureScope(checkAnnotation: ClassSymbol, checkSymbol: Symbol => Boolean, checkOwner: Symbol => Boolean)(using Context): Boolean = def isDefaultArgumentOfCheckedMethod = self.name.is(DefaultGetterName) @@ -390,7 +390,7 @@ class SymUtils: self.hasAnnotation(checkAnnotation) || isDefaultArgumentOfCheckedMethod || (!self.is(Package) && checkOwner(self.owner)) - + /** 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/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 42e636f3779f..e8d3a75b4dec 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1079,7 +1079,6 @@ trait Checking { case Some(prefix) => val required = if prefix == nme.experimental then defn.LanguageExperimentalModule - else if prefix == nme.preview then defn.LanguagePreviewModule else if prefix == nme.deprecated then defn.LanguageDeprecatedModule else defn.LanguageModule if path.symbol != required then @@ -1088,7 +1087,6 @@ trait Checking { val foundClasses = path.tpe.classSymbols if foundClasses.contains(defn.LanguageModule.moduleClass) || foundClasses.contains(defn.LanguageExperimentalModule.moduleClass) - || foundClasses.contains(defn.LanguagePreviewModule.moduleClass) then report.error(em"no aliases can be used to refer to a language import", path.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 0ed7e0f3a969..0ff36d8ec629 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -306,7 +306,7 @@ object RefChecks { * that passes its value on to O. * 1.13. If O is non-experimental, M must be non-experimental. * 1.14. If O has @publicInBinary, M must have @publicInBinary. - * 1.15. If O is non-preview, M must be non-preview + * 1.15. If O is non-preview, M must be non-preview * 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. @@ -644,10 +644,10 @@ object RefChecks { MigrationVersion.OverrideValParameter) else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.13) overrideError("may not override non-experimental member") - else if !member.hasAnnotation(defn.PublicInBinaryAnnot) && other.hasAnnotation(defn.PublicInBinaryAnnot) then // (1.14) + else if !member.hasAnnotation(defn.PublicInBinaryAnnot) && other.hasAnnotation(defn.PublicInBinaryAnnot) then // (1.14) overrideError("also needs to be declared with @publicInBinary") - else if !other.isPreview && member.hasAnnotation(defn.PreviewAnnot) then // (1.15) - overrideError("may not override non-preview member") + else if !other.isPreview && member.hasAnnotation(defn.PreviewAnnot) then // (1.15) + overrideError("may not override non-preview member") else if other.hasAnnotation(defn.DeprecatedOverridingAnnot) then overrideDeprecation("", member, other, "removed or renamed") end checkOverride diff --git a/docs/_docs/reference/other-new-features/preview-defs.md b/docs/_docs/reference/other-new-features/preview-defs.md index 05b994d74538..4cd4c92b27c8 100644 --- a/docs/_docs/reference/other-new-features/preview-defs.md +++ b/docs/_docs/reference/other-new-features/preview-defs.md @@ -4,23 +4,23 @@ title: "Preview Definitions" nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/preview-defs.html --- -The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/preview.html) annotation allows the definition of an API that is not guaranteed backward binary, but might become stable in next minor version of the compiler. +New Scala language features or standard library APIs are initially introduced as experimental, but once they become fully implemented and acceppted by the [SIP](https://docs.scala-lang.org/sips/) these can become a preview features. +Preview language features and APIs are guaranteed to be standarized in some next Scala minor release, but allow compiler team to introduce small, possibly binary incompatible, changes based on the community feedback. +These can be used by early adopters who can accept possibility of binary compatibility breakage. As an example these can be used for project internal tools and applications, but are discouraged to be used by libraries. -New Scala language features or standard library APIs initially introduced as experimental can become a preview features when they have become fully implemented and acceppted by the [SIP](https://docs.scala-lang.org/sips/) before they're accepted as standard features. -Such definitions can be used by early adopters that can accept possibility of binary compatibility breakage, for example these can be used for project internal tools and applications, but are discouraged to be used by libraries. - -The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/preview.html) definitions follows similar rules as the [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) - to enable access to preview feature or API in given compilation unit Scala compiler requires either: - -- explicit `-preview` flag passed to the compiler, -- top level import for explicit `scala.language.preview.`, -- annotating defintion that referes to preview feature with `@preview` +Users can enable access to preview features and definitions by compiling with `-preview` flag. The flag would enable all preview features and definitions. There is no way for enabling only a subset of preview features. The biggest difference of preview features when compared with experimental features is their non-viral behaviour. -Any defintion that was compiles in the preview scope (using `-preview` flag or `scala.language.preview` top-level import) is not annotated as `@preview` defintion itself. It behaviour allows to use preview features transitively in other compilation units without enabled preview mode. +Any defintion compiled in the preview mode (using `-preview` flag) is not marked as preview defintion itself. +This behaviour allows to use preview features transitively in other compilation units without explicitlly enabled preview mode, as long as it does not directly reference APIs or features marked as preview. + +The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/internal/preview.html) annotations are used to mark Scala 3 standard library APIs currently available under enabled preview mode. +The definitions follows similar rules as the [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) when it comes to accessing, subtyping, overriding or overloading definitions marked with this annotation - all of these can only be performed in compilation unit that enables preview mode. ```scala //> using options -preview -import scala.annotation.preview +package scala.stdlib +import scala.annotation.internal.preview @preview def previewFeature: Unit = () @@ -29,10 +29,6 @@ def usePreviewFeature = previewFeature ``` ```scala -def usePreviewFeatureTransitively = usePreviewFeature -def usePreviewFeatureDirectly = previewFeature // error - refering to preview definition outside preview scope -def useWrappedPreviewFeature = wrappedPreviewFeature // error - refering to preview definition outside preview scope - -@scala.annotation.preview -def wrappedPreviewFeature = previewFeature +def usePreviewFeatureTransitively = scala.stdlib.usePreviewFeature +def usePreviewFeatureDirectly = scala.stdlib.previewFeature // error - refering to preview definition outside preview scope ``` diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 8cae4e95725a..6f2154287091 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -85,6 +85,7 @@ subsection: - page: reference/other-new-features/safe-initialization.md - page: reference/other-new-features/type-test.md - page: reference/other-new-features/experimental-defs.md + - page: reference/other-new-features/preview-defs.md - page: reference/other-new-features/binary-literals.md - title: Other Changed Features directory: changed-features diff --git a/library/src/scala/annotation/preview.scala b/library/src/scala/annotation/internal/preview.scala similarity index 70% rename from library/src/scala/annotation/preview.scala rename to library/src/scala/annotation/internal/preview.scala index cc32d9389f82..a6e797d78e97 100644 --- a/library/src/scala/annotation/preview.scala +++ b/library/src/scala/annotation/internal/preview.scala @@ -1,9 +1,11 @@ package scala.annotation +package internal + /** An annotation that can be used to mark a definition as preview. * * @see [[https://dotty.epfl.ch/docs/reference/other-new-features/preview-defs]] * @syntax markdown */ -final class preview(message: String) extends StaticAnnotation: +private[scala] final class preview(message: String) extends StaticAnnotation: def this() = this("") diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 8e589d317991..402e5af1735f 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -5,21 +5,6 @@ import scala.annotation.compileTimeOnly /** Scala 3 additions and replacements to the `scala.language` object. */ object language: - - /** The preview object contains previously experimental features that are fully implemented - * but are awaiting to be stablized as a standard features. - * - * Preview features '''may undergo binary compatibility changes''' in future releases, - * but their API is unlikely to change. These can be used by early adopters that do don't care - * about the binary breakage, i.e. applications, but not libraries. - * - * Programmers are encouraged to try out preview features and - * [[https://github.com/scala/scala3/issues report any bugs or API inconsistencies]] - * they encounter so they can be improved in future releases. - * - * @group preview - */ - object preview /** The experimental object contains features that have been recently added but have not * been thoroughly tested in production yet. diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 05aa95ffef9a..08e1d7850a46 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -13,9 +13,7 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.quotedPatternsWithPolymorphicFunctions"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$quotedPatternsWithPolymorphicFunctions$"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.runtime.Patterns.higherOrderHoleWithTypes"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.preview"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.preview"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$preview$"), + ProblemFilters.exclude[MissingClassProblem]("scala.annotation.internal.preview"), ), // Additions since last LTS diff --git a/tests/neg/preview-message.check b/tests/neg/preview-message.check index 86638de33308..c3478c27fd46 100644 --- a/tests/neg/preview-message.check +++ b/tests/neg/preview-message.check @@ -3,25 +3,16 @@ | ^^ | method f1 is marked @preview | - | Preview definition may only be used under preview mode: - | 1. in a definition marked as @preview, or - | 2. a preview feature is imported at the package level, or - | 3. compiling with the -preview compiler flag. + | Preview definition may only be used when compiling with the `-preview` compiler flag -- Error: tests/neg/preview-message.scala:16:2 ------------------------------------------------------------------------- 16 | f2() // error | ^^ | method f2 is marked @preview | - | Preview definition may only be used under preview mode: - | 1. in a definition marked as @preview, or - | 2. a preview feature is imported at the package level, or - | 3. compiling with the -preview compiler flag. + | Preview definition may only be used when compiling with the `-preview` compiler flag -- Error: tests/neg/preview-message.scala:17:2 ------------------------------------------------------------------------- 17 | f3() // error | ^^ | method f3 is marked @preview: not yet stable | - | Preview definition may only be used under preview mode: - | 1. in a definition marked as @preview, or - | 2. a preview feature is imported at the package level, or - | 3. compiling with the -preview compiler flag. + | Preview definition may only be used when compiling with the `-preview` compiler flag diff --git a/tests/neg/preview-message.scala b/tests/neg/preview-message.scala index db85869d9345..40261be3014e 100644 --- a/tests/neg/preview-message.scala +++ b/tests/neg/preview-message.scala @@ -1,6 +1,6 @@ +package scala // @preview in private[scala] - -import scala.annotation.preview +import scala.annotation.internal.preview @preview def f1() = ??? diff --git a/tests/neg/preview-non-viral/defs_1.scala b/tests/neg/preview-non-viral/defs_1.scala index 2fc7d51ab260..27f0817455d1 100644 --- a/tests/neg/preview-non-viral/defs_1.scala +++ b/tests/neg/preview-non-viral/defs_1.scala @@ -1,5 +1,6 @@ //> using options -preview -import scala.annotation.preview +package scala // @preview in private[scala] +import scala.annotation.internal.preview @preview def previewFeature = 42 diff --git a/tests/neg/preview-non-viral/usage_2.scala b/tests/neg/preview-non-viral/usage_2.scala index 5b533cf1b0d6..50404e582dff 100644 --- a/tests/neg/preview-non-viral/usage_2.scala +++ b/tests/neg/preview-non-viral/usage_2.scala @@ -1,5 +1,2 @@ -def usePreviewFeatureTransitively = usePreviewFeature -def usePreviewFeatureDirectly = previewFeature // error - -@scala.annotation.preview -def wrappedPreviewFeature = previewFeature \ No newline at end of file +def usePreviewFeatureTransitively = scala.usePreviewFeature +def usePreviewFeatureDirectly = scala.previewFeature // error diff --git a/tests/neg/previewOverloads.scala b/tests/neg/previewOverloads.scala index e55eee554c13..4bfe3bb0bc70 100644 --- a/tests/neg/previewOverloads.scala +++ b/tests/neg/previewOverloads.scala @@ -1,6 +1,6 @@ +package scala // @preview in private[scala] - -import scala.annotation.preview +import scala.annotation.internal.preview trait A: def f: Int diff --git a/tests/neg/previewOverride.scala b/tests/neg/previewOverride.scala index 0a10c6ffe440..ff87157e6837 100644 --- a/tests/neg/previewOverride.scala +++ b/tests/neg/previewOverride.scala @@ -1,6 +1,6 @@ +package scala // @preview in private[scala] - -import scala.annotation.preview +import scala.annotation.internal.preview @preview class A: diff --git a/tests/pos/preview-flag.scala b/tests/pos/preview-flag.scala index 7498b5dacf40..94d482a97ae7 100644 --- a/tests/pos/preview-flag.scala +++ b/tests/pos/preview-flag.scala @@ -1,6 +1,8 @@ //> using options -preview +package scala // @preview in private[scala] +import scala.annotation.internal.preview -import scala.annotation.preview +@preview def previewDef: Int = 42 class Foo: def foo: Int = previewDef @@ -14,5 +16,3 @@ object Baz: def bar: Int = previewDef def toplevelMethod: Int = previewDef - -@preview def previewDef: Int = 1 From fd67fc1c44f359ae57c00c26920db1bfea62cfe4 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 23 Jan 2025 15:48:41 +0100 Subject: [PATCH 4/6] Fix typos --- tests/neg/preview-message.scala | 2 +- tests/neg/preview-non-viral/defs_1.scala | 2 +- tests/neg/previewOverloads.scala | 2 +- tests/neg/previewOverride.scala | 2 +- tests/pos/preview-flag.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/neg/preview-message.scala b/tests/neg/preview-message.scala index 40261be3014e..99bdd72cd936 100644 --- a/tests/neg/preview-message.scala +++ b/tests/neg/preview-message.scala @@ -1,4 +1,4 @@ -package scala // @preview in private[scala] +package scala // @preview is private[scala] import scala.annotation.internal.preview diff --git a/tests/neg/preview-non-viral/defs_1.scala b/tests/neg/preview-non-viral/defs_1.scala index 27f0817455d1..434f39e13c94 100644 --- a/tests/neg/preview-non-viral/defs_1.scala +++ b/tests/neg/preview-non-viral/defs_1.scala @@ -1,5 +1,5 @@ //> using options -preview -package scala // @preview in private[scala] +package scala // @preview is private[scala] import scala.annotation.internal.preview @preview def previewFeature = 42 diff --git a/tests/neg/previewOverloads.scala b/tests/neg/previewOverloads.scala index 4bfe3bb0bc70..e324bc535772 100644 --- a/tests/neg/previewOverloads.scala +++ b/tests/neg/previewOverloads.scala @@ -1,4 +1,4 @@ -package scala // @preview in private[scala] +package scala // @preview is private[scala] import scala.annotation.internal.preview diff --git a/tests/neg/previewOverride.scala b/tests/neg/previewOverride.scala index ff87157e6837..4a772506f7b6 100644 --- a/tests/neg/previewOverride.scala +++ b/tests/neg/previewOverride.scala @@ -1,4 +1,4 @@ -package scala // @preview in private[scala] +package scala // @preview is private[scala] import scala.annotation.internal.preview diff --git a/tests/pos/preview-flag.scala b/tests/pos/preview-flag.scala index 94d482a97ae7..dbcfcaaff5b3 100644 --- a/tests/pos/preview-flag.scala +++ b/tests/pos/preview-flag.scala @@ -1,5 +1,5 @@ //> using options -preview -package scala // @preview in private[scala] +package scala // @preview is private[scala] import scala.annotation.internal.preview @preview def previewDef: Int = 42 From 18ac6919b3677da332e4a972e5ad9c673aea739b Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Feb 2025 17:56:28 +0100 Subject: [PATCH 5/6] Fix typos and address review sugesions --- compiler/src/dotty/tools/dotc/config/Feature.scala | 2 +- .../reference/other-new-features/preview-defs.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index c6dbd31dc9bb..b726f0e33544 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -264,7 +264,7 @@ object Feature: if previewSym.exists then i"$previewSym is marked @preview$msg" else i"$sym inherits @preview$msg" - report.error(markedPreview + "\n\n" + previewUseSite("definition"), srcPos) + report.error(i"${markedPreview}\n\n${previewUseSite("definition")}", srcPos) private def previewUseSite(which: String): String = s"Preview $which may only be used when compiling with the `-preview` compiler flag" diff --git a/docs/_docs/reference/other-new-features/preview-defs.md b/docs/_docs/reference/other-new-features/preview-defs.md index 4cd4c92b27c8..3a8c8c214c9e 100644 --- a/docs/_docs/reference/other-new-features/preview-defs.md +++ b/docs/_docs/reference/other-new-features/preview-defs.md @@ -6,16 +6,16 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/previ New Scala language features or standard library APIs are initially introduced as experimental, but once they become fully implemented and acceppted by the [SIP](https://docs.scala-lang.org/sips/) these can become a preview features. Preview language features and APIs are guaranteed to be standarized in some next Scala minor release, but allow compiler team to introduce small, possibly binary incompatible, changes based on the community feedback. -These can be used by early adopters who can accept possibility of binary compatibility breakage. As an example these can be used for project internal tools and applications, but are discouraged to be used by libraries. +These can be used by early adopters who can accept the possibility of binary compatibility breakage. For instance, preview features could be used in some internal tool or application. On the other hand, preview features are discouraged in publicly available libraries. -Users can enable access to preview features and definitions by compiling with `-preview` flag. The flag would enable all preview features and definitions. There is no way for enabling only a subset of preview features. +Users can enable access to preview features and definitions by compiling with the `-preview` flag. The flag would enable all preview features and definitions. There is no scheme for enabling only a subset of preview features. -The biggest difference of preview features when compared with experimental features is their non-viral behaviour. -Any defintion compiled in the preview mode (using `-preview` flag) is not marked as preview defintion itself. +The biggest difference of preview features compared to experimental features is their non-viral behaviour. +A definition compiled in preview mode (using the `-preview` flag) is not marked as a preview definition itself. This behaviour allows to use preview features transitively in other compilation units without explicitlly enabled preview mode, as long as it does not directly reference APIs or features marked as preview. -The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/internal/preview.html) annotations are used to mark Scala 3 standard library APIs currently available under enabled preview mode. -The definitions follows similar rules as the [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) when it comes to accessing, subtyping, overriding or overloading definitions marked with this annotation - all of these can only be performed in compilation unit that enables preview mode. +The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/internal/preview.html) annotation is used to mark Scala 3 standard library APIs currently available under preview mode. +The rules for `@preview` are similar to [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) when it comes to accessing, subtyping, overriding or overloading definitions marked with this annotation - all of these can only be performed in compilation units that enable preview mode. ```scala //> using options -preview From 12526fb37be58bd8bacfacee659db645ed2f03bb Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 20 Feb 2025 17:59:43 +0100 Subject: [PATCH 6/6] Fix more typos --- .../_docs/reference/other-new-features/preview-defs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/_docs/reference/other-new-features/preview-defs.md b/docs/_docs/reference/other-new-features/preview-defs.md index 3a8c8c214c9e..3fc25bc48f9e 100644 --- a/docs/_docs/reference/other-new-features/preview-defs.md +++ b/docs/_docs/reference/other-new-features/preview-defs.md @@ -4,15 +4,15 @@ title: "Preview Definitions" nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/preview-defs.html --- -New Scala language features or standard library APIs are initially introduced as experimental, but once they become fully implemented and acceppted by the [SIP](https://docs.scala-lang.org/sips/) these can become a preview features. -Preview language features and APIs are guaranteed to be standarized in some next Scala minor release, but allow compiler team to introduce small, possibly binary incompatible, changes based on the community feedback. +New Scala language features or standard library APIs are initially introduced as experimental, but once they become fully implemented and accepted by the [SIP](https://docs.scala-lang.org/sips/) these can become a preview features. +Preview language features and APIs are guaranteed to be standardized in some next Scala minor release, but allow the compiler team to introduce small, possibly binary incompatible, changes based on the community feedback. These can be used by early adopters who can accept the possibility of binary compatibility breakage. For instance, preview features could be used in some internal tool or application. On the other hand, preview features are discouraged in publicly available libraries. Users can enable access to preview features and definitions by compiling with the `-preview` flag. The flag would enable all preview features and definitions. There is no scheme for enabling only a subset of preview features. -The biggest difference of preview features compared to experimental features is their non-viral behaviour. +The biggest difference of preview features compared to experimental features is their non-viral behavior. A definition compiled in preview mode (using the `-preview` flag) is not marked as a preview definition itself. -This behaviour allows to use preview features transitively in other compilation units without explicitlly enabled preview mode, as long as it does not directly reference APIs or features marked as preview. +This behavior allows to use preview features transitively in other compilation units without explicitly enabled preview mode, as long as it does not directly reference APIs or features marked as preview. The [`@preview`](https://scala-lang.org/api/3.x/scala/annotation/internal/preview.html) annotation is used to mark Scala 3 standard library APIs currently available under preview mode. The rules for `@preview` are similar to [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) when it comes to accessing, subtyping, overriding or overloading definitions marked with this annotation - all of these can only be performed in compilation units that enable preview mode. @@ -30,5 +30,5 @@ def usePreviewFeature = previewFeature ```scala def usePreviewFeatureTransitively = scala.stdlib.usePreviewFeature -def usePreviewFeatureDirectly = scala.stdlib.previewFeature // error - refering to preview definition outside preview scope +def usePreviewFeatureDirectly = scala.stdlib.previewFeature // error - referring to preview definition outside preview scope ```