Skip to content

Commit a8b727e

Browse files
committed
Introduce open modifier on classes
1 parent e1679f9 commit a8b727e

File tree

11 files changed

+40
-10
lines changed

11 files changed

+40
-10
lines changed

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
182182

183183
case class Opaque()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Opaque)
184184

185+
case class Open()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Open)
186+
185187
case class Override()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Override)
186188

187189
case class Abstract()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Abstract)

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ object Flags {
3737
else {
3838
val tbits = x.bits & y.bits & KINDFLAGS
3939
if (tbits == 0)
40-
assert(false, s"illegal flagset combination: $x and $y")
40+
assert(false, s"illegal flagset combination: ${x.flagsString} and ${y.flagsString}")
4141
FlagSet(tbits | ((x.bits | y.bits) & ~KINDFLAGS))
4242
}
4343

@@ -237,8 +237,8 @@ object Flags {
237237
/** A value or variable accessor (getter or setter) */
238238
val (AccessorOrSealed @ _, Accessor @ _, Sealed @ _) = newFlags(11, "<accessor>", "sealed")
239239

240-
/** A mutable var */
241-
val (_, Mutable @ _, _) = newFlags(12, "mutable")
240+
/** A mutable var, an open class */
241+
val (MutableOrOpen @ __, Mutable @ _, Open @ _) = newFlags(12, "mutable", "open")
242242

243243
/** Symbol is local to current class (i.e. private[this] or protected[this]
244244
* pre: Private or Protected are also set
@@ -422,7 +422,7 @@ object Flags {
422422
commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic)
423423

424424
val TypeSourceModifierFlags: FlagSet =
425-
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque
425+
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open
426426

427427
val TermSourceModifierFlags: FlagSet =
428428
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased
@@ -439,7 +439,7 @@ object Flags {
439439
val FromStartFlags: FlagSet = commonFlags(
440440
Module, Package, Deferred, Method, Case,
441441
HigherKinded, Param, ParamAccessor,
442-
Scala2ExistentialCommon, Mutable, Opaque, Touched, JavaStatic,
442+
Scala2ExistentialCommon, MutableOrOpen, Opaque, Touched, JavaStatic,
443443
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
444444
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
445445
SuperAccessorOrScala2x, Inline, Macro)
@@ -509,6 +509,8 @@ object Flags {
509509
/** Flags retained in export forwarders */
510510
val RetainedExportFlags = Given | Implicit | Extension
511511

512+
val ClassOnlyFlags = Sealed | Open | Abstract.toTypeFlags
513+
512514
// ------- Other flag sets -------------------------------------
513515

514516
val AbstractFinal: FlagSet = Abstract | Final

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ object StdNames {
506506
val nullExpr: N = "nullExpr"
507507
val ofDim: N = "ofDim"
508508
val opaque: N = "opaque"
509+
val open: N = "open"
509510
val ordinal: N = "ordinal"
510511
val ordinalDollar: N = "$ordinal"
511512
val ordinalDollar_ : N = "_$ordinal"

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ Standard-Section: "ASTs" TopLevelStat*
211211
EXTENSION -- An extension method
212212
PARAMsetter -- The setter part `x_=` of a var parameter `x` which itself is pickled as a PARAM
213213
EXPORTED -- An export forwarder
214+
OPEN -- an open class
214215
Annotation
215216
216217
Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term -- An annotation, given (class) type of constructor, and full application tree
@@ -332,6 +333,7 @@ object TastyFormat {
332333
final val GIVEN = 37
333334
final val PARAMsetter = 38
334335
final val EXPORTED = 39
336+
final val OPEN = 40
335337

336338
// Cat. 2: tag Nat
337339

@@ -460,7 +462,7 @@ object TastyFormat {
460462

461463
/** Useful for debugging */
462464
def isLegalTag(tag: Int): Boolean =
463-
firstSimpleTreeTag <= tag && tag <= EXPORTED ||
465+
firstSimpleTreeTag <= tag && tag <= OPEN ||
464466
firstNatTreeTag <= tag && tag <= RENAMED ||
465467
firstASTTreeTag <= tag && tag <= BOUNDED ||
466468
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
@@ -505,6 +507,7 @@ object TastyFormat {
505507
| GIVEN
506508
| PARAMsetter
507509
| EXPORTED
510+
| OPEN
508511
| ANNOTATION
509512
| PRIVATEqualified
510513
| PROTECTEDqualified => true
@@ -565,6 +568,7 @@ object TastyFormat {
565568
case GIVEN => "GIVEN"
566569
case PARAMsetter => "PARAMsetter"
567570
case EXPORTED => "EXPORTED"
571+
case OPEN => "OPEN"
568572

569573
case SHAREDterm => "SHAREDterm"
570574
case SHAREDtype => "SHAREDtype"

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ class TreePickler(pickler: TastyPickler) {
669669
if (flags.is(Covariant)) writeModTag(COVARIANT)
670670
if (flags.is(Contravariant)) writeModTag(CONTRAVARIANT)
671671
if (flags.is(Opaque)) writeModTag(OPAQUE)
672+
if (flags.is(Open)) writeModTag(OPEN)
672673
}
673674
}
674675

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,7 @@ class TreeUnpickler(reader: TastyReader,
631631
case GIVEN => addFlag(Given)
632632
case PARAMsetter => addFlag(ParamAccessor)
633633
case EXPORTED => addFlag(Exported)
634+
case OPEN => addFlag(Open)
634635
case PRIVATEqualified =>
635636
readByte()
636637
privateWithin = readWithin(ctx)
@@ -890,9 +891,9 @@ class TreeUnpickler(reader: TastyReader,
890891
untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType)
891892
}
892893
else EmptyValDef
894+
cls.setNoInitsFlags(parentsKind(parents), bodyFlags)
893895
cls.info = ClassInfo(cls.owner.thisType, cls, parentTypes, cls.unforcedDecls,
894896
if (self.isEmpty) NoType else self.tpt.tpe)
895-
cls.setNoInitsFlags(parentsKind(parents), bodyFlags)
896897
val constr = readIndexedDef().asInstanceOf[DefDef]
897898
val mappedParents = parents.map(_.changeOwner(localDummy, constr.symbol))
898899

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2590,6 +2590,7 @@ object Parsers {
25902590
name match {
25912591
case nme.inline => Mod.Inline()
25922592
case nme.opaque => Mod.Opaque()
2593+
case nme.open => Mod.Open()
25932594
}
25942595
}
25952596

@@ -2661,7 +2662,7 @@ object Parsers {
26612662
* | AccessModifier
26622663
* | override
26632664
* | opaque
2664-
* LocalModifier ::= abstract | final | sealed | implicit | lazy | erased | inline
2665+
* LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline
26652666
*/
26662667
def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
26672668
@tailrec

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,5 +286,5 @@ object Tokens extends TokensCommon {
286286

287287
final val scala3keywords = BitSet(ENUM, ERASED, GIVEN)
288288

289-
final val softModifierNames = Set(nme.inline, nme.opaque)
289+
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open)
290290
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@ object Checking {
417417
}
418418
if (!sym.isClass && sym.is(Abstract))
419419
fail(OnlyClassesCanBeAbstract(sym))
420+
// note: this is not covered by the next test since terms can be abstract (which is a dual-mode flag)
421+
// but they can never be one of ClassOnlyFlags
422+
if !sym.isClass && sym.isOneOf(ClassOnlyFlags) then
423+
fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}")
420424
if (sym.is(AbsOverride) && !sym.owner.is(Trait))
421425
fail(AbstractOverrideOnlyInTraits(sym))
422426
if (sym.is(Trait) && sym.is(Final))
@@ -437,6 +441,8 @@ object Checking {
437441
}
438442
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
439443
fail(CannotExtendAnyVal(sym))
444+
checkCombination(Final, Open)
445+
checkCombination(Sealed, Open)
440446
checkCombination(Final, Sealed)
441447
checkCombination(Private, Protected)
442448
checkCombination(Abstract, Override)

docs/docs/internals/syntax.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ yield
103103
### Soft keywords
104104

105105
```
106-
as derives inline opaque
106+
as derives inline opaque open
107107
~ * | & + -
108108
```
109109

@@ -325,6 +325,7 @@ Modifier ::= LocalModifier
325325
LocalModifier ::= ‘abstract’
326326
| ‘final’
327327
| ‘sealed’
328+
| ‘open’
328329
| ‘implicit’
329330
| ‘lazy’
330331
| ‘inline’

tests/neg/class-mods.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
open final class Foo1 // error
2+
sealed open class Foo2 // error
3+
4+
open type T1 // error
5+
sealed type T2 // error
6+
abstract type T3 // error
7+
abstract open type T4 // error
8+
9+
object foo {
10+
abstract val x: Int = 1 // error
11+
}

0 commit comments

Comments
 (0)