Skip to content

Commit 3557698

Browse files
committed
Drop erased class
Replace with test whether a type derivesFrom Erased.
1 parent 798ced9 commit 3557698

32 files changed

+68
-94
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ class Definitions {
266266
@tu lazy val CompiletimeOpsDoubleModuleClass: Symbol = requiredModule("scala.compiletime.ops.double").moduleClass
267267
@tu lazy val CompiletimeOpsStringModuleClass: Symbol = requiredModule("scala.compiletime.ops.string").moduleClass
268268
@tu lazy val CompiletimeOpsBooleanModuleClass: Symbol = requiredModule("scala.compiletime.ops.boolean").moduleClass
269+
@tu lazy val ErasedClass: ClassSymbol = requiredClass("scala.compiletime.Erased")
269270

270271
/** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter)
271272
* because after erasure the Any and AnyVal references get remapped to the Object methods
@@ -1015,7 +1016,7 @@ class Definitions {
10151016
@tu lazy val Caps_Mutable: ClassSymbol = requiredClass("scala.caps.Mutable")
10161017
@tu lazy val Caps_SharedCapability: ClassSymbol = requiredClass("scala.caps.SharedCapability")
10171018

1018-
@tu lazy val PureClass: Symbol = requiredClass("scala.Pure")
1019+
@tu lazy val PureClass: ClassSymbol = requiredClass("scala.Pure")
10191020

10201021
// Annotation base classes
10211022
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ class TypeUtils:
2323
def isPrimitiveValueType(using Context): Boolean =
2424
self.classSymbol.isPrimitiveValueClass
2525

26-
def isErasedClass(using Context): Boolean =
27-
val cls = self.underlyingClassRef(refinementOK = true).typeSymbol
28-
cls.is(Flags.Erased)
29-
30-
3126
/** Is this type a checked exception? This is the case if the type
3227
* derives from Exception but not from RuntimeException. According to
3328
* that definition Throwable is unchecked. That makes sense since you should

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ object Types extends TypeUtils {
275275
tp.isBottomType
276276
&& (tp.hasClassSymbol(defn.NothingClass)
277277
|| cls != defn.NothingClass && !cls.isValueClass)
278-
def loop(tp: Type): Boolean = tp match {
278+
def loop(tp: Type): Boolean = try tp match
279279
case tp: TypeRef =>
280280
val sym = tp.symbol
281281
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType)
@@ -301,7 +301,7 @@ object Types extends TypeUtils {
301301
cls == defn.ObjectClass
302302
case _ =>
303303
false
304-
}
304+
catch case ex: Throwable => handleRecursive(i"derivesFrom $cls:", show, ex)
305305
loop(this)
306306
}
307307

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

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -579,8 +579,7 @@ object Erasure {
579579
|it should have been processed and eliminated during expansion of an enclosing macro or term erasure."""
580580
report.error(message, tree.srcPos)
581581
case _ => // OK
582-
583-
checkNotErasedClass(tree)
582+
tree
584583
end checkNotErased
585584

586585
def checkPureErased(tree: untpd.Tree, isArgument: Boolean, isImplicit: Boolean = false)(using Context): Unit =
@@ -589,21 +588,6 @@ object Erasure {
589588
if !tpd.isPureExpr(tree1) then
590589
report.error(ErasedNotPure(tree1, isArgument, isImplicit), tree1.srcPos)
591590

592-
private def checkNotErasedClass(tp: Type, tree: untpd.Tree)(using Context): Unit = tp match
593-
case JavaArrayType(et) =>
594-
checkNotErasedClass(et, tree)
595-
case _ =>
596-
if tp.isErasedClass then
597-
val (kind, tree1) = tree match
598-
case tree: untpd.ValOrDefDef => ("definition", tree.tpt)
599-
case tree: untpd.DefTree => ("definition", tree)
600-
case _ => ("expression", tree)
601-
report.error(em"illegal reference to erased ${tp.typeSymbol} in $kind that is not itself erased", tree1.srcPos)
602-
603-
private def checkNotErasedClass(tree: Tree)(using Context): tree.type =
604-
checkNotErasedClass(tree.tpe.widen.finalResultType, tree)
605-
tree
606-
607591
def erasedDef(sym: Symbol)(using Context): Tree =
608592
if sym.isClass then
609593
// We cannot simply drop erased classes, since then they would not generate classfiles
@@ -631,7 +615,7 @@ object Erasure {
631615
* are handled separately by [[typedDefDef]], [[typedValDef]] and [[typedTyped]].
632616
*/
633617
override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(using Context): TypeTree =
634-
checkNotErasedClass(tree.withType(erasure(tree.typeOpt)))
618+
tree.withType(erasure(tree.typeOpt))
635619

636620
/** This override is only needed to semi-erase type ascriptions */
637621
override def typedTyped(tree: untpd.Typed, pt: Type)(using Context): Tree =
@@ -650,7 +634,7 @@ object Erasure {
650634
if (tree.typeOpt.isRef(defn.UnitClass))
651635
tree.withType(tree.typeOpt)
652636
else if (tree.const.tag == Constants.ClazzTag)
653-
checkNotErasedClass(clsOf(tree.const.typeValue))
637+
clsOf(tree.const.typeValue)
654638
else
655639
super.typedLiteral(tree)
656640

@@ -932,7 +916,6 @@ object Erasure {
932916
checkPureErased(vdef.rhs, isArgument = false)
933917
erasedDef(sym)
934918
else trace(i"erasing $vdef"):
935-
checkNotErasedClass(sym.info, vdef)
936919
super.typedValDef(untpd.cpy.ValDef(vdef)(
937920
tpt = untpd.TypedSplice(TypeTree(sym.info).withSpan(vdef.tpt.span))), sym)
938921

@@ -944,7 +927,6 @@ object Erasure {
944927
if sym.isEffectivelyErased || sym.name.is(BodyRetainerName) then
945928
erasedDef(sym)
946929
else
947-
checkNotErasedClass(sym.info.finalResultType, ddef)
948930
val restpe = if sym.isConstructor then defn.UnitType else sym.info.resultType
949931
var vparams = outerParamDefs(sym)
950932
::: ddef.paramss.collect {
@@ -1063,9 +1045,6 @@ object Erasure {
10631045
adaptClosure(implClosure)
10641046
}
10651047

1066-
override def typedNew(tree: untpd.New, pt: Type)(using Context): Tree =
1067-
checkNotErasedClass(super.typedNew(tree, pt))
1068-
10691048
override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree =
10701049
EmptyTree
10711050

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,10 +682,9 @@ object Checking {
682682
if sym.isUpdateMethod && !sym.owner.derivesFrom(defn.Caps_Mutable) then
683683
fail(em"Update methods can only be used as members of classes extending the `Mutable` trait")
684684
val unerasable =
685-
sym.is(Lazy, butNot = Given)
686-
|| sym.is(Method, butNot = Macro)
685+
sym.is(Method, butNot = Macro)
687686
|| sym.is(Mutable)
688-
|| sym.isType && !sym.isClass
687+
|| sym.isType
689688
checkApplicable(Erased, !unerasable)
690689
checkCombination(Final, Open)
691690
checkCombination(Sealed, Open)

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

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,24 +1770,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
17701770
if (mt.isParamDependent)
17711771
report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos)
17721772
// Restart typechecking if there are erased classes that we want to mark erased
1773-
if mt.paramErasureStatuses.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then
1774-
val newParams = params3.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) =>
1775-
if isErasedClass then arg.withAddedFlags(Erased) else arg
1776-
}
1777-
return typedDependent(newParams)
1778-
val core =
1779-
if mt.hasErasedParams then TypeTree(defn.PolyFunctionClass.typeRef)
1780-
else
1781-
val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span)
1782-
val paramTpts = appDef.termParamss.head.map(p => TypeTree(p.tpt.tpe).withSpan(p.tpt.span))
1783-
val funSym = defn.FunctionSymbol(numArgs, isContextual)
1784-
val tycon = TypeTree(funSym.typeRef)
1785-
AppliedTypeTree(tycon, paramTpts :+ resTpt)
1786-
val res = RefinedTypeTree(core, List(appDef), ctx.owner.asClass)
1787-
if isImpure then
1788-
typed(untpd.makeRetaining(untpd.TypedSplice(res), Nil, tpnme.retainsCap), pt)
1773+
if mt.paramErasureStatuses.lazyZip(mt.paramInfos).exists: (paramErased, info) =>
1774+
!paramErased && info.derivesFrom(defn.ErasedClass)
1775+
then
1776+
val newParams = params3.zipWithConserve(mt.paramInfos): (param, info) =>
1777+
if info.derivesFrom(defn.ErasedClass) then param.withAddedFlags(Erased) else param
1778+
typedDependent(newParams)
17891779
else
1790-
res
1780+
val core =
1781+
if mt.hasErasedParams then TypeTree(defn.PolyFunctionClass.typeRef)
1782+
else
1783+
val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span)
1784+
val paramTpts = appDef.termParamss.head.map(p => TypeTree(p.tpt.tpe).withSpan(p.tpt.span))
1785+
val funSym = defn.FunctionSymbol(numArgs, isContextual)
1786+
val tycon = TypeTree(funSym.typeRef)
1787+
AppliedTypeTree(tycon, paramTpts :+ resTpt)
1788+
val res = RefinedTypeTree(core, List(appDef), ctx.owner.asClass)
1789+
if isImpure then
1790+
typed(untpd.makeRetaining(untpd.TypedSplice(res), Nil, tpnme.retainsCap), pt)
1791+
else
1792+
res
17911793
end typedDependent
17921794

17931795
args match {
@@ -1802,7 +1804,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
18021804
val result = typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt)
18031805
// if there are any erased classes, we need to re-do the typecheck.
18041806
result match
1805-
case r: AppliedTypeTree if r.args.exists(_.tpe.isErasedClass) =>
1807+
case r: AppliedTypeTree if r.args.init.exists(_.tpe.derivesFrom(defn.ErasedClass)) =>
18061808
typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt)
18071809
case _ => result
18081810
}
@@ -2947,6 +2949,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29472949
completeAnnotations(vdef, sym)
29482950
if sym.is(Implicit) then checkImplicitConversionDefOK(sym)
29492951
if sym.is(Module) then checkNoModuleClash(sym)
2952+
else if sym.info.derivesFrom(defn.ErasedClass) then
2953+
sym.setFlag(Erased)
29502954
val tpt1 = checkSimpleKinded(typedType(tpt))
29512955
val rhs1 = vdef.rhs match
29522956
case rhs @ Ident(nme.WILDCARD) =>
@@ -3071,16 +3075,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
30713075
}
30723076

30733077
/** (1) Check that the signature of the class member does not return a repeated parameter type
3074-
* (2) If info is an erased class, set erased flag of member
3075-
* (3) Check that erased classes are not parameters of polymorphic functions.
3076-
* (4) Make sure the definition's symbol is `sym`.
3077-
* (5) Set the `defTree` of `sym` to be `mdef`.
3078+
* (2) Make sure the definition's symbol is `sym`.
3079+
* (3) Set the `defTree` of `sym` to be `mdef`.
30783080
*/
30793081
private def postProcessInfo(mdef: MemberDef, sym: Symbol)(using Context): MemberDef =
30803082
if (!sym.isOneOf(Synthetic | InlineProxy | Param) && sym.info.finalResultType.isRepeatedParam)
30813083
report.error(em"Cannot return repeated parameter type ${sym.info.finalResultType}", sym.srcPos)
3082-
if !sym.is(Module) && !sym.isConstructor && sym.info.finalResultType.isErasedClass then
3083-
sym.setFlag(Erased)
30843084
mdef.ensureHasSym(sym)
30853085
mdef.setDefTree
30863086

library/src/scala/CanThrow.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import annotation.{implicitNotFound, experimental, capability}
88
*/
99
@experimental
1010
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - Adding a using clause `(using CanThrow[${E}])` to the definition of the enclosing method\n - Adding `throws ${E}` clause after the result type of the enclosing method\n - Wrapping this piece of code with a `try` block that catches ${E}")
11-
erased class CanThrow[-E <: Exception] extends caps.SharedCapability
11+
class CanThrow[-E <: Exception] extends caps.SharedCapability, compiletime.Erased
1212

1313
@experimental
1414
object unsafeExceptions:

library/src/scala/Precise.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ import language.experimental.erasedDefinitions
77
* in precise mode. This means that singleton types and union types are not
88
* widened.
99
*/
10-
@experimental erased trait Precise:
10+
@experimental trait Precise extends compiletime.Erased:
1111
type Self

tests/neg/safeThrowsStrawman2.scala renamed to tests/invalid/neg/safeThrowsStrawman2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import language.experimental.erasedDefinitions
22

33
object scalax:
4-
erased class CanThrow[E <: Exception]
4+
class CanThrow[E <: Exception] extends compiletime.Erased
55
type CTF = CanThrow[Fail]
66

77
infix type raises[R, E <: Exception] = CanThrow[E] ?=> R

0 commit comments

Comments
 (0)