Skip to content

Commit 51f60b6

Browse files
committed
Disallow bottom types in erased implementations
1 parent 8da6ba7 commit 51f60b6

25 files changed

+46
-35
lines changed

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
352352
)
353353
}
354354
case tree: ValDef =>
355+
checkErasedDef(tree)
355356
val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
356357
processValOrDefDef(super.transform(tree1))
357358
case tree: DefDef =>
359+
checkErasedDef(tree)
358360
annotateContextResults(tree)
359361
val tree1 = cpy.DefDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
360362
processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef]))
@@ -464,6 +466,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
464466
private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(using Context) =
465467
if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs
466468

469+
private def checkErasedDef(tree: ValOrDefDef)(using Context): Unit =
470+
if tree.symbol.is(Erased, butNot = Macro) then
471+
val tpe = tree.rhs.tpe
472+
if tpe.derivesFrom(defn.NothingClass) then
473+
report.error("`erased` definition cannot be implemented with en expression of type Nothing", tree.srcPos)
474+
else if tpe.derivesFrom(defn.NullClass) then
475+
report.error("`erased` definition cannot be implemented with en expression of type Null", tree.srcPos)
476+
467477
private def annotateExperimental(sym: Symbol)(using Context): Unit =
468478
if sym.is(Module) && sym.companionClass.hasAnnotation(defn.ExperimentalAnnot) then
469479
sym.addAnnotation(defn.ExperimentalAnnot)

docs/docs/reference/experimental/canthrow.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ catch
120120
the compiler generates an accumulated capability of type `CanThrow[Ex1 | ... | Ex2]` that is available as a given in the scope of `body`. It does this by augmenting the `try` roughly as follows:
121121
```scala
122122
try
123-
erased given CanThrow[Ex1 | ... | ExN] = ???
123+
erased given CanThrow[Ex1 | ... | ExN] = compiletime.erasedValue
124124
body
125125
catch ...
126126
```
@@ -196,7 +196,7 @@ Everything typechecks and works as expected. But wait - we have called `map` wit
196196
// compiler-generated code
197197
@main def test(xs: Double*) =
198198
try
199-
erased given ctl: CanThrow[LimitExceeded] = ???
199+
erased given ctl: CanThrow[LimitExceeded] = compiletime.erasedValue
200200
println(xs.map(x => f(x)(using ctl)).sum)
201201
catch case ex: LimitExceeded => println("too large")
202202
```

library/src/scala/CanThrow.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ erased class CanThrow[-E <: Exception]
1212

1313
@experimental
1414
object unsafeExceptions:
15-
given canThrowAny: CanThrow[Exception] = ???
15+
given canThrowAny: CanThrow[Exception] = compiletime.erasedValue
1616

library/src/scala/compiletime/package.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import annotation.compileTimeOnly
2424
* @syntax markdown
2525
*/
2626
// TODO add `erased` once it is not an experimental feature anymore
27-
def erasedValue[T]: T = ???
27+
def erasedValue[T]: T = erasedValue[T]
2828

2929
/** Used as the initializer of a mutable class or object field, like this:
3030
*

tests/invalid/neg/typelevel-erased-leak.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
object typelevel {
3-
erased def erasedValue[T]: T = ???
3+
erased def erasedValue[T]: T = compiletime.erasedValue
44
}
55

66
object Test {

tests/invalid/run/Tuple.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import annotation.showAsInfix
22

33
// This version of Tuple requires full retyping of untyped trees on inlining
44
object typelevel {
5-
erased def erasedValue[T]: T = ???
5+
erased def erasedValue[T]: T = compiletime.erasedValue
66
class Typed[T](val value: T) { type Type = T }
77
}
88

tests/neg-custom-args/typeclass-derivation2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ object TypeLevel {
117117
type Subtype[t] = Type[_, t]
118118
type Supertype[t] = Type[t, _]
119119
type Exactly[t] = Type[t, t]
120-
erased def typeOf[T]: Type[T, T] = ???
120+
erased def typeOf[T]: Type[T, T] = compiletime.erasedValue
121121
}
122122

123123
// An algebraic datatype

tests/neg/erased-class.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import language.experimental.erasedDefinitions
2+
import scala.annotation.compileTimeOnly
23
erased class AA
34
erased class BB extends AA // ok
45

56
@main def Test =
6-
val f1: Array[AA] = ??? // error
7-
def f2(x: Int): Array[AA] = ??? // error
8-
def bar: AA = ??? // ok
9-
val baz: AA = ??? // ok
7+
val f1: Array[AA] = compiletime.erasedValue // error // error
8+
def f2(x: Int): Array[AA] = compiletime.erasedValue // error // error
9+
def bar: AA = compiletime.erasedValue // ok
10+
val baz: AA = compiletime.erasedValue // ok

0 commit comments

Comments
 (0)