Skip to content

Commit ccb6eef

Browse files
committed
Require erased values to be pure expressions
1 parent 1d453d6 commit ccb6eef

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

+91
-94
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
229229
case PointlessAppliedConstructorTypeID // errorNumber: 213
230230
case IllegalContextBoundsID // errorNumber: 214
231231
case NamedPatternNotApplicableID // errorNumber: 215
232+
case ErasedNotPureID // errornumber 216
232233

233234
def errorNumber = ordinal - 1
234235

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3538,4 +3538,36 @@ final class NamedPatternNotApplicable(selectorType: Type)(using Context) extends
35383538
override protected def msg(using Context): String =
35393539
i"Named patterns cannot be used with $selectorType, because it is not a named tuple or case class"
35403540

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

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)