Skip to content

Commit 7939ddb

Browse files
authored
Merge pull request #14267 from dotty-staging/disallow-bottom-types-in-erased-implementations
Disallow bottom types in erased implementations
2 parents eed6820 + 51f60b6 commit 7939ddb

26 files changed

+48
-37
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)

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,8 +1765,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
17651765
untpd.ValDef(
17661766
EvidenceParamName.fresh(),
17671767
untpd.TypeTree(defn.CanThrowClass.typeRef.appliedTo(tp)),
1768-
untpd.ref(defn.Predef_undefined))
1769-
.withFlags(Given | Final | Lazy | Erased)
1768+
untpd.ref(defn.Compiletime_erasedValue))
1769+
.withFlags(Given | Final | Erased)
17701770
.withSpan(expr.span)
17711771
val caughtExceptions =
17721772
if Feature.enabled(Feature.saferExceptions) then

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

0 commit comments

Comments
 (0)