Skip to content

Commit

Permalink
Allow experimental erased in experimental scopes
Browse files Browse the repository at this point in the history
Closes #13392
  • Loading branch information
nicolasstucki committed Aug 30, 2021
1 parent 9696219 commit 791aeb3
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 3 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3115,7 +3115,7 @@ object Parsers {
case Some(prefix) =>
in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol)
if prefix == nme.experimental
&& selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros)
&& selectors.exists(sel => Feature.experimental(sel.name) != Feature.scala2macros && Feature.experimental(sel.name) != Feature.erasedDefinitions)
then
Feature.checkExperimentalFeature("features", imp.srcPos)
for
Expand Down
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import typer.RefChecks
import MegaPhase.MiniPhase
import StdNames.nme
import ast.tpd
import SymUtils._
import config.Feature

/** This phase makes all erased term members of classes private so that they cannot
* conflict with non-erased members. This is needed so that subsequent phases like
Expand Down Expand Up @@ -39,13 +41,22 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
else cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree))

override def transformValDef(tree: ValDef)(using Context): Tree =
checkErasedInExperimental(tree.symbol)
if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree
else cpy.ValDef(tree)(rhs = trivialErasedTree(tree.rhs))

override def transformDefDef(tree: DefDef)(using Context): Tree =
checkErasedInExperimental(tree.symbol)
if !tree.symbol.isEffectivelyErased || tree.rhs.isEmpty then tree
else cpy.DefDef(tree)(rhs = trivialErasedTree(tree.rhs))

override def transformTypeDef(tree: TypeDef)(using Context): Tree =
checkErasedInExperimental(tree.symbol)
tree

def checkErasedInExperimental(sym: Symbol)(using Context): Unit =
if sym.is(Erased) && sym != defn.Compiletime_erasedValue && !sym.isInExperimentalScope then
Feature.checkExperimentalFeature("erased", sym.sourcePos)
}

object PruneErasedDefs {
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/reference/experimental/erased-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ title: "Erased Definitions"
import scala.language.experimental.erasedDefinitions
```
or by setting the command line option `-language:experimental.erasedDefinitions`.
Erased definitions must be in an experimental scope (see [../other-new-features/experimental-defs.md]).

## Why erased terms?

Let's describe the motivation behind erased terms with an example. In the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import language.experimental.erasedDefinitions // error
import language.experimental.erasedDefinitions
import annotation.experimental

@experimental
erased class CanThrow[-E <: Exception]

erased class CanThrow2[-E <: Exception] // error

def other = 1
2 changes: 1 addition & 1 deletion tests/neg-custom-args/no-experimental/experimental.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Test0 {
}

class Test1 {
import scala.language.experimental.erasedDefinitions // error
import scala.language.experimental.erasedDefinitions
import scala.compiletime.erasedValue
type UnivEq[A]
object UnivEq:
Expand Down
22 changes: 22 additions & 0 deletions tests/neg-custom-args/no-experimental/experimentalErased.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import language.experimental.erasedDefinitions
import annotation.experimental

@experimental
erased class Foo

erased class Bar // error

@experimental
erased def foo = 2

erased def bar = 2 // error

@experimental
erased val foo2 = 2

erased val bar2 = 2 // error

@experimental
def foo3(erased a: Int) = 2

def bar3(erased a: Int) = 2 // error
22 changes: 22 additions & 0 deletions tests/pos/experimentalErased.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import language.experimental.erasedDefinitions
import annotation.experimental

@experimental
erased class Foo

erased class Bar

@experimental
erased def foo = 2

erased def bar = 2

@experimental
erased val foo2 = 2

erased val bar2 = 2

@experimental
def foo3(erased a: Int) = 2

def bar3(erased a: Int) = 2
11 changes: 11 additions & 0 deletions tests/pos/i13392.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package scala
import language.experimental.erasedDefinitions
import annotation.{implicitNotFound, experimental}

@experimental
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - A using clause `(using CanThrow[${E}])`\n - A `throws` clause in a result type such as `X throws ${E}`\n - an enclosing `try` that catches ${E}")
erased class CanThrow[-E <: Exception]

@experimental
object unsafeExceptions:
given canThrowAny: CanThrow[Exception] = ???

0 comments on commit 791aeb3

Please sign in to comment.