8000 Accept (non-)sealed in Java · scala/scala@42bf9bb · GitHub
[go: up one dir, main page]

Skip to content

Commit 42bf9bb

Browse files
committed
Accept (non-)sealed in Java
1 parent 15538c7 commit 42bf9bb

39 files changed

+357
-76
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,10 +1725,8 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
17251725
} // class Run
17261726

17271727
def printAllUnits(): Unit = {
1728-
print("[[syntax trees at end of %25s]]".format(phase))
1729-
exitingPhase(phase)(currentRun.units foreach { unit =>
1730-
nodePrinters showUnit unit
1731-
})
1728+
print(f"[[syntax trees at end of $phase%25s]]")
1729+
exitingPhase(phase)(currentRun.units.foreach(nodePrinters.showUnit(_)))
17321730
}
17331731

17341732
/** We resolve the class/object ambiguity by passing a type/term name.

src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ abstract class CommonTokens {
5151
// J: PUBLIC = 42
5252
final val PROTECTED = 43
5353
final val PRIVATE = 44
54-
// S: SEALED = 45
54+
final val SEALED = 45 // J: contextual keyword
5555
final val ABSTRACT = 46
5656
// J: DEFAULT = 47
5757
// J: STATIC = 48

src/compiler/scala/tools/nsc/ast/parser/Tokens.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ object Tokens extends CommonTokens {
2828
/** modifiers */
2929
final val IMPLICIT = 40
3030
final val OVERRIDE = 41
31-
final val SEALED = 45
3231
final val LAZY = 55
3332
final val MACRO = 57
3433

src/compiler/scala/tools/nsc/javac/JavaParsers.scala

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
package scala.tools.nsc
1717
package javac
1818

19-
import scala.collection.mutable.ListBuffer
2019
import symtab.Flags
2120
import JavaTokens._
22-
import scala.annotation.tailrec
21+
import scala.annotation._
22+
import scala.collection.mutable.ListBuffer
2323
import scala.language.implicitConversions
24-
import scala.reflect.internal.util.Position
25-
import scala.reflect.internal.util.ListOfNil
24+
import scala.reflect.internal.util.{ListOfNil, Position}
2625
import scala.tools.nsc.Reporting.WarningCategory
26+
import scala.util.chaining._
2727

2828
trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
2929
val global : Global
@@ -493,11 +493,39 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
493493
case SYNCHRONIZED =>
494494
in.nextToken()
495495
case _ =>
496-
val privateWithin: TypeName =
497-
if (isPackageAccess && !inInterface) thisPackageName
498-
else tpnme.EMPTY
499-
500-
return Modifiers(flags, privateWithin) withAnnotations annots
496+
val unsealed = 0L // no flag for UNSEALED
497+
def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false }
498+
def lookingAhead(s: String): Boolean = {
499+
import scala.reflect.internal.Chars._
500+
var i = 0
501+
val n = s.length
502+
val lookahead = in.in.lookahead
503+
while (i < n && lookahead.ch != SU) {
504+
if (lookahead.ch != s.charAt(i)) return false
505+
lookahead.next()
506+
i += 1
507+
}
508+
i == n && Character.isWhitespace(lookahead.ch)
509+
}
510+
val done = (in.token != IDENTIFIER) || (
511+
in.name match {
512+
case nme.javaRestrictedIdentifiers.SEALED => consume(Flags.SEALED)
513+
case nme.javaRestrictedIdentifiers.UNSEALED => consume(unsealed)
514+
case nme.javaRestrictedIdentifiers.NON =>
515+
!lookingAhead("-sealed") || {
516+
in.nextToken()
517+
in.nextToken()
518+
consume(unsealed)
519+
}
520+
case _ => true
521+
}
522+
)
523+
if (done) {
524+
val privateWithin: TypeName =
525+
if (isPackageAccess && !inInterface) thisPackageName
526+
else tpnme.EMPTY
527+
return Modifiers(flags, privateWithin) withAnnotations annots
528+
}
501529
}
502530
}
503531
abort("should not be here")
@@ -802,6 +830,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
802830
List()
803831
}
804832

833+
def permitsOpt() =
834+
if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.PERMITS) {
835+
in.nextToken()
836+
repsep(() => typ(), COMMA)
837+
}
838+
else Nil
839+
805840
def classDecl(mods: Modifiers): List[Tree] = {
806841
accept(CLASS)
807842
val pos = in.currentPos
@@ -815,9 +850,11 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
815850
javaLangObject()
816851
}
817852
val interfaces = interfacesOpt()
853+
val permits = permitsOpt()
818854
val (statics, body) = typeBody(CLASS)
819855
addCompanionObject(statics, atPos(pos) {
820856
ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body))
857+
.tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits)))
821858
})
822859
}
823860

@@ -878,11 +915,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
878915
} else {
879916
List(javaLangObject())
880917
}
918+
val permits = permitsOpt()
881919
val (statics, body) = typeBody(INTERFACE)
882920
addCompanionObject(statics, atPos(pos) {
883921
ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT,
884922
name, tparams,
885923
makeTemplate(parents, body))
924+
.tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits)))
886925
})
887926
}
888927

@@ -905,7 +944,6 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
905944
} else if (in.token == SEMI) {
906945
in.nextToken()
907946
} else {
908-
909947
// See "14.3. Local Class and Interface Declarations"
910948
adaptRecordIdentifier()
911949
if (in.token == ENUM || in.token == RECORD || definesInterface(in.token))

src/compiler/scala/tools/nsc/javac/JavaTokens.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ object JavaTokens extends ast.parser.CommonTokens {
3838
final val NATIVE = 53
3939
final val STRICTFP = 54
4040
final val THROWS = 56
41+
final val UNSEALED = 57 // contextual keyword
4142

4243
/** templates */
4344
final val INTERFACE = 66

src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -853,10 +853,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
853853
in.skip(attrLen)
854854

855855
case tpnme.RuntimeAnnotationATTR =>
856-
val numAnnots = u2()
856+
val numAnnots = u2()
857857
val annots = new ListBuffer[AnnotationInfo]
858-
for (n <- 0 until numAnnots; annot <- parseAnnotation(u2()))
859-
annots += annot
858+
numAnnots times {
859+
parseAnnotation(u2()).foreach(annots.addOne)
860+
}
860861
/* `sym.withAnnotations(annots)`, like `sym.addAnnotation(annot)`, prepends,
861862
* so if we parsed in classfile order we would wind up with the annotations
862863
* in reverse order in `sym.annotations`. Instead we just read them out the
@@ -905,6 +906,17 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
905906
}
906907
in.skip(attrLen)
907908

909+
case tpnme.PermittedSubclassesATTR =>
910+
sym.setFlag(SEALED)
911+
val numberOfClasses = u2()
912+
numberOfClasses times {
913+
val k = pool.getClassSymbol(u2())
914+
completer match {
915+
case ctc: ClassTypeCompleter => ctc.permittedSubclasses ::= k // sym.addChild(k)
916+
case _ =>
917+
}
918+
}
919+
908920
case _ =>
909921
in.skip(attrLen)
910922
}
@@ -1357,6 +1369,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
13571369
var exceptions: List[NameOrString] = Nil
13581370
}
13591371
private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter {
1372+
var permittedSubclasses: List[symbolTable.Symbol] = Nil
13601373
override def complete(sym: symbolTable.Symbol): Unit = {
13611374
val info = if (sig != null) sigToType(sym, sig) else {
13621375
val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_*
@@ -1365,6 +1378,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
13651378
ClassInfoType(superTpe1 :: ifacesTypes, instanceScope, clazz)
13661379
}
13671380
sym.setInfo(info)
1381+
for (k <- permittedSubclasses)
1382+
if (k.parentSymbols.contains(sym))
1383+
sym.addChild(k)
13681384
}
13691385
}
13701386

@@ -1494,7 +1510,13 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
14941510
if (flags.isStatic) staticScope else instanceScope
14951511
}
14961512
object ClassfileParser {
1497-
private implicit class GoodTimes(val n: Int) extends AnyVal {
1498-
def times(body: => Unit) = (1 to n).foreach(_ => body)
1513+
private implicit class GoodTimes(private val n: Int) extends AnyVal {
1514+
def times(body: => Unit): Unit = {
1515+
var i = n
1516+
while (i > 0) {
1517+
body
1518+
i -= 1
1519+
}
1520+
}
14991521
}
15001522
}

src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ trait ContextErrors extends splain.SplainErrors {
885885

886886
// def stabilize
887887
def NotAValueError(tree: Tree, sym: Symbol) = {
888-
issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value")
888+
issueNormalTypeError(tree, s"${sym.kindString} ${sym.fullName} is not a value")
889889
setError(tree)
890890
}
891891

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -427,29 +427,27 @@ trait Namers extends MethodSynthesis {
427427
/** Given a ClassDef or ModuleDef, verifies there isn't a companion which
428428
* has been defined in a separate file.
429429
*/
430-
@nowarn("cat=lint-nonlocal-return")
431430
def validateCompanionDefs(tree: ImplDef): Unit = {
432-
val sym = tree.symbol orElse { return }
433-
val ctx = if (context.owner.isPackageObjectClass) context.outer else context
434-
val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name
435-
val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name
436-
val fails = (
437-
module.isModule
438-
&& clazz.isClass
439-
&& !module.isSynthetic
440-
&& !clazz.isSynthetic
441-
&& (clazz.sourceFile ne null)
442-
&& (module.sourceFile ne null)
443-
&& !(module isCoDefinedWith clazz)
444-
&& module.exists
445-
&& clazz.exists
446-
&& (currentRun.compiles(clazz) == currentRun.compiles(module))
447-
)
448-
if (fails) {
449-
reporter.error(tree.pos, (
450-
s"Companions '$clazz' and '$module' must be defined in same file:\n"
451-
+ s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}")
431+
val sym = tree.symbol
432+
if (sym != NoSymbol) {
433+
val ctx = if (context.owner.isPackageObjectClass) context.outer else context
434+
val module = if (sym.isModule) sym else ctx.scope.lookupModule(tree.name)
435+
val clazz = if (sym.isClass) sym else ctx.scope.lookupClass(tree.name)
436+
val fails = (
437+
module.isModule
438+
&& clazz.isClass
439+
&& !module.isSynthetic
440+
&& !clazz.isSynthetic
441+
&& (clazz.sourceFile ne null)
442+
&& (module.sourceFile ne null)
443+
&& !module.isCoDefinedWith(clazz)
444+
&& module.exists
445+
&& clazz.exists
446+
&& currentRun.compiles(clazz) == currentRun.compiles(module)
452447
)
448+
if (fails) reporter.error(tree.pos,
449+
sm"""|Companions '$clazz' and '$module' must be defined in same file:
450+
| Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}""")
453451
}
454452
}
455453

@@ -1186,17 +1184,25 @@ trait Namers extends MethodSynthesis {
11861184
val pending = mutable.ListBuffer[AbsTypeError]()
11871185
parentTrees foreach { tpt =>
11881186
val ptpe = tpt.tpe
1189-
if (!ptpe.isError) {
1187+
if (!ptpe.isError && !phase.erasedTypes) {
11901188
val psym = ptpe.typeSymbol
1191-
val sameSourceFile = context.unit.source.file == psym.sourceFile
1192-
1193-
if (psym.isSealed && !phase.erasedTypes)
1194-
if (sameSourceFile)
1195-
psym addChild context.owner
1189+
if (psym.isSealed) {
1190+
val sameSourceFile = context.unit.source.file == psym.sourceFile
1191+
val okChild =
1192+
if (psym.isJava)
1193+
psym.attachments.get[PermittedSubclassSymbols] match {
1194+
case Some(permitted) => permitted.permits.exists(_ == clazz)
1195+
case _ => sameSourceFile
1196+
}
1197+
else
1198+
sameSourceFile
1199+
if (okChild)
1200+
psym.addChild(clazz)
11961201
else
11971202
pending += ParentSealedInheritanceError(tpt, psym)
1198-
if (psym.isLocalToBlock && psym.isClass && !phase.erasedTypes)
1199-
psym addChild context.owner
1203+
}
1204+
if (psym.isLocalToBlock && psym.isClass)
1205+
psym.addChild(clazz)
12001206
}
12011207
}
12021208
pending.foreach(ErrorUtils.issueTypeError)
@@ -1214,13 +1220,12 @@ trait Namers extends MethodSynthesis {
12141220

12151221
// add apply and unapply methods to companion objects of case classes,
12161222
// unless they exist already; here, "clazz" is the module class
1217-
if (clazz.isModuleClass) {
1223+
if (clazz.isModuleClass)
12181224
clazz.attachments.get[ClassForCaseCompanionAttachment] foreach { cma =>
12191225
val cdef = cma.caseClass
12201226
assert(cdef.mods.isCase, "expected case class: "+ cdef)
12211227
addApplyUnapply(cdef, templateNamer)
12221228
}
1223-
}
12241229

12251230
// add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because
12261231
// the namer phase must traverse this copy method to create default getters for its parameters.
@@ -1266,6 +1271,11 @@ trait Namers extends MethodSynthesis {
12661271
val res = GenPolyType(tparams0, resultType)
12671272

12681273
val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType)
1274+
cdef.getAndRemoveAttachment[PermittedSubclasses].foreach { permitted =>
1275+
clazz.updateAttachment[PermittedSubclassSymbols] {
1276+
PermittedSubclassSymbols(permitted.permits.map(typer.typed(_, Mode.NOmode).symbol))
1277+
}
1278+
}
12691279

12701280
// Already assign the type to the class symbol (monoTypeCompleter will do it again).
12711281
// Allows isDerivedValueClass to look at the info.

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1798,7 +1798,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
17981798
if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature)
17991799

18001800
def validateParentClass(parent: Tree, superclazz: Symbol) =
1801-
if (!parent.isErrorTyped) {
1801+
if (!parent.isErrorTyped) { // redundant
18021802
val psym = parent.tpe.typeSymbol.initialize
18031803

18041804
if (!context.unit.isJava)
@@ -1863,7 +1863,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
18631863

18641864
if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) {
18651865
val superclazz = parents.head.tpe.typeSymbol
1866-
for (p <- parents) validateParentClass(p, superclazz)
1866+
parents.foreach(validateParentClass(_, superclazz))
18671867
}
18681868

18691869
pending.foreach(ErrorUtils.issueTypeError)
@@ -2041,7 +2041,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
20412041
val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body))
20422042
enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1)
20432043
if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
2044-
validateParentClasses(parents1, selfType, clazz.isTrait)
2044+
validateParentClasses(parents1, selfType, clazz.isTrait)
20452045
if (clazz.isCase)
20462046
validateNoCaseAncestor(clazz)
20472047
if (clazz.isTrait && hasSuperArgs(parents1.head))
@@ -2075,11 +2075,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
20752075
ctors.foreach(AuxConstrInConstantAnnotation(_, clazz))
20762076
}
20772077

2078-
20792078
if (clazz.isTrait) {
2080-
for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
2081-
context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other)
2082-
}
2079+
for (decl <- clazz.info.decls)
2080+
if (decl.isTerm && decl.isEarlyInitialized)
2081+
context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other)
20832082
}
20842083

20852084
treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_*

src/partest/scala/tools/partest/nest/AbstractRunner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour
291291
List(pathSettings.testParent / norm)
292292
}
293293
}
294-
.distinct
294+
.distinct.filter(denotesTestPath)
295295
}
296296

297297
val isRerun = config.optFailed

src/reflect/scala/reflect/internal/StdAttachments.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,8 @@ trait StdAttachments {
154154
case object FieldTypeInferred extends PlainAttachment
155155

156156
case class LookupAmbiguityWarning(msg: String) extends PlainAttachment
157+
158+
/** Java sealed classes may be qualified with a permits clause specifying allowed subclasses. */
159+
case class PermittedSubclasses(permits: List[Tree]) extends PlainAttachment
160+
case class PermittedSubclassSymbols(permits: List[Symbol]) extends PlainAttachment
157161
}

0 commit comments

Comments
 (0)
0