diff options
35 files changed, 480 insertions, 70 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index af80bedf5b..6e260872c9 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -16,6 +16,32 @@ filter { { matchName="scala.sys.process.ProcessImpl#CompoundProcess.getExitValue" problemName=DirectMissingMethodProblem + }, + # these next seven can be removed once there is a fix for + # https://github.com/typesafehub/migration-manager/issues/147 + { + matchName="scala.collection.Iterator#Leading#1.hd_=" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.collection.Iterator#Leading#1.hd" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.collection.Iterator#Leading#1.status_=" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.collection.Iterator#Leading#1.lookahead_=" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.collection.Iterator#Leading#1.status" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.collection.Iterator#Leading#1.lookahead" + problemName=DirectMissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index 541268e50e..7ccd2ea8fa 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -20,6 +20,20 @@ filter { { matchName="scala.sys.process.ProcessImpl#CompoundProcess.futureThread" problemName=DirectMissingMethodProblem + }, + { + matchName="scala.reflect.runtime.Settings.Yvirtpatmat" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.reflect.io.PlainNioFile" + problemName=MissingClassProblem + }, + # this one can be removed once there is a fix for + # https://github.com/typesafehub/migration-manager/issues/147 + { + matchName="scala.collection.Iterator#Leading#1.trailer" + problemName=DirectMissingMethodProblem } ] } diff --git a/project/plugins.sbt b/project/plugins.sbt index e056de55ec..d2476d9207 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -19,4 +19,4 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -libraryDependencies += "com.typesafe" %% "mima-reporter" % "0.1.11" +libraryDependencies += "com.typesafe" %% "mima-reporter" % "0.1.12" diff --git a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl index 5e6b3c041e..b5a238f7be 100755 --- a/src/compiler/scala/tools/ant/templates/tool-unix.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-unix.tmpl @@ -160,7 +160,7 @@ while [[ $# -gt 0 ]]; do shift 2 ;; -nobootcp) - unset usebootcp + usebootcp="false" shift ;; -usebootcp) @@ -189,15 +189,22 @@ declare -a classpath_args # default to the boot classpath for speed, except on cygwin/mingw/msys because # JLine on Windows requires a custom DLL to be loaded. -unset usebootcp -if [[ -z "$cygwin$mingw$msys" ]]; then +if [[ "$usebootcp" != "false" && -z "$cygwin$mingw$msys" ]]; then usebootcp="true" fi # If using the boot classpath, also pass an empty classpath # to java to suppress "." from materializing. -if [[ -n $usebootcp ]]; then +if [[ "$usebootcp" == "true" ]]; then classpath_args=("-Xbootclasspath/a:$TOOL_CLASSPATH" -classpath "\"\"") + # Note that the version numbers go 1.7, 1.8, 9, 10, ... + java_release="$(cat $JAVA_HOME/release | grep JAVA_VERSION)" + if [[ ! "$java_release" =~ JAVA_VERSION=\"1\. ]]; then + # Java 9 removed sun.boot.class.path, and the supposed replacement to at least see + # the appended boot classpath (jdk.boot.class.path.append) is not visible. + # So we have to pass a custom system property that PathResolver will find. + classpath_args+=("-Dscala.boot.class.path=$TOOL_CLASSPATH") + fi else classpath_args=(-classpath "$TOOL_CLASSPATH") fi diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index e58d2d3b43..464fa1ad18 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -185,6 +185,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } + private var propCnt = 0 + @inline final def withPropagateCyclicReferences[T](t: => T): T = { + try { + propCnt = propCnt+1 + t + } finally { + propCnt = propCnt-1 + assert(propCnt >= 0) + } + } + + def propagateCyclicReferences: Boolean = propCnt > 0 + /** Representing ASTs as graphs */ object treeBrowsers extends { val global: Global.this.type = Global.this @@ -333,8 +346,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // to create it on that side. For this one my strategy is a constant def at the file // where I need it, and then an override in Global with the setting. override protected val etaExpandKeepsStar = settings.etaExpandKeepsStar.value - // Here comes another one... - override protected val enableTypeVarExperimentals = settings.Xexperimental.value def getSourceFile(f: AbstractFile): BatchSourceFile = new BatchSourceFile(f, reader read f) diff --git a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala index 3a29f1ba11..80c5ec8828 100644 --- a/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala +++ b/src/compiler/scala/tools/nsc/classpath/ClassPathFactory.scala @@ -4,6 +4,7 @@ package scala.tools.nsc.classpath import scala.reflect.io.{AbstractFile, VirtualDirectory} +import scala.reflect.io.Path.string2path import scala.tools.nsc.Settings import FileUtils.AbstractFileOps import scala.tools.nsc.util.ClassPath @@ -52,7 +53,10 @@ class ClassPathFactory(settings: Settings) { protected def classesInPathImpl(path: String, expand: Boolean) = for { file <- expandPath(path, expand) - dir <- Option(AbstractFile.getDirectory(file)) + dir <- { + def asImage = if (file.endsWith(".jimage")) Some(AbstractFile.getFile(file)) else None + Option(AbstractFile.getDirectory(file)).orElse(asImage) + } } yield newClassPath(dir) private def createSourcePath(file: AbstractFile): ClassPath = diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index c4be59d7eb..133a656206 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -4,7 +4,9 @@ package scala.tools.nsc.classpath import java.io.File -import java.net.URL +import java.net.{URI, URL} +import java.nio.file.{FileSystems, Files, SimpleFileVisitor} +import java.util.function.IntFunction import java.util import java.util.Comparator @@ -119,6 +121,53 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo def asClassPathStrings: Seq[String] = Seq(dir.getPath) } +object JImageDirectoryLookup { + import java.nio.file._, java.net.URI, scala.collection.JavaConverters._ + def apply(): List[ClassPath] = { + try { + val fs = FileSystems.getFileSystem(URI.create("jrt:/")) + val dir: Path = fs.getPath("/modules") + val modules = Files.list(dir).iterator().asScala.toList + modules.map(m => new JImageDirectoryLookup(fs, m.getFileName.toString)) + } catch { + case _: ProviderNotFoundException | _: FileSystemNotFoundException => + Nil + } + } +} +class JImageDirectoryLookup(fs: java.nio.file.FileSystem, module: String) extends DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + import java.nio.file.Path, java.nio.file._ + type F = Path + val dir: Path = fs.getPath("/modules/" + module) + + protected def emptyFiles: Array[Path] = Array.empty + protected def getSubDir(packageDirName: String): Option[Path] = { + val packageDir = dir.resolve(packageDirName) + if (Files.exists(packageDir) && Files.isDirectory(packageDir)) Some(packageDir) + else None + } + protected def listChildren(dir: Path, filter: Option[Path => Boolean]): Array[Path] = { + import scala.collection.JavaConverters._ + val f = filter.getOrElse((p: Path) => true) + Files.list(dir).iterator().asScala.filter(f).toArray[Path] + } + protected def getName(f: Path): String = f.getFileName.toString + protected def toAbstractFile(f: Path): AbstractFile = new scala.reflect.io.PlainNioFile(f) + protected def isPackage(f: Path): Boolean = Files.isDirectory(f) && mayBeValidPackage(f.getFileName.toString) + + def asURLs: Seq[URL] = Seq(dir.toUri.toURL) + def asClassPathStrings: Seq[String] = asURLs.map(_.toString) + + def findClassFile(className: String): Option[AbstractFile] = { + val relativePath = FileUtils.dirPath(className) + ".class" + val classFile = dir.resolve(relativePath) + if (Files.exists(classFile)) Some(new scala.reflect.io.PlainNioFile(classFile)) else None + } + override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) + override protected def isMatchingFile(f: Path): Boolean = Files.isRegularFile(f) && f.getFileName.toString.endsWith(".class") + override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) +} + case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl diff --git a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala index bbcfcb24ca..2ade83c6f9 100644 --- a/src/compiler/scala/tools/nsc/classpath/FileUtils.scala +++ b/src/compiler/scala/tools/nsc/classpath/FileUtils.scala @@ -63,7 +63,7 @@ object FileUtils { // probably it should match a pattern like [a-z_]{1}[a-z0-9_]* but it cannot be changed // because then some tests in partest don't pass - private def mayBeValidPackage(dirName: String): Boolean = + def mayBeValidPackage(dirName: String): Boolean = (dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.') def mkFileFilter(f: JFile => Boolean) = new FileFilter { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 5eb99e0d98..a3b9df1518 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -215,6 +215,7 @@ trait ScalaSettings extends AbsScalaSettings val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212) val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") val YpartialUnification = BooleanSetting ("-Ypartial-unification", "Enable partial unification in type constructor inference") + val Yvirtpatmat = BooleanSetting ("-Yvirtpatmat", "Enable pattern matcher virtualization") val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method") diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index fcfcc8feb9..2d8d591b6d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -655,9 +655,6 @@ trait ContextErrors { def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) = NormalTypeError(parent, "illegal inheritance from final "+mixin) - def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = - NormalTypeError(parent, "illegal inheritance from sealed " + psym ) - def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) = NormalTypeError(parent, "illegal inheritance;\n self-type "+selfType+" does not conform to "+ @@ -1172,6 +1169,9 @@ trait ContextErrors { def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") + def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = + NormalTypeError(parent, "illegal inheritance from sealed " + psym ) + def RootImportError(tree: Tree) = issueNormalTypeError(tree, "_root_ cannot be imported") diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 27a586e543..b445bc5837 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -115,7 +115,7 @@ trait Namers extends MethodSynthesis { protected def owner = context.owner def contextFile = context.unit.source.file def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // H@ need to ensure that we handle only cyclic references TypeSigError(tree, ex) alt @@ -1031,12 +1031,33 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner + + val parentTrees = typer.typedParentTypes(templ) + + val pending = mutable.ListBuffer[AbsTypeError]() + parentTrees foreach { tpt => + val ptpe = tpt.tpe + if(!ptpe.isError) { + val psym = ptpe.typeSymbol + val sameSourceFile = context.unit.source.file == psym.sourceFile + + if (psym.isSealed && !phase.erasedTypes) + if (sameSourceFile) + psym addChild context.owner + else + pending += ParentSealedInheritanceError(tpt, psym) + if (psym.isLocalToBlock && !phase.erasedTypes) + psym addChild context.owner + } + } + pending.foreach(ErrorUtils.issueTypeError) + def checkParent(tpt: Tree): Type = { if (tpt.tpe.isError) AnyRefTpe else tpt.tpe } - val parents = typer.typedParentTypes(templ) map checkParent + val parents = parentTrees map checkParent enterSelf(templ.self) @@ -1830,6 +1851,12 @@ trait Namers extends MethodSynthesis { abstract class TypeCompleter extends LazyType { val tree: Tree + override def forceDirectSuperclasses: Unit = { + tree.foreach { + case dt: DefTree => global.withPropagateCyclicReferences(Option(dt.symbol).map(_.maybeInitialize)) + case _ => + } + } } def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 82733c1643..fc0a0368ab 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1678,7 +1678,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) } catch { - case ex: TypeError => + case ex: TypeError if !global.propagateCyclicReferences => // fallback in case of cyclic errors // @H none of the tests enter here but I couldn't rule it out // upd. @E when a definition inherits itself, we end up here @@ -1739,13 +1739,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper context.deprecationWarning(parent.pos, psym, report, version) } - if (psym.isSealed && !phase.erasedTypes) - if (sameSourceFile) - psym addChild context.owner - else - pending += ParentSealedInheritanceError(parent, psym) - if (psym.isLocalToBlock && !phase.erasedTypes) - psym addChild context.owner val parentTypeOfThis = parent.tpe.dealias.typeOfThis if (!(selfType <:< parentTypeOfThis) && @@ -2561,7 +2554,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + if (!(settings.Yvirtpatmat && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match)), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize @@ -5554,6 +5547,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } try runTyper() catch { + case ex: CyclicReference if global.propagateCyclicReferences => + throw ex case ex: TypeError => tree.clearType() // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere. diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index c351b6ace1..188cabbc8d 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -234,6 +234,7 @@ final class PathResolver(settings: Settings) { // Assemble the elements! def basis = List[Traversable[ClassPath]]( + JImageDirectoryLookup.apply(), // 0. The Java 9 classpath (backed by the jrt:/ virtual system) classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index fd8f51cfb1..fc49de1cf6 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -81,4 +81,10 @@ trait StdAttachments { /** An attachment carrying information between uncurry and erasure */ case class TypeParamVarargsAttachment(val typeParamRef: Type) + + /** Attached to a class symbol to indicate that its children have been observed + * via knownDirectSubclasses. Children added subsequently will trigger an + * error to indicate that the earlier observation was incomplete. + */ + case object KnownDirectSubclassesCalled extends PlainAttachment } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 0da153349a..e664b5ad08 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -121,6 +121,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => def knownDirectSubclasses = { // See `getFlag` to learn more about the `isThreadsafe` call in the body of this method. if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize + + enclosingPackage.info.decls.foreach { sym => + if(sourceFile == sym.sourceFile) { + sym.rawInfo.forceDirectSuperclasses + } + } + + if(!isPastTyper) + updateAttachment(KnownDirectSubclassesCalled) + children } @@ -3298,7 +3308,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var childSet: Set[Symbol] = Set() override def children = childSet - override def addChild(sym: Symbol) { childSet = childSet + sym } + override def addChild(sym: Symbol) { + if(!isPastTyper && hasAttachment[KnownDirectSubclassesCalled.type] && !childSet.contains(sym)) + globalError(s"knownDirectSubclasses of ${this.name} observed before subclass ${sym.name} registered") + + childSet = childSet + sym + } def anonOrRefinementString = { if (hasCompleteInfo) { diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 5e1bf37b80..ad7e3ffe8f 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -91,7 +91,6 @@ trait Types private var explainSwitch = false private final val emptySymbolSet = immutable.Set.empty[Symbol] - private final val traceTypeVars = sys.props contains "scalac.debug.tvar" private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn on type parameter bounds being used * to seed type constraints. @@ -99,8 +98,6 @@ trait Types private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints" private final val sharperSkolems = sys.props contains "scalac.experimental.sharper-skolems" - protected val enableTypeVarExperimentals = settings.Xexperimental.value - /** Caching the most recent map has a 75-90% hit rate. */ private object substTypeMapCache { private[this] var cached: SubstTypeMap = new SubstTypeMap(Nil, Nil) @@ -315,6 +312,11 @@ trait Types /** If this is a lazy type, assign a new type to `sym`. */ def complete(sym: Symbol) {} + /** If this is a lazy type corresponding to a subclass add it to its + * parents children + */ + def forceDirectSuperclasses: Unit = () + /** The term symbol associated with the type * Note that the symbol of the normalized type is returned (@see normalize) */ @@ -2823,13 +2825,13 @@ trait Types // now, pattern-matching returns the most recent constr object TypeVar { @inline final def trace[T](action: String, msg: => String)(value: T): T = { - if (traceTypeVars) { - val s = msg match { - case "" => "" - case str => "( " + str + " )" - } - Console.err.println("[%10s] %-25s%s".format(action, value, s)) - } + // Uncomment the following for a compiler that has some diagnostics about type inference + // I doubt this is ever useful in the wild, so a recompile will be needed +// val s = msg match { +// case "" => "" +// case str => "( " + str + " )" +// } +// Console.err.println("[%10s] %-25s%s".format(action, value, s)) value } @@ -2850,7 +2852,9 @@ trait Types val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType) if (exclude) new TypeConstraint - else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds)) + else TypeVar.trace("constraint", "For " + tparam.fullLocationString)( + new TypeConstraint(bounds) + ) } else new TypeConstraint } @@ -2879,7 +2883,9 @@ trait Types else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params))) ) - trace("create", "In " + tv.originLocation)(tv) + trace("create", "In " + tv.originLocation)( + tv + ) } private def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar = createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable) @@ -2983,7 +2989,9 @@ trait Types else if (newArgs.size == params.size) { val tv = TypeVar(origin, constr, newArgs, params) tv.linkSuspended(this) - TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv) + TypeVar.trace("applyArgs", s"In $originLocation, apply args ${newArgs.mkString(", ")} to $originName")( + tv + ) } else TypeVar(typeSymbol).setInst(ErrorType) @@ -3002,31 +3010,20 @@ trait Types // only one of them is in the set of tvars that need to be solved, but // they share the same TypeConstraint instance - // When comparing to types containing skolems, remember the highest level - // of skolemization. If that highest level is higher than our initial - // skolemizationLevel, we can't re-use those skolems as the solution of this - // typevar, which means we'll need to repack our inst into a fresh existential. - // were we compared to skolems at a higher skolemizationLevel? - // EXPERIMENTAL: value will not be considered unless enableTypeVarExperimentals is true - // see SI-5729 for why this is still experimental - private var encounteredHigherLevel = false - private def shouldRepackType = enableTypeVarExperimentals && encounteredHigherLevel - // <region name="constraint mutators + undoLog"> // invariant: before mutating constr, save old state in undoLog // (undoLog is used to reset constraints to avoid piling up unrelated ones) - def setInst(tp: Type): this.type = { - if (tp eq this) { + def setInst(tp: Type): this.type = + if (tp ne this) { + undoLog record this + constr.inst = TypeVar.trace("setInst", s"In $originLocation, $originName=$tp")( + tp + ) + this + } else { log(s"TypeVar cycle: called setInst passing $this to itself.") - return this + this } - undoLog record this - // if we were compared against later typeskolems, repack the existential, - // because skolems are only compatible if they were created at the same level - val res = if (shouldRepackType) repackExistential(tp) else tp - constr.inst = TypeVar.trace("setInst", "In " + originLocation + ", " + originName + "=" + res)(res) - this - } def addLoBound(tp: Type, isNumericBound: Boolean = false) { assert(tp != this, tp) // implies there is a cycle somewhere (?) @@ -3251,19 +3248,13 @@ trait Types case ts: TypeSkolem => ts.level > level case _ => false } - // side-effects encounteredHigherLevel - private def containsSkolemAboveLevel(tp: Type) = - (tp exists isSkolemAboveLevel) && { encounteredHigherLevel = true ; true } - /** Can this variable be related in a constraint to type `tp`? + + /** Can this variable be related in a constraint to type `tp`? * This is not the case if `tp` contains type skolems whose * skolemization level is higher than the level of this variable. */ - def isRelatable(tp: Type) = ( - shouldRepackType // short circuit if we already know we've seen higher levels - || !containsSkolemAboveLevel(tp) // side-effects tracking boolean - || enableTypeVarExperimentals // -Xexperimental: always say we're relatable, track consequences - ) + def isRelatable(tp: Type) = !(tp exists isSkolemAboveLevel) override def normalize: Type = ( if (instValid) inst @@ -3311,7 +3302,7 @@ trait Types // to never be resumed with the current implementation assert(!suspended, this) TypeVar.trace("clone", originLocation)( - TypeVar(origin, constr.cloneInternal, typeArgs, params) // @M TODO: clone args/params? + TypeVar(origin, constr.cloneInternal, typeArgs, params) ) } } diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index 5a2c802476..ab933ae617 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -54,6 +54,7 @@ abstract class MutableSettings extends AbsSettings { def uniqid: BooleanSetting def verbose: BooleanSetting def YpartialUnification: BooleanSetting + def Yvirtpatmat: BooleanSetting def Yrecursion: IntSetting def maxClassfileName: IntSetting diff --git a/src/reflect/scala/reflect/io/PlainFile.scala b/src/reflect/scala/reflect/io/PlainFile.scala index eb0940e703..989081ebe0 100644 --- a/src/reflect/scala/reflect/io/PlainFile.scala +++ b/src/reflect/scala/reflect/io/PlainFile.scala @@ -40,7 +40,6 @@ class PlainFile(val givenPath: Path) extends AbstractFile { override def output = givenPath.toFile.outputStream() override def sizeOption = Some(givenPath.length.toInt) - override def toString = path override def hashCode(): Int = fpath.hashCode() override def equals(that: Any): Boolean = that match { case x: PlainFile => fpath == x.fpath @@ -91,3 +90,82 @@ class PlainFile(val givenPath: Path) extends AbstractFile { def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = new PlainFile(givenPath / name) } + +private[scala] class PlainNioFile(nioPath: java.nio.file.Path) extends AbstractFile { + import java.nio.file._ + + assert(nioPath ne null) + + /** Returns the underlying File if any and null otherwise. */ + override def file: java.io.File = try { + nioPath.toFile + } catch { + case _: UnsupportedOperationException => null + } + + override def underlyingSource = Some(this) + + private val fpath = nioPath.toAbsolutePath.toString + + /** Returns the name of this abstract file. */ + def name = nioPath.getFileName.toString + + /** Returns the path of this abstract file. */ + def path = nioPath.toString + + /** The absolute file. */ + def absolute = new PlainNioFile(nioPath.toAbsolutePath) + + override def container: AbstractFile = new PlainNioFile(nioPath.getParent) + override def input = Files.newInputStream(nioPath) + override def output = Files.newOutputStream(nioPath) + override def sizeOption = Some(Files.size(nioPath).toInt) + override def hashCode(): Int = fpath.hashCode() + override def equals(that: Any): Boolean = that match { + case x: PlainNioFile => fpath == x.fpath + case _ => false + } + + /** Is this abstract file a directory? */ + def isDirectory: Boolean = Files.isDirectory(nioPath) + + /** Returns the time that this abstract file was last modified. */ + def lastModified: Long = Files.getLastModifiedTime(nioPath).toMillis + + /** Returns all abstract subfiles of this abstract directory. */ + def iterator: Iterator[AbstractFile] = { + try { + import scala.collection.JavaConverters._ + val it = Files.newDirectoryStream(nioPath).iterator() + it.asScala.map(new PlainNioFile(_)) + } catch { + case _: NotDirectoryException => Iterator.empty + } + } + + /** + * Returns the abstract file in this abstract directory with the + * specified name. If there is no such file, returns null. The + * argument "directory" tells whether to look for a directory or + * or a regular file. + */ + def lookupName(name: String, directory: Boolean): AbstractFile = { + val child = nioPath.resolve(name) + if ((Files.isDirectory(child) && directory) || (Files.isRegularFile(child) && !directory)) new PlainNioFile(child) + else null + } + + /** Does this abstract file denote an existing file? */ + def create(): Unit = if (!exists) Files.createFile(nioPath) + + /** Delete the underlying file or directory (recursively). */ + def delete(): Unit = + if (Files.isRegularFile(nioPath)) Files.deleteIfExists(nioPath) + else if (Files.isDirectory(nioPath)) new Directory(nioPath.toFile).deleteRecursively() + + /** Returns a plain file with the given name. It does not + * check that it exists. + */ + def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = + new PlainNioFile(nioPath.resolve(name)) +} diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index dbafbfc6ba..d5d62b2203 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -48,6 +48,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.OuterArgCanBeElided this.UseInvokeSpecial this.TypeParamVarargsAttachment + this.KnownDirectSubclassesCalled this.noPrint this.typeDebug this.Range diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 3b33f089e1..2d8bacd3b2 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -48,6 +48,7 @@ private[reflect] class Settings extends MutableSettings { val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) val YpartialUnification = new BooleanSetting(false) + val Yvirtpatmat = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/test/files/neg/t7046-2.check b/test/files/neg/t7046-2.check new file mode 100644 index 0000000000..b4efd8b5e9 --- /dev/null +++ b/test/files/neg/t7046-2.check @@ -0,0 +1,3 @@ +error: knownDirectSubclasses of Foo observed before subclass Bar registered +error: knownDirectSubclasses of Foo observed before subclass Baz registered +two errors found diff --git a/test/files/neg/t7046-2/Macros_1.scala b/test/files/neg/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/neg/t7046-2/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/neg/t7046-2/Test_2.scala b/test/files/neg/t7046-2/Test_2.scala new file mode 100644 index 0000000000..18a2ebcbc2 --- /dev/null +++ b/test/files/neg/t7046-2/Test_2.scala @@ -0,0 +1,14 @@ +object Test extends App { + def nested: Unit = { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Bar", "Baz")) + + sealed trait Foo + object Foo { + trait Bar extends Foo + trait Baz extends Foo + } + } + + nested +} diff --git a/test/files/neg/t7046.check b/test/files/neg/t7046.check new file mode 100644 index 0000000000..689520a0aa --- /dev/null +++ b/test/files/neg/t7046.check @@ -0,0 +1,3 @@ +error: knownDirectSubclasses of Foo observed before subclass Local registered +error: knownDirectSubclasses of Foo observed before subclass Riddle registered +two errors found diff --git a/test/files/neg/t7046/Macros_1.scala b/test/files/neg/t7046/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/neg/t7046/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/neg/t7046/Test_2.scala b/test/files/neg/t7046/Test_2.scala new file mode 100644 index 0000000000..fcb3e46a0f --- /dev/null +++ b/test/files/neg/t7046/Test_2.scala @@ -0,0 +1,35 @@ +object Test extends App { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Wibble", "Wobble", "Bar", "Baz")) +} + +sealed trait Foo +object Foo { + trait Wibble extends Foo + case object Wobble extends Foo +} + +trait Bar extends Foo + +object Blah { + type Quux = Foo +} + +import Blah._ + +trait Baz extends Quux + +class Boz[T](t: T) +class Unrelated extends Boz(Test.subs) + +object Enigma { + locally { + // local class not seen + class Local extends Foo + } + + def foo: Unit = { + // local class not seen + class Riddle extends Foo + } +} diff --git a/test/files/pos/t7046-2/Macros_1.scala b/test/files/pos/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..07c0c61281 --- /dev/null +++ b/test/files/pos/t7046-2/Macros_1.scala @@ -0,0 +1,14 @@ +package p1 + +import scala.reflect.macros.blackbox._ +import language.experimental._ + +object Macro { + def impl(c: Context): c.Tree = { + import c.universe._ + val tsym = rootMirror.staticClass("p1.Base") + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + q"$subclasses" + } + def p1_Base_knownDirectSubclasses: List[String] = macro impl +} diff --git a/test/files/pos/t7046-2/Test_2.scala b/test/files/pos/t7046-2/Test_2.scala new file mode 100644 index 0000000000..74e30a863d --- /dev/null +++ b/test/files/pos/t7046-2/Test_2.scala @@ -0,0 +1,9 @@ +package p1 + +sealed trait Base + +object Test { + val x = Macro.p1_Base_knownDirectSubclasses +} + +case class B(val b: Test.x.type) diff --git a/test/files/run/t7046-1/Macros_1.scala b/test/files/run/t7046-1/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/run/t7046-1/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/run/t7046-1/Test_2.scala b/test/files/run/t7046-1/Test_2.scala new file mode 100644 index 0000000000..28459fde72 --- /dev/null +++ b/test/files/run/t7046-1/Test_2.scala @@ -0,0 +1,23 @@ +object Test extends App { + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Wibble", "Wobble", "Bar", "Baz")) +} + +sealed trait Foo +object Foo { + trait Wibble extends Foo + case object Wobble extends Foo +} + +trait Bar extends Foo + +object Blah { + type Quux = Foo +} + +import Blah._ + +trait Baz extends Quux + +class Boz[T](t: T) +class Unrelated extends Boz(Test.subs) diff --git a/test/files/run/t7046-2/Macros_1.scala b/test/files/run/t7046-2/Macros_1.scala new file mode 100644 index 0000000000..2a5bf82f62 --- /dev/null +++ b/test/files/run/t7046-2/Macros_1.scala @@ -0,0 +1,15 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = { + import c.universe._; + val ttpe = ttag.tpe + val tsym = ttpe.typeSymbol.asClass + val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString) + + c.Expr[List[String]](q"$subclasses") + } + + def knownDirectSubclasses[T]: List[String] = macro impl[T] +} diff --git a/test/files/run/t7046-2/Test_2.scala b/test/files/run/t7046-2/Test_2.scala new file mode 100644 index 0000000000..79407f522f --- /dev/null +++ b/test/files/run/t7046-2/Test_2.scala @@ -0,0 +1,14 @@ +object Test extends App { + def nested: Unit = { + sealed trait Foo + object Foo { + trait Bar extends Foo + trait Baz extends Foo + } + + val subs = Macros.knownDirectSubclasses[Foo] + assert(subs == List("Bar", "Baz")) + } + + nested +} diff --git a/test/files/run/virtpatmat_staging.flags b/test/files/run/virtpatmat_staging.flags index 0a22f7c729..bec3aa96e9 100644 --- a/test/files/run/virtpatmat_staging.flags +++ b/test/files/run/virtpatmat_staging.flags @@ -1,2 +1,2 @@ -Yrangepos:false --Xexperimental +-Yvirtpatmat diff --git a/test/pending/neg/t5729.check b/test/pending/neg/t5729.check new file mode 100644 index 0000000000..10c13db8b6 --- /dev/null +++ b/test/pending/neg/t5729.check @@ -0,0 +1,7 @@ +t5729.scala:5: error: ambiguous reference to overloaded definition, +both method join in object Test of type [S](in: Seq[T[S]])String +and method join in object Test of type (in: Seq[T[_]])Int +match argument types (Seq[T[_]]) + join(null: Seq[T[_]]) + ^ +one error found diff --git a/test/files/pos/t5729.scala b/test/pending/neg/t5729.scala index 9fd9c9ffbb..9fd9c9ffbb 100644 --- a/test/files/pos/t5729.scala +++ b/test/pending/neg/t5729.scala |