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

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

tests/neg/safeThrowsStrawman.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def bar: Int raises Exception =
2121

2222
@main def Test =
2323
try
24-
erased given CanThrow[Fail] = ???
24+
erased given CanThrow[Fail] = compiletime.erasedValue
2525
println(foo(true))
2626
println(foo(false))
2727
println(bar) // error

tests/neg/safeThrowsStrawman2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def bar(x: Boolean)(using CanThrow[Fail]): Int =
2020

2121
@main def Test =
2222
try
23-
given ctf: CanThrow[Fail] = ???
23+
given ctf: CanThrow[Fail] = new CanThrow[Fail]
2424
val x = new CanThrow[Fail]() // OK, x is erased
2525
val y: Any = new CanThrow[Fail]() // error: illegal reference to erased class CanThrow
2626
val y2: Any = new CTF() // error: illegal reference to erased class CanThrow

tests/pos-custom-args/inline-match-gadt.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object `inline-match-gadt` {
22
class Exactly[T]
3-
erased def exactType[T]: Exactly[T] = ???
3+
erased def exactType[T]: Exactly[T] = compiletime.erasedValue
44

55
inline def foo[T](t: T): T =
66
inline exactType[T] match {

tests/pos-custom-args/phantom-Eq.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ object EqUtil {
2020
extension [T](x: T)
2121
def ===[U](y: U)(using erased PhantomEq[T, U]) = x.equals(y)
2222

23-
erased given eqString: PhantomEqEq[String] = ???
24-
erased given eqInt: PhantomEqEq[Int] = ???
25-
erased given eqDouble: PhantomEqEq[Double] = ???
23+
erased given eqString: PhantomEqEq[String] = compiletime.erasedValue
24+
erased given eqInt: PhantomEqEq[Int] = compiletime.erasedValue
25+
erased given eqDouble: PhantomEqEq[Double] = compiletime.erasedValue
2626

27-
erased given eqByteNum: PhantomEq[Byte, Number] = ???
28-
erased given eqNumByte: PhantomEq[Number, Byte] = ???
27+
erased given eqByteNum: PhantomEq[Byte, Number] = compiletime.erasedValue
28+
erased given eqNumByte: PhantomEq[Number, Byte] = compiletime.erasedValue
2929

30-
erased given eqSeq[T, U](using erased PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = ???
30+
erased given eqSeq[T, U](using erased PhantomEq[T, U]): PhantomEq[Seq[T], Seq[U]] = compiletime.erasedValue
3131
}

tests/pos-custom-args/phantom-Evidence.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ object WithNormalState {
2424

2525
object Utils {
2626
type =::=[From, To]
27-
erased given tpEquals[A]: A =::= A = ???
27+
erased given tpEquals[A]: A =::= A = compiletime.erasedValue
2828
}

tests/pos/i11864.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final class CallbackTo[+A] {
4040
object CallbackTo {
4141

4242
type MapGuard[A] = { type Out = A }
43-
erased given MapGuard[A]: MapGuard[A] = ???
43+
erased given MapGuard[A]: MapGuard[A] = compiletime.erasedValue
4444

4545
def traverse[A, B](ta: List[A]): CallbackTo[List[B]] =
4646
val x: CallbackTo[List[A] => List[B]] = ???

tests/pos/i13392.scala

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

99
@experimental
1010
object unsafeExceptions:
11-
given canThrowAny: CanThrow[Exception] = ???
11+
given canThrowAny: CanThrow[Exception] = new CanThrow

tests/run-custom-args/generic-tuples.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class HNil extends Tuple
77
case object HNil extends HNil
88

99
trait Pair[H, T <: Tuple] {
10-
erased inline def size = ???
10+
erased inline def size = compiletime.erasedValue
1111
}
1212
}
1313

tests/run-custom-args/phantom-OnHList.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,6 @@ object Appender {
8888

8989
object PhantomAppender {
9090
type Aux[L1 <: HList, L2 <: HList, O <: HList]
91-
implicit erased def caseHNil[L <: HList]: Aux[HNil, L, L] = ???
92-
implicit erased def caseHCons[H, T <: HList, L <: HList, O <: HList] (using erased p: Aux[T, L, O]): Aux[H :: T, L, H :: O] = ???
91+
implicit erased def caseHNil[L <: HList]: Aux[HNil, L, L] = compiletime.erasedValue
92+
implicit erased def caseHCons[H, T <: HList, L <: HList, O <: HList] (using erased p: Aux[T, L, O]): Aux[H :: T, L, H :: O] = compiletime.erasedValue
9393
}

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

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

125125
// An algebraic datatype

tests/run-custom-args/typeclass-derivation2c.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ object Deriving {
2424
/** The number of cases in the sum.
2525
* Implemented by an inline method in concrete subclasses.
2626
*/
27-
erased def numberOfCases: Int = ???
27+
erased def numberOfCases: Int = compiletime.erasedValue
2828

2929
/** The Generic representations of the sum's alternatives.
3030
* Implemented by an inline method in concrete subclasses.
3131
*/
32-
erased def alternative(n: Int): Generic[_ <: T] = ???
32+
erased def alternative(n: Int): Generic[_ <: T] = compiletime.erasedValue
3333
}
3434

3535
/** The Generic for a product type */

tests/run-custom-args/typelevel-defaultValue.scala

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

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

66
object Test extends App {

tests/run/i13691.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ erased class Foo
55
class Bar
66

77
object unsafeExceptions:
8-
given canThrowAny: CanThrow[Exception] = null
8+
given canThrowAny: CanThrow[Exception] = new CanThrow
99

1010
object test1:
1111
trait Decoder[+T]:
@@ -44,7 +44,7 @@ object test5:
4444

4545
@main def Test(): Unit =
4646
import unsafeExceptions.canThrowAny
47-
given Foo = ???
47+
given Foo = Foo()
4848
given Bar = Bar()
4949
test1.deco.apply().apply
5050
test2.deco.apply().apply

tests/run/safeThrowsStrawman.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ def baz: Int raises Exception = foo(false)
1919

2020
@main def Test =
2121
try
22-
given CanThrow[Fail] = ???
22+
given CanThrow[Fail] = new CanThrow
2323
println(foo(true))
2424
println(foo(false))
2525
catch case ex: Fail =>
2626
println("failed")
2727
try
28-
given CanThrow[Exception] = ???
28+
given CanThrow[Exception] = new CanThrow
2929
println(baz)
3030
catch case ex: Fail =>
3131
println("failed")

tests/run/safeThrowsStrawman2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ object scalax:
1818
def try2[R, E <: Exception](body: => R raises E)(c: E => Unit)(f: => Unit): R =
1919
val res = new Result[R]
2020
try
21-
given CanThrow[E] = ???
21+
given CanThrow[E] = new CanThrow
2222
res.value = body
2323
catch c.asInstanceOf[Throwable => Unit]
2424
finally f

0 commit comments

Comments
 (0)