Skip to content

Commit 69ee4d9

Browse files
committed
Require erased values to be pure expressions
1 parent ea8f4ee commit 69ee4d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+90
-93
lines changed

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
228228
case OnlyFullyDependentAppliedConstructorTypeID // errorNumber: 212
229229
case PointlessAppliedConstructorTypeID // errorNumber: 213
230230
case IllegalContextBoundsID // errorNumber: 214
231+
case ErasedNotPureID // errornumber 215
231232

232233
def errorNumber = ordinal - 1
233234

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3535,3 +3535,35 @@ final class IllegalContextBounds(using Context) extends SyntaxMsg(IllegalContext
35353535
override protected def explain(using Context): String = ""
35363536

35373537
end IllegalContextBounds
3538+
3539+
final class ErasedNotPure(tree: tpd.Tree, isArgument: Boolean, isImplicit: Boolean)(using Context) extends TypeMsg(ErasedNotPureID):
3540+
def what =
3541+
if isArgument then s"${if isImplicit then "implicit " else ""}argument to an erased parameter"
3542+
else "right-hand-side of an erased value"
3543+
override protected def msg(using Context): String =
3544+
i"$what fails to be a pure expression"
3545+
3546+
override protected def explain(using Context): String =
3547+
def alternatives =
3548+
if tree.symbol == defn.Compiletime_erasedValue then
3549+
i"""An accepted (but unsafe) alternative for this expression uses function
3550+
|
3551+
| caps.unsafe.unsafeErasedValue
3552+
|
3553+
|instead."""
3554+
else
3555+
"""A pure expression is an expression that is clearly side-effect free and terminating.
3556+
|Some examples of pure expressions are:
3557+
| - literals,
3558+
| - references to values,
3559+
| - side-effect-free instance creations,
3560+
| - applications of inline functions to pure arguments."""
3561+
3562+
i"""The $what must be a pure expression, but I found:
3563+
|
3564+
| $tree
3565+
|
3566+
|This expression is not classified to be pure.
3567+
|$alternatives"""
3568+
end ErasedNotPure
3569+

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

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import core.Names.*
1414
import core.StdNames.*
1515
import core.NameOps.*
1616
import core.Periods.currentStablePeriod
17-
import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName}
17+
import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName, InlineScrutineeName}
1818
import core.Scopes.newScopeWith
1919
import core.Decorators.*
2020
import core.Constants.*
@@ -583,16 +583,11 @@ object Erasure {
583583
checkNotErasedClass(tree)
584584
end checkNotErased
585585

586-
def checkPureErased(tree: untpd.Tree, isArgument: Boolean)(using Context): Unit =
587-
if false then inContext(preErasureCtx):
588-
if tpd.isPureExpr(tree.asInstanceOf[tpd.Tree]) then
589-
val tree1 = tree.asInstanceOf[tpd.Tree]
590-
println(i"$tree1 is pure, ${tree1.tpe.widen}")
591-
else
592-
def what =
593-
if isArgument then "argument to erased parameter"
594-
else "right-hand-side of erased value"
595-
report.error(em"$what fails to be a pure expression", tree.srcPos)
586+
def checkPureErased(tree: untpd.Tree, isArgument: Boolean, isImplicit: Boolean = false)(using Context): Unit =
587+
val tree1 = tree.asInstanceOf[tpd.Tree]
588+
inContext(preErasureCtx):
589+
if !tpd.isPureExpr(tree1) then
590+
report.error(ErasedNotPure(tree1, isArgument, isImplicit), tree1.srcPos)
596591

597592
private def checkNotErasedClass(tp: Type, tree: untpd.Tree)(using Context): Unit = tp match
598593
case JavaArrayType(et) =>
@@ -861,7 +856,8 @@ object Erasure {
861856
case mt: MethodType if mt.hasErasedParams =>
862857
args.lazyZip(mt.paramErasureStatuses).flatMap: (arg, isErased) =>
863858
if isErased then
864-
checkPureErased(arg, isArgument = true)
859+
checkPureErased(arg, isArgument = true,
860+
isImplicit = mt.isImplicitMethod && arg.span.isSynthetic)
865861
Nil
866862
else
867863
arg :: Nil
@@ -933,9 +929,10 @@ object Erasure {
933929

934930
override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree =
935931
if sym.isEffectivelyErased then
936-
checkPureErased(vdef.rhs, isArgument = false)
932+
if !sym.name.is(InlineScrutineeName) then
933+
checkPureErased(vdef.rhs, isArgument = false)
937934
erasedDef(sym)
938-
else
935+
else trace(i"erasing $vdef"):
939936
checkNotErasedClass(sym.info, vdef)
940937
super.typedValDef(untpd.cpy.ValDef(vdef)(
941938
tpt = untpd.TypedSplice(TypeTree(sym.info).withSpan(vdef.tpt.span))), sym)
@@ -946,7 +943,6 @@ object Erasure {
946943
*/
947944
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree =
948945
if sym.isEffectivelyErased || sym.name.is(BodyRetainerName) then
949-
checkPureErased(ddef.rhs, isArgument = false)
950946
erasedDef(sym)
951947
else
952948
checkNotErasedClass(sym.info.finalResultType, ddef)

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] extends caps.SharedCapability
1212

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

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)