Skip to content

Commit 0519c9c

Browse files
committed
Fix handling of compiletime.erasedValue
Fixes #23406
1 parent b3642c0 commit 0519c9c

File tree

8 files changed

+66
-9
lines changed

8 files changed

+66
-9
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,11 @@ class Definitions {
15581558
@tu lazy val pureSimpleClasses =
15591559
Set(StringClass, NothingClass, NullClass) ++ ScalaValueClasses()
15601560

1561+
@tu lazy val capsErasedValueMethods =
1562+
Set[Symbol]()
1563+
@tu lazy val erasedValueMethods =
1564+
capsErasedValueMethods + Compiletime_erasedValue
1565+
15611566
@tu lazy val AbstractFunctionType: Array[TypeRef] = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0).asInstanceOf[Array[TypeRef]]
15621567
val AbstractFunctionClassPerRun: PerRun[Array[Symbol]] = new PerRun(AbstractFunctionType.map(_.symbol.asClass))
15631568
def AbstractFunctionClass(n: Int)(using Context): Symbol = AbstractFunctionClassPerRun()(using ctx)(n)

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,7 @@ object SymDenotations {
10541054
def isEffectivelyErased(using Context): Boolean =
10551055
isOneOf(EffectivelyErased)
10561056
|| is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)
1057+
|| defn.erasedValueMethods.contains(symbol)
10571058

10581059
/** Is this a member that will become public in the generated binary */
10591060
def hasPublicInBinary(using Context): Boolean =

compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ class InlineReducer(inliner: Inliner)(using Context):
341341
val scrutineeSym = newSym(InlineScrutineeName.fresh(), Synthetic, scrutType).asTerm
342342
val scrutineeBinding = normalizeBinding(ValDef(scrutineeSym, scrutinee))
343343

344+
// If scrutinee has embedded `compiletime.erasedValue[T]` expressions, convert them to
345+
// mark scrutineeSym as Erased. This means that the scrutinee cannot be referenced in
346+
// the reduced term. It is NOT checked that scrutinee is a pure expression, since
347+
// there is a special case in Erase that exempts the RHS of an erased scrutinee definition.
348+
if scrutinee.existsSubTree:
349+
case tree @ TypeApply(fn, args) => tree.symbol == defn.Compiletime_erasedValue
350+
case _ => false
351+
then
352+
scrutineeSym.setFlag(Erased)
353+
354+
344355
def reduceCase(cdef: CaseDef): MatchReduxWithGuard = {
345356
val caseBindingMap = new mutable.ListBuffer[(Symbol, MemberDef)]()
346357

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2950,14 +2950,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29502950
EmptyTree
29512951

29522952
def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = if !sym.info.exists then retractDefDef(sym) else ctx.profiler.onTypedDef(sym) {
2953-
2954-
// TODO: - Remove this when `scala.language.experimental.erasedDefinitions` is no longer experimental.
2955-
// - Modify signature to `erased def erasedValue[T]: T`
2956-
if sym.eq(defn.Compiletime_erasedValue) then
2957-
// scala.compiletime.erasedValue should be `erased` but we cannot add this in the source.
2958-
// The library cannot use experimental language features,
2959-
// hence we special case it until `erased` is no longer experimental.
2960-
sym.setFlag(Erased)
29612953
val DefDef(name, paramss, tpt, _) = ddef
29622954
checkNonRootName(ddef.name, ddef.nameSpan)
29632955
completeAnnotations(ddef, sym)

library/src/scala/compiletime/package.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import annotation.{compileTimeOnly, experimental}
2323
* the branches.
2424
* @syntax markdown
2525
*/
26-
// TODO add `erased` once it is not an experimental feature anymore
2726
def erasedValue[T]: T = erasedValue[T]
2827

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

tests/neg/i23406.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
inline def funny[T]: String =
2+
inline compiletime.erasedValue[T] match
3+
case x: String => x
4+
5+
@main def Test = funny[String] // error

tests/pending/neg/erased-impure.check

Whitespace-only changes.

tests/pending/neg/erased-impure.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//> using options -explain
2+
import language.experimental.erasedDefinitions
3+
import java.io.IOException
4+
import caps.unsafe.unsafeErasedValue
5+
6+
class CanThrow[-E <: Exception]
7+
8+
def foo[E <: Exception](e: E)(using erased CanThrow[E]): Nothing = throw e
9+
10+
erased val magic1: IOException = ??? // error
11+
erased val magic2: IOException = compiletime.erasedValue[IOException] // error
12+
erased val magic3: IOException = null.asInstanceOf[IOException] // error
13+
14+
inline def inlineId[T](x: T) = x
15+
16+
class C()
17+
18+
def testPure[T](erased x: T): Unit = ()
19+
20+
case class Pair[A, B](x: A, y: B)
21+
object Pair:
22+
def apply(x: Int): Pair2[Int, Int] =
23+
println("Pair2")
24+
Pair2(x, x + 1)
25+
26+
case class Box[A](x: A):
27+
println(x)
28+
29+
def Test =
30+
foo(new IOException)(using ???) // error
31+
foo(new IOException)(using inlineId(???)) // error
32+
33+
testPure(C()) // OK
34+
testPure(inlineId(C())) // OK
35+
testPure(identity(C())) // error, identity is not an inline function
36+
37+
testPure(Pair(unsafeErasedValue[Int], unsafeErasedValue[String])) // OK
38+
testPure(Pair(unsafeErasedValue[Int])) // error
39+
testPure(Box(unsafeErasedValue[Int])) // error
40+
41+
42+
43+
44+

0 commit comments

Comments
 (0)