Skip to content

Commit e57412e

Browse files
committed
Drop erased class
Replace with test whether a type derivesFrom Erased.
1 parent 4efcb8a commit e57412e

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
@@ -608,10 +608,9 @@ object Checking {
608608
if sym.isUpdateMethod && !sym.owner.derivesFrom(defn.Caps_Mutable) then
609609
fail(em"Update methods can only be used as members of classes extending the `Mutable` trait")
610610
val unerasable =
611-
sym.is(Lazy, butNot = Given)
612-
|| sym.is(Method, butNot = Macro)
611+
sym.is(Method, butNot = Macro)
613612
|| sym.is(Mutable)
614-
|| sym.isType && !sym.isClass
613+
|| sym.isType
615614
checkApplicable(Erased, !unerasable)
616615
checkCombination(Final, Open)
617616
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
@@ -1769,24 +1769,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
17691769
if (mt.isParamDependent)
17701770
report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos)
17711771
// Restart typechecking if there are erased classes that we want to mark erased
1772-
if mt.paramErasureStatuses.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then
1773-
val newParams = params3.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) =>
1774-
if isErasedClass then arg.withAddedFlags(Erased) else arg
1775-
}
1776-
return typedDependent(newParams)
1777-
val core =
1778-
if mt.hasErasedParams then TypeTree(defn.PolyFunctionClass.typeRef)
1779-
else
1780-
val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span)
1781-
val paramTpts = appDef.termParamss.head.map(p => TypeTree(p.tpt.tpe).withSpan(p.tpt.span))
1782-
val funSym = defn.FunctionSymbol(numArgs, isContextual)
1783-
val tycon = TypeTree(funSym.typeRef)
1784-
AppliedTypeTree(tycon, paramTpts :+ resTpt)
1785-
val res = RefinedTypeTree(core, List(appDef), ctx.owner.asClass)
1786-
if isImpure then
1787-
typed(untpd.makeRetaining(untpd.TypedSplice(res), Nil, tpnme.retainsCap), pt)
1772+
if mt.paramErasureStatuses.lazyZip(mt.paramInfos).exists: (paramErased, info) =>
1773+
!paramErased && info.derivesFrom(defn.ErasedClass)
1774+
then
1775+
val newParams = params3.zipWithConserve(mt.paramInfos): (param, info) =>
1776+
if info.derivesFrom(defn.ErasedClass) then param.withAddedFlags(Erased) else param
1777+
typedDependent(newParams)
17881778
else
1789-
res
1779+
val core =
1780+
if mt.hasErasedParams then TypeTree(defn.PolyFunctionClass.typeRef)
1781+
else
1782+
val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span)
1783+
val paramTpts = appDef.termParamss.head.map(p => TypeTree(p.tpt.tpe).withSpan(p.tpt.span))
1784+
val funSym = defn.FunctionSymbol(numArgs, isContextual)
1785+
val tycon = TypeTree(funSym.typeRef)
1786+
AppliedTypeTree(tycon, paramTpts :+ resTpt)
1787+
val res = RefinedTypeTree(core, List(appDef), ctx.owner.asClass)
1788+
if isImpure then
1789+
typed(untpd.makeRetaining(untpd.TypedSplice(res), Nil, tpnme.retainsCap), pt)
1790+
else
1791+
res
17901792
end typedDependent
17911793

17921794
args match {
@@ -1801,7 +1803,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
18011803
val result = typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt)
18021804
// if there are any erased classes, we need to re-do the typecheck.
18031805
result match
1804-
case r: AppliedTypeTree if r.args.exists(_.tpe.isErasedClass) =>
1806+
case r: AppliedTypeTree if r.args.init.exists(_.tpe.derivesFrom(defn.ErasedClass)) =>
18051807
typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt)
18061808
case _ => result
18071809
}
@@ -2944,6 +2946,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29442946
completeAnnotations(vdef, sym)
29452947
if sym.is(Implicit) then checkImplicitConversionDefOK(sym)
29462948
if sym.is(Module) then checkNoModuleClash(sym)
2949+
else if sym.info.derivesFrom(defn.ErasedClass) then
2950+
sym.setFlag(Erased)
29472951
val tpt1 = checkSimpleKinded(typedType(tpt))
29482952
val rhs1 = vdef.rhs match
29492953
case rhs @ Ident(nme.WILDCARD) =>
@@ -3068,16 +3072,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
30683072
}
30693073

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

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)