diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 89da5fe0ac59..d8575d20914e 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1727,10 +1727,8 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } // class Run def printAllUnits(): Unit = { - print("[[syntax trees at end of %25s]]".format(phase)) - exitingPhase(phase)(currentRun.units foreach { unit => - nodePrinters showUnit unit - }) + print(f"[[syntax trees at end of $phase%25s]]") + exitingPhase(phase)(currentRun.units.foreach(nodePrinters.showUnit(_))) } /** We resolve the class/object ambiguity by passing a type/term name. diff --git a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala index 090c517054f7..a3b858c34fbc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/CommonTokens.scala @@ -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 diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala index 56dbf3db7494..c846cc55ec81 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala @@ -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 diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 2049693a81f3..c6894afba4df 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -16,14 +16,14 @@ 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 +import scala.util.chaining._ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val global : Global @@ -493,11 +493,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") @@ -802,6 +830,13 @@ 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 @@ -815,9 +850,11 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { javaLangObject() } val interfaces = interfacesOpt() + val permits = permitsOpt() val (statics, body) = typeBody(CLASS) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -878,11 +915,13 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } else { List(javaLangObject()) } + val permits = permitsOpt() val (statics, body) = typeBody(INTERFACE) addCompanionObject(statics, atPos(pos) { ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT, name, tparams, makeTemplate(parents, body)) + .tap(cd => if (permits.nonEmpty) cd.updateAttachment(PermittedSubclasses(permits))) }) } @@ -905,7 +944,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)) diff --git a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala index a124d1b90aaa..66fcdf7c069c 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaTokens.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaTokens.scala @@ -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 diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 80d7285f9e56..c841725e004e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -853,10 +853,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { in.skip(attrLen) case tpnme.RuntimeAnnotationATTR => - val numAnnots = u2() + val numAnnots = u2() val annots = new ListBuffer[AnnotationInfo] - for (n <- 0 until numAnnots; annot <- parseAnnotation(u2())) - annots += annot + numAnnots times { + parseAnnotation(u2()).foreach(annots.addOne) + } /* `sym.withAnnotations(annots)`, like `sym.addAnnotation(annot)`, prepends, * so if we parsed in classfile order we would wind up with the annotations * in reverse order in `sym.annotations`. Instead we just read them out the @@ -905,6 +906,17 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } in.skip(attrLen) + case tpnme.PermittedSubclassesATTR => + sym.setFlag(SEALED) + val numberOfClasses = u2() + numberOfClasses times { + val k = pool.getClassSymbol(u2()) + completer match { + case ctc: ClassTypeCompleter => ctc.permittedSubclasses ::= k // sym.addChild(k) + case _ => + } + } + case _ => in.skip(attrLen) } @@ -1357,6 +1369,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { var exceptions: List[NameOrString] = Nil } private final class ClassTypeCompleter(name: Name, jflags: JavaAccFlags, parent: NameOrString, ifaces: List[NameOrString]) extends JavaTypeCompleter { + var permittedSubclasses: List[symbolTable.Symbol] = Nil override def complete(sym: symbolTable.Symbol): Unit = { val info = if (sig != null) sigToType(sym, sig) else { val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_* @@ -1365,6 +1378,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { ClassInfoType(superTpe1 :: ifacesTypes, instanceScope, clazz) } sym.setInfo(info) + for (k <- permittedSubclasses) + if (k.parentSymbols.contains(sym)) + sym.addChild(k) } } @@ -1494,7 +1510,13 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { if (flags.isStatic) staticScope else instanceScope } object ClassfileParser { - private implicit class GoodTimes(val n: Int) extends AnyVal { - def times(body: => Unit) = (1 to n).foreach(_ => body) + private implicit class GoodTimes(private val n: Int) extends AnyVal { + def times(body: => Unit): Unit = { + var i = n + while (i > 0) { + body + i -= 1 + } + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 09b2bb644d22..800fef31e99d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -885,7 +885,7 @@ trait ContextErrors extends splain.SplainErrors { // def stabilize def NotAValueError(tree: Tree, sym: Symbol) = { - issueNormalTypeError(tree, sym.kindString + " " + sym.fullName + " is not a value") + issueNormalTypeError(tree, s"${sym.kindString} ${sym.fullName} is not a value") setError(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index a4b54dc2d6f6..f4487e2bfa5e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -427,29 +427,27 @@ trait Namers extends MethodSynthesis { /** Given a ClassDef or ModuleDef, verifies there isn't a companion which * has been defined in a separate file. */ - @nowarn("cat=lint-nonlocal-return") def validateCompanionDefs(tree: ImplDef): Unit = { - val sym = tree.symbol orElse { return } - val ctx = if (context.owner.isPackageObjectClass) context.outer else context - val module = if (sym.isModule) sym else ctx.scope lookupModule tree.name - val clazz = if (sym.isClass) sym else ctx.scope lookupClass tree.name - val fails = ( - module.isModule - && clazz.isClass - && !module.isSynthetic - && !clazz.isSynthetic - && (clazz.sourceFile ne null) - && (module.sourceFile ne null) - && !(module isCoDefinedWith clazz) - && module.exists - && clazz.exists - && (currentRun.compiles(clazz) == currentRun.compiles(module)) - ) - if (fails) { - reporter.error(tree.pos, ( - s"Companions '$clazz' and '$module' must be defined in same file:\n" - + s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}") + val sym = tree.symbol + if (sym != NoSymbol) { + val ctx = if (context.owner.isPackageObjectClass) context.outer else context + val module = if (sym.isModule) sym else ctx.scope.lookupModule(tree.name) + val clazz = if (sym.isClass) sym else ctx.scope.lookupClass(tree.name) + val fails = ( + module.isModule + && clazz.isClass + && !module.isSynthetic + && !clazz.isSynthetic + && (clazz.sourceFile ne null) + && (module.sourceFile ne null) + && !module.isCoDefinedWith(clazz) + && module.exists + && clazz.exists + && currentRun.compiles(clazz) == currentRun.compiles(module) ) + if (fails) reporter.error(tree.pos, + sm"""|Companions '$clazz' and '$module' must be defined in same file: + | Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}""") } } @@ -1186,17 +1184,25 @@ trait Namers extends MethodSynthesis { val pending = mutable.ListBuffer[AbsTypeError]() parentTrees foreach { tpt => val ptpe = tpt.tpe - if (!ptpe.isError) { + if (!ptpe.isError && !phase.erasedTypes) { val psym = ptpe.typeSymbol - val sameSourceFile = context.unit.source.file == psym.sourceFile - - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner + if (psym.isSealed) { + val sameSourceFile = context.unit.source.file == psym.sourceFile + val okChild = + if (psym.isJava) + psym.attachments.get[PermittedSubclassSymbols] match { + case Some(permitted) => permitted.permits.exists(_ == clazz) + case _ => sameSourceFile + } + else + sameSourceFile + if (okChild) + psym.addChild(clazz) else pending += ParentSealedInheritanceError(tpt, psym) - if (psym.isLocalToBlock && psym.isClass && !phase.erasedTypes) - psym addChild context.owner + } + if (psym.isLocalToBlock && psym.isClass) + psym.addChild(clazz) } } pending.foreach(ErrorUtils.issueTypeError) @@ -1214,13 +1220,12 @@ trait Namers extends MethodSynthesis { // add apply and unapply methods to companion objects of case classes, // unless they exist already; here, "clazz" is the module class - if (clazz.isModuleClass) { + if (clazz.isModuleClass) clazz.attachments.get[ClassForCaseCompanionAttachment] foreach { cma => val cdef = cma.caseClass assert(cdef.mods.isCase, "expected case class: "+ cdef) addApplyUnapply(cdef, templateNamer) } - } // add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because // the namer phase must traverse this copy method to create default getters for its parameters. @@ -1266,6 +1271,11 @@ trait Namers extends MethodSynthesis { val res = GenPolyType(tparams0, resultType) val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType) + cdef.getAndRemoveAttachment[PermittedSubclasses].foreach { permitted => + clazz.updateAttachment[PermittedSubclassSymbols] { + PermittedSubclassSymbols(permitted.permits.map(typer.typed(_, Mode.NOmode).symbol)) + } + } // Already assign the type to the class symbol (monoTypeCompleter will do it again). // Allows isDerivedValueClass to look at the info. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8ac7eef53025..1c66e180e85c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1811,7 +1811,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature) def validateParentClass(parent: Tree, superclazz: Symbol) = - if (!parent.isErrorTyped) { + if (!parent.isErrorTyped) { // redundant val psym = parent.tpe.typeSymbol.initialize if (!context.unit.isJava) @@ -1876,7 +1876,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) { val superclazz = parents.head.tpe.typeSymbol - for (p <- parents) validateParentClass(p, superclazz) + parents.foreach(validateParentClass(_, superclazz)) } pending.foreach(ErrorUtils.issueTypeError) @@ -2054,7 +2054,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body)) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1) if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore - validateParentClasses(parents1, selfType, clazz.isTrait) + validateParentClasses(parents1, selfType, clazz.isTrait) if (clazz.isCase) validateNoCaseAncestor(clazz) if (clazz.isTrait && hasSuperArgs(parents1.head)) @@ -2088,11 +2088,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ctors.foreach(AuxConstrInConstantAnnotation(_, clazz)) } - if (clazz.isTrait) { - for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) { - context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) - } + for (decl <- clazz.info.decls) + if (decl.isTerm && decl.isEarlyInitialized) + context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other) } treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_* diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index 4212873beaf1..e6473ed074a7 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -291,7 +291,7 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour List(pathSettings.testParent / norm) } } - .distinct + .distinct.filter(denotesTestPath) } val isRerun = config.optFailed diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 1f80be5f61cb..3089d24483c8 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -154,4 +154,8 @@ trait StdAttachments { case object FieldTypeInferred extends PlainAttachment case class LookupAmbiguityWarning(msg: String) extends PlainAttachment + + /** Java sealed classes may be qualified with a permits clause specifying allowed subclasses. */ + case class PermittedSubclasses(permits: List[Tree]) extends PlainAttachment + case class PermittedSubclassSymbols(permits: List[Symbol]) extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c41d05c658dd..566a352797b9 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -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") @@ -1277,11 +1278,16 @@ trait StdNames { final val keywords = kw.result } - // "The identifiers var, yield, and record are restricted identifiers because they are not allowed in some contexts" - // 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. + // The identifiers non-sealed, permits, record, sealed, var, and yield are restricted identifiers + // because they are not allowed in some contexts. + // A type identifier is an identifier that is not the character sequence permits, record, sealed, var, or yield. + // An unqualified method identifier is an identifier that is not the character sequence yield. (JLS 3.8) 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") } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 167835cd08cd..016c7ed6c245 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1011,13 +1011,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isStaticOwner: Boolean = isPackageClass || isModuleClass && isStatic - /** A helper function for isEffectivelyFinal. */ - private def isNotOverridden = - owner.isClass && ( - owner.isEffectivelyFinal - || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) - ) - /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( hasFlag(FINAL | PACKAGE) && this != SingletonClass @@ -1028,7 +1021,14 @@ trait Symbols extends api.Symbols { self: SymbolTable => || isClass && !isRefinementClass && originalOwner.isTerm && children.isEmpty ) /** Is this symbol effectively final or a concrete term member of sealed class whose children do not override it */ - final def isEffectivelyFinalOrNotOverridden: Boolean = isEffectivelyFinal || (isTerm && !isDeferred && isNotOverridden) + final def isEffectivelyFinalOrNotOverridden: Boolean = { + def isNotOverridden = + owner.isClass && ( + owner.isEffectivelyFinal + || owner.isSealed && owner.sealedChildren.forall(c => c.isEffectivelyFinal && overridingSymbol(c) == NoSymbol) + ) + isEffectivelyFinal || isTerm && !isDeferred && isNotOverridden + } /** Is this symbol owned by a package? */ final def isTopLevel = owner.isPackageClass diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 8fb0977b3efc..fff71d710f77 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -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 diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 9ab54deaa744..990bb8a438b6 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1769,9 +1769,9 @@ trait Types } } } - //Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache eq undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.typeSymbol) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.typeSymbol}") } protected def defineBaseClassesOfCompoundType(tpe: CompoundType): Unit = { @@ -2792,8 +2792,9 @@ trait Types } } } + //Console.println(s"baseTypeSeq(${tpe.typeSymbol}) = ${tpe.baseTypeSeqCache.toList}") //DEBUG if (tpe.baseTypeSeqCache == undetBaseTypeSeq) - throw new TypeError("illegal cyclic inheritance involving " + tpe.sym) + throw new TypeError(s"illegal cyclic inheritance involving ${tpe.sym}") } /** A class representing a method type with parameters. diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 703e000cacd1..36a67706e249 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -80,6 +80,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.TypedExpectingUnitAttachment this.FieldTypeInferred this.LookupAmbiguityWarning + this.PermittedSubclasses + this.PermittedSubclassSymbols this.noPrint this.typeDebug // inaccessible: this.posAssigner diff --git a/test/files/neg/t12159.check b/test/files/neg/t12159.check new file mode 100644 index 000000000000..178017725d11 --- /dev/null +++ b/test/files/neg/t12159.check @@ -0,0 +1,4 @@ +s.scala:11: error: not found: type ZZ +class Z extends ZZ // fail compile: remove when source flags are restored + ^ +1 error diff --git a/test/files/neg/t12159/H.java b/test/files/neg/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/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 { +} diff --git a/test/files/neg/t12159/I.java b/test/files/neg/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159/J.java b/test/files/neg/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/neg/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/neg/t12159/M.java b/test/files/neg/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/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 { +} diff --git a/test/files/neg/t12159/s.scala b/test/files/neg/t12159/s.scala new file mode 100644 index 000000000000..58a01877e3c3 --- /dev/null +++ b/test/files/neg/t12159/s.scala @@ -0,0 +1,11 @@ +// javaVersion: 17+ + +package p + +class S extends H { +} + +trait T extends I { +} + +class Z extends ZZ // fail compile: remove when source flags are restored diff --git a/test/files/neg/t12159b.check b/test/files/neg/t12159b.check new file mode 100644 index 000000000000..14dd6627065d --- /dev/null +++ b/test/files/neg/t12159b.check @@ -0,0 +1,4 @@ +s_2.scala:5: error: illegal inheritance from sealed trait I +class S extends I + ^ +1 error diff --git a/test/files/neg/t12159b/I.java b/test/files/neg/t12159b/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/neg/t12159b/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/neg/t12159b/J.java b/test/files/neg/t12159b/J.java new file mode 100644 index 000000000000..12de6f9fcbd4 --- /dev/null +++ b/test/files/neg/t12159b/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +public final class J implements I { +} diff --git a/test/files/neg/t12159b/s_2.scala b/test/files/neg/t12159b/s_2.scala new file mode 100644 index 000000000000..ec5f40dba180 --- /dev/null +++ b/test/files/neg/t12159b/s_2.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +package p + +class S extends I diff --git a/test/files/neg/t12159c.check b/test/files/neg/t12159c.check new file mode 100644 index 000000000000..189c51ef6817 --- /dev/null +++ b/test/files/neg/t12159c.check @@ -0,0 +1,7 @@ +s_2.scala:7: warning: match may not be exhaustive. +It would fail on the following input: K() + h match { + ^ +error: No warnings can be incurred under -Werror. +1 warning +1 error diff --git a/test/files/neg/t12159c/H.java b/test/files/neg/t12159c/H.java new file mode 100644 index 000000000000..bf6394e1e869 --- /dev/null +++ b/test/files/neg/t12159c/H.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H { +} + +final class J extends H { +} + +final class K extends H { +} + +final class L extends H { +} diff --git a/test/files/neg/t12159c/s_2.scala b/test/files/neg/t12159c/s_2.scala new file mode 100644 index 000000000000..ec9c445cad42 --- /dev/null +++ b/test/files/neg/t12159c/s_2.scala @@ -0,0 +1,12 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H) = + h match { + case j: J => j.toString + case l: L => l.toString + } +} + diff --git a/test/files/pos/t12159/H.java b/test/files/pos/t12159/H.java new file mode 100644 index 000000000000..3a15309f733e --- /dev/null +++ b/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 { +} diff --git a/test/files/pos/t12159/I.java b/test/files/pos/t12159/I.java new file mode 100644 index 000000000000..f91c69dd7828 --- /dev/null +++ b/test/files/pos/t12159/I.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed interface I permits J { +} diff --git a/test/files/pos/t12159/J.java b/test/files/pos/t12159/J.java new file mode 100644 index 000000000000..5bd2c4c92374 --- /dev/null +++ b/test/files/pos/t12159/J.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p; + +sealed public class J implements I permits M { +} diff --git a/test/files/pos/t12159/M.java b/test/files/pos/t12159/M.java new file mode 100644 index 000000000000..245c79304d29 --- /dev/null +++ b/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 { +} diff --git a/test/files/pos/t12159/s.scala b/test/files/pos/t12159/s.scala new file mode 100644 index 000000000000..29eb9518ea6c --- /dev/null +++ b/test/files/pos/t12159/s.scala @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +package p + +class S extends L { +} diff --git a/test/files/pos/t12159b/H_1.java b/test/files/pos/t12159b/H_1.java new file mode 100644 index 000000000000..cb3ccb9749fc --- /dev/null +++ b/test/files/pos/t12159b/H_1.java @@ -0,0 +1,14 @@ +// javaVersion: 17+ +package p; + +sealed abstract public class H_1 { +} + +final class J extends H_1 { +} + +final class K extends H_1 { +} + +final class L extends H_1 { +} diff --git a/test/files/pos/t12159b/s_2.scala b/test/files/pos/t12159b/s_2.scala new file mode 100644 index 000000000000..881204f4d830 --- /dev/null +++ b/test/files/pos/t12159b/s_2.scala @@ -0,0 +1,13 @@ +// javaVersion: 17+ +// scalac: -Werror +package p + +class C { + def f(h: H_1) = + h match { + case j: J => j.toString + case k: K => k.toString + case l: L => l.toString + } +} + diff --git a/test/files/pos/t12474/Nat.java b/test/files/pos/t12474/Nat.java new file mode 100644 index 000000000000..c9de3f590d84 --- /dev/null +++ b/test/files/pos/t12474/Nat.java @@ -0,0 +1,6 @@ +// javaVersion: 17+ + +public sealed interface Nat permits Nat.Zero, Nat.Succ { + public static final record Zero() implements Nat {} + public static final record Succ(Nat pred) implements Nat {} +} diff --git a/test/files/pos/t12474/s.scala b/test/files/pos/t12474/s.scala new file mode 100644 index 000000000000..2f4e4ed3a9ad --- /dev/null +++ b/test/files/pos/t12474/s.scala @@ -0,0 +1,5 @@ +// javaVersion: 17+ + +class S { + def j: Nat = new Nat.Zero +}