diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index e1c137377c2b..4852eaba9334 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -170,9 +170,8 @@ object Feature: ctx.settings.experimental.value || experimentalAutoEnableFeatures.exists(enabled) - def isExperimentalEnabledBySetting(using Context): Boolean = - ctx.settings.experimental.value || - experimentalAutoEnableFeatures.exists(enabledBySetting) + def experimentalEnabledByLanguageSetting(using Context): Option[TermName] = + experimentalAutoEnableFeatures.find(enabledBySetting) def isExperimentalEnabledByImport(using Context): Boolean = experimentalAutoEnableFeatures.exists(enabledByImport) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 82baebfc45f4..8b9d4c166294 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -416,7 +416,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => ) } case tree: ValDef => - annotateExperimental(tree.symbol) + annotateExperimentalCompanion(tree.symbol) registerIfHasMacroAnnotations(tree) checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) @@ -425,7 +425,6 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => checkStableSelection(tree.rhs) processValOrDefDef(super.transform(tree1)) case tree: DefDef => - annotateExperimental(tree.symbol) registerIfHasMacroAnnotations(tree) checkErasedDef(tree) Checking.checkPolyFunctionType(tree.tpt) @@ -437,7 +436,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => val sym = tree.symbol if (sym.isClass) VarianceChecker.check(tree) - annotateExperimental(sym) + annotateExperimentalCompanion(sym) checkMacroAnnotation(sym) if sym.isOneOf(GivenOrImplicit) then sym.keepAnnotationsCarrying(thisPhase, Set(defn.CompanionClassMetaAnnot), orNoneOf = defn.MetaAnnots) @@ -583,16 +582,9 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => else if tpe.derivesFrom(defn.NullClass) then report.error("`erased` definition cannot be implemented with en expression of type Null", tree.srcPos) - private def annotateExperimental(sym: Symbol)(using Context): Unit = - def isTopLevelDefinitionInSource(sym: Symbol) = - !sym.is(Package) && !sym.name.isPackageObjectName && - (sym.owner.is(Package) || (sym.owner.isPackageObject && !sym.isConstructor)) + private def annotateExperimentalCompanion(sym: Symbol)(using Context): Unit = if sym.is(Module) then ExperimentalAnnotation.copy(sym.companionClass).foreach(sym.addAnnotation) - if !sym.hasAnnotation(defn.ExperimentalAnnot) - && Feature.isExperimentalEnabledBySetting && isTopLevelDefinitionInSource(sym) - then - sym.addAnnotation(ExperimentalAnnotation("Added by -experimental or -language:experimental.*", sym.span)) // It needs to run at the phase of the postTyper --- otherwise, the test of the symbols will use // the transformed denotation with added `Serializable` and `AbstractFunction1`. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index fd3e9a733f50..5ab8ab63e873 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -841,20 +841,23 @@ object Checking { } if ctx.owner.is(Package) || ctx.owner.name.startsWith(str.REPL_SESSION_LINE) then + def markTopLevelDefsAsExperimental(why: String): Unit = + for tree <- nonExperimentalStats(trees) do + tree match + case tree: MemberDef => + val sym = tree.symbol + if !sym.isExperimental then + sym.addAnnotation(ExperimentalAnnotation(s"Added by $why", sym.span)) + case tree => + // There is no definition to attach the @experimental annotation + report.error(s"Implementation restriction: top-level `val _ = ...` is not supported with $why.", tree.srcPos) unitExperimentalLanguageImports match - case imp :: _ => - // mark all top-level definitions as @experimental - for tree <- nonExperimentalStats(trees) do - tree match - case tree: MemberDef => - // TODO move this out of checking (into posttyper?) - val sym = tree.symbol - if !sym.isExperimental then - sym.addAnnotation(ExperimentalAnnotation(i"Added by top level $imp", sym.span)) - case tree => - // There is no definition to attach the @experimental annotation - report.error("Implementation restriction: top-level `val _ = ...` is not supported with experimental language imports.", tree.srcPos) + case imp :: _ => markTopLevelDefsAsExperimental(i"top level $imp") case _ => + Feature.experimentalEnabledByLanguageSetting match + case Some(sel) => markTopLevelDefsAsExperimental(i"-language:experimental.$sel") + case _ if ctx.settings.experimental.value => markTopLevelDefsAsExperimental(i"-experimental") + case _ => else for imp <- unitExperimentalLanguageImports do Feature.checkExperimentalFeature("feature local import", imp.srcPos) diff --git a/tests/neg-macros/i18677-a.check b/tests/neg-macros/i18677-a.check index 07f0a66cec81..d190ce36318a 100644 --- a/tests/neg-macros/i18677-a.check +++ b/tests/neg-macros/i18677-a.check @@ -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("Added by -experimental or -language: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("Added by -experimental or -language: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 diff --git a/tests/neg-macros/i18677-b.check b/tests/neg-macros/i18677-b.check index 28cbc0521c30..eca2bdcde726 100644 --- a/tests/neg-macros/i18677-b.check +++ b/tests/neg-macros/i18677-b.check @@ -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("Added by -experimental or -language: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("Added by -experimental or -language: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 diff --git a/tests/neg/experimental-import-with-top-level-val-underscore.check b/tests/neg/experimental-import-with-top-level-val-underscore.check index 7d595949cdeb..505492583abb 100644 --- a/tests/neg/experimental-import-with-top-level-val-underscore.check +++ b/tests/neg/experimental-import-with-top-level-val-underscore.check @@ -1,6 +1,6 @@ -- Error: tests/neg/experimental-import-with-top-level-val-underscore.scala:4:4 ---------------------------------------- 4 |val _ = // error | ^ - | Implementation restriction: top-level `val _ = ...` is not supported with experimental language imports. + |Implementation restriction: top-level `val _ = ...` is not supported with top level import language.experimental.erasedDefinitions. 5 | println("Hello, world!") 6 | 42 diff --git a/tests/neg/experimental-message-experimental-flag.check b/tests/neg/experimental-message-experimental-flag.check index 8d2fdb340c75..69174eaa789f 100644 --- a/tests/neg/experimental-message-experimental-flag.check +++ b/tests/neg/experimental-message-experimental-flag.check @@ -2,7 +2,7 @@ -- 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 or -language:experimental.* + | 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