Skip to content

Commit d9dcbc9

Browse files
committed
Flag lazy erased vals as errors
But erased givens are OK since they don't get the lazy flag set.
1 parent d1b2b8a commit d9dcbc9

File tree

12 files changed

+76
-15
lines changed

12 files changed

+76
-15
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,11 +529,11 @@ object Flags {
529529
val RetainedModuleValAndClassFlags: FlagSet =
530530
AccessFlags | Package | Case |
531531
Synthetic | JavaDefined | JavaStatic | Artifact |
532-
Lifted | MixedIn | Specialized | PhantomSymbol | Invisible | Erased
532+
Lifted | MixedIn | Specialized | PhantomSymbol | Invisible
533533

534534
/** Flags that can apply to a module val */
535535
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
536-
Override | Final | Method | Implicit | Given | Lazy |
536+
Override | Final | Method | Implicit | Given | Lazy | Erased |
537537
Accessor | AbsOverride | StableRealizable | Captured | Synchronized | Transparent
538538

539539
/** Flags that can apply to a module class */

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4374,7 +4374,7 @@ object Parsers {
43744374
accept(EQUALS)
43754375
mods1 |= Final
43764376
if !hasParams && !mods.is(Inline) then
4377-
mods1 |= Lazy
4377+
if !mods.is(Erased) then mods1 |= Lazy
43784378
ValDef(name, parents.head, subExpr())
43794379
else
43804380
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, subExpr())

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
230230
case IllegalContextBoundsID // errorNumber: 214
231231
case NamedPatternNotApplicableID // errorNumber: 215
232232
case UnnecessaryNN // errorNumber: 216
233-
case ErasedNotPureID // errornumber 217
233+
case ErasedNotPureID // errorNumber: 217
234+
case IllegalErasedDefID // errorNumber: 218
234235

235236
def errorNumber = ordinal - 1
236237

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2824,7 +2824,7 @@ class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context)
28242824
|"""
28252825
}
28262826

2827-
class ModifierNotAllowedForDefinition(flag: Flag, explanation: String = "")(using Context)
2827+
class ModifierNotAllowedForDefinition(flag: Flag, explanation: => String = "")(using Context)
28282828
extends SyntaxMsg(ModifierNotAllowedForDefinitionID) {
28292829
def msg(using Context) = i"Modifier ${hl(flag.flagsString)} is not allowed for this definition"
28302830
def explain(using Context) = explanation
@@ -3640,3 +3640,15 @@ final class ErasedNotPure(tree: tpd.Tree, isArgument: Boolean, isImplicit: Boole
36403640
|$alternatives"""
36413641
end ErasedNotPure
36423642

3643+
final class IllegalErasedDef(sym: Symbol)(using Context) extends TypeMsg(IllegalErasedDefID):
3644+
override protected def msg(using Context): String =
3645+
def notAllowed = "`erased` is not allowed for this kind of definition."
3646+
def result = if sym.is(Method) then " result" else ""
3647+
if sym.is(Erased) then notAllowed
3648+
else
3649+
i"""$sym is implicitly `erased` since its$result type extends trait `compiletime.Erased`.
3650+
|But $notAllowed"""
3651+
3652+
override protected def explain(using Context): String =
3653+
"Only non-lazy immutable values can be `erased`"
3654+
end IllegalErasedDef

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,12 @@ object Checking {
586586
|""")
587587
end checkScala2Implicit
588588

589+
def checkErasedOK(sym: Symbol)(using Context): Unit =
590+
if sym.is(Method, butNot = Macro)
591+
|| sym.isOneOf(Mutable | Lazy)
592+
|| sym.isType
593+
then report.error(IllegalErasedDef(sym), sym.srcPos)
594+
589595
/** Check that symbol's definition is well-formed. */
590596
def checkWellFormed(sym: Symbol)(using Context): Unit = {
591597
def fail(msg: Message) = report.error(msg, sym.srcPos)
@@ -681,11 +687,7 @@ object Checking {
681687
fail(ModifierNotAllowedForDefinition(Flags.Infix, s"A top-level ${sym.showKind} cannot be infix."))
682688
if sym.isUpdateMethod && !sym.owner.derivesFrom(defn.Caps_Mutable) then
683689
fail(em"Update methods can only be used as members of classes extending the `Mutable` trait")
684-
val unerasable =
685-
sym.is(Method, butNot = Macro)
686-
|| sym.is(Mutable)
687-
|| sym.isType
688-
checkApplicable(Erased, !unerasable)
690+
if sym.is(Erased) then checkErasedOK(sym)
689691
checkCombination(Final, Open)
690692
checkCombination(Sealed, Open)
691693
checkCombination(Final, Sealed)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,7 @@ class Namer { typer: Typer =>
19111911
mdef.tpt match
19121912
case tpt: untpd.ContextBoundTypeTree if mbrTpe.typeSymbol == defn.SingletonClass =>
19131913
sym.setFlag(Erased)
1914+
sym.resetFlag(Lazy)
19141915
case _ =>
19151916
if (ctx.explicitNulls && mdef.mods.is(JavaDefined))
19161917
JavaNullInterop.nullifyMember(sym, mbrTpe, mdef.mods.isAllOf(JavaEnumValue))

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2950,6 +2950,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29502950
if sym.is(Implicit) then checkImplicitConversionDefOK(sym)
29512951
if sym.is(Module) then checkNoModuleClash(sym)
29522952
else if sym.info.derivesFrom(defn.ErasedClass) then
2953+
if sym.isAllOf(Given | Lazy) && !vdef.mods.mods.exists(_.flags.is(Lazy)) then
2954+
// reset implied Lazy flag of givens, but keep explicit modifier
2955+
sym.resetFlag(Lazy)
2956+
checkErasedOK(sym)
29532957
sym.setFlag(Erased)
29542958
val tpt1 = checkSimpleKinded(typedType(tpt))
29552959
val rhs1 = vdef.rhs match
@@ -2967,6 +2971,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
29672971
val nnInfo = rhs1.notNullInfo
29682972
vdef1.withNotNullInfo(if sym.is(Lazy) then nnInfo.retractedInfo else nnInfo)
29692973
}
2974+
29702975
private def retractDefDef(sym: Symbol)(using Context): Tree =
29712976
// it's a discarded method (synthetic case class method or synthetic java record constructor or overridden member), drop it
29722977
val canBeInvalidated: Boolean =

tests/neg/erased-lazy-given.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E217] Type Error: tests/neg/erased-lazy-given.scala:8:13 -----------------------------------------------------------
2+
8 | lazy given E = E() // error
3+
| ^
4+
| given instance given_E is implicitly `erased` since its type extends trait `compiletime.Erased`.
5+
| But `erased` is not allowed for this kind of definition.
6+
|
7+
| longer explanation available when compiling with `-explain`

tests/neg/erased-lazy-given.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import language.experimental.erasedDefinitions
2+
3+
class E extends compiletime.Erased
4+
5+
object test1:
6+
given E = E() // OK
7+
object test2:
8+
lazy given E = E() // error
9+

tests/neg/erased-lazy-val.check

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-- [E217] Type Error: tests/neg/erased-lazy-val.scala:6:18 -------------------------------------------------------------
2+
6 | erased lazy val i: Int = 1 // error
3+
| ^
4+
| `erased` is not allowed for this kind of definition.
5+
|
6+
| longer explanation available when compiling with `-explain`
7+
-- [E217] Type Error: tests/neg/erased-lazy-val.scala:7:11 -------------------------------------------------------------
8+
7 | lazy val e: E = E() // error
9+
| ^
10+
| lazy value e is implicitly `erased` since its type extends trait `compiletime.Erased`.
11+
| But `erased` is not allowed for this kind of definition.
12+
|
13+
| longer explanation available when compiling with `-explain`
14+
-- [E217] Type Error: tests/neg/erased-lazy-val.scala:8:2 --------------------------------------------------------------
15+
8 | erased object obj1 // error
16+
| ^
17+
| `erased` is not allowed for this kind of definition.
18+
|
19+
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)