Skip to content

Commit

Permalink
Accept (non-)sealed in Java
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Aug 6, 2022
1 parent 43ee509 commit 0cf154b
Show file tree
Hide file tree
Showing 22 changed files with 185 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala
Expand Up @@ -51,7 +51,7 @@ abstract class CommonTokens {
// J: PUBLIC = 42
final val PROTECTED = 43
final val PRIVATE = 44
// S: SEALED = 45
final val SEALED = 45 // J: contextual keyword
final val ABSTRACT = 46
// J: DEFAULT = 47
// J: STATIC = 48
Expand Down
1 change: 0 additions & 1 deletion src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
Expand Up @@ -28,7 +28,6 @@ object Tokens extends CommonTokens {
/** modifiers */
final val IMPLICIT = 40
final val OVERRIDE = 41
final val SEALED = 45
final val LAZY = 55
final val MACRO = 57

Expand Down
56 changes: 46 additions & 10 deletions src/compiler/scala/tools/nsc/javac/JavaParsers.scala
Expand Up @@ -16,13 +16,12 @@
package scala.tools.nsc
package javac

import scala.collection.mutable.ListBuffer
import symtab.Flags
import JavaTokens._
import scala.annotation.tailrec
import scala.annotation._
import scala.collection.mutable.ListBuffer
import scala.language.implicitConversions
import scala.reflect.internal.util.Position
import scala.reflect.internal.util.ListOfNil
import scala.reflect.internal.util.{ListOfNil, Position}
import scala.tools.nsc.Reporting.WarningCategory

trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
Expand Down Expand Up @@ -493,11 +492,39 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
case SYNCHRONIZED =>
in.nextToken()
case _ =>
val privateWithin: TypeName =
if (isPackageAccess && !inInterface) thisPackageName
else tpnme.EMPTY

return Modifiers(flags, privateWithin) withAnnotations annots
val unsealed = 0L // no flag for UNSEALED
def consume(added: FlagSet): false = { in.nextToken(); flags |= added; false }
def lookingAhead(s: String): Boolean = {
import scala.reflect.internal.Chars._
var i = 0
val n = s.length
val lookahead = in.in.lookahead
while (i < n && lookahead.ch != SU) {
if (lookahead.ch != s.charAt(i)) return false
lookahead.next()
i += 1
}
i == n && Character.isWhitespace(lookahead.ch)
}
val done = (in.token != IDENTIFIER) || (
in.name match {
case nme.javaRestrictedIdentifiers.SEALED => consume(Flags.SEALED)
case nme.javaRestrictedIdentifiers.UNSEALED => consume(unsealed)
case nme.javaRestrictedIdentifiers.NON =>
!lookingAhead("-sealed") || {
in.nextToken()
in.nextToken()
consume(unsealed)
}
case _ => true
}
)
if (done) {
val privateWithin: TypeName =
if (isPackageAccess && !inInterface) thisPackageName
else tpnme.EMPTY
return Modifiers(flags, privateWithin) withAnnotations annots
}
}
}
abort("should not be here")
Expand Down Expand Up @@ -802,6 +829,14 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
List()
}

def permitsOpt() =
if (in.token == IDENTIFIER && in.name == nme.javaRestrictedIdentifiers.PERMITS) {
in.nextToken()
repsep(() => typ(), COMMA)
} else {
Nil
}

def classDecl(mods: Modifiers): List[Tree] = {
accept(CLASS)
val pos = in.currentPos
Expand All @@ -815,6 +850,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
javaLangObject()
}
val interfaces = interfacesOpt()
@unused val permits = permitsOpt()
val (statics, body) = typeBody(CLASS)
addCompanionObject(statics, atPos(pos) {
ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body))
Expand Down Expand Up @@ -878,6 +914,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
} else {
List(javaLangObject())
}
@unused val permits = permitsOpt()
val (statics, body) = typeBody(INTERFACE)
addCompanionObject(statics, atPos(pos) {
ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT,
Expand Down Expand Up @@ -905,7 +942,6 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
} else if (in.token == SEMI) {
in.nextToken()
} else {

// See "14.3. Local Class and Interface Declarations"
adaptRecordIdentifier()
if (in.token == ENUM || in.token == RECORD || definesInterface(in.token))
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/javac/JavaTokens.scala
Expand Up @@ -38,6 +38,7 @@ object JavaTokens extends ast.parser.CommonTokens {
final val NATIVE = 53
final val STRICTFP = 54
final val THROWS = 56
final val UNSEALED = 57 // contextual keyword

/** templates */
final val INTERFACE = 66
Expand Down
Expand Up @@ -904,6 +904,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
}
in.skip(attrLen)

case tpnme.PermittedSubclassesATTR =>
debuglog(s"$sym is Java sealed.")
sym.setFlag(SEALED)
in.skip(attrLen) // https://openjdk.java.net/jeps/360 but ignore permits and respect only Scala semantics

case _ =>
in.skip(attrLen)
}
Expand Down
5 changes: 5 additions & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -320,6 +320,7 @@ trait StdNames {
final val SignatureATTR: NameType = nameType("Signature")
final val SourceFileATTR: NameType = nameType("SourceFile")
final val SyntheticATTR: NameType = nameType("Synthetic")
final val PermittedSubclassesATTR: NameType = nameType("PermittedSubclasses")

final val scala_ : NameType = nameType("scala")

Expand Down Expand Up @@ -1281,7 +1282,11 @@ trait StdNames {
// A type identifier is an identifier that is not the character sequence var, yield, or record.
// An unqualified method identifier is an identifier that is not the character sequence yield.
class JavaRestrictedIdentifiers {
final val PERMITS: TermName = TermName("permits")
final val RECORD: TermName = TermName("record")
final val SEALED: TermName = TermName("sealed")
final val UNSEALED: TermName = TermName("non-sealed")
final val NON: TermName = TermName("non")
final val VAR: TermName = TermName("var")
final val YIELD: TermName = TermName("yield")
}
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Trees.scala
Expand Up @@ -1426,7 +1426,7 @@ trait Trees extends api.Trees {
else Modifiers(flags, privateWithin, newAnns) setPositions positions
}

override def toString = "Modifiers(%s, %s, %s)".format(flagString, annotations mkString ", ", positions)
override def toString = s"Modifiers($flagString, ${annotations.mkString(",")}, $positions)"
}

object Modifiers extends ModifiersExtractor
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/t12159.check
@@ -0,0 +1,7 @@
s.scala:5: error: illegal inheritance from sealed class H
class S extends H {
^
s.scala:8: error: illegal inheritance from sealed trait I
trait T extends I {
^
2 errors
19 changes: 19 additions & 0 deletions test/files/neg/t12159/H.java
@@ -0,0 +1,19 @@
// javaVersion: 17+
package p;

sealed public class H {
}

final class K extends H {
}

non-sealed class L extends H {
}

sealed
class P extends H {
}

final
class Q extends P {
}
6 changes: 6 additions & 0 deletions test/files/neg/t12159/I.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

sealed interface I permits J {
}
6 changes: 6 additions & 0 deletions test/files/neg/t12159/J.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

sealed public class J implements I permits M {
}
9 changes: 9 additions & 0 deletions test/files/neg/t12159/M.java
@@ -0,0 +1,9 @@
// javaVersion: 17+

package p;

public final class M extends J {
}

final class N extends L {
}
9 changes: 9 additions & 0 deletions test/files/neg/t12159/s.scala
@@ -0,0 +1,9 @@
// javaVersion: 17+

package p

class S extends H {
}

trait T extends I {
}
4 changes: 4 additions & 0 deletions test/files/neg/t12159b.check
@@ -0,0 +1,4 @@
s_2.scala:5: error: illegal inheritance from sealed trait I_1
class S extends I_1
^
1 error
6 changes: 6 additions & 0 deletions test/files/neg/t12159b/I_1.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

sealed interface I_1 permits J_1 {
}
6 changes: 6 additions & 0 deletions test/files/neg/t12159b/J_1.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

public final class J_1 implements I_1 {
}
8 changes: 8 additions & 0 deletions test/files/neg/t12159b/s_2.scala
@@ -0,0 +1,8 @@
// javaVersion: 17+
// skalac: -Vdebug -Vlog:_

package p

class S extends I_1

//[log typer] trait I_1 is Java sealed.
19 changes: 19 additions & 0 deletions test/files/pos/t12159/H.java
@@ -0,0 +1,19 @@
// javaVersion: 17+
package p;

sealed public class H {
}

final class K extends H {
}

non-sealed class L extends H {
}

sealed
class P extends H {
}

final
class Q extends P {
}
6 changes: 6 additions & 0 deletions test/files/pos/t12159/I.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

sealed interface I permits J {
}
6 changes: 6 additions & 0 deletions test/files/pos/t12159/J.java
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p;

sealed public class J implements I permits M {
}
9 changes: 9 additions & 0 deletions test/files/pos/t12159/M.java
@@ -0,0 +1,9 @@
// javaVersion: 17+

package p;

public final class M extends J {
}

final class N extends L {
}
6 changes: 6 additions & 0 deletions test/files/pos/t12159/s.scala
@@ -0,0 +1,6 @@
// javaVersion: 17+

package p

class S extends L {
}

0 comments on commit 0cf154b

Please sign in to comment.