@@ -24,6 +24,7 @@ import reporting._
2424import scala .util .matching .Regex ._
2525import Constants .Constant
2626import NullOpsDecorator ._
27+ import dotty .tools .dotc .config .Feature
2728
2829object RefChecks {
2930 import tpd ._
@@ -212,6 +213,7 @@ object RefChecks {
212213 * 1.9. If M is erased, O is erased. If O is erased, M is erased or inline.
213214 * 1.10. If O is inline (and deferred, otherwise O would be final), M must be inline
214215 * 1.11. If O is a Scala-2 macro, M must be a Scala-2 macro.
216+ * 1.12. If O is non-experimental, M must be non-experimental.
215217 * 2. Check that only abstract classes have deferred members
216218 * 3. Check that concrete classes do not have deferred definitions
217219 * that are not implemented in a subclass.
@@ -477,6 +479,8 @@ object RefChecks {
477479 overrideError(i " needs to be declared with @targetName( ${" \" " }${other.targetName}${" \" " }) so that external names match " )
478480 else
479481 overrideError(" cannot have a @targetName annotation since external names would be different" )
482+ else if ! other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot ) then // (1.12)
483+ overrideError(" may not override non-experimental member" )
480484 else
481485 checkOverrideDeprecated()
482486 }
@@ -924,6 +928,7 @@ object RefChecks {
924928 // arbitrarily choose one as more important than the other.
925929 private def checkUndesiredProperties (sym : Symbol , pos : SrcPos )(using Context ): Unit =
926930 checkDeprecated(sym, pos)
931+ checkExperimental(sym, pos)
927932
928933 val xMigrationValue = ctx.settings.Xmigration .value
929934 if xMigrationValue != NoScalaVersion then
@@ -964,6 +969,29 @@ object RefChecks {
964969 val since = annot.argumentConstant(1 ).map(" since " + _.stringValue).getOrElse(" " )
965970 report.deprecationWarning(s " ${sym.showLocated} is deprecated ${since}${msg}" , pos)
966971
972+ private def checkExperimental (sym : Symbol , pos : SrcPos )(using Context ): Unit =
973+ if sym.isExperimental
974+ && ! sym.isConstructor // already reported on the class
975+ && ! ctx.owner.isExperimental // already reported on the @experimental of the owner
976+ && ! sym.is(ModuleClass ) // already reported on the module
977+ && (sym.span.exists || sym != defn.ExperimentalAnnot ) // already reported on inferred annotations
978+ then
979+ Feature .checkExperimentalDef(sym, pos)
980+
981+ private def checkExperimentalSignature (sym : Symbol , pos : SrcPos )(using Context ): Unit =
982+ val checker = new TypeTraverser :
983+ def traverse (tp : Type ): Unit =
984+ if tp.typeSymbol.isExperimental then
985+ Feature .checkExperimentalDef(tp.typeSymbol, pos)
986+ else
987+ traverseChildren(tp)
988+ if ! sym.owner.isExperimental && ! pos.span.isSynthetic then // avoid double errors
989+ checker.traverse(sym.info)
990+
991+ private def checkExperimentalAnnots (sym : Symbol )(using Context ): Unit =
992+ for annot <- sym.annotations if annot.symbol.isExperimental && annot.tree.span.exists do
993+ Feature .checkExperimentalDef(annot.symbol, annot.tree)
994+
967995 /** If @migration is present (indicating that the symbol has changed semantics between versions),
968996 * emit a warning.
969997 */
@@ -1136,6 +1164,15 @@ object RefChecks {
11361164
11371165 end checkImplicitNotFoundAnnotation
11381166
1167+
1168+ /** Check that classes extending experimental classes or nested in experimental classes have the @experimental annotation. */
1169+ private def checkExperimentalInheritance (cls : ClassSymbol )(using Context ): Unit =
1170+ if ! cls.hasAnnotation(defn.ExperimentalAnnot ) then
1171+ cls.info.parents.find(_.typeSymbol.isExperimental) match
1172+ case Some (parent) =>
1173+ report.error(em " extension of experimental ${parent.typeSymbol} must have @experimental annotation " , cls.srcPos)
1174+ case _ =>
1175+ end checkExperimentalInheritance
11391176}
11401177import RefChecks ._
11411178
@@ -1192,6 +1229,8 @@ class RefChecks extends MiniPhase { thisPhase =>
11921229 override def transformValDef (tree : ValDef )(using Context ): ValDef = {
11931230 checkNoPrivateOverrides(tree)
11941231 checkDeprecatedOvers(tree)
1232+ checkExperimentalAnnots(tree.symbol)
1233+ checkExperimentalSignature(tree.symbol, tree)
11951234 val sym = tree.symbol
11961235 if (sym.exists && sym.owner.isTerm) {
11971236 tree.rhs match {
@@ -1212,6 +1251,8 @@ class RefChecks extends MiniPhase { thisPhase =>
12121251 override def transformDefDef (tree : DefDef )(using Context ): DefDef = {
12131252 checkNoPrivateOverrides(tree)
12141253 checkDeprecatedOvers(tree)
1254+ checkExperimentalAnnots(tree.symbol)
1255+ checkExperimentalSignature(tree.symbol, tree)
12151256 checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
12161257 tree
12171258 }
@@ -1224,6 +1265,8 @@ class RefChecks extends MiniPhase { thisPhase =>
12241265 checkCompanionNameClashes(cls)
12251266 checkAllOverrides(cls)
12261267 checkImplicitNotFoundAnnotation.template(cls.classDenot)
1268+ checkExperimentalInheritance(cls)
1269+ checkExperimentalAnnots(cls)
12271270 tree
12281271 }
12291272 catch {
@@ -1268,6 +1311,17 @@ class RefChecks extends MiniPhase { thisPhase =>
12681311 }
12691312 tree
12701313 }
1314+
1315+ override def transformTypeTree (tree : TypeTree )(using Context ): TypeTree = {
1316+ checkExperimental(tree.symbol, tree.srcPos)
1317+ tree
1318+ }
1319+
1320+ override def transformTypeDef (tree : TypeDef )(using Context ): TypeDef = {
1321+ checkExperimental(tree.symbol, tree.srcPos)
1322+ checkExperimentalAnnots(tree.symbol)
1323+ tree
1324+ }
12711325}
12721326
12731327/* todo: rewrite and re-enable
0 commit comments