Skip to content

Commit 2fd5991

Browse files
committed
Allow erased on classes
1 parent c616203 commit 2fd5991

File tree

14 files changed

+107
-25
lines changed

14 files changed

+107
-25
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,8 @@ object Flags {
360360
/** An export forwarder */
361361
val (Exported @ _, _, _) = newFlags(41, "exported")
362362

363-
/** Labeled with `erased` modifier (erased value) */
364-
val (_, Erased @ _, _) = newFlags(42, "erased")
363+
/** Labeled with `erased` modifier (erased value or class) */
364+
val (Erased @ _, _, _) = newFlags(42, "erased")
365365

366366
/** An opaque type alias or a class containing one */
367367
val (Opaque @ _, _, _) = newFlags(43, "opaque")
@@ -439,13 +439,13 @@ object Flags {
439439

440440
/** Flags representing source modifiers */
441441
private val CommonSourceModifierFlags: FlagSet =
442-
commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent)
442+
commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased)
443443

444444
val TypeSourceModifierFlags: FlagSet =
445445
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open
446446

447447
val TermSourceModifierFlags: FlagSet =
448-
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased
448+
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy
449449

450450
/** Flags representing modifiers that can appear in trees */
451451
val ModifierFlags: FlagSet =
@@ -515,12 +515,12 @@ object Flags {
515515
val RetainedModuleValAndClassFlags: FlagSet =
516516
AccessFlags | Package | Case |
517517
Synthetic | JavaDefined | JavaStatic | Artifact |
518-
Lifted | MixedIn | Specialized | ConstructorProxy | Invisible
518+
Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | Erased
519519

520520
/** Flags that can apply to a module val */
521521
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
522522
Override | Final | Method | Implicit | Given | Lazy |
523-
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Erased | Transparent
523+
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Transparent
524524

525525
/** Flags that can apply to a module class */
526526
val RetainedModuleClassFlags: FlagSet = RetainedModuleValAndClassFlags | Enum

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,10 +729,10 @@ class TreePickler(pickler: TastyPickler) {
729729
if flags.is(Transparent) then writeModTag(TRANSPARENT)
730730
if flags.is(Infix) then writeModTag(INFIX)
731731
if flags.is(Invisible) then writeModTag(INVISIBLE)
732+
if (flags.is(Erased)) writeModTag(ERASED)
732733
if (isTerm) {
733734
if (flags.is(Implicit)) writeModTag(IMPLICIT)
734735
if (flags.is(Given)) writeModTag(GIVEN)
735-
if (flags.is(Erased)) writeModTag(ERASED)
736736
if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY)
737737
if (flags.is(AbsOverride)) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) }
738738
if (flags.is(Mutable)) writeModTag(MUTABLE)

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
925925
else PrintableFlags(isType)
926926
if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= GivenOrImplicit // drop implicit/given from classes
927927
val rawFlags = if (sym.exists) sym.flags else mods.flags
928-
if (rawFlags.is(Param)) flagMask = flagMask &~ Given
928+
if (rawFlags.is(Param)) flagMask = flagMask &~ Given &~ Erased
929929
val flags = rawFlags & flagMask
930930
var flagsText = toTextFlags(sym, flags)
931931
val annotations =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
3434
override def runsAfterGroupsOf: Set[String] = Set(RefChecks.name, ExplicitOuter.name)
3535

3636
override def transformSym(sym: SymDenotation)(using Context): SymDenotation =
37-
if (sym.isEffectivelyErased && !sym.is(Private) && sym.owner.isClass)
37+
if (sym.isEffectivelyErased && sym.isTerm && !sym.is(Private) && sym.owner.isClass)
3838
sym.copySymDenotation(initFlags = sym.flags | Private)
3939
else sym
4040

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ object Checking {
501501
sym.setFlag(Private) // break the overriding relationship by making sym Private
502502
}
503503
if (sym.is(Erased))
504-
checkApplicable(Erased, !sym.isOneOf(MutableOrLazy, butNot = Given))
504+
checkApplicable(Erased,
505+
!sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass)
505506
}
506507

507508
/** Check the type signature of the symbol `M` defined by `tree` does not refer
@@ -997,11 +998,6 @@ trait Checking {
997998
errorTree(tpt, MissingTypeParameterFor(tpt.tpe))
998999
else tpt
9991000

1000-
/** Check that the signature of the class mamber does not return a repeated parameter type */
1001-
def checkSignatureRepeatedParam(sym: Symbol)(using Context): Unit =
1002-
if (!sym.isOneOf(Synthetic | InlineProxy | Param) && sym.info.finalResultType.isRepeatedParam)
1003-
report.error(em"Cannot return repeated parameter type ${sym.info.finalResultType}", sym.srcPos)
1004-
10051001
/** Verify classes extending AnyVal meet the requirements */
10061002
def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(using Context): Unit =
10071003
Checking.checkDerivedValueClass(clazz, stats)

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

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,8 +1144,24 @@ class Typer extends Namer
11441144

11451145
assert(!funFlags.is(Erased) || !args.isEmpty, "An empty function cannot not be erased")
11461146

1147-
val funCls = defn.FunctionClass(args.length,
1148-
isContextual = funFlags.is(Given), isErased = funFlags.is(Erased))
1147+
val numArgs = args.length
1148+
val isContextual = funFlags.is(Given)
1149+
val isErased = funFlags.is(Erased)
1150+
val funCls = defn.FunctionClass(numArgs, isContextual, isErased)
1151+
1152+
/** If `app` is a function type with arguments that are all erased classes,
1153+
* turn it into an erased function type.
1154+
*/
1155+
def propagateErased(app: Tree): Tree = app match
1156+
case AppliedTypeTree(tycon: TypeTree, args)
1157+
if !isErased
1158+
&& numArgs > 0
1159+
&& args.indexWhere(arg => !isErasedClass(arg.tpe)) == numArgs =>
1160+
val tycon1 = TypeTree(defn.FunctionClass(numArgs, isContextual, isErased = true).typeRef)
1161+
.withSpan(tycon.span)
1162+
assignType(cpy.AppliedTypeTree(app)(tycon1, args), tycon1, args)
1163+
case _ =>
1164+
app
11491165

11501166
/** Typechecks dependent function type with given parameters `params` */
11511167
def typedDependent(params: List[untpd.ValDef])(using Context): Tree =
@@ -1169,7 +1185,7 @@ class Typer extends Namer
11691185
val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span)
11701186
val typeArgs = appDef.termParamss.head.map(_.tpt) :+ resTpt
11711187
val tycon = TypeTree(funCls.typeRef)
1172-
val core = AppliedTypeTree(tycon, typeArgs)
1188+
val core = propagateErased(AppliedTypeTree(tycon, typeArgs))
11731189
RefinedTypeTree(core, List(appDef), ctx.owner.asClass)
11741190
end typedDependent
11751191

@@ -1178,7 +1194,8 @@ class Typer extends Namer
11781194
typedDependent(args.asInstanceOf[List[untpd.ValDef]])(
11791195
using ctx.fresh.setOwner(newRefinedClassSymbol(tree.span)).setNewScope)
11801196
case _ =>
1181-
typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt)
1197+
propagateErased(
1198+
typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funCls.typeRef), args :+ body), pt))
11821199
}
11831200
}
11841201

@@ -2062,7 +2079,7 @@ class Typer extends Namer
20622079
case rhs => typedExpr(rhs, tpt1.tpe.widenExpr)
20632080
}
20642081
val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
2065-
checkSignatureRepeatedParam(sym)
2082+
postProcessInfo(sym)
20662083
vdef1.setDefTree
20672084
}
20682085

@@ -2150,11 +2167,23 @@ class Typer extends Namer
21502167

21512168
val ddef2 = assignType(cpy.DefDef(ddef)(name, paramss1, tpt1, rhs1), sym)
21522169

2153-
checkSignatureRepeatedParam(sym)
2170+
postProcessInfo(sym)
21542171
ddef2.setDefTree
21552172
//todo: make sure dependent method types do not depend on implicits or by-name params
21562173
}
21572174

2175+
private def isErasedClass(tpe: Type)(using Context): Boolean =
2176+
tpe.underlyingClassRef(refinementOK = true).typeSymbol.is(Erased)
2177+
2178+
/** (1) Check that the signature of the class mamber does not return a repeated parameter type
2179+
* (2) If info is an erased class, set erased flag of member
2180+
*/
2181+
private def postProcessInfo(sym: Symbol)(using Context): Unit =
2182+
if (!sym.isOneOf(Synthetic | InlineProxy | Param) && sym.info.finalResultType.isRepeatedParam)
2183+
report.error(em"Cannot return repeated parameter type ${sym.info.finalResultType}", sym.srcPos)
2184+
if !sym.is(Module) && isErasedClass(sym.info) then
2185+
sym.setFlag(Erased)
2186+
21582187
def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
21592188
val TypeDef(name, rhs) = tdef
21602189
completeAnnotations(tdef, sym)

tests/neg-custom-args/erased/erased-class.scala

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/neg-custom-args/erased/erased-trait.scala

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/neg-custom-args/erased/i4058.scala

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/neg-custom-args/erased/i6795.scala

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/neg/safeThrowsStrawman.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Error: tests/neg/safeThrowsStrawman.scala:17:32 ---------------------------------------------------------------------
2+
17 | if x then 1 else raise(Fail()) // error
3+
| ^
4+
| The capability to throw exception scalax.Fail is missing.
5+
| The capability can be provided by one of the following:
6+
| - A using clause `(using CanThrow[scalax.Fail])`
7+
| - A throws clause in a result type `X throws scalax.Fail`
8+
| - an enclosing `try` that catches scalax.Fail

tests/neg/safeThrowsStrawman.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import language.experimental.erasedTerms
2+
import annotation.implicitNotFound
3+
4+
object scalax:
5+
@implicitNotFound("The capability to throw exception ${E} is missing.\nThe capability can be provided by one of the following:\n - A using clause `(using CanThrow[${E}])`\n - A throws clause in a result type `X throws ${E}`\n - an enclosing `try` that catches ${E}")
6+
erased class CanThrow[E <: Exception]
7+
8+
infix type throws[R, E <: Exception] = CanThrow[E] ?=> R
9+
10+
class Fail extends Exception
11+
12+
def raise[E <: Exception](e: E): Nothing throws E = throw e
13+
14+
import scalax._
15+
16+
def foo(x: Boolean): Int =
17+
if x then 1 else raise(Fail()) // error
18+
19+
@main def Test =
20+
try
21+
erased given CanThrow[Fail] = ???
22+
println(foo(true))
23+
println(foo(false))
24+
catch case ex: Fail =>
25+
println("failed")

tests/run/safeThrowsStrawman.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1
2+
failed

tests/run/safeThrowsStrawman.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import language.experimental.erasedTerms
2+
3+
object scalax:
4+
erased class CanThrow[E <: Exception]
5+
6+
infix type throws[R, E <: Exception] = CanThrow[E] ?=> R
7+
8+
class Fail extends Exception
9+
10+
def raise[E <: Exception](e: E): Nothing throws E = throw e
11+
12+
import scalax._
13+
14+
def foo(x: Boolean): Int throws Fail =
15+
if x then 1 else raise(Fail())
16+
17+
def bar(x: Boolean)(using CanThrow[Fail]): Int =
18+
if x then 1 else raise(Fail())
19+
20+
@main def Test =
21+
try
22+
erased given CanThrow[Fail] = ???
23+
println(foo(true))
24+
println(foo(false))
25+
catch case ex: Fail =>
26+
println("failed")

0 commit comments

Comments
 (0)