Skip to content

Commit

Permalink
Refactor the way we mark experimental top-level definition
Browse files Browse the repository at this point in the history
Move similar logic from PostTyper into `checkAndAdaptExperimentalImports`.
Also make the message of `@experimental` more precise for experimental
language settings.
  • Loading branch information
nicolasstucki committed Apr 18, 2024
1 parent ec0b92c commit ac64505
Show file tree
Hide file tree
Showing 7 changed files with 26 additions and 32 deletions.
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 3 additions & 11 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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`.
Expand Down
27 changes: 15 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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("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
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("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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/neg/experimental-message-experimental-flag.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ac64505

Please sign in to comment.