8000 Merge pull request #10105 from som-snytt/issue/12159-java-sealed · scala/scala@ab8feac · GitHub
[go: up one dir, main page]

Skip to content

Commit ab8feac

Browse files
authored
Merge pull request #10105 from som-snytt/issue/12159-java-sealed
Support JDK 17 `PermittedSubclasses` in classfiles
2 parents 28eef15 + 5849a58 commit ab8feac

File tree

38 files changed

+349
-76
lines changed
  • t12159b
  • t12474
  • 38 files changed

    +349
    -76
    lines changed

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

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

    17291729
    def printAllUnits(): Unit = {
    1730-
    print("[[syntax trees at end of %25s]]".format(phase))
    1731-
    exitingPhase(phase)(currentRun.units foreach { unit =>
    1732-
    nodePrinters showUnit unit
    1733-
    })
    1730+
    print(f"[[syntax trees at end of $phase%25s]]")
    1731+
    exitingPhase(phase)(currentRun.units.foreach(nodePrinters.showUnit(_)))
    17341732
    }
    17351733

    17361734
    /** 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< 10000 /code>-
    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
    @@ -1811,7 +1811,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
    18111811
    if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature)
    18121812

    18131813
    def validateParentClass(parent: Tree, superclazz: Symbol) =
    1814-
    if (!parent.isErrorTyped) {
    1814+
    if (!parent.isErrorTyped) { // redundant
    18151815
    val psym = parent.tpe.typeSymbol.initialize
    18161816

    18171817
    if (!context.unit.isJava)
    @@ -1876,7 +1876,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
    18761876

    18771877
    if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) {
    18781878
    val superclazz = parents.head.tpe.typeSymbol
    1879-
    for (p <- parents) validateParentClass(p, superclazz)
    1879+
    parents.foreach(validateParentClass(_, superclazz))
    18801880
    }
    18811881

    18821882
    pending.foreach(ErrorUtils.issueTypeError)
    @@ -2054,7 +2054,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
    20542054
    val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body))
    20552055
    enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1)
    20562056
    if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
    2057-
    validateParentClasses(parents1, selfType, clazz.isTrait)
    2057+
    validateParentClasses(parents1, selfType, clazz.isTrait)
    20582058
    if (clazz.isCase)
    20592059
    validateNoCaseAncestor(clazz)
    20602060
    if (clazz.isTrait && hasSuperArgs(parents1.head))
    @@ -2088,11 +2088,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
    20882088
    ctors.foreach(AuxConstrInConstantAnnotation(_, clazz))
    20892089
    }
    20902090

    2091-
    20922091
    if (clazz.isTrait) {
    2093-
    for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
    2094-
    context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other)
    2095-
    }
    2092+
    for (decl <- clazz.info.decls)
    2093+
    if (decl.isTerm && decl.isEarlyInitialized)
    2094+
    context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other)
    20962095
    }
    20972096

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

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

    Lines changed: 1 addition & 1 deletion
    8FFE
    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